/*
* 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 <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <glib.h>
#include <syslog.h>
#include <syslogd.h>
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";
static char *service_display_name = "syslogd";
static char *service_stop_event_name = "syslogd-stop";
static OSVERSIONINFO vi;
static SERVICE_STATUS_HANDLE hss;
static SERVICE_STATUS sstatus;
/******************************************************************************
* display message
*/
void display_message( FILE* fd, 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( fd, formatstr, args );
va_end( args );
fflush( fd );
}
/******************************************************************************
* shutdown_service
*
* set service stop event
* wait until event exists
*/
static void shutdown_service( gboolean quiet )
{
HANDLE he;
BOOL ret;
for(;;)
{
he = OpenEvent( EVENT_MODIFY_STATE, FALSE, service_stop_event_name );
if( !he )
{
if( !quiet )
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 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;
}
/******************************************************************************
* service installer
*/
static BOOL win9x_InstallService( char* command_line )
{
BOOL ret;
HKEY hk;
STARTUPINFO si;
PROCESS_INFORMATION pi;
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 );
if( !ret )
return FALSE;
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 );
TRACE_LEAVE( "done\n" );
return TRUE;
}
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
{
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 install_service( char* priority )
{
BOOL ret;
char command_line[ MAX_PATH + 64 ];
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( priority )
{
strcat( command_line, " --priority " );
strcat( command_line, priority );
}
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;
int i;
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;
}
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 )
break;
}
if( !DeleteService( hsvc ) )
TRACE( "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();
/* always try to shutdown because it's possible that daemon
is running not as service under winNT */
shutdown_service( TRUE );
TRACE_LEAVE( "done\n" );
}
/******************************************************************************
* exception_handler
*/
LONG WINAPI exception_handler( PEXCEPTION_POINTERS ei )
{
DWORD i;
BYTE *addr;
fprintf( stderr,
"*********************************\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( stderr, "\t%lX\n", ei->ExceptionRecord->ExceptionInformation[i] );
#if defined(_X86_)
fprintf( stderr,
"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( stderr, "FIXME: add more machines\n" );
#endif
while( !IsBadReadPtr( addr, 16 ) )
{
int skip = ((DWORD) addr) & 15;
BYTE *keep_addr = addr;
fprintf( stderr, "%08lX", ((DWORD) addr) & ~15 );
for( i = 0; i < skip; i++ )
fprintf( stderr, " " );
for( ; i < 8; i++ )
fprintf( stderr, " %02X", *addr++ );
if( i == 8 )
fputc( '-', stderr );
for( ; i < 16; i++ )
fprintf( stderr, "%02X ", *addr++ );
fputc( ' ', stderr );
addr = keep_addr;
for( i = 0; i < skip; i++ )
fputc( ' ', stderr );
for( ; i < 16; i++ )
{
BYTE b = *addr++;
if( b < 32 ) b = ' ';
fputc( b, stderr );
}
fputc( '\n', stderr );
}
fprintf( stderr, "*********************************\n" );
fflush( stderr );
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 shutdown_flag = 0;
char *instance_name = NULL;
char *priority = NULL;
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[] =
{
{ "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 },
{ "shutdown", no_argument, &shutdown_flag,1 },
{ "instance", required_argument, NULL, 'I'},
{ "priority", required_argument, NULL, 'p'},
{ 0, 0, 0, 0 }
};
int option_char;
int option_index;
option_char = getopt_long( argc, argv, "vhirsI:p:",
long_options, &option_index );
if( -1 == option_char )
break;
switch( option_char )
{
case 0:
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 '?':
/* getopt_long already printed an error message. */
getopt_failure++;
break;
default:
abort();
}
}
if( getopt_failure )
return 1;
TRACE( "local host name=%s\n", local_hostname->gstr->str );
/* handle flags in order of priority */
/* at first, 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( "-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( "-s, --shutdown\tsend shutdown signal to the 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\t'high', or 'highest'\n" );
printf( "-h, --help\tdisplay this message\n" );
printf( "--version\tdisplay version information\n" );
return 0;
}
/* install/remove/shutdown */
if( remove_flag )
{
if( install_flag || shutdown_flag )
ERR( "Remove option has priority over install/shutdown\n" );
remove_service();
return 0;
}
if( install_flag )
{
if( shutdown_flag )
ERR( "Install option has priority over shutdown\n" );
install_service( priority );
return 0;
}
if( shutdown_flag )
{
shutdown_service( FALSE );
return 0;
}
/* run as ordinary console application */
return win9x_ServiceMain();
}