/* * main.c - syslogd implementation for windows, main function * * Created by Alexander Yaworsky * * THIS SOFTWARE IS NOT COPYRIGHTED * * This source code is offered for use in the public domain. You may * use, modify or distribute it freely. * * This code is distributed in the hope that it will be useful but * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY * DISCLAIMED. This includes but is not limited to warranties of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * */ #include #include #include #include #include #include #include #include HANDLE service_stop_event; struct string *local_hostname; struct string *self_program_name; struct string *space; struct string *line_feed; int verbosity_level = 0; static char *service_name = "syslogd-win32"; static char *service_display_name = "syslogd"; static char *service_stop_event_name = "syslogd-win32-stop"; static OSVERSIONINFO vi; static SERVICE_STATUS_HANDLE hss; static SERVICE_STATUS sstatus; /* options */ char *conf_file_name = NULL; static char *instance_name = NULL; static char *priority = NULL; static char *trace_file_name = NULL; static FILE *trace_fd; /****************************************************************************** * display message */ void display_message( char* file, int line, const char* func, char* fmt, ... ) { va_list args; char formatstr[512]; snprintf( formatstr, sizeof(formatstr), "%08lX:%s:%d:%s: %s", GetCurrentThreadId(), file, line, func, fmt ); va_start( args, fmt ); vfprintf( trace_fd, formatstr, args ); va_end( args ); fflush( trace_fd ); } /****************************************************************************** * service control handler */ static void WINAPI ServiceControlHandler( DWORD Control ) { switch( Control ) { case SERVICE_CONTROL_STOP: sstatus.dwCurrentState = SERVICE_STOP_PENDING; sstatus.dwCheckPoint = 0; SetEvent( service_stop_event ); break; case SERVICE_CONTROL_INTERROGATE: /* return status immediately */ break; } SetServiceStatus( hss, &sstatus ); } /****************************************************************************** * service main */ static void WINAPI winnt_ServiceMain( DWORD Argc, LPTSTR* Argv ) { hss = RegisterServiceCtrlHandler( PACKAGE_NAME, (LPHANDLER_FUNCTION)ServiceControlHandler ); if( !hss ) return; service_stop_event = CreateEvent( NULL, TRUE, FALSE, NULL ); if( !service_stop_event ) return; sstatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; sstatus.dwCurrentState = SERVICE_RUNNING; sstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; sstatus.dwWin32ExitCode = NO_ERROR; sstatus.dwCheckPoint = 0; sstatus.dwWaitHint = 1000; SetServiceStatus( hss, &sstatus ); syslogd_main(); sstatus.dwWin32ExitCode = 0; sstatus.dwServiceSpecificExitCode = 0; sstatus.dwCurrentState = SERVICE_STOPPED; sstatus.dwCheckPoint = 0; SetServiceStatus( hss, &sstatus ); CloseHandle( service_stop_event ); } static int win9x_ServiceMain() { service_stop_event = CreateEvent( NULL, TRUE, FALSE, service_stop_event_name ); if( !service_stop_event ) { ERR( "Cannot create event %s; error %lu\n", service_stop_event_name, GetLastError() ); return 1; } if( ERROR_ALREADY_EXISTS == GetLastError() ) { CloseHandle( service_stop_event ); ERR( "Service is already running\n" ); return 1; } syslogd_main(); CloseHandle( service_stop_event ); return 0; } /****************************************************************************** * open system Run key */ static BOOL open_run_key( PHKEY hk ) { static char runkey[] = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; TRACE_ENTER( "\n" ); if( RegOpenKey( HKEY_LOCAL_MACHINE, runkey, hk ) ) { ERR( "Cannot open registry key %s; error %lu\n", runkey, GetLastError() ); return FALSE; } TRACE_LEAVE( "done\n" ); return TRUE; } /****************************************************************************** * start service */ static BOOL win9x_StartService() { BOOL ret = FALSE; HKEY hk; LONG status; DWORD type, size; char command_line[ MAX_PATH ]; STARTUPINFO si; PROCESS_INFORMATION pi; TRACE_ENTER( "\n" ); if( !open_run_key( &hk ) ) goto done; size = sizeof(command_line); status = RegQueryValueEx( hk, service_name, NULL, &type, command_line, &size ); RegCloseKey( hk ); if( status != ERROR_SUCCESS ) { ERR( "Cannot get registry value; error %lu\n", GetLastError() ); goto done; } if( type != REG_SZ ) { ERR( "Invalid type of registry value\n" ); goto done; } memset( &si, 0, sizeof(si ) ); si.cb = sizeof(si); ret = CreateProcess( NULL, command_line, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi ); if( !ret ) { ERR( "Cannot create process; error %lu\n", GetLastError() ); return FALSE; } CloseHandle( pi.hThread ); CloseHandle( pi.hProcess ); done: TRACE_LEAVE( "done; ret=%d\n", ret ); return ret; } static BOOL winnt_StartService() { SC_HANDLE hscm, hsvc; BOOL ret; TRACE_ENTER( "\n" ); hscm = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); if( !hscm ) { ERR( "Cannot open service control manager; error %lu\n", GetLastError() ); return FALSE; } hsvc = OpenService( hscm, service_name, SERVICE_ALL_ACCESS ); if( !hsvc ) { ERR( "Cannot open service %s; error %lu\n", service_name, GetLastError() ); ret = FALSE; } else { ret = StartService( hsvc, 0, NULL ); if( !ret ) ERR( "Cannot start service; error %lu\n", GetLastError() ); CloseServiceHandle( hsvc ); } CloseServiceHandle( hscm ); TRACE_LEAVE( "done\n" ); return ret; } static BOOL start_service() { BOOL ret; TRACE_ENTER( "\n" ); if( VER_PLATFORM_WIN32_NT == vi.dwPlatformId ) ret = winnt_StartService(); else ret = win9x_StartService(); TRACE_LEAVE( "done; ret=%d\n", ret ); return ret; } /****************************************************************************** * shutdown_service */ static BOOL winnt_shutdown_service() { BOOL ret = FALSE; SC_HANDLE hscm, hsvc; int i; hscm = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); if( !hscm ) { TRACE( "OpenSCManager error %lu\n", GetLastError() ); return FALSE; } hsvc = OpenService( hscm, service_name, SERVICE_ALL_ACCESS ); if( !hsvc ) { TRACE( "OpenService error %lu\n", GetLastError() ); CloseServiceHandle( hscm ); return FALSE; } ControlService( hsvc, SERVICE_CONTROL_STOP, &sstatus ); for( i = 0; i < 10; i++ ) { Sleep( 100 ); if( !QueryServiceStatus( hsvc, &sstatus ) ) { TRACE( "Cannot query service status; error %lu\n", GetLastError() ); break; } if( SERVICE_STOPPED == sstatus.dwCurrentState ) { ret = TRUE; break; } } CloseServiceHandle( hsvc ); return ret; } static void shutdown_service( gboolean quiet ) { BOOL stopped; /* try to stop windows NT service */ stopped = winnt_shutdown_service(); /* try to shutdown using stop_event because daemon may run not as service; set event and wait for completion */ for(;;) { BOOL ret; HANDLE he = OpenEvent( EVENT_MODIFY_STATE, FALSE, service_stop_event_name ); if( !he ) { if( (!quiet) && (!stopped) ) ERR( "cannot open event; error %lu\n", GetLastError() ); return; } TRACE( "setting stop event\n" ); ret = SetEvent( he ); CloseHandle( he ); if( !ret ) { if( !quiet ) { ERR( "cannot set event; error %lu\n", GetLastError() ); return; } } quiet = TRUE; Sleep(0); } } /****************************************************************************** * service installer */ static BOOL win9x_InstallService( char* command_line ) { BOOL ret; HKEY hk; TRACE_ENTER( "command_line=%s\n", command_line ); ret = open_run_key( &hk ); if( !ret ) { TRACE_LEAVE( "done\n" ); return FALSE; } if( RegSetValueEx( hk, service_name, 0, REG_SZ, command_line, strlen( command_line ) + 1 ) ) { ERR( "Cannot set registry value; error %lu\n", GetLastError() ); ret = FALSE; } RegCloseKey( hk ); TRACE_LEAVE( "done; ret=%d\n", ret ); return ret; } static BOOL winnt_InstallService( char* command_line ) { SC_HANDLE hscm, hsvc; BOOL ret; TRACE_ENTER( "command_line=%s\n", command_line ); hscm = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); if( !hscm ) { ERR( "Cannot open service control manager; error %lu\n", GetLastError() ); return FALSE; } hsvc = CreateService( hscm, service_name, service_display_name, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, command_line, NULL, NULL, NULL, NULL, NULL ); if( !hsvc ) { ERR( "Cannot create service; error %lu\n", GetLastError() ); ret = FALSE; } else { CloseServiceHandle( hsvc ); ret = TRUE; } CloseServiceHandle( hscm ); TRACE_LEAVE( "done\n" ); return ret; } static BOOL install_service() { BOOL ret; char command_line[ MAX_PATH ]; int i; TRACE_ENTER( "\n" ); if( __argv[0][1] == ':' ) command_line[0] = 0; else { TRACE( "argv[0] contains no absolute path\n" ); GetCurrentDirectory( MAX_PATH, command_line ); strcat( command_line, "\\" ); } strcat( command_line, __argv[0] ); strcat( command_line, " --service" ); if( conf_file_name ) { strcat( command_line, " --conf " ); strcat( command_line, conf_file_name ); } if( priority ) { strcat( command_line, " --priority " ); strcat( command_line, priority ); } if( instance_name ) { strcat( command_line, " --instance " ); strcat( command_line, instance_name ); } if( trace_file_name ) { strcat( command_line, " --trace " ); strcat( command_line, trace_file_name ); } for( i = 0; i < verbosity_level; i++ ) strcat( command_line, " -v" ); if( VER_PLATFORM_WIN32_NT == vi.dwPlatformId ) ret = winnt_InstallService( command_line ); else ret = win9x_InstallService( command_line ); TRACE_LEAVE( "done; ret=%d\n", ret ); return ret; } /****************************************************************************** * service uninstaller */ static void win9x_RemoveService() { HKEY hk; TRACE_ENTER( "\n" ); if( !open_run_key( &hk ) ) return; RegDeleteValue( hk, service_name ); RegCloseKey( hk ); TRACE_LEAVE( "done\n" ); } static void winnt_RemoveService() { SC_HANDLE hscm, hsvc; TRACE_ENTER( "\n" ); hscm = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); if( !hscm ) { ERR( "Cannot open service control manager; error %lu\n", GetLastError() ); return; } hsvc = OpenService( hscm, service_name, SERVICE_ALL_ACCESS ); if( !hsvc ) { ERR( "Cannot open service %s; error %lu\n", service_name, GetLastError() ); CloseServiceHandle( hscm ); return; } if( !DeleteService( hsvc ) ) ERR( "Cannot delete service; error %lu\n", GetLastError() ); CloseServiceHandle( hsvc ); CloseServiceHandle( hscm ); TRACE_LEAVE( "done\n" ); } static void remove_service() { TRACE_ENTER( "\n" ); if( VER_PLATFORM_WIN32_NT == vi.dwPlatformId ) winnt_RemoveService(); else win9x_RemoveService(); TRACE_LEAVE( "done\n" ); } /****************************************************************************** * exception_handler */ LONG WINAPI exception_handler( PEXCEPTION_POINTERS ei ) { DWORD i; BYTE *addr; fprintf( trace_fd, "*********************************\n" "thread id:\t\t%lX\n" "ExceptionCode:\t\t%lX\n" "ExceptionFlags:\t\t%lX\n" "ExceptionRecord:\t%p\n" "ExceptionAddress:\t%p\n" "NumberParameters:\t%lx\n" "ExceptionInformation:\n", GetCurrentThreadId(), ei->ExceptionRecord->ExceptionCode, ei->ExceptionRecord->ExceptionFlags, ei->ExceptionRecord->ExceptionRecord, ei->ExceptionRecord->ExceptionAddress, ei->ExceptionRecord->NumberParameters ); for( i = 0; i < ei->ExceptionRecord->NumberParameters; i++ ) fprintf( trace_fd, "\t%lX\n", ei->ExceptionRecord->ExceptionInformation[i] ); #if defined(_X86_) fprintf( trace_fd, "ContextFlags=%lX\n" "CS=%lX DS=%lX ES=%lX SS=%lX FS=%lX GS=%lX\n" "EAX=%lX EBX=%lX ECX=%lX EDX=%lX ESI=%lX EDI=%lX\n" "EBP=%lX ESP=%lX EIP=%lX EFLAGS=%lX\n" "Stack Dump:\n", ei->ContextRecord->ContextFlags, ei->ContextRecord->SegCs, ei->ContextRecord->SegDs, ei->ContextRecord->SegEs, ei->ContextRecord->SegSs, ei->ContextRecord->SegFs, ei->ContextRecord->SegGs, ei->ContextRecord->Eax, ei->ContextRecord->Ebx, ei->ContextRecord->Ecx, ei->ContextRecord->Edx, ei->ContextRecord->Esi, ei->ContextRecord->Edi, ei->ContextRecord->Ebp, ei->ContextRecord->Esp, ei->ContextRecord->Eip, ei->ContextRecord->EFlags ); addr = (LPBYTE) (ei->ContextRecord->Esp); #else fprintf( trace_fd, "FIXME: add more machines\n" ); #endif while( !IsBadReadPtr( addr, 16 ) ) { int skip = ((DWORD) addr) & 15; BYTE *keep_addr = addr; fprintf( trace_fd, "%08lX", ((DWORD) addr) & ~15 ); for( i = 0; i < skip; i++ ) fprintf( trace_fd, " " ); for( ; i < 8; i++ ) fprintf( trace_fd, " %02X", *addr++ ); if( i == 8 ) fputc( '-', trace_fd ); for( ; i < 16; i++ ) fprintf( trace_fd, "%02X ", *addr++ ); fputc( ' ', trace_fd ); addr = keep_addr; for( i = 0; i < skip; i++ ) fputc( ' ', trace_fd ); for( ; i < 16; i++ ) { BYTE b = *addr++; if( b < 32 ) b = ' '; fputc( b, trace_fd ); } fputc( '\n', trace_fd ); } fprintf( trace_fd, "*********************************\n" ); fflush( trace_fd ); ExitProcess(2); } /****************************************************************************** * main * * Parse command line */ int main( int argc, char* argv[] ) { static int help_flag = 0; static int version_flag = 0; static int install_flag = 0; static int remove_flag = 0; static int service_flag = 0; static int start_flag = 0; static int shutdown_flag = 0; static int restart_flag = 0; int getopt_failure = 0; WSADATA wsd; char name_buf[ MAX_COMPUTERNAME_LENGTH + 1 ]; DWORD size; SetUnhandledExceptionFilter( exception_handler ); self_program_name = string_new( "syslog" ); space = string_new( " " ); line_feed = string_new( "\n" ); if( WSAStartup( MAKEWORD( 2, 2 ), &wsd ) ) { ERR( "Cannot initialize winsock; error %lu\n", WSAGetLastError() ); return 1; } /* get local host name */ size = sizeof(name_buf); if( !GetComputerName( name_buf, &size ) ) { ERR( "Cannot get computer name; error %lu\n", GetLastError() ); return 1; } local_hostname = string_new_len( name_buf, size ); /* get windows version */ vi.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); if( ! GetVersionEx( &vi ) ) { ERR( "Cannot get windows version; error %lu\n", GetLastError() ); return 1; } for(;;) { static struct option long_options[] = { { "conf", required_argument, NULL, 'c'}, { "verbose", no_argument, NULL, 'v'}, { "help", no_argument, &help_flag, 1 }, { "version", no_argument, &version_flag, 1 }, { "install", no_argument, &install_flag, 1 }, { "remove", no_argument, &remove_flag, 1 }, { "service", no_argument, &service_flag, 1 }, { "start", no_argument, &start_flag, 1 }, { "shutdown", no_argument, &shutdown_flag,1 }, { "restart", no_argument, &restart_flag, 1 }, { "instance", required_argument, NULL, 'I'}, { "priority", required_argument, NULL, 'p'}, { "trace", required_argument, NULL, 't'}, { 0, 0, 0, 0 } }; int option_char; int option_index; option_char = getopt_long( argc, argv, "c:vhirsI:p:t:", long_options, &option_index ); if( -1 == option_char ) break; switch( option_char ) { case 0: break; case 'c': conf_file_name = optarg; break; case 'v': verbosity_level++; break; case 'h': help_flag = 1; break; case 'i': install_flag = 1; break; case 'r': remove_flag = 1; break; case 'I': instance_name = optarg; break; case 'p': { DWORD pclass; if( strcmpi( optarg, "normal" ) == 0 ) { priority = optarg; pclass = NORMAL_PRIORITY_CLASS; } else if( strcmpi( optarg, "high" ) == 0 ) { priority = optarg; pclass = ABOVE_NORMAL_PRIORITY_CLASS; } else if( strcmpi( optarg, "highest" ) == 0 ) { priority = optarg; pclass = HIGH_PRIORITY_CLASS; } else { ERR( "Invalid priority %s; must be 'normal', 'high' or 'highest'\n", optarg ); break; } SetPriorityClass( GetCurrentProcess(), pclass ); break; } case 't': trace_file_name = optarg; break; case '?': /* getopt_long already printed an error message. */ getopt_failure++; break; default: abort(); } } if( getopt_failure ) return 1; /* handle flags in order of priority */ /* at first, open trace file, if given */ if( trace_file_name ) { if( *trace_file_name == '+' ) trace_fd = fopen( trace_file_name + 1, "a" ); else trace_fd = fopen( trace_file_name, "w" ); if( !trace_fd ) { perror( trace_file_name ); trace_fd = stderr; } } else trace_fd = stderr; TRACE( "local host name=%s\n", local_hostname->gstr->str ); /* check instance name */ if( instance_name ) { service_name = g_strconcat( service_name, "_", instance_name, NULL ); service_display_name = g_strconcat( service_display_name, "_", instance_name, NULL ); service_stop_event_name = g_strconcat( service_stop_event_name, "_", instance_name, NULL ); } /* check service flag */ if( service_flag ) { if( VER_PLATFORM_WIN32_NT == vi.dwPlatformId ) { /* run as service under windows NT */ static SERVICE_TABLE_ENTRY service_table[] = { { "", (LPSERVICE_MAIN_FUNCTION) winnt_ServiceMain }, { NULL, NULL } }; if( !StartServiceCtrlDispatcher( service_table ) ) { ERR( "Cannot start service control dispatcher; error %lu\n", GetLastError() ); return 1; } return 0; } else { /* run as service under windows 9x */ FreeConsole(); return win9x_ServiceMain(); } } /* print version */ if( version_flag ) { printf( "%s %s\n", PACKAGE_NAME, PACKAGE_VERSION ); return 0; } /* print help */ if( help_flag ) { printf( "Usage: syslogd [OPTION]\n" ); printf( "This is syslog daemon for windows.\n" ); printf( "\n" ); printf( "-c, --conf\tspecify alternate configuration file\n" ); printf( "-v, --verbose\tbe verbose; each occurence of this parameter\n" "\t\tincreases verbosity\n" ); printf( "-i, --install\tinstall and start service\n" ); printf( "-r, --remove\tstop and remove service\n" ); printf( "--start\t\tstart installed daemon\n" ); printf( "-s, --shutdown\tsend shutdown signal to the daemon\n" ); printf( "--restart\trestart daemon\n" ); printf( "-I, --instance\tset instance name in the case of multiple daemons\n" ); printf( "-p, --priority\tset priority class; value may be 'normal' (default),\n" "\t\t'high', or 'highest'\n" ); printf( "-t, --trace\toutput trace and error messages to the specified file\n" ); printf( "-h, --help\tdisplay this message\n" ); printf( "--version\tdisplay version information\n" ); return 0; } /* install/remove/start/shutdown/restart */ if( remove_flag ) { if( install_flag || start_flag || shutdown_flag || restart_flag ) ERR( "Remove option has priority over install/start/shutdown/restart\n" ); shutdown_service( TRUE ); remove_service(); return 0; } if( install_flag ) { if( start_flag || shutdown_flag || restart_flag ) ERR( "Install option has priority over start/shutdown/restart\n" ); if( !install_service( priority ) ) return 1; if( !start_service() ) return 1; return 0; } if( start_flag ) { if( shutdown_flag || restart_flag ) { ERR( "Please specify only one action\n" ); return 1; } if( !start_service() ) return 1; return 0; } if( shutdown_flag ) { if( restart_flag ) { ERR( "Please specify only one action\n" ); return 1; } shutdown_service( FALSE ); return 0; } if( restart_flag ) { shutdown_service( FALSE ); if( !start_service() ) return 1; return 0; } /* run as ordinary console application */ return win9x_ServiceMain(); }