/*****************************************************************************
* Copyright (c) 1990-99 Digi International Inc., All Rights Reserved
*
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
*
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/

static const char winstart_c_rcsid[] = "@(#) " "$Id: winstart.c,v 1.2 2000/04/19 22:47:47 chrism Exp $";

#include <stdio.h>

#include "pamsocket.h"
#include "winstart.h"
#include "dbgprintf.h"


static SERVICE_STATUS_HANDLE status_handle = 0;
static SERVICE_STATUS service_status = {0};
static HANDLE stop_event = NULL;

static void WINAPI ctrl_handler(DWORD opcode)
{
	BOOL stopping = FALSE;

	trcprintf("[ PAMA ] ctrl_handler(%ld)\n", opcode);
	switch(opcode)
	{
		case SERVICE_CONTROL_STOP:
			trcprintf("[ PAMA ] changing status to stop pending\n");
			service_status.dwWin32ExitCode = 0;
			service_status.dwCurrentState = SERVICE_STOP_PENDING;
			service_status.dwCheckPoint = 0;
			service_status.dwWaitHint = 0;
			stopping = TRUE;
			break;

		case SERVICE_CONTROL_INTERROGATE:
			// Just return current status
			break;

		case SERVICE_CONTROL_SHUTDOWN:
		case SERVICE_CONTROL_PAUSE:
		case SERVICE_CONTROL_CONTINUE:
		default:
			trcprintf("[ PAMA ] unrecognized opcode: %ld\n", opcode);
			break;
	}

	// Send current status
	if (!SetServiceStatus(status_handle, &service_status))
		errprintf("[ PAMA ] SetServiceStatus() failed %d\n", GetLastError());

	if (stopping && (stop_event != NULL))
			SetEvent(stop_event);		// tell worker thread to stop

	return;
}

static DWORD WINAPI WorkerThread(LPVOID lpv)
{
	unsigned short our_port = (unsigned short) lpv;
	work(our_port);
	return 0;
}

static unsigned short imagepath_port = 0;

static void WINAPI service_start(DWORD argc, LPTSTR *argv)
{
	unsigned short our_port;
	WSADATA wsa_data = {0};

	DWORD worker_thread_id = 0;
	HANDLE worker_thread_handle = NULL;

	trcprintf("[ PAMA ] service_start()\n");

#if defined(_DEBUG)
	{
		DWORD i;
		for (i = 0; i < argc; i++)
			trcprintf("  argv[%d] = %s\n", i, argv[i]);
	}
#endif

	trcprintf("[ PAMA ] Setting status to start pending\n");
	service_status.dwServiceType					= SERVICE_WIN32_OWN_PROCESS;
	service_status.dwCurrentState					= SERVICE_START_PENDING;
	service_status.dwControlsAccepted			= SERVICE_ACCEPT_STOP;
	service_status.dwWin32ExitCode				= 0;
	service_status.dwServiceSpecificExitCode	= 0;
	service_status.dwCheckPoint					= 0;
	service_status.dwWaitHint						= 0;

	trcprintf("[ PAMA ] Register ctrl_hanlder()\n");
	status_handle = RegisterServiceCtrlHandler("DigiPamA", ctrl_handler);
	if (status_handle == 0)
	{
		errprintf("[ PAMA ] RegisterServiceCtrlHandler() failed %d\n", GetLastError());
		goto cleanup;
	}

	trcprintf("[ PAMA ] Initializing\n");

	if (argc == 2)
	{
		our_port = htons((unsigned short)(atoi(argv[1])));
		trcprintf("[ PAMA ] Port number %d was specified in our startup parameters\n", our_port);
	}
	else
	{
		if (imagepath_port > 0)
		{
			our_port = htons(imagepath_port);
			trcprintf("[ PAMA ] Port number %d was specified by our ImagePath\n", our_port);
		}
		else
		{
			our_port = htons(PAM_PORT);
			trcprintf("[ PAMA ] Using default port number %d\n", our_port);
		}
	}

	if (WSAStartup(MAKEWORD(1, 1), &wsa_data) != 0)
	{
		errprintf("[ PAMA ] WSAStartup call failed %d\n", errno);
		goto done;
	}

	stop_event = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (stop_event == NULL)
	{
		errprintf("[ PAMA ] CreateEvent() failed %d\n", GetLastError());
		goto done;
	}

	worker_thread_handle = CreateThread(NULL, 0, WorkerThread, (void *)our_port, CREATE_SUSPENDED, &worker_thread_id);
	if (worker_thread_handle == NULL)
	{
		errprintf("[ PAMA ] CreateThread() failed %d\n", GetLastError());
		goto done;
	}

	trcprintf("[ PAMA ] Initialization complete - report running status\n");
	service_status.dwCurrentState	= SERVICE_RUNNING;
	service_status.dwCheckPoint	= 0;
	service_status.dwWaitHint		= 0;

	if (!SetServiceStatus(status_handle, &service_status))
		errprintf("[ PAMA ] SetServiceStatus() failed %d\n", GetLastError());

	// actually wake up the worker thread
	ResumeThread(worker_thread_handle);

	// now wait for something to happen
	{
		enum { STOP_EVENT_SIGNALED, WORKER_THREAD_EXITED, EVENT_COUNT };
		HANDLE events_to_wait_on[EVENT_COUNT];
		events_to_wait_on[STOP_EVENT_SIGNALED] = stop_event;
		events_to_wait_on[WORKER_THREAD_EXITED] = worker_thread_handle;
		{
			DWORD wait_ret = WaitForMultipleObjects(EVENT_COUNT, events_to_wait_on, FALSE, INFINITE);
			switch (wait_ret)
			{
				case WAIT_OBJECT_0 + STOP_EVENT_SIGNALED:
					trcprintf("[ PAMA ] Got signaled to stop\n");
					TerminateThread(worker_thread_handle, 0);
					break;

				case WAIT_OBJECT_0 + WORKER_THREAD_EXITED:
				default:
					trcprintf("[ PAMA ] Not really sure why we're stopping, but we are\n");
					break;
			}
		}
	}

done:
	trcprintf("[ PAMA ] Done - report we've stopped\n");
	service_status.dwCurrentState		= SERVICE_STOPPED;
	service_status.dwCheckPoint		= 0;
	service_status.dwWaitHint			= 0;

	if (!SetServiceStatus(status_handle, &service_status))
		errprintf("[ PAMA ] SetServiceStatus() failed %d\n", GetLastError());

cleanup:
	trcprintf("[ PAMA ] Cleaning up\n");

	if (worker_thread_handle != NULL)
	{
		CloseHandle(worker_thread_handle);
		worker_thread_handle = NULL;
	}

	if (stop_event != NULL)
	{
		CloseHandle(stop_event);
		stop_event = NULL;
	}

	trcprintf("[ PAMA ] Bye\n");
	return;
}

void start(int argc, char *argv[])
{
	SERVICE_TABLE_ENTRY dispatch_table[] =
	{
		{ "DigiPamA",	service_start	},
		{ NULL,			NULL				}
	};

	trcprintf("[ PAMA ] start()\n");

#if defined(_DEBUG)
	{
		int i;
		for (i = 0; i < argc; i++)
			trcprintf("  argv[%d] = %s\n", i, argv[i]);
	}
#endif

	imagepath_port = (argc == 2) ? (unsigned short)(atoi(argv[1])) : 0;
	trcprintf("[ PAMA ] ImagePath port number = %d\n", imagepath_port);

	if (!StartServiceCtrlDispatcher(dispatch_table))
		errprintf("[ PAMA ] StartServiceCtrlDispatcher() failed %d\n", GetLastError());
}
