aboutsummaryrefslogblamecommitdiffstats
path: root/daemon/main.c
blob: 282b6972c38b8431813101461ed3d1217f234619 (plain) (tree)





























                                                                      



                                 


                        
                                            
                                              
                                                            





                                     
             
                            

                                  


                                    
 


                                                                               
                                                                              






                                                                 
                                          
                   
                       


                                                                               


























                                                                                  
                                                                




























































                                                                                              
                
   
                                
 
                     
            


                                  


                           







                                                                                   
     

                                                                        
     
                        
     

                                                  
     












                                                                                 































                                                                                   
                            


















                                                                               

                   
                                    
 
                     






                                                              
                     






                                                                 
                     











                                                                                

                       
                  
         


                               
               



                                              

                 
                                        
                                       
 

                                                                              





                                                                                    
                                        



















                                                                               























                                                                                                
























                                                                                   
                                   
                   





                               
                             

             
                                  
          











                                                       




                                               




                                               




                                               






                                                



























                                                                               
















                                                                                   
                                
                                                                    












                                                  










                                                                               
                      















                                                                
                                                                                     
 

                  
                      























                                                                

     
                                                      

      




                                       
                                                           
                                   
                                       
                           
                                                  
                    
                                   
                            

                                                  

                                   
                                   




                                 
                                 
         
                                
     

                                                               














                                                                               
                              
                                 
                                

                           
                                                 



                                                     



                                               






                                                                           

                                             



                                                                       
                                                      












                                                                         
                                                                       





                                                                       
                                                                       
                                                                       
                                                                       

                                                                       
                                                                       




                          
                                                               








                                                                 



                                    













































                                                                                              



                                     











                                                               
















                                                         

                                                               
                             












































                                                                                                   
                                                                       



                                                                               
                                                        
                                                                         
                                                

                                                                                        
                                                         
                                                                                         




                                                             
                                               

                     

                                                                                      

                                 




                         

                                                                               
 














                                                      



                       









                                                      
                                  

                              





                                             
/*
 * 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-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();
}