
#include <stdlib.h>
#include <windows.h>
#include <glib.h>
#include <process.h>
#include <errno.h>

#define SVC_NAME "spawntest"

static DWORD WINAPI
svc_handler(DWORD dwControl,
            DWORD dwEventType,
            LPVOID lpEventData,
            LPVOID lpContext)
{
   exit(0);
   return NO_ERROR;
}


static void
test_spawn_async(gchar **argv, gboolean reap)
{
  DWORD exitcode;
  GError *err = NULL;
  GPid child;

  argv[1] = __FUNCTION__;
  g_spawn_async(NULL,
		argv,
		NULL,
		G_SPAWN_WIN32_NO_CMD_WINDOW |
		(reap ? 0 : G_SPAWN_DO_NOT_REAP_CHILD),
		NULL,
		NULL,
		&child,
		&err);

  if (err != NULL) {
    gchar *msg = g_strdup_printf("g_spawn_async failed: %s\n", err->message);
    g_error(msg);
  }

  if (!reap) {
    WaitForSingleObject(child, INFINITE);
    if (!GetExitCodeProcess(child, &exitcode))
      g_error("GetExitCodeProcess() failed: %u\n", GetLastError());
  
    CloseHandle(child);
  }
  g_print("%s: success\n", __FUNCTION__);
}


static void
test_spawn_sync_env(gchar **argv)
{
  GError *err = NULL;
  gint exitcode = -1;
  gchar *envp[] = {
    "MESSAGE=Hello World!",
    "FUNCTION=" __FUNCTION__,
    NULL
  };

  argv[1] = __FUNCTION__;
  g_spawn_sync(NULL,
	       argv,
	       envp,
	       G_SPAWN_WIN32_NO_CMD_WINDOW,
	       NULL,
	       NULL,
	       NULL,
	       NULL,
	       &exitcode,
	       &err);

  if (err != NULL) {
    gchar *msg = g_strdup_printf("g_spawn_sync failed: %s\n", err->message);
    g_error(msg);
  }

  g_assert(exitcode == 0);
  g_print("%s: success\n", __FUNCTION__);
}


static void
test_spawn_sync_streams(gchar **argv)
{
  GError *err = NULL;
  gchar *serr = NULL;
  gchar *sout = NULL;
  gint exitcode = -1;

  argv[1] = __FUNCTION__;
  g_spawn_sync(NULL,
	       argv,
	       NULL,
	       G_SPAWN_WIN32_NO_CMD_WINDOW,
	       NULL,
	       NULL,
	       &sout,
	       &serr,
	       &exitcode,
	       &err);

  if (err != NULL) {
    gchar *msg = g_strdup_printf("g_spawn_sync failed: %s\n", err->message);
    g_error(msg);
  }

  g_assert(exitcode == 0);
  if (sout)
    g_print("proc stdout: %s\n", sout);
  if (serr)
    g_print("proc stderr: %s\n", serr);

  g_free(sout);
  g_free(serr);

  g_print("%s: success\n", __FUNCTION__);
}

static void
test_spawn_sync(gchar **argv)
{
  GError *err = NULL;
  gint exitcode = -1;

  argv[1] = __FUNCTION__;
  g_spawn_sync(NULL,
	       argv,
	       NULL,
	       G_SPAWN_WIN32_NO_CMD_WINDOW |
	       G_SPAWN_STDOUT_TO_DEV_NULL |
	       G_SPAWN_STDERR_TO_DEV_NULL,
	       NULL,
	       NULL,
	       NULL,
	       NULL,
	       &exitcode,
	       &err);

  if (err != NULL) {
    gchar *msg = g_strdup_printf("g_spawn_sync failed: %s\n", err->message);
    g_error(msg);
  }

  g_assert(exitcode == 0);
  g_print("%s: success\n", __FUNCTION__);
}


static void
run(void)
{
   gchar *argv[3];
   argv[0] = "c:\\temp\\test.bat";
   argv[2] = NULL;

   test_spawn_sync(argv);
   test_spawn_sync_streams(argv);
   test_spawn_sync_env(argv);
   test_spawn_async(argv, TRUE);
   test_spawn_async(argv, FALSE);
}


static VOID WINAPI
run_service(DWORD dwArgc,
	    LPTSTR *lpszArgv)
{
   SERVICE_STATUS_HANDLE hstatus;
   SERVICE_STATUS status;

   memset(&status, 0, sizeof status);
   hstatus = RegisterServiceCtrlHandlerEx(SVC_NAME, svc_handler, NULL);

   status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
   status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
   status.dwCurrentState = SERVICE_RUNNING;
   SetServiceStatus(hstatus, &status);

   OutputDebugString("service is up, running test\n");
   run();
}


int
main(int argc,
     char *argv[])
{

   if (argc == 2) {
      if (strcmp(argv[1], "-c") == 0) {
         run();
      } else if (strcmp(argv[1], "-i") == 0) {
         SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
         SC_HANDLE hService = OpenService(hSCM, SVC_NAME, SC_MANAGER_ALL_ACCESS);
         if (!hService) {
            char svcPath[MAX_PATH];
            GetModuleFileName(NULL, svcPath, sizeof svcPath);
            hService = CreateService(hSCM,
                                     SVC_NAME,
                                     "gspawn win32 test",
                                     SERVICE_ALL_ACCESS,
                                     SERVICE_WIN32_OWN_PROCESS |
                                     SERVICE_INTERACTIVE_PROCESS,
                                     SERVICE_AUTO_START,
                                     SERVICE_ERROR_NORMAL,
                                     svcPath,
                                     NULL,
                                     NULL,
                                     NULL,
                                     NULL,
                                     NULL);
            if (!hService) {
               g_error("unable to create service: %d\n", GetLastError());
            }
         }
         CloseServiceHandle(hService);
         CloseServiceHandle(hSCM);
      } else if (strcmp(argv[1], "-u") == 0) {
         SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
         SC_HANDLE hService = OpenService(hSCM, SVC_NAME, SC_MANAGER_ALL_ACCESS);
         if (hService && !DeleteService(hService)) {
            g_warning("failed to delete service\n");
         }
         CloseServiceHandle(hService);
         CloseServiceHandle(hSCM);
      } else {
         g_print("invalid argument: %s\n", argv[1]);
         return 1;
      }
   } else if (argc == 1) {
      SERVICE_TABLE_ENTRY st[] = {
         {SVC_NAME, run_service},
         {NULL, NULL}
      };

      FreeConsole();
      if (!StartServiceCtrlDispatcher(st)) {
         g_error("Failed to start service: %u\n", GetLastError());
      }
   } else {
      g_print("too many arguments");
      return 1;
   }

   return 0;
}

