/* Copyright (c) 2002-2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file rshdown.c The rshdown program (Linux/Unix) or service (Windows). */ #include #ifdef ON_WINDOWS_SYSTEM #undef ON_WINDOWS_SYSTEM #endif #if defined(_WIN32) || defined(WIN32) #define ON_WINDOWS_SYSTEM 1 #else #if defined(_WIN64) || defined(WIN64) #define ON_WINDOWS_SYSTEM 1 #endif #endif #if ON_WINDOWS_SYSTEM /* ========== Start of Windows code */ #include "rshdown.h" #include #include #include #include #include #include #include #include "rshdownm.h" #line 80 "rshdown.ctr" /** Typedef necessary for sizeof() operator. */ typedef struct sockaddr_in SOIN; /** Size of Internet socket address. */ #define SZ_SOIN (sizeof(SOIN)) /** Service name. */ static WCHAR service_name[] = { L"RshDown" }; /** Text constants used by program. */ static WCHAR *n[] = { /* 0 */ L"sender", /* sender of the packet */ /* 1 */ L"file", /* file to compare data packet */ /* 2 */ L"port", /* local port to listen */ /* 3 */ L"Software\\RshDown", /* registry key */ /* 4 */ L"SeShutdownPrivilege", /* shutdown privilege / 5 */ L"Power supply failure!", /* message for shutdown */ NULL }; /** Service status. */ SERVICE_STATUS st; /** Service status handle. */ SERVICE_STATUS_HANDLE hSt; /** Event: Service must stop. */ static volatile HANDLE hEventMustStop = NULL; /** Event: Service did stop. */ static volatile HANDLE hEventDidStop = NULL; /** Buffer for datagram from file. */ static char filebuffer[RSHDOWN_DATA_BUFFER_SIZE]; /** Buffer for datagram from network. */ static char netbuffer[RSHDOWN_DATA_BUFFER_SIZE]; /** Number of bytes used in file buffer. */ static size_t u_filebuffer = 0; /** Log one syslog event. @param t Message type. @param e Event ID. @param s1 Additional string 1. @param s2 Additional string 2 */ static void log_event(WORD t, DWORD e, WCHAR *s1, WCHAR *s2) { HANDLE hEventLog; WCHAR *msgs[16]; int i; hEventLog = RegisterEventSource(NULL, service_name); if(hEventLog != NULL) { for(i = 0; i < 16; i++) msgs[i] = NULL; i = 0; if(s1) { msgs[0] = s1; i = 1; if(s2) { msgs[1] = s2; i = 2; } } ReportEvent( hEventLog, /* hEventLog */ t, /* wType */ CAT_RSHDOWN, /* wCategoy */ e, /* dwEventID */ NULL, /* lpUserSid */ (WORD)i, /* wNumStrings */ (DWORD)0, /* dwDataSize */ ((i > 0) ? msgs : NULL), /* lpStrings */ NULL /* lpRawData */ ); DeregisterEventSource(hEventLog); } } /** Callback function for service events. @param ctrl_code Event code. */ VOID CALLBACK service_ctrl_handler(IN DWORD ctrl_code) { int i; DWORD res; int success = 0; st.dwWaitHint = 0; st.dwCheckPoint = 0; switch(ctrl_code) { case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: { st.dwCurrentState = SERVICE_STOP_PENDING; st.dwWaitHint = RSHDOWN_STOP_WAIT_HINT; if((hEventMustStop != NULL) && (hEventDidStop != NULL)) { SetEvent(hEventMustStop); for(i = 0; i < RSHDOWN_MAX_ATTEMPTS_TO_STOP; i++) { res = WaitForSingleObjectEx(hEventDidStop, RSHDOWN_STOP_SLEEP_TIME, FALSE); switch(res) { case WAIT_OBJECT_0: { i = RSHDOWN_MAX_ATTEMPTS_TO_STOP; success = 1; } break; default: { SetServiceStatus(hSt, &st); st.dwCheckPoint++; } break; } } if(!success) { log_event(EVENTLOG_ERROR_TYPE,MSG_BG_THREAD_NOT_RESPONDING,NULL,NULL); } } else { log_event(EVENTLOG_ERROR_TYPE,MSG_ALREADY_STOPPED,NULL,NULL); } st.dwCurrentState = SERVICE_STOPPED; st.dwWaitHint = 0; CloseHandle(hEventMustStop); hEventMustStop = 0; CloseHandle(hEventDidStop); hEventDidStop = 0; } break; } SetServiceStatus(hSt, &st); } /** Initialize Internet socket address. @param s Internet socket address. */ static void initializeSOIN(SOIN *s) { memset((void *)s, 0, SZ_SOIN); s->sin_family = AF_INET; s->sin_addr.s_addr = htonl(INADDR_ANY); s->sin_port = htons(0); } /** Get string from registry. @param b Result buffer. @param sz Size of \a b in WCHAR. @param hk Registry key. @param k Entry name (key). @return TRUE on success, FALSE on error. */ static BOOL regWstring(WCHAR *b, size_t /* WCHAR */ sz, HKEY hk, WCHAR *k) { BOOL back = FALSE; LONG res; DWORD dwType; DWORD dwSz; dwType = REG_SZ; dwSz = sz * sizeof(WCHAR); res = RegQueryValueEx(hk, k, NULL, &dwType, (LPBYTE)b, &dwSz); if(res == ERROR_SUCCESS) { if(dwType == REG_SZ) { if(dwSz > 0) { back = TRUE; dwSz = dwSz / sizeof(WCHAR); b[(dwSz < sz) ? dwSz : (sz - 1)] = L'\0'; } } } return back; } /** Convert dotted decimal notation to IP address. @param hn Host name (IP address in dotted decimal notation). @return IP4 address in _host_ byte order on success, 0UL on error. */ static unsigned long dotted_string_to_ip (char *hn) { unsigned long back = 0UL; unsigned long u1, u2, u3, u4, u; int ende, state; char *ptr; if(hn) { state = 0; u = u1 = u2 = u3 = u4 = 0UL; ptr = hn; ende = 0; while(!ende) { if(*ptr) { if(isdigit(*ptr)) { u = 0UL; switch(*ptr) { case '0': u = 0UL; break; case '1': u = 1UL; break; case '2': u = 2UL; break; case '3': u = 3UL; break; case '4': u = 4UL; break; case '5': u = 5UL; break; case '6': u = 6UL; break; case '7': u = 7UL; break; case '8': u = 8UL; break; case '9': u = 9UL; break; } switch(state) { case 0: u1 = 10UL * u1 + u; break; case 1: u2 = 10UL * u2 + u; break; case 2: u3 = 10UL * u3 + u; break; case 3: u4 = 10UL * u4 + u; break; } } else { if(*ptr == '.') { state++; if(state >= 4) { ende = 1; } } } ptr++; } else { ende = 1; } } } u1 = u1 << 24; u1 = u1 & 0xFF000000UL; u2 = u2 << 16; u2 = u2 & 0x00FF0000UL; u3 = u3 << 8; u3 = u3 & 0x0000FF00UL; u4 = u4 & 0x000000FFUL; back = u1 | u2 | u3 | u4; return back; } /** Set IP address in socket address. @param sin Internet socket address. @param str String containing host name or IP address. @return 1 on success, 0 on error. */ static int set_ip_address(struct sockaddr_in *sin, char *str) { int back = 0; sin->sin_addr.s_addr = htonl(dotted_string_to_ip(str)); if(sin->sin_addr.s_addr) { back = 1; } else { struct hostent *hp; unsigned long *lp; hp = gethostbyname(str); if(hp) { if(hp->h_addr_list) { if(hp->h_length) { lp = (unsigned long*)(*(hp->h_addr_list)); if(lp) { sin->sin_addr.s_addr = *lp; back = 1; } } } } } return back; } /** Set remote address. @param rw Remote socket address or address:port text. @param s Host name of remote host. @return TRUE on success, FALSE on error. */ static BOOL setRemote(SOIN *rw, WCHAR *s) { BOOL back = FALSE; WCHAR *ptr; char *cptr; unsigned u; unsigned short us; ptr = wcsrchr(s, L':'); if(ptr) { *(ptr++) = L'\0'; if(swscanf(ptr, L"%u", &u) == 1) { us = (unsigned short)u; rw->sin_port = htons(us); if(wcslen(s) < sizeof(netbuffer)) { ptr = s; cptr = netbuffer; while(*ptr) { *(cptr++) = (char)(*(ptr++)); } *cptr = '\0'; if(set_ip_address(rw, netbuffer)) { back = TRUE; } else { log_event(EVENTLOG_ERROR_TYPE,MSG_HOST_NOT_FOUND,s,NULL); } } else { log_event(EVENTLOG_ERROR_TYPE,MSG_HOST_NAME_TOO_LONG,s,NULL); } } else { log_event(EVENTLOG_ERROR_TYPE,MSG_NOT_A_NUMBER,ptr,NULL); } } else { log_event(EVENTLOG_ERROR_TYPE,MSG_NOT_HOST_AND_PORT,s,NULL); } return back; } /** Get configuration from registry entries. @param rw Remote address. @param rf Socket address. @param la Local address. @return TRUE on success, FALSE on error. */ static BOOL setupFromRegistry(SOIN *rw, SOIN *rf, SOIN *la) { BOOL back = FALSE; HKEY hk; DWORD dwDisp; LONG res; unsigned u; WCHAR rb[RSHDOWN_BUFFER_SIZE]; FILE *fipo; initializeSOIN(rw); initializeSOIN(rf); initializeSOIN(la); res = RegCreateKeyEx( HKEY_LOCAL_MACHINE, n[3], 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hk, &dwDisp ); if(res == ERROR_SUCCESS) { if(regWstring(rb, SIZEOF(rb,WCHAR), hk, n[0])) { if(setRemote(rw, rb)) { if(regWstring(rb, SIZEOF(rb,WCHAR), hk, n[2])) { if(swscanf(rb, L"%u", &u) == 1) { la->sin_port = htons((unsigned short)u); if(regWstring(rb, SIZEOF(rb,WCHAR), hk, n[1])) { fipo = _wfopen(rb, L"rb"); if(fipo) { u_filebuffer = fread(filebuffer, 1, sizeof(filebuffer), fipo); if(u_filebuffer > 0) { back = TRUE; } else { log_event(EVENTLOG_ERROR_TYPE,MSG_FILE_EMPTY,rb,NULL); } fclose(fipo); } else { log_event(EVENTLOG_ERROR_TYPE,MSG_FOPEN_FAILED,rb,NULL); } } else { log_event(EVENTLOG_ERROR_TYPE,MSG_FILE_NOT_SPECIFIED,NULL,NULL); } } else { log_event(EVENTLOG_ERROR_TYPE,MSG_NOT_A_NUMBER,rb,NULL); } } else { log_event(EVENTLOG_ERROR_TYPE,MSG_NO_LOCAL_PORT,NULL,NULL); } } else { } } else { log_event(EVENTLOG_ERROR_TYPE,MSG_NO_SENDER,NULL,NULL); } RegCloseKey(hk); } else { log_event(EVENTLOG_ERROR_TYPE,MSG_REGKEY_FAILED,NULL,NULL); } return back; } /** Service function, run in a separated thread. @param dataforthread Data for the thread. */ static void __cdecl thread_function(void *dataforthread) { unsigned char can_continue = 0x01; /* flag, can continue */ unsigned char must_shutdown = 0x00; /* flag whether or not to shut down */ WORD version_requested; WSADATA wsa_data; struct sockaddr_in rw; /* remote wanted */ struct sockaddr_in rf; /* remote found */ struct sockaddr_in la; /* local address */ int chars_in_netbuffer; SOCKET sock; FD_SET rfds, wfds, efds; struct timeval to; int lgt; int err; LONG res; HANDLE current_proc; /* the current process */ HANDLE proc_token; /* process token */ TOKEN_PRIVILEGES ns; /* new state of token privileges */ LUID myluid; /* lookup restult for SE_SHUTDOWN_NAME */ #line 525 "rshdown.ctr" version_requested = MAKEWORD(2,2); err = WSAStartup(version_requested, &wsa_data); if(err == 0) { if(setupFromRegistry(&rw, &rf, &la)) { sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock != INVALID_SOCKET) { if(bind(sock, (struct sockaddr *)(&la), SZ_SOIN) == 0) { can_continue = 0x01; log_event(EVENTLOG_SUCCESS, MSG_STARTED, NULL, NULL); while(can_continue) { FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); FD_SET(sock, &rfds); FD_SET(sock, &efds); to.tv_sec = 2; to.tv_usec = 0; err = select((sock + 1), &rfds, &wfds, &efds, &to); if((err != SOCKET_ERROR) && (err > 0)) { if((FD_ISSET(sock, &rfds)) || (FD_ISSET(sock, &efds))) { memcpy((void *)(&rf), (void *)(&rw), SZ_SOIN); lgt = SZ_SOIN; chars_in_netbuffer = recvfrom( sock, netbuffer, sizeof(netbuffer), 0, (struct sockaddr *)(&rf), &lgt ); if(chars_in_netbuffer != SOCKET_ERROR) { if(rf.sin_addr.s_addr = rw.sin_addr.s_addr) { if(rf.sin_port == rw.sin_port) { if(chars_in_netbuffer == u_filebuffer) { if(memcmp( (void *)netbuffer, (void *)filebuffer, u_filebuffer ) == 0) { can_continue = 0x00; must_shutdown = 0x01; } else { } } else { } } else { } } else { } } } } else { } if(can_continue) { res = WaitForSingleObjectEx(hEventMustStop, 0, FALSE); switch(res) { case WAIT_OBJECT_0: { can_continue = 0x00; } break; } } } log_event(EVENTLOG_SUCCESS,MSG_GOING_DOWN,NULL,NULL); } else { log_event(EVENTLOG_ERROR_TYPE,MSG_BIND_FAILED,NULL,NULL); } closesocket(sock); } else { log_event(EVENTLOG_ERROR_TYPE,MSG_SOCKET_FAILED,NULL,NULL); } } WSACleanup(); } else { log_event(EVENTLOG_ERROR_TYPE,MSG_WINSOCK_FAILED,NULL,NULL); } if(must_shutdown) { /* ACTION: Start shutdown */ current_proc = GetCurrentProcess(); if(current_proc != (HANDLE)0) { LookupPrivilegeValue(NULL, n[4], &myluid); if(OpenProcessToken(current_proc, (TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY),&proc_token)) { ns.PrivilegeCount = 1; ns.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; ns.Privileges[0].Luid.HighPart = myluid.HighPart; ns.Privileges[0].Luid.LowPart = myluid.LowPart; if(AdjustTokenPrivileges(proc_token, FALSE, &ns, 0, NULL, NULL)) { } else { /* ERROR: Failed to obtain shutdown privilege */ log_event(EVENTLOG_ERROR_TYPE,MSG_PRIVILEGE_FAILED,NULL,NULL); } CloseHandle(proc_token); } else { /* ERROR: Failed to access current process token */ log_event(EVENTLOG_ERROR_TYPE,MSG_PROCESS_TOKEN,NULL,NULL); } } else { /* ERROR: Failed to obtain handle for current process */ log_event(EVENTLOG_ERROR_TYPE,MSG_PROCESS_HANDLE,NULL,NULL); } if(InitiateSystemShutdownEx( NULL, n[5], 15, TRUE, FALSE, (SHTDN_REASON_MAJOR_POWER | SHTDN_REASON_MINOR_ENVIRONMENT) )) { /* Shutdown initiated successfully */ log_event(EVENTLOG_SUCCESS, MSG_SHUTDOWN_INITIALIZED, NULL, NULL); } else { /* ERROR: Failed to initiate shutdown */ log_event(EVENTLOG_ERROR_TYPE,MSG_SHUTDOWN_FAILED,NULL,NULL); } } #line 637 "rshdown.ctr" SetEvent(hEventDidStop); _endthread(); } /** The service function registers the service control handler and runs the service function in a separate thread. @param argc Number of command line arguments. @param argv Command line arguments array. */ VOID CALLBACK service_function(DWORD argc, LPTSTR *argv) { uintptr_t thread_id; memset((void *)(&st), 0, sizeof(SERVICE_STATUS)); st.dwServiceType = SERVICE_WIN32_OWN_PROCESS; st.dwCurrentState = SERVICE_START_PENDING; st.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP; st.dwWin32ExitCode = 0; st.dwServiceSpecificExitCode = 0; st.dwCheckPoint = 0; st.dwWaitHint = 0; hSt = RegisterServiceCtrlHandler(service_name, service_ctrl_handler); if(hSt != (SERVICE_STATUS_HANDLE)0) { st.dwCurrentState = SERVICE_STOPPED; hEventMustStop = CreateEvent(NULL, TRUE, FALSE, NULL); if(hEventMustStop != NULL) { hEventDidStop = CreateEvent(NULL, TRUE, FALSE, NULL); if(hEventDidStop != NULL) { ResetEvent(hEventMustStop); ResetEvent(hEventDidStop); thread_id = _beginthread(thread_function, 0, NULL); if(thread_id != -1L) { st.dwCurrentState = SERVICE_RUNNING; } else { CloseHandle(hEventDidStop); hEventDidStop = NULL; CloseHandle(hEventMustStop); hEventMustStop = NULL; } } else { CloseHandle(hEventMustStop); hEventMustStop = NULL; } } else { } SetServiceStatus(hSt, &st); } } /** Service dispatch table. */ static SERVICE_TABLE_ENTRY dispatch_table[] = { { service_name, service_function }, { NULL, NULL } }; /** The main() function of the rshdown program (Windows). @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, any other value indicates an error. */ int wmain(int argc, WCHAR *argv[]) { if(StartServiceCtrlDispatcher(dispatch_table) == 0) { fprintf(stderr, "ERROR: This program is a Windows service\n"); fprintf(stderr, "and can not be run on the command line!\n"); fflush(stderr); } exit(0); return 0; } /* vim: set ai sw=2 : */ /* ========== End of Windows code */ #else /* ========== Start of Linux/Unix code */ #include #include #include #include #include #include #include #if DK_HAVE_LIMITS_H #include #else #if DK_HAVE_SYS_PARAM_H #include #endif #endif #if DK_HAVE_STDLIB_H #include #endif #if DK_HAVE_UNISTD_H #include #endif #if DK_HAVE_SIGNAL_H #include #endif #if DK_HAVE_SOCKET_H #include #endif #if DK_HAVE_NETINET_IN_H #include #endif #if DK_HAVE_NETDB_H #include #endif #if DK_HAVE_STRING_H #include #endif #if DK_HAVE_STRINGS_H #include #endif #if DK_HAVE_CTYPE_H #include #endif #if DK_HAVE_SYSLOG_H #include #endif #if DK_HAVE_ERRNO_H #include #endif #include "dktools-version.h" #line 780 "rshdown.ctr" #ifndef MAXPATHLEN #ifdef PATH_MAX /** Maximum file name length. */ #define MAXPATHLEN (PATH_MAX) #else /** Maximum file name length. */ #define MAXPATHLEN 1024 #endif #endif /** Flag: SIGHUP received. */ static #if DK_HAVE_VOLATILE volatile #endif int sighup_received = 0; /** Flag: SIGINT received. */ static #if DK_HAVE_VOLATILE volatile #endif int sigint_received = 0; /** Flag: SIGTERM received. */ static #if DK_HAVE_VOLATILE volatile #endif int sigterm_received = 0; /** Flag: SIGPIPE received. */ static #if DK_HAVE_VOLATILE volatile #endif int sigpipe_received = 0; /** SIGHUP handler. @param signo Signal number (SIGHUP). */ dk_signal_ret_t handler_sighup DK_P1(int, signo) { dksignal_refresh(signo, handler_sighup); sighup_received = 1; } /** SIGINT handler. @param signo Signal number (SIGINT). */ dk_signal_ret_t handler_sigint DK_P1(int, signo) { dksignal_refresh(signo, handler_sigint); sigint_received = 1; } /** SIGTERM handler. @param signo Signal number (SIGTERM). */ dk_signal_ret_t handler_sigterm DK_P1(int, signo) { dksignal_refresh(signo, handler_sigterm); sigterm_received = 1; } /** SIGPIPE handler. @param signo Signal number (SIGPIPE). */ dk_signal_ret_t handler_sigpipe DK_P1(int, signo) { dksignal_refresh(signo, handler_sigpipe); sigpipe_received = 1; } /** Flag set, configuration entries found. */ static int flags; /** Exit status. */ static int exval = 0; /** Program name. */ static char progname[] = { "rshdown" }; /** Syslog facility. */ static int syslog_facility = LOG_DAEMON; /** Version number. */ static char versnumb[] = { VERSNUMB }; /** Current line number in configuration file. */ static unsigned long lineno = 0UL; #ifndef RSHDOWN_BUFFER_SIZE /** Size for datagram buffer. */ #define RSHDOWN_BUFFER_SIZE 512 #endif /** Flag: Local IP address is configured. */ #define FLAG_HAVE_LOCAL_IP 1 /** Flag: Local port number is configured. */ #define FLAG_HAVE_LOCAL_PORT 2 /** Flag: Remote IP address is configured. */ #define FLAG_HAVE_REMOTE_IP 4 /** Flag: Remote port number is configured. */ #define FLAG_HAVE_REMOTE_PORT 8 /** Flag: Have local datagram file name. */ #define FLAG_HAVE_LOCAL_DATA 16 /** Flag: Have shutdown program name. */ #define FLAG_HAVE_SCRIPT 32 /** The required configuration settings. */ #define FLAGS_REQUIRED 31 /** Buffer for datagram from file. */ static char buffer_for_filedata[RSHDOWN_BUFFER_SIZE]; /** Buffer for datagram received from network. */ static char buffer_for_network[RSHDOWN_BUFFER_SIZE]; /** File name of datagram file. */ static char script_name[MAXPATHLEN]; /** Number of bytes used in message buffer. */ static size_t buffer_used; /** Option names in configuration file. */ static char *keywords[] = { "sender-ip", "sender-port", "receiver-ip", "receiver-port", "file", "action", "syslog", NULL }; /** Sender address. */ static struct sockaddr_in sender_addr; /** My own address. */ static struct sockaddr_in this_addr; /** Receiver address. */ static struct sockaddr_in receiver_addr; #line 1014 "rshdown.ctr" #line 1015 "rshdown.ctr" #line 1016 "rshdown.ctr" #line 1017 "rshdown.ctr" /** Convert dotted decimal notation string to IP4 address in _host_ notation. @param hn Host name (IP4 address in dotted decimal notation. @return IP4 address in _host_ byte order on success, 0UL on error. */ static unsigned long dotted_string_to_ip DK_P1(char *, hn) { unsigned long back = 0UL; unsigned long u1 = 0U, u2 = 0U, u3 = 0U, u4 = 0U, u = 0U; int ende, state; char *ptr; if(hn) { state = 0; u = u1 = u2 = u3 = u4 = 0UL; ptr = hn; ende = 0; while(!ende) { if(*ptr) { if(isdigit(*ptr)) { u = 0UL; switch(*ptr) { case '0': u = 0UL; break; case '1': u = 1UL; break; case '2': u = 2UL; break; case '3': u = 3UL; break; case '4': u = 4UL; break; case '5': u = 5UL; break; case '6': u = 6UL; break; case '7': u = 7UL; break; case '8': u = 8UL; break; case '9': u = 9UL; break; } switch(state) { case 0: u1 = 10UL * u1 + u; break; case 1: u2 = 10UL * u2 + u; break; case 2: u3 = 10UL * u3 + u; break; case 3: u4 = 10UL * u4 + u; break; } } else { if(*ptr == '.') { state++; if(state >= 4) { ende = 1; } } } ptr++; } else { ende = 1; } } } u1 = u1 << 24; u1 = u1 & 0xFF000000UL; u2 = u2 << 16; u2 = u2 & 0x00FF0000UL; u3 = u3 << 8; u3 = u3 & 0x0000FF00UL; u4 = u4 & 0x000000FFUL; back = u1 | u2 | u3 | u4; return back; } /** Convert host name string to IP address. @param str Host name as string. @return IP4 address on success, 0UL on error. */ static unsigned long string_to_ip DK_P1(char *,str) { unsigned long back = 0UL; struct hostent *hp; unsigned long *lp; back = dotted_string_to_ip(str); if(back) { back = htonl(back); } else { hp = gethostbyname(str); if(hp) { if(hp->h_addr_list) { if(hp->h_length) { lp = (unsigned long *)(*(hp->h_addr_list)); if(lp) { back = *lp; } else { /* ERROR: Empty host list */ } } else { /* ERROR: Empty address list */ } } else { /* ERROR: Empty address list */ } } else { /* ERROR: Host name not found */ } } return back; } /** Read configuration file. @param file Configuration file, opened for reading. @return 1 on success, 0 on error. */ static int read_config_file DK_P1(FILE *,file) { int back = 0; char buffer[MAXPATHLEN+256], *p1, *p2; int cc, act, i; unsigned long ul; unsigned u; unsigned short us; #line 1134 "rshdown.ctr" cc = back = 1; flags = 0; sender_addr.sin_family = AF_INET; receiver_addr.sin_family= AF_INET; sender_addr.sin_port = 0; receiver_addr.sin_port = 0; sender_addr.sin_addr.s_addr = 0UL; receiver_addr.sin_addr.s_addr = 0UL; while(cc && back) { if(fgets(buffer,sizeof(buffer),file)) { dkstr_delcomm(buffer, '#'); #line 1147 "rshdown.ctr" lineno++; p1 = dkstr_start(buffer, NULL); if(p1) { p2 = dkstr_next(p1, NULL); if(p2) { dkstr_chomp(p2, NULL); act = dkstr_array_index(keywords, p1, 1); switch(act) { case 0: { ul = string_to_ip(p2); if(ul) { sender_addr.sin_addr.s_addr = ul; flags |= FLAG_HAVE_REMOTE_IP; } else { back = 0; fprintf(stderr, "Host \"%s\" not found!\n", p2); fflush(stderr); } } break; case 1: { if(sscanf(p2, "%u", &u) ==1) { us = u; us = htons(us); sender_addr.sin_port = us; flags |= FLAG_HAVE_REMOTE_PORT; } else { back = 0; fprintf(stderr, "Illegal port number \"%s\"!\n", p2); fflush(stderr); } } break; case 2: { ul = string_to_ip(p2); if(ul) { receiver_addr.sin_addr.s_addr = ul; flags |= FLAG_HAVE_LOCAL_IP; } else { back = 0; fprintf(stderr, "Host \"%s\" not found!\n", p2); fflush(stderr); } } break; case 3: { if(sscanf(p2, "%u", &u) ==1) { us = u; us = htons(us); receiver_addr.sin_port = us; flags |= FLAG_HAVE_LOCAL_PORT; } else { back = 0; fprintf(stderr, "Illegal port number \"%s\"!\n", p2); fflush(stderr); } } break; case 4: { FILE *file2; file2 = dksf_fopen(p2, "r"); if(file2) { buffer_used = fread( buffer_for_filedata, 1, (sizeof(buffer_for_filedata)), file2 ); if(buffer_used > 0) { flags |= FLAG_HAVE_LOCAL_DATA; } else { back = 0; fprintf(stderr, "Failed to read data from file \"%s\"!\n", p2); fflush(stderr); } fclose(file2); } else { back = 0; fprintf(stderr, "Failed to open file \"%s\"!\n", p2); fflush(stderr); } } break; case 5: { if(strlen(p2) < sizeof(script_name)) { strcpy(script_name, p2); flags |= FLAG_HAVE_SCRIPT; } else { back = 0; fprintf(stderr, "Script name \"%s\" too long!\n", p2); fflush(stderr); } } break; case 6: { i = dkslsupp_get_facility(p2); if(i) { syslog_facility = i; } else { fprintf(stderr, "Unknown syslog facility \"%s\"!\n", p2); fflush(stderr); } } break; default: { back = 0; fprintf(stderr, "Unknown configuration keyword \"%s\"!\n", p1); fflush(stderr); } break; } } else { } } else { } } else { cc = 0; } } if(back) { if((flags & FLAGS_REQUIRED) != FLAGS_REQUIRED) { back = 0; fprintf(stderr, "Incomplete configuration!\n"); fflush(stderr); } } #line 1269 "rshdown.ctr" return back; } /** Type definition required to use sizeof() operator. */ typedef struct sockaddr_in SAIN; /** Conversion to struct sockaddr *. */ #define SA(x) ((struct sockaddr *)x) /** Size of Internet socket address. */ #define SZSAIN sizeof(SAIN) /** Run the service loop. @return 1 on success, 0 on error. */ static int run_the_service DK_P0() { int back = 0; int sock; int sz; ssize_t bytes_read; int cc, action; action = 0; sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock > -1) { if(bind(sock, SA(&receiver_addr), SZSAIN) == 0) { cc = 1; back = 1; #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_NOTICE, "Service starting. (%s %s)", progname, versnumb); closelog(); #endif while(cc) { if(sighup_received) cc = 0; if(sigint_received) cc = 0; if(sigterm_received) cc = 0; if(sigpipe_received) cc = 0; if(cc) { sz = SZSAIN; DK_MEMCPY(&this_addr,&sender_addr,SZSAIN); bytes_read = recvfrom(sock, buffer_for_network, sizeof(buffer_for_network), 0, SA(&this_addr), &sz); #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_NOTICE, "Received %lu bytes!",(unsigned long)bytes_read); closelog(); #endif if(bytes_read > 0) { if((size_t)bytes_read == buffer_used) { if(sz == SZSAIN) { if(this_addr.sin_family == sender_addr.sin_family) { if(this_addr.sin_addr.s_addr == sender_addr.sin_addr.s_addr) { if(this_addr.sin_port == sender_addr.sin_port) { if(DK_MEMCMP(buffer_for_network,buffer_for_filedata,buffer_used) == 0) { cc = 0; action = 1; } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Illegal data!"); closelog(); #endif } } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Invalid sender address port!"); closelog(); #endif } } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Invalid sender address ip!"); closelog(); #endif } } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Invalid sender address family!"); closelog(); #endif } } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Invalid sender address length!"); closelog(); #endif } } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Invalid data packet size!"); closelog(); #endif } } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Error while receiving data!"); closelog(); #endif } } } #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_NOTICE, "Service going down (%s %s)",progname,versnumb); closelog(); #endif } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Failed to bind local address!"); closelog(); #endif } close(sock); } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Failed to obtain socket (errno=%d)!",errno); closelog(); #endif } if(back) { if(action) { if(flags & FLAG_HAVE_SCRIPT) { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_EMERG, "Running power-fail script!"); syslog(LOG_EMERG, "Script name %s", script_name); closelog(); #endif (void)system(script_name); } else { #if DK_HAVE_SYSLOG_H #endif #ifdef SIGPWR openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_EMERG, "Sending power-fail signal SIGPWR=%d to init process!",SIGPWR); closelog(); kill(1, SIGPWR); #else #ifdef SIGINFO openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_EMERG, "Sending power-fail SIGINFO=%d signal to init process!",SIGINFO); closelog(); kill(1, SIGINFO); #else openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_EMERG, "Sending power-fail signal 9 to init process!"); closelog(); kill(1, 9); #endif #endif } } } return back; } /** Run the daemon. @return 1 on success, 0 on error. */ static int run_daemon DK_P0() { int back = 0; int maxfiles, i; dk_signal_disp_t disp_hup, disp_int, disp_term, disp_pipe; pid_t pid; (void)chdir("/"); umask(077); maxfiles = (int)dksf_get_maxfiles(); for(i = 0; i < maxfiles; i++) (void)close(i); disp_hup = disp_int = disp_term = disp_pipe = NULL; sighup_received = sigint_received = sigterm_received = sigpipe_received = 0; pid = fork(); if(pid == 0) { #if DK_HAVE_SETSID setsid(); #else #if DK_HAVE_SETPGRP setpgrp(); #endif #endif disp_hup = dksignal_set(SIGHUP,handler_sighup); pid = fork(); if(pid == 0) { dksf_write_pid_file(progname, 1); disp_int = dksignal_set(SIGINT,handler_sigint); disp_term = dksignal_set(SIGTERM,handler_sigterm); disp_pipe = dksignal_set(SIGPIPE,handler_sigpipe); back = run_the_service(); if(disp_pipe) { dksignal_set(SIGPIPE,disp_pipe); } if(disp_term) { dksignal_set(SIGTERM,disp_term); } if(disp_int) { dksignal_set(SIGINT,disp_int); } dksf_write_pid_file(progname, 0); } else { if(pid > (pid_t)(-1)) { back = 1; } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Failed to create new process!"); closelog(); #endif } } if(disp_hup) { dksignal_set(SIGHUP,disp_hup); } } else { if(pid > (pid_t)(-1)) { back = 1; } else { #if DK_HAVE_SYSLOG_H openlog(progname, (LOG_CONS | LOG_NDELAY | LOG_PID), syslog_facility); syslog(LOG_ERR, "Failed to create new process!"); closelog(); #endif } } return back; } /** Run with specified configuration file. @param filename Configuration file name. @return 1 on success, 0 on error. */ static int run_for_config_file DK_P1(char *,filename) { int back = 0; FILE *file; file = dksf_fopen(filename, "r"); if(file) { if(read_config_file(file)) { back = run_daemon(); } fclose(file); } else { /* ERROR: Failed to open config file */ fprintf(stderr, "Failed to open configuration file!\n"); fflush(stderr); } return back; } /** Default configuration file name. */ static char default_config_file_name[] = { DK_SYSCONFDIR "/rshdown/rshdown.conf" }; /** The main() function of the rshdown program (Unix/Linux). @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, any other value indicates an error. */ #ifdef DK_HAVE_PROTOTYPES int main(int argc, char *argv[]) #else int main(argc, argv) int argc; char *argv[]; #endif { if(argc > 1) { exval = run_for_config_file(argv[1]); } else { exval = run_for_config_file(default_config_file_name); } exval = (exval ? 0 : 1); exit(exval); return exval; } /* ========== End of Linux/Unix code */ #endif