PROWAREtech

articles » current » windows » application-programming-interface » remote-shutdown-service

Windows API: Remote Shutdown Service Program and Linux Daemon

An example Windows service program and Linux daemon that allows shutdown or reboot from a remote Windows computer; written in C/C++.

This is a good example of a Windows Service written in C++.

This Windows Service installs itself and uninstalls itself. Run "ShutdownService.exe /help" for instructions.

This software will shutdown or reboot the Windows or Linux computer it is running on. It can be invoked from another Windows computer, remotely. This is useful to shutdown all computers on a network when there is a power failure. APC PowerChute Business Edition is able to execute CMD files that can execute the shutdown commands of this program. The computer that the service is running on should allow a hole through its firewall for this application, "ShutdownService.exe" which uses port 27015.

Download the Windows 32-bit executable (including Linux daemon shutdown server binary) with example usage CMD files and all the source code: SHUTDOWNSERVICE.zip.

Windows Code

Here are all the source files available in the above ZIP file.

#ifndef SHUTDOWNSERVICE_H

#define SHUTDOWNSERVICE_H

#define ALLOC(siz) HeapAlloc(GetProcessHeap(), 0, siz)
#define FREE(mem) HeapFree(GetProcessHeap(), 0, mem)

void printf(const char* s);
void OpenLog();
void WriteLog(const char* szText);
void CloseLog();
void PrintLastErrorToLog(unsigned long lastError, const char* szAddOn);
void WaitForInput();
void PrintLastErrorToScreen(unsigned long lastError);
void Shutdown(DWORD bReboot, DWORD dwReason);

void ExecServerNamedPipe(DWORD* dwCurrentState);
void ExecClientNamedPipe(char* szComputer, DWORD dwReboot, DWORD dwReason);
void ExecServerWinsock(DWORD* dwCurrentState);
void ExecClientWinsock(char* szComputer, DWORD dwReboot, DWORD dwReason);

#endif

This is all the code common to other source modules:

#include <windows.h>

HANDLE hLog;

void printf(const char* s)
{
	unsigned long i;

	for (i = 0; s[i]; i++);
	WriteFile(CreateFileA("CONOUT$", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL), s, i, &i, NULL);
}

void WriteLog(const char* szText)
{
	if (hLog != INVALID_HANDLE_VALUE)
	{
		DWORD dw;
		WriteFile(hLog, szText, strlen(szText), &dw, NULL);
	}
}

void PrintLastErrorToLog(DWORD lastError, const char* szAddOn)
{
	LPSTR lpMsgBuf;
	int i = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpMsgBuf, 0, NULL);
	while (lpMsgBuf[i - 1] == '\r' || lpMsgBuf[i - 1] == '\n')i--;
	lpMsgBuf[i] = 0;
	WriteLog(lpMsgBuf);
	LocalFree(lpMsgBuf);
	if (szAddOn)
		WriteLog(szAddOn);
}

void WaitForInput()
{
	DWORD dw;
	ReadFile(CreateFileA("CONIN$", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL), &dw, 1, &dw, NULL);
}

void PrintLastErrorToScreen(DWORD lastError)
{
	LPSTR lpMsgBuf;
	FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpMsgBuf, 0, NULL);
	printf("Error: ");
	printf(lpMsgBuf);
	LocalFree(lpMsgBuf);
	WaitForInput();
}

void OpenLog()
{
	hLog = CreateFileA("ShutdownServiceLog.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (INVALID_HANDLE_VALUE != hLog)
		SetFilePointer(hLog, 0, NULL, FILE_END);
}

void CloseLog()
{
	if (INVALID_HANDLE_VALUE != hLog)
		CloseHandle(hLog);
}

void Shutdown(DWORD bReboot, DWORD dwReason)
{
	HANDLE hToken;
	TOKEN_PRIVILEGES tkp;

	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
		PrintLastErrorToLog(GetLastError(), " : OpenProcessToken()\r\n");

	LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);

	tkp.PrivilegeCount = 1;
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	// need to adjust privileges to allow user to shutdown
	if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0))
		PrintLastErrorToLog(GetLastError(), " : AdjustTokenPrivileges()\r\n");

	if (!InitiateSystemShutdownExA(NULL, NULL, 0, TRUE, bReboot, dwReason))
		PrintLastErrorToLog(GetLastError(), " : InitiateSystemShutdownExA()\r\n");

	tkp.Privileges[0].Attributes = 0;

	AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
}

This is all the networking resource code (Note: the Winsock code is what is used by the client and server, the named pipes code is deprecated though fully functional):

#undef UNICODE
#define _CRT_SECURE_NO_WARNINGS

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#include "ShutdownService.h"

#pragma comment (lib, "Ws2_32.lib") // this only works with Microsoft VC++, otherwise, include this library in the project

#define DEFAULT_PORT "27015" // this is used by winsock

const char szPipeName[] = "\\\\.\\pipe\\shutdown"; // this is used by named pipes

// NOTE: Named Pipes not compatible with Linux as it is a Windows only technology
void ExecServerNamedPipe(DWORD* dwCurrentState)
{
	HANDLE hPipe;
	DWORD dwTotal, dwRead;
	union
	{
		DWORD dwMessage[3];
		char buffer[sizeof(dwMessage)];
	};
	dwMessage[0] = 0xFFFFFFFF;

	hPipe = CreateNamedPipeA(szPipeName,
		PIPE_ACCESS_INBOUND,
		PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT,
		1,
		sizeof(dwMessage),
		sizeof(dwMessage),
		NMPWAIT_USE_DEFAULT_WAIT,
		NULL);

	if (hPipe == INVALID_HANDLE_VALUE)
		PrintLastErrorToLog(GetLastError(), " : CreateNamedPipeA()\r\n");
	else
	{
		while (SERVICE_RUNNING == *dwCurrentState)
		{
			bool ret = ConnectNamedPipe(hPipe, NULL);
			if (0 == ret)
			{
				DWORD last = GetLastError();
				switch (last)
				{
				case ERROR_PIPE_CONNECTED:
					dwTotal = dwRead = 0;
				case ERROR_IO_PENDING:
					while (dwTotal < sizeof(buffer))
					{
						while (ReadFile(hPipe, &buffer[dwTotal], sizeof(buffer) - dwTotal, &dwRead, NULL) != FALSE)
							dwTotal += dwRead;
					}
					DisconnectNamedPipe(hPipe);
					if (!dwMessage[0]) // first four bytes should be zero
						Shutdown(dwMessage[1], dwMessage[2]);
					break;
				case ERROR_PIPE_LISTENING:
					continue;
				default:
					PrintLastErrorToLog(last, " : CreateNamedPipeA()\r\n");
					break;
				}
			}
		}
	}
}


// NOTE: Named Pipes not compatible with Linux as it is a Windows only technology
void ExecClientNamedPipe(char* szComputer, DWORD dwReboot, DWORD dwReason)
{
	char szPipe[sizeof(szPipeName) + 256];
	union
	{
		DWORD dwMessage[3];
		char buffer[sizeof(dwMessage)];
	};
	dwMessage[0] = 0; // always make first four bytes zero
	dwMessage[1] = dwReboot;
	dwMessage[2] = dwReason;
	strcpy(szPipe, "\\\\");
	strcpy(&szPipe[strlen(szPipe)], szComputer);
	strcpy(&szPipe[strlen(szPipe)], "\\pipe\\shutdown");

	if (!CallNamedPipeA(szPipe, dwMessage, sizeof(dwMessage), NULL, 0, NULL, NMPWAIT_NOWAIT))
		PrintLastErrorToScreen(GetLastError());
	return;

	HANDLE hPipe = CreateFileA(szPipe, FILE_GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
	if (hPipe != INVALID_HANDLE_VALUE)
	{
		DWORD dwTotal = 0, dwWritten;
		while (dwTotal < sizeof(buffer))
		{
			if (!WriteFile(hPipe, &buffer[dwTotal], sizeof(buffer) - dwTotal, &dwWritten, NULL))
				break;
			dwTotal += dwWritten;
		}

		CloseHandle(hPipe);
	}
	else
		PrintLastErrorToScreen(GetLastError());
}

void ExecServerWinsock(DWORD* dwCurrentState)
{
	union
	{
		DWORD dwMessage[3];
		char buffer[sizeof(dwMessage)];
	};

	while (SERVICE_RUNNING == *dwCurrentState)
	{
		dwMessage[0] = 0xFFFFFFFF;

		WSADATA wsaData;
		int iResult;

		SOCKET ListenSocket = INVALID_SOCKET;
		SOCKET ClientSocket = INVALID_SOCKET;

		struct addrinfo* result = NULL;
		struct addrinfo hints;


		// Initialize Winsock
		iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
		if (iResult != 0)
		{
			return;
		}

		ZeroMemory(&hints, sizeof(hints));
		hints.ai_family = AF_INET;
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_protocol = IPPROTO_TCP;
		hints.ai_flags = AI_PASSIVE;

		// Resolve the server address and port
		iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
		if (iResult != 0)
		{
			WSACleanup();
			return;
		}

		// Create a SOCKET for connecting to server
		ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
		if (ListenSocket == INVALID_SOCKET)
		{
			freeaddrinfo(result);
			WSACleanup();
			return;
		}

		// Setup the TCP listening socket
		iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
		if (iResult == SOCKET_ERROR)
		{
			freeaddrinfo(result);
			closesocket(ListenSocket);
			WSACleanup();
			return;
		}

		freeaddrinfo(result);

		// listen for client
		iResult = listen(ListenSocket, SOMAXCONN);
		if (iResult == SOCKET_ERROR)
		{
			closesocket(ListenSocket);
			WSACleanup();
			return;
		}

		// Accept a client socket
		ClientSocket = accept(ListenSocket, NULL, NULL);
		if (ClientSocket == INVALID_SOCKET)
		{
			closesocket(ListenSocket);
			WSACleanup();
			return;
		}

		// No longer need server socket
		closesocket(ListenSocket);

		unsigned total = 0;

		// Receive until the peer shuts down the connection
		do
		{
			iResult = recv(ClientSocket, &buffer[total], sizeof(buffer) - total, 0);
			if (iResult > 0)
				total += iResult;
			else if (iResult == 0)
			{
				if (!dwMessage[0]) // first four bytes must be zero
					Shutdown(dwMessage[1], dwMessage[2]);
			}
			else
			{
				closesocket(ClientSocket);
				WSACleanup();
				return;
			}

		} while (iResult > 0);

		// shutdown the connection since we're done
		iResult = shutdown(ClientSocket, SD_SEND);
		if (iResult == SOCKET_ERROR)
		{
			closesocket(ClientSocket);
			WSACleanup();
			return;
		}

		// cleanup
		closesocket(ClientSocket);
		WSACleanup();
	}
}

// szComputer can be an IP address
void ExecClientWinsock(char* szComputer, DWORD dwReboot, DWORD dwReason)
{
	union
	{
		DWORD dwMessage[3];
		char buffer[sizeof(dwMessage)];
	};
	dwMessage[0] = 0; // always make first four bytes zero
	dwMessage[1] = dwReboot;
	dwMessage[2] = dwReason;

	WSADATA wsaData;
	SOCKET ConnectSocket = INVALID_SOCKET;
	struct addrinfo* result = NULL, * ptr = NULL, hints;

	int iResult;


	// Initialize Winsock
	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0)
	{
		return;
	}

	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;

	// Resolve the server address and port
	iResult = getaddrinfo(szComputer, DEFAULT_PORT, &hints, &result);
	if (iResult != 0)
	{
		printf("getaddrinfo failed (see step 1)\r\n");
		WSACleanup();
		return;
	}

	// Attempt to connect to an address until one succeeds
	for (ptr = result; ptr != NULL;ptr = ptr->ai_next)
	{

		// Create a SOCKET for connecting to server
		ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
			ptr->ai_protocol);
		if (ConnectSocket == INVALID_SOCKET)
		{
			printf("socket failed (see step 2)\r\n");
			WSACleanup();
			return;
		}

		// Connect to server.
		iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
		if (iResult == SOCKET_ERROR)
		{
			closesocket(ConnectSocket);
			ConnectSocket = INVALID_SOCKET;
			continue;
		}
		break;
	}

	freeaddrinfo(result);

	if (ConnectSocket == INVALID_SOCKET)
	{
		printf("Unable to connect to server!\r\n");
		WSACleanup();
		return;
	}

	// Send an initial buffer
	iResult = send(ConnectSocket, buffer, (int)sizeof(buffer), 0);
	if (iResult == SOCKET_ERROR)
	{
		printf("send failed (see step 3)\r\n");
		closesocket(ConnectSocket);
		WSACleanup();
		return;
	}

	// shutdown the connection since no more data will be sent
	iResult = shutdown(ConnectSocket, SD_SEND);
	if (iResult == SOCKET_ERROR)
	{
		printf("shutdown failed (see step 4)\r\n");
		closesocket(ConnectSocket);
		WSACleanup();
		return;
	}

	// cleanup
	closesocket(ConnectSocket);
	WSACleanup();
}

Here is the source for the main executable / Windows service:

#undef UNICODE
#define _CRT_SECURE_NO_WARNINGS

#include <windows.h>
#include <winsvc.h>

#include "ShutdownService.h"

const char* szServiceName = "ShutdownService";

SERVICE_STATUS		  ServStatus;
SERVICE_STATUS_HANDLE   ServStatusHandle;

void __stdcall ServCtrlHandler(DWORD Opcode)
{
	switch (Opcode)
	{
	case SERVICE_CONTROL_STOP:
		ServStatus.dwWin32ExitCode = 0;
		ServStatus.dwCurrentState = SERVICE_STOP_PENDING;
		ServStatus.dwCheckPoint = 0;
		ServStatus.dwWaitHint = 3000;

		if (!SetServiceStatus(ServStatusHandle, &ServStatus))
			PrintLastErrorToLog(GetLastError(), " : SetServiceStatus()\r\n");
		Sleep(ServStatus.dwWaitHint);
		CloseLog();
		ServStatus.dwWaitHint = 0;
		ServStatus.dwCurrentState = SERVICE_STOPPED;
		break;

	case SERVICE_CONTROL_INTERROGATE:
		break;

	default:
		WriteLog("Unrecognized Control Handler Code\r\n");
	}

	// Send current status. 
	if (!SetServiceStatus(ServStatusHandle, &ServStatus))
		PrintLastErrorToLog(GetLastError(), " : SetServiceStatus()\r\n");
	return;
}

void MakeTime(SYSTEMTIME* st, char* output)
{
	GetLocalTime(st);
	int i = 0;
	_itoa(st->wYear, &output[i], 10);
	while (output[++i]);
	output[i++] = '-';
	if (st->wMonth < 10)
		output[i++] = '0';
	_itoa(st->wMonth, &output[i], 10);
	while (output[++i]);
	output[i++] = '-';
	if (st->wDay < 10)
		output[i++] = '0';
	_itoa(st->wDay, &output[i], 10);
	while (output[++i]);
	output[i++] = ' ';
	if (st->wHour < 10)
		output[i++] = '0';
	_itoa(st->wHour, &output[i], 10);
	while (output[++i]);
	output[i++] = ':';
	if (st->wMinute < 10)
		output[i++] = '0';
	_itoa(st->wMinute, &output[i], 10);
	while (output[++i]);
	output[i++] = ':';
	if (st->wSecond < 10)
		output[i++] = '0';
	_itoa(st->wSecond, &output[i], 10);
}

void __stdcall StartServ(DWORD argc, LPSTR* argv)
{
	SYSTEMTIME st;
	char tim[20];

	ServStatus.dwServiceType = SERVICE_WIN32;
	ServStatus.dwCurrentState = SERVICE_START_PENDING;
	ServStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
	ServStatus.dwWin32ExitCode = 0;
	ServStatus.dwServiceSpecificExitCode = 0;
	ServStatus.dwCheckPoint = 0;
	ServStatus.dwWaitHint = 0;

	ServStatusHandle = RegisterServiceCtrlHandlerA(szServiceName, ServCtrlHandler);

	if (ServStatusHandle == (SERVICE_STATUS_HANDLE)0)
	{
		PrintLastErrorToLog(GetLastError(), " : RegisterServiceCtrlHandler()\r\n");
		goto exit;
	}
	MakeTime(&st, tim);
	WriteLog(tim);
	WriteLog(" service started\r\n");

	// Initialization complete - report running status. 
	ServStatus.dwCurrentState = SERVICE_RUNNING;
	ServStatus.dwCheckPoint = 0;
	ServStatus.dwWaitHint = 0;

	if (!SetServiceStatus(ServStatusHandle, &ServStatus))
		PrintLastErrorToLog(GetLastError(), " : SetServiceStatus()\r\n");

	ExecServerWinsock(&ServStatus.dwCurrentState);
	// ExecServerNamedPipe(&ServStatus.dwCurrentState);

exit:
	return;
}

#define INSTALL_FOUND   1
#define UNINSTALL_FOUND 2
#define HELP_FOUND      4

const char* HELP = "\r\nUSAGE\r\n\r\nInstall Service: ShutdownService.exe /install\r\n\r\nUninstall Service: ShutdownService.exe /uninstall\r\n\r\nShutdown Computer: ShutdownService.exe /shutdown COMPUTER_NAME\r\n\r\nReboot Computer: ShutdownService.exe /reboot COMPUTER_NAME\r\n\r\n";
int main(int argc, char* argv[])
{
	int action;
	int i;

	char* sz = (char*)ALLOC(strlen(argv[0]) + 2);
	strcpy(sz, argv[0]);
	for (i = strlen(sz) - 1; sz[i] != '\\'; i--);
	sz[i] = 0;
	SetCurrentDirectoryA(sz);
	sz[i] = '\\';

	OpenLog();

	SERVICE_TABLE_ENTRYA DispatchTable[] =
	{
		{ (char*)szServiceName, StartServ },
		{ NULL, NULL }
	};

	for (action = i = 0; i < argc; i++)
	{
		if (0 == _stricmp(argv[i], "/test")) // TEST THE SERVER
		{
			DWORD dwState = SERVICE_RUNNING;
			ExecServerWinsock(&dwState);
			// ExecServerNamedPipe(&dwState);
			return 0;
		}
		else if (0 == _stricmp(argv[i], "/shutdown"))
		{
			if (i + 1 < argc)
				ExecClientWinsock(argv[i + 1], FALSE, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_MAINTENANCE);
			return 0;

			bool admin = false;
			for (int j = i; !admin && j < argc; j++)
				admin = 0 == _stricmp(argv[j], "/admin");
			if (i + 1 < argc)
			{
				if (admin)
					ExecClientNamedPipe(argv[i + 1], FALSE, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_MAINTENANCE);
				else
				{
					char* cmd = (char*)ALLOC(strlen(argv[i + 1]) + 25);
					strcpy(cmd, "/shutdown ");
					strcpy(&cmd[strlen(cmd)], argv[i + 1]);
					strcpy(&cmd[strlen(cmd)], " /admin");
					ShellExecuteA(NULL, "runas", argv[0], cmd, NULL, SW_SHOWNORMAL); // MUST RUN AS ADMINISTRATOR TO ACCESS THE NAMED PIPE OF THE SERVICE
					FREE(cmd);
				}
			}
			return 0;
		}
		else if (0 == _stricmp(argv[i], "/reboot"))
		{
			if (i + 1 < argc)
				ExecClientWinsock(argv[i + 1], TRUE, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_MAINTENANCE);
			return 0;

			bool admin = false;
			for (int j = i; !admin && j < argc; j++)
				admin = 0 == _stricmp(argv[j], "/admin");
			if (i + 1 < argc)
			{
				if (admin)
					ExecClientNamedPipe(argv[i + 1], TRUE, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_MAINTENANCE);
				else
				{
					char* cmd = (char*)ALLOC(strlen(argv[i + 1]) + 25);
					strcpy(cmd, "/reboot ");
					strcpy(&cmd[strlen(cmd)], argv[i + 1]);
					strcpy(&cmd[strlen(cmd)], " /admin");
					ShellExecuteA(NULL, "runas", argv[0], cmd, NULL, SW_SHOWNORMAL); // MUST RUN AS ADMINISTRATOR TO ACCESS THE NAMED PIPE OF THE SERVICE
					FREE(cmd);
				}
			}
 			return 0;
		}
		else if (0 == _stricmp(argv[i], "/install"))
			action |= INSTALL_FOUND;
		else if (0 == _stricmp(argv[i], "/uninstall"))
			action |= UNINSTALL_FOUND;
		else if (0 == _stricmp(argv[i], "/help"))
			action |= HELP_FOUND;
		else if (0 == _stricmp(argv[i], "/?"))
			action |= HELP_FOUND;
		else if (0 == _stricmp(argv[i], "help"))
			action |= HELP_FOUND;
		else if (0 == _stricmp(argv[i], "?"))
			action |= HELP_FOUND;
	}

	if (((action & INSTALL_FOUND) || (action & UNINSTALL_FOUND)) && 2 == argc)
		ShellExecuteA(NULL, "runas", argv[0], ((action & INSTALL_FOUND) != 0 ? "/install /admin" : "/uninstall /admin"), NULL, SW_SHOWNORMAL);
	else if ((action & INSTALL_FOUND) && 3 == argc)
	{
		SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
		if (scm != NULL)
		{
			SC_HANDLE serv = CreateServiceA(scm, szServiceName, szServiceName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, argv[0], NULL, NULL, NULL, NULL, NULL);
			if (NULL != serv)
			{
				if (!StartService(serv, 0, NULL))
					PrintLastErrorToScreen(GetLastError());
				else
				{
					printf(szServiceName);
					printf(" is installed and running\r\n\r\n");
					WaitForInput();
				}
				CloseServiceHandle(serv);
			}
			else
				PrintLastErrorToScreen(GetLastError());
			CloseServiceHandle(scm);
		}
		else
			PrintLastErrorToScreen(GetLastError());
	}
	else if ((action & UNINSTALL_FOUND) && 3 == argc)
	{
		SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
		if (scm != NULL)
		{
			SC_HANDLE serv = OpenServiceA(scm, szServiceName, SERVICE_ALL_ACCESS);
			if (NULL != serv)
			{
				SERVICE_STATUS stat;
				if (QueryServiceStatus(serv, &stat))
				{
					if (stat.dwCurrentState != SERVICE_STOPPED)
					{
						if (ControlService(serv, SERVICE_CONTROL_STOP, &stat))
						{
							while (stat.dwCurrentState != SERVICE_STOP_PENDING && stat.dwCurrentState != SERVICE_STOPPED)
							{
								Sleep(1000);
								if (!QueryServiceStatus(serv, &stat))
								{
									PrintLastErrorToLog(GetLastError(), "\r\n");
									break;
								}
							}
							while (stat.dwCurrentState != SERVICE_STOPPED)
							{
								Sleep(1000);
								if (!QueryServiceStatus(serv, &stat))
								{
									PrintLastErrorToLog(GetLastError(), "\r\n");
									break;
								}
							}
						}
					}
				}
				if (!DeleteService(serv))
					PrintLastErrorToScreen(GetLastError());
				else
					printf("Service uninstalled\r\n\r\n");
				CloseServiceHandle(serv);
				WaitForInput();
			}
			else
				PrintLastErrorToScreen(GetLastError());
			CloseServiceHandle(scm);
		}
		else
			PrintLastErrorToScreen(GetLastError());
	}
	else if (action & HELP_FOUND)
	{
		printf(HELP);
		WaitForInput();
	}
	else
	{
		SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
		if (scm != NULL)
		{
			SC_HANDLE serv = OpenServiceA(scm, szServiceName, SERVICE_ALL_ACCESS);
			if (NULL != serv)
			{
				CloseServiceHandle(serv);
				CloseServiceHandle(scm);
				printf("\r\nAttempting to start service.");
				StartServiceCtrlDispatcherA(DispatchTable);
			}
			else
			{
				switch (GetLastError())
				{
				case ERROR_ACCESS_DENIED:
					printf("\r\nAccess denied.");
					break;
				case ERROR_INVALID_NAME:
					printf("\r\nInvalid Service Name.");
					break;
				case ERROR_SERVICE_DOES_NOT_EXIST:
					printf("\r\nService not installed.\r\n");
					printf(HELP);
					break;
				}
			}
			CloseServiceHandle(scm);
		}
		WaitForInput();
	}
	return 0;
}
Linux Daemon Code

This is an example Linux daemon written in C that listens for a shutdown or reboot command from the above Windows service application. This application was designed to allow Windows servers to shutdown Linux servers on a local area network. An example is in the event of a total power loss and several machines must share a single uninterruptible power supply. It could also be used to forcibly shutdown running processes by issuing a shutdown command.

  1. save the ShutdownDaemon.out file to the /usr/bin directory
  2. give the file execute rights with command: sudo chmod 0777 /usr/bin/ShutdownDaemon.out
  3. save the remote_shutdown.service file to /etc/systemd/system/remote_shutdown.service
  4. issue this command: sudo systemctl enable remote_shutdown
  5. issue this command: sudo systemctl daemon-reload
  6. issue this command: sudo systemctl start remote_shutdown
  7. reboot the linux machine and wait for the operating system to reload
  8. to check the the daemon is running, issue this command: sudo systemctl status remote_shutdown
  9. issue this command from a Windows machine using the Windows executable: ShutdownService.exe /reboot IP-OR-COMPUTER-NAME

Alternatively,

  1. save the ShutdownDaemon.out file to the /usr/bin directory
  2. give the file execute rights with command: sudo chmod 0777 /usr/bin/ShutdownDaemon.out
  3. edit cron with command: sudo crontab -e
  4. Add this line: @reboot sudo /usr/bin/ShutdownDaemon.out
  5. reboot the linux machine and wait for the operating system to reload
  6. issue this command from a Windows machine using the Windows executable: ShutdownService.exe /reboot IP-OR-COMPUTER-NAME

The remote_shutdown.service file:

[Unit]
Description=REMOTE_SHUTDOWN_DAEMON
After=syslog.target network.target
Wants=poweroff.target

[Service]
Type=simple
ExecStart=/usr/bin/ShutdownDaemon.out
Restart=on-failure
RestartSec=10s
KillMode=process

[Install]
WantedBy=poweroff.target
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <syslog.h>

#define MAXCONNECTIONS 1

void shutdown(bool reboot);

// NOTE: this daemon function is not used or needed
static void daemon()
{
	pid_t pid;

	// Fork off the parent process
	pid = fork();

	if (pid < 0)
		exit(EXIT_FAILURE);

	// Success; let the parent terminate
	if (pid > 0)
		exit(EXIT_SUCCESS);

	// On success the child process becomes session leader
	if (setsid() < 0)
		exit(EXIT_FAILURE);

	// Catch, ignore and handle signals
	signal(SIGCHLD, SIG_IGN);
	signal(SIGHUP, SIG_IGN);

	// Fork off a second time
	pid = fork();

	// Error occurred
	if (pid < 0)
		exit(EXIT_FAILURE);

	// Success; let the parent terminate
	if (pid > 0)
		exit(EXIT_SUCCESS);

	// Close all open file descriptors
	int x;
	for (x = sysconf(_SC_OPEN_MAX); x >= 0; x--)
	{
		close(x);
	}

	// Open the log file
	openlog("remote-shutdown-daemon", LOG_PID, LOG_DAEMON);
}

int main()
{
	//daemon();

	syslog(LOG_NOTICE, "Remote Shutdown daemon started.");

	int servSock;
	int clntSock;
	struct sockaddr_in servAddr;
	struct sockaddr_in clntAddr;
	const unsigned short servPort = 27015;
	unsigned int clntLen, recvTotal;
	int recvSize;
	union
	{
		unsigned int message[3];
		char buffer[sizeof(message)];
	};

	servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (servSock < 0)
		return EXIT_FAILURE;

	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servAddr.sin_port = htons(servPort);

	if (bind(servSock, (struct sockaddr*)&servAddr, sizeof(servAddr)) < 0)
	{
		close(servSock);
		return EXIT_FAILURE;
	}

	if (listen(servSock, MAXCONNECTIONS) < 0)
	{
		close(servSock);
		return EXIT_FAILURE;
	}

	while (true)
	{
		message[0] = 0xFFFFFFFF;
		clntLen = sizeof(clntAddr);
		clntSock = accept(servSock, (struct sockaddr*)&clntAddr, &clntLen);
		if(clntSock < 0)
		{
			close(servSock);
			return EXIT_FAILURE;
		}
		recvTotal = 0;
		while (recvTotal < sizeof(buffer))
		{
			recvSize = recv(clntSock, &buffer[recvTotal], sizeof(buffer) - recvTotal, 0);
			if (recvSize < 0)
			{
				close(clntSock);
				close(servSock);
				return EXIT_FAILURE;
			}
			recvTotal += recvSize;
		}
		if (!message[0]) // FIRST FOUR BYTES SHOULD BE ZERO
		{
			shutdown(message[1]); // REBOOT IF message[1] != 0
			close(clntSock);
			break;
		}
		close(clntSock);
	}
	close(servSock);

	syslog(LOG_NOTICE, "Remote shutdown daemon terminated.");
	closelog();

	return EXIT_SUCCESS;
}

void shutdown(bool reboot) {
	if (reboot)
		system("reboot");
	else
		system("shutdown -h now");
}

Download the binaries all the source code: SHUTDOWNSERVICE.zip.


This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
CLOSE