PROWAREtech
Windows API: Remote Shutdown Service Program and Linux Daemon
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.
- save the ShutdownDaemon.out file to the /usr/bin directory
- give the file execute rights with command: sudo chmod 0777 /usr/bin/ShutdownDaemon.out
- save the remote_shutdown.service file to /etc/systemd/system/remote_shutdown.service
- issue this command: sudo systemctl enable remote_shutdown
- issue this command: sudo systemctl daemon-reload
- issue this command: sudo systemctl start remote_shutdown
- reboot the linux machine and wait for the operating system to reload
- to check the the daemon is running, issue this command: sudo systemctl status remote_shutdown
- issue this command from a Windows machine using the Windows executable: ShutdownService.exe /reboot IP-OR-COMPUTER-NAME
Alternatively,
- save the ShutdownDaemon.out file to the /usr/bin directory
- give the file execute rights with command: sudo chmod 0777 /usr/bin/ShutdownDaemon.out
- edit cron with command: sudo crontab -e
- Add this line: @reboot sudo /usr/bin/ShutdownDaemon.out
- reboot the linux machine and wait for the operating system to reload
- 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.