/* * syslog-client.c - syslog client implementation for windows * * 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. * */ /* define SYSLOG_CONF_DIR where syslog.host should be */ #ifndef SYSLOG_CONF_DIR static const char *syslog_conf_dir = "."; #else static const char *syslog_conf_dir = SYSLOG_CONF_DIR; #endif #include #include #include #include #include #ifdef TEST # define SYSLOG_DGRAM_SIZE 80 #else # define SYSLOG_DGRAM_SIZE 1024 #endif static BOOL initialized = FALSE; static int log_mask = 0xFF; static char *syslog_ident; static int syslog_facility; static char str_pid[ 40 ]; static SOCKADDR_IN sa_logger; static SOCKET sock; static char local_hostname[ MAX_COMPUTERNAME_LENGTH + 1 ]; static char datagramm[ SYSLOG_DGRAM_SIZE ]; static int datagramm_size; /****************************************************************************** * set_syslog_conf_dir * * maybe this function will be useful... */ const char* set_syslog_conf_dir( const char* dir ) { const char *ret = syslog_conf_dir; syslog_conf_dir = dir; return ret; } /****************************************************************************** * init_logger_addr * * Read configuration file syslog.host. This file should contain host address * and, optionally, port. Initialize sa_logger. If the configuration file does * not exist, use localhost:514. * Returns: 0 - ok, -1 - error. */ static void init_logger_addr() { char pathname[ FILENAME_MAX ]; char *p; FILE *fd; char host[256]; struct hostent * phe; memset( &sa_logger, 0, sizeof(SOCKADDR_IN) ); sa_logger.sin_family = AF_INET; if( '\\' == syslog_conf_dir[0] || '/' == syslog_conf_dir[0] || ':' == syslog_conf_dir[1] ) { /* absolute path */ strcpy( pathname, syslog_conf_dir ); } else { /* relative path */ char *q; strcpy( pathname, __argv[0] ); p = strrchr( pathname, '\\' ) + 1; q = strrchr( pathname, '/' ) + 1; if( p < q ) *q = 0; else if( p > q ) *p = 0; else pathname[0] = 0; strcat( pathname, syslog_conf_dir ); } p = &pathname[ strlen( pathname ) - 1 ]; if( '\\' != *p && '/' != *p ) { p++; *p = '/'; } strcpy( ++p, "syslog.host" ); /* read destination host name */ fd = fopen( pathname, "r" ); if( !fd ) goto use_default; if( NULL == fgets( host, sizeof(host), fd ) ) host[0] = 0; else { p = strchr( host, '\n' ); if( p ) *p = 0; p = strchr( host, '\r' ); if( p ) *p = 0; } fclose( fd ); p = strchr( host, ':' ); if( p ) *p++ = 0; phe = gethostbyname( host ); if( !phe ) goto use_default; memcpy( &sa_logger.sin_addr.s_addr, phe->h_addr, phe->h_length ); if( p ) sa_logger.sin_port = htons( (unsigned short) strtoul( p, NULL, 0 ) ); else sa_logger.sin_port = htons( SYSLOG_PORT ); return; use_default: sa_logger.sin_addr.S_un.S_addr = htonl( 0x7F000001 ); sa_logger.sin_port = htons( SYSLOG_PORT ); } /****************************************************************************** * closelog * * Close desriptor used to write to system logger. */ void closelog() { if( !initialized ) return; closesocket( sock ); WSACleanup(); initialized = FALSE; } /****************************************************************************** * openlog * * Open connection to system logger. */ void openlog( char* ident, int option, int facility ) { BOOL failed = TRUE, wsa_initialized = FALSE; WSADATA wsd; SOCKADDR_IN sa_local; DWORD n; int size; if( initialized ) return; syslog_facility = facility? facility : LOG_USER; /* FIXME: should we reset logmask? */ if( option & LOG_PID ) snprintf( str_pid, sizeof(str_pid), "[%lu]", GetCurrentProcessId() ); else str_pid[0] = 0; /* FIXME: handle other options */ n = sizeof(local_hostname); if( !GetComputerName( local_hostname, &n ) ) goto done; sock = INVALID_SOCKET; if( WSAStartup( MAKEWORD( 2, 2 ), &wsd ) ) goto done; wsa_initialized = TRUE; init_logger_addr(); for( n = 0;; n++ ) { sock = socket( AF_INET, SOCK_DGRAM, 0 ); if( INVALID_SOCKET == sock ) goto done; memset( &sa_local, 0, sizeof(SOCKADDR_IN) ); sa_local.sin_family = AF_INET; if( bind( sock, (SOCKADDR*) &sa_local, sizeof(SOCKADDR_IN) ) == 0 ) break; closesocket( sock ); sock = INVALID_SOCKET; if( n == 100 ) goto done; Sleep(0); } /* get size of datagramm */ size = sizeof(datagramm_size); if( getsockopt( sock, SOL_SOCKET, SO_MAX_MSG_SIZE, (char*) &datagramm_size, &size ) ) goto done; if( datagramm_size - strlen(local_hostname) - (ident? strlen(ident) : 0) < 64 ) goto done; if( datagramm_size > sizeof(datagramm) ) datagramm_size = sizeof(datagramm); if( atexit( closelog ) ) goto done; syslog_ident = ident; syslog_facility = facility; failed = FALSE; done: if( failed ) { if( sock != INVALID_SOCKET ) closesocket( sock ); if( wsa_initialized ) WSACleanup(); } initialized = !failed; } /****************************************************************************** * setlogmask * * Set the log mask level. */ int setlogmask( int mask ) { int ret = log_mask; if( mask ) log_mask = mask; return ret; } /****************************************************************************** * syslog * * Generate a log message using FMT string and option arguments. */ void syslog( int pri, char* fmt, ... ) { va_list ap; va_start( ap, fmt ); vsyslog( pri, fmt, ap ); va_end( ap ); } /****************************************************************************** * vsyslog * * Generate a log message using FMT and using arguments pointed to by AP. */ void vsyslog( int pri, char* fmt, va_list ap ) { static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; SYSTEMTIME stm; int len; char *p; if( !(LOG_MASK( LOG_PRI( pri )) & log_mask) ) return; openlog( NULL, 0, pri & LOG_FACMASK ); if( !initialized ) return; if( !(pri & LOG_FACMASK) ) pri |= syslog_facility; GetLocalTime( &stm ); len = sprintf( datagramm, "<%d>%s %2d %02d:%02d:%02d %s %s%s: ", pri, month[ stm.wMonth - 1 ], stm.wDay, stm.wHour, stm.wMinute, stm.wSecond, local_hostname, syslog_ident? syslog_ident : "", str_pid ); vsnprintf( datagramm + len, datagramm_size - len, fmt, ap ); p = strchr( datagramm, '\n' ); if( p ) *p = 0; p = strchr( datagramm, '\r' ); if( p ) *p = 0; sendto( sock, datagramm, strlen(datagramm), 0, (SOCKADDR*) &sa_logger, sizeof(SOCKADDR_IN) ); } /****************************************************************************** * test */ #ifdef TEST static HANDLE hRxEvent = NULL, hAckEvent = NULL; static SOCKET rxsock = INVALID_SOCKET; static char buffer[ sizeof(datagramm)+1 ]; static DWORD WINAPI listener( LPVOID param ) { for(;;) { int ret; WaitForSingleObject( hAckEvent, INFINITE ); ret = recv( rxsock, buffer, sizeof(datagramm), 0 ); if( ret <= 0 ) break; buffer[ ret ] = 0; SetEvent( hRxEvent ); } return 0; } static int transact( int pri, char* fmt, ... ) { va_list ap; DWORD r; va_start( ap, fmt ); vsyslog( pri, fmt, ap ); va_end( ap ); r = WaitForSingleObject( hRxEvent, 2000 ); if( WAIT_TIMEOUT == r ) { fprintf( stderr, "timeout\n" ); return -1; } if( WAIT_FAILED == r ) { fprintf( stderr, "wait failed, error %lu\n", GetLastError() ); return -1; } printf( "*** %s\n", buffer ); SetEvent( hAckEvent ); return 0; } int main( int argc, char* argv[] ) { int ret = 1; WSADATA wsd; SOCKADDR_IN sa_local; DWORD tid; HANDLE hThread = NULL; if( WSAStartup( MAKEWORD( 2, 2 ), &wsd ) ) { fprintf( stderr, "WSAStartup() failed, error %d\n", WSAGetLastError() ); return 1; } hRxEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if( !hRxEvent ) { fprintf( stderr, "CreateEvent() failed, error %lu\n", GetLastError() ); goto done; } hAckEvent = CreateEvent( NULL, FALSE, TRUE, NULL ); if( !hAckEvent ) { fprintf( stderr, "CreateEvent() failed, error %lu\n", GetLastError() ); goto done; } rxsock = socket( AF_INET, SOCK_DGRAM, 0 ); if( INVALID_SOCKET == sock ) { fprintf( stderr, "socket() failed, error %d\n", WSAGetLastError() ); goto done; } memset( &sa_local, 0, sizeof(SOCKADDR_IN) ); sa_local.sin_family = AF_INET; sa_local.sin_addr.S_un.S_addr = htonl( 0x7F000001 ); sa_local.sin_port = htons( SYSLOG_PORT ); if( bind( rxsock, (SOCKADDR*) &sa_local, sizeof(SOCKADDR_IN) ) ) { fprintf( stderr, "bind() failed, error %d\n", WSAGetLastError() ); goto done; } hThread = CreateThread( NULL, 0, listener, NULL, 0, &tid ); if( !hThread ) { fprintf( stderr, "CreateThread() failed, error %lu\n", GetLastError() ); goto done; } openlog( "test_ident", 0, LOG_USER ); if( !initialized ) { fprintf( stderr, "openlog() failed\n" ); goto done; } if( transact( LOG_DEBUG, "test message %d", 1 ) ) goto done; setlogmask( LOG_MASK( LOG_EMERG ) ); if( !transact( LOG_DEBUG, "test message %d", 2 ) ) goto done; if( transact( LOG_EMERG, "test message %d", 3 ) ) goto done; closelog(); openlog( "test_ident", LOG_PID, LOG_USER ); if( !initialized ) { fprintf( stderr, "openlog() failed\n" ); goto done; } setlogmask( LOG_MASK( LOG_DEBUG ) ); if( transact( LOG_DEBUG, "test message %d with pid", 4 ) ) goto done; if( transact( LOG_DEBUG, "long test message %d 1234567890 1234567890 1234567890 1234567890 1234567890", 5 ) ) goto done; ret = 0; done: if( rxsock != INVALID_SOCKET ) closesocket( rxsock ); if( hAckEvent ) { if( hThread ) { SetEvent( hAckEvent ); puts( "waiting for thread shutdown" ); WaitForSingleObject( hThread, INFINITE ); } CloseHandle( hAckEvent ); } if( hThread ) CloseHandle( hThread ); if( hRxEvent ) CloseHandle( hRxEvent ); WSACleanup(); return 0; } #endif /* TEST */