The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * Service.xs
 */

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "../ppport.h"

#define RETURNRESULT(x)		if ((x)){ XST_mYES(0); }\
                     		else { XST_mNO(0); }\
                     		XSRETURN(1)
#define SETIV(index,value) sv_setiv(ST(index), value)
#define SETPV(index,string) sv_setpv(ST(index), string)

/* constant function for exporting NT definitions. */

static long
constant(char *name)
{
    errno = 0;
    switch (*name) {
    case 'A':
	break;
    case 'B':
	break;
    case 'C':
	break;
    case 'D':
	break;
    case 'E':
	break;
    case 'F':
	break;
    case 'G':
	break;
    case 'H':
	break;
    case 'I':
	break;
    case 'J':
	break;
    case 'K':
	break;
    case 'L':
	break;
    case 'M':
	break;
    case 'N':
	break;
    case 'O':
	break;
    case 'P':
	break;
    case 'Q':
	break;
    case 'R':
	break;
    case 'S':
	if (strEQ(name, "SERVICE_WIN32_OWN_PROCESS"))
#ifdef SERVICE_WIN32_OWN_PROCESS
		return SERVICE_WIN32_OWN_PROCESS;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_WIN32_SHARE_PROCESS"))
#ifdef SERVICE_WIN32_SHARE_PROCESS
		return SERVICE_WIN32_SHARE_PROCESS;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_KERNEL_DRIVER"))
#ifdef SERVICE_KERNEL_DRIVER
		return SERVICE_KERNEL_DRIVER;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_FILE_SYSTEM_DRIVER"))
#ifdef SERVICE_FILE_SYSTEM_DRIVER
		return SERVICE_FILE_SYSTEM_DRIVER;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_INTERACTIVE_PROCESS"))
#ifdef SERVICE_INTERACTIVE_PROCESS
		return SERVICE_INTERACTIVE_PROCESS;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_STOPPED"))
#ifdef SERVICE_STOPPED
		return SERVICE_STOPPED;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_START_PENDING"))
#ifdef SERVICE_START_PENDING
		return SERVICE_START_PENDING;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_STOP_PENDING"))
#ifdef SERVICE_STOP_PENDING
		return SERVICE_STOP_PENDING;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_RUNNING"))
#ifdef SERVICE_RUNNING
		return SERVICE_RUNNING;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_CONTINUE_PENDING"))
#ifdef SERVICE_CONTINUE_PENDING
		return SERVICE_CONTINUE_PENDING;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_PAUSE_PENDING"))
#ifdef SERVICE_PAUSE_PENDING
		return SERVICE_PAUSE_PENDING;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_PAUSED"))
#ifdef SERVICE_PAUSED
		return SERVICE_PAUSED;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_ACCEPT_STOP"))
#ifdef SERVICE_ACCEPT_STOP
		return SERVICE_ACCEPT_STOP;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_ACCEPT_PAUSE_CONTINUE"))
#ifdef SERVICE_ACCEPT_PAUSE_CONTINUE
		return SERVICE_ACCEPT_PAUSE_CONTINUE;
#else
		goto not_there;
#endif	
	if (strEQ(name, "SERVICE_ACCEPT_SHUTDOWN"))
#ifdef SERVICE_ACCEPT_SHUTDOWN
		return SERVICE_ACCEPT_SHUTDOWN;
#else
		goto not_there;
#endif	
	break;
    case 'T':
	break;
    case 'U':
	break;
    case 'V':
	break;
    case 'W':
	break;
    case 'X':
	break;
    case 'Y':
	break;
    case 'Z':
	break;
    }
    errno = EINVAL;
    return 0;

not_there:
    errno = ENOENT;
    return 0;
}

MODULE = Win32::Service		PACKAGE = Win32::Service

PROTOTYPES: DISABLE

long
constant(name)
	char *name
    CODE:
	RETVAL = constant(name);
    OUTPUT:
	RETVAL


bool
StartService(lpHostName, lpServiceName)
    	char *lpHostName
	char *lpServiceName
    CODE:
	{
	    SC_HANDLE hSCManager, hSCService;
	    WCHAR wbuffer[MAX_PATH+1];
	    RETVAL = FALSE;
	    if (lpHostName && *lpHostName == '\0')
		lpHostName = NULL;
	    if (lpServiceName && *lpServiceName != '\0') {
		if(lpHostName && USING_WIDE()) {
		    A2WHELPER(lpHostName, wbuffer, sizeof(wbuffer));
		    hSCManager = OpenSCManagerW(wbuffer, NULL, SC_MANAGER_CONNECT);
		}
		else
		    hSCManager = OpenSCManagerA(lpHostName, NULL, SC_MANAGER_CONNECT);

		if (hSCManager != NULL)	{
		    if(USING_WIDE()) {
			A2WHELPER(lpServiceName, wbuffer, sizeof(wbuffer));
			hSCService = OpenServiceW(hSCManager, wbuffer, SERVICE_START);
		    }
		    else
			hSCService = OpenServiceA(hSCManager, lpServiceName, SERVICE_START);

		    if (hSCService != NULL)  {
			RETVAL = StartService(hSCService, 0, NULL);
			CloseServiceHandle(hSCService);
		    }
		    CloseServiceHandle(hSCManager);
		}
	    }
	}
    OUTPUT:
	RETVAL


bool
StopService(lpHostName, lpServiceName)
    	char *lpHostName
	char *lpServiceName
    CODE:
	{
	    SERVICE_STATUS serviceStatus;
	    WCHAR wbuffer[MAX_PATH+1];
	    SC_HANDLE hSCManager, hSCService;
	    RETVAL = FALSE;
	    if (lpHostName && *lpHostName == '\0')
		lpHostName = NULL;
	    if (lpServiceName && *lpServiceName != '\0') {
		if(lpHostName && USING_WIDE()) {
		    A2WHELPER(lpHostName, wbuffer, sizeof(wbuffer));
		    hSCManager = OpenSCManagerW(wbuffer, NULL, SC_MANAGER_CONNECT);
		}
		else
		    hSCManager = OpenSCManagerA(lpHostName, NULL, SC_MANAGER_CONNECT);

		if (hSCManager != NULL)	{
		    if(USING_WIDE()) {
			A2WHELPER(lpServiceName, wbuffer, sizeof(wbuffer));
			hSCService = OpenServiceW(hSCManager, wbuffer, SERVICE_STOP);
		    }
		    else
			hSCService = OpenServiceA(hSCManager, lpServiceName, SERVICE_STOP);

		    if (hSCService != NULL) {
			RETVAL = ControlService(hSCService, SERVICE_CONTROL_STOP,
						&serviceStatus);
			CloseServiceHandle(hSCService);
		    }
		    CloseServiceHandle(hSCManager);
		}
	    }
	}
    OUTPUT:
	RETVAL

bool
GetStatus(lpHostName,lpServiceName,status)
    	char *lpHostName
	char *lpServiceName
	SV *status
    CODE:
	{
	    SERVICE_STATUS serviceStatus;
	    WCHAR wbuffer[MAX_PATH+1];
	    SC_HANDLE hSCManager, hSCService;

	    RETVAL = FALSE;
	    if (!(status && SvROK(status) &&
		  (status = SvRV(status)) && SvTYPE(status) == SVt_PVHV))
		croak("third arg must be a HASHREF");
	    
	    if (lpHostName && *lpHostName == '\0')
		lpHostName = NULL;
	    if (lpServiceName && *lpServiceName != '\0') {
		if(lpHostName && USING_WIDE()) {
		    A2WHELPER(lpHostName, wbuffer, sizeof(wbuffer));
		    hSCManager = OpenSCManagerW(wbuffer, NULL, SC_MANAGER_CONNECT);
		}
		else
		    hSCManager = OpenSCManagerA(lpHostName, NULL, SC_MANAGER_CONNECT);

		if (hSCManager != NULL)	{
		    if(USING_WIDE()) {
			A2WHELPER(lpServiceName, wbuffer, sizeof(wbuffer));
			hSCService = OpenServiceW(hSCManager, wbuffer, SERVICE_INTERROGATE);
		    }
		    else
			hSCService = OpenServiceA(hSCManager, lpServiceName, SERVICE_INTERROGATE);

		    if (hSCService != NULL) {
			RETVAL = ControlService(hSCService, SERVICE_CONTROL_INTERROGATE,
						&serviceStatus);
			if (!RETVAL && GetLastError() == ERROR_SERVICE_NOT_ACTIVE) {
			    Zero(&serviceStatus, 1, SERVICE_STATUS);
			    serviceStatus.dwCurrentState = SERVICE_STOPPED;
			    RETVAL = TRUE;
			}
			CloseServiceHandle(hSCService);
		    }
		    CloseServiceHandle(hSCManager);
		    if (RETVAL) {
			SV *sv;
			sv = newSViv(serviceStatus.dwServiceType);
			hv_store((HV*)status, "ServiceType", strlen("ServiceType"), sv, 0);
			
			sv = newSViv(serviceStatus.dwCurrentState);
			hv_store((HV*)status, "CurrentState", strlen("CurrentState"), sv, 0);
			
			sv = newSViv(serviceStatus.dwControlsAccepted);
			hv_store((HV*)status, "ControlsAccepted", strlen("ControlsAccepted"), sv, 0);
			
			sv = newSViv(serviceStatus.dwWin32ExitCode);
			hv_store((HV*)status, "Win32ExitCode", strlen("Win32ExitCode"), sv, 0);
			
			sv = newSViv(serviceStatus.dwServiceSpecificExitCode);
			hv_store((HV*)status, "ServiceSpecificExitCode", strlen("ServiceSpecificExitCode"), sv, 0);
			
			sv = newSViv(serviceStatus.dwCheckPoint);
			hv_store((HV*)status, "CheckPoint", strlen("CheckPoint"), sv, 0);
			
			sv = newSViv(serviceStatus.dwWaitHint);
			hv_store((HV*)status, "WaitHint", strlen("WaitHint"), sv, 0);
		    }
		}
	    }
	}
    OUTPUT:
	RETVAL

bool	
PauseService(lpHostName,lpServiceName)
    	char *lpHostName
	char *lpServiceName
    CODE:
	{
	    SERVICE_STATUS serviceStatus;
	    WCHAR wbuffer[MAX_PATH+1];
	    SC_HANDLE hSCManager, hSCService;
	    RETVAL = FALSE;
	    if (lpHostName && *lpHostName == '\0')
		lpHostName = NULL;
	    if (lpServiceName && *lpServiceName != '\0') {
		if(lpHostName && USING_WIDE()) {
		    A2WHELPER(lpHostName, wbuffer, sizeof(wbuffer));
		    hSCManager = OpenSCManagerW(wbuffer, NULL, SC_MANAGER_CONNECT);
		}
		else
		    hSCManager = OpenSCManagerA(lpHostName, NULL, SC_MANAGER_CONNECT);

		if (hSCManager != NULL)	{
		    if(USING_WIDE()) {
			A2WHELPER(lpServiceName, wbuffer, sizeof(wbuffer));
			hSCService = OpenServiceW(hSCManager, wbuffer, SERVICE_PAUSE_CONTINUE);
		    }
		    else
			hSCService = OpenServiceA(hSCManager, lpServiceName, SERVICE_PAUSE_CONTINUE);

		    if (hSCService != NULL) {
			RETVAL = ControlService(hSCService, SERVICE_CONTROL_PAUSE, &serviceStatus);
			CloseServiceHandle(hSCService);
		    }
		    CloseServiceHandle(hSCManager);
		}
	    }
	}
    OUTPUT:
	RETVAL

bool
ResumeService(lpHostName,lpServiceName)
    	char *lpHostName
	char *lpServiceName
    CODE:
	{
	    SERVICE_STATUS serviceStatus;
	    WCHAR wbuffer[MAX_PATH+1];
	    SC_HANDLE hSCManager, hSCService;
	    RETVAL = FALSE;
	    if (lpHostName && *lpHostName == '\0')
		lpHostName = NULL;
	    if (lpServiceName && *lpServiceName != '\0') {
		if(lpHostName && USING_WIDE()) {
		    A2WHELPER(lpHostName, wbuffer, sizeof(wbuffer));
		    hSCManager = OpenSCManagerW(wbuffer, NULL, SC_MANAGER_CONNECT);
		}
		else
		    hSCManager = OpenSCManagerA(lpHostName, NULL, SC_MANAGER_CONNECT);

		if (hSCManager != NULL)	{
		    if(USING_WIDE()) {
			A2WHELPER(lpServiceName, wbuffer, sizeof(wbuffer));
			hSCService = OpenServiceW(hSCManager, wbuffer, SERVICE_PAUSE_CONTINUE);
		    }
		    else
			hSCService = OpenServiceA(hSCManager, lpServiceName, SERVICE_PAUSE_CONTINUE);

		    if (hSCService != NULL) {
			RETVAL = ControlService(hSCService, SERVICE_CONTROL_CONTINUE,
						&serviceStatus);
			CloseServiceHandle(hSCService);
		    }
		    CloseServiceHandle(hSCManager);
		}
	    }
	}
    OUTPUT:
	RETVAL

bool
GetServices(lpHostName, hv)
    	char *lpHostName
	SV *hv
    CODE:
	{
	    DWORD dwBytesNeeded, dwServicesReturned, dwResumeHandle, dwIndex;
	    ENUM_SERVICE_STATUSA essA[1000];
	    ENUM_SERVICE_STATUSW essW[1000];
	    WCHAR wbuffer[MAX_PATH+1];
	    char szService[MAX_PATH+1];
	    char szDisplay[MAX_PATH+1];
	    LPSTR lpDisplayName, lpServiceName;
	    BOOL bUsingWide = USING_WIDE();
	    SC_HANDLE hSCManager;
	    SV *sv;

	    RETVAL = FALSE;
	    if (!(hv && SvROK(hv) &&
		  (hv = SvRV(hv)) && SvTYPE(hv) == SVt_PVHV))
		croak("second argument must be a HASHREF");

	    if (lpHostName && *lpHostName == '\0')
		lpHostName = NULL;

	    if(lpHostName && bUsingWide) {
		A2WHELPER(lpHostName, wbuffer, sizeof(wbuffer));
		hSCManager = OpenSCManagerW(wbuffer, NULL, SC_MANAGER_CONNECT|SC_MANAGER_ENUMERATE_SERVICE);
	    }
	    else
		hSCManager = OpenSCManagerA(lpHostName, NULL, SC_MANAGER_CONNECT|SC_MANAGER_ENUMERATE_SERVICE);

	    if (hSCManager != NULL) {
		dwResumeHandle = 0;
		dwBytesNeeded = 0;
		dwServicesReturned = 0;
		while ((bUsingWide
		    ? EnumServicesStatusW(hSCManager, SERVICE_WIN32,
					 SERVICE_ACTIVE | SERVICE_INACTIVE,
					 essW, sizeof(essW), &dwBytesNeeded,
					 &dwServicesReturned,
					 &dwResumeHandle)
		    : EnumServicesStatusA(hSCManager, SERVICE_WIN32,
					 SERVICE_ACTIVE | SERVICE_INACTIVE,
					 essA, sizeof(essA), &dwBytesNeeded,
					 &dwServicesReturned,
					 &dwResumeHandle)) == TRUE
		       || GetLastError() == ERROR_MORE_DATA)
		{
		    lpServiceName = szService;
		    lpDisplayName = szDisplay;
		    for (dwIndex = 0; dwIndex < dwServicesReturned; ++dwIndex) {
			if (bUsingWide) {
			    W2AHELPER(essW[dwIndex].lpServiceName, szService, sizeof(szService));
			    W2AHELPER(essW[dwIndex].lpDisplayName, szDisplay, sizeof(szDisplay));
			}
			else {
			    lpServiceName = essA[dwIndex].lpServiceName;
			    lpDisplayName = essA[dwIndex].lpDisplayName;
			}

			sv = newSVpv(lpServiceName, 0);
			hv_store((HV*)hv, lpDisplayName,
				 strlen(lpDisplayName), sv, 0);
		    }
		    if (dwResumeHandle == 0) {
			RETVAL = TRUE;
			break;
		    }
		}
		CloseServiceHandle(hSCManager);
	    }
	}
    OUTPUT:
	RETVAL