/* * logrotate.c - syslogd implementation for windows, log rotation * * 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 #include #include /****************************************************************************** * expand_options * * Substitute $PATHNAME and $FILENAME */ static gchar* expand_options( const gchar* compressoptions, const gchar* pathname ) { gchar *filename = g_path_get_basename( pathname ); GString *str = g_string_new( compressoptions ); gchar *substr_pathname; gchar *substr_filename; TRACE_ENTER( "\n" ); do { substr_pathname = strstr( str->str, "$PATHNAME" ); substr_filename = strstr( str->str, "$FILENAME" ); if( substr_pathname ) { int pos = substr_pathname - str->str; g_string_erase( str, pos, 9 ); g_string_insert( str, pos, pathname ); } if( substr_filename ) { int pos = substr_filename - str->str; g_string_erase( str, pos, 9 ); g_string_insert( str, pos, filename ); } } while( substr_pathname || substr_filename ); g_free( filename ); TRACE_LEAVE( "return %s\n", str->str ); return g_string_free( str, FALSE ); } /****************************************************************************** * compress_backlog * * Run external compression program. * Return TRUE if all right. */ static gboolean compress_backlog( const gchar* compresscmd, const gchar* compressoptions, const gchar* backlog ) { gboolean ret = FALSE; char command_pathname[ MAX_PATH ]; LPTSTR command_filename; gchar *options; gchar *command_line; STARTUPINFO si; PROCESS_INFORMATION pi; DWORD exit_code; TRACE_ENTER( "\n" ); if( !SearchPath( NULL, compresscmd, ".exe", sizeof(command_pathname), command_pathname, &command_filename ) ) { ERR( "Command %s not found\n", compresscmd ); TRACE_LEAVE( "error\n" ); return FALSE; } options = expand_options( compressoptions, backlog ); command_line = g_strconcat( command_pathname, " ", options, NULL ); memset( &si, 0, sizeof(si ) ); si.cb = sizeof(si); TRACE_2( "command_line=%s\n", command_line ); if( !CreateProcess( NULL, command_line, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi ) ) ERR( "Cannot create process %s; error %lu\n", command_line, GetLastError() ); else { CloseHandle( pi.hThread ); TRACE_2( "waiting for %s\n", command_line ); WaitForSingleObject( pi.hProcess, INFINITE ); if( !GetExitCodeProcess( pi.hProcess, &exit_code ) ) exit_code = 1; CloseHandle( pi.hProcess ); ret = 0 == exit_code; } g_free( command_line ); g_free( options ); TRACE_LEAVE( "ret=%d\n", ret ); return ret; } /****************************************************************************** * do_rotate * * All criteria for rotation are met, just do it. */ static void do_rotate( const gchar* pathname, struct destination* destination ) { int i; gchar *dest_pathname; gchar *backlog; TRACE_ENTER( "\n" ); /* construct destination pathname for backlogs */ if( destination->u.file.olddir ) { gchar *filename = g_path_get_basename( pathname ); if( g_path_is_absolute( destination->u.file.olddir ) ) dest_pathname = g_build_filename( destination->u.file.olddir, filename, NULL ); else { gchar *prefix = g_path_get_dirname( __argv[0] ); dest_pathname = g_build_filename( prefix, destination->u.file.olddir, filename, NULL ); g_free( prefix ); } g_free( filename ); } else dest_pathname = g_strdup( pathname ); /* remove earliest backlog */ backlog = g_strdup_printf( "%s.%d", dest_pathname, destination->u.file.backlogs ); DeleteFile( backlog ); g_free( backlog ); /* rotate backlogs */ for( i = destination->u.file.backlogs; i > 1; i-- ) { gchar *old_backlog = g_strdup_printf( "%s.%d", dest_pathname, i - 1 ); gchar *new_backlog = g_strdup_printf( "%s.%d", dest_pathname, i ); TRACE_2( "Move %s to %s\n", old_backlog, new_backlog ); if( !MoveFile( old_backlog, new_backlog ) ) { /* most possible that old backlog file does not exist */ TRACE_2( "Can't move %s to %s; error %lu\n", old_backlog, new_backlog, GetLastError() ); } g_free( old_backlog ); g_free( new_backlog ); } backlog = g_strconcat( dest_pathname, ".1", NULL ); /* move current log */ TRACE_2( "Move %s to %s\n", pathname, backlog ); if( !MoveFile( pathname, backlog ) ) ERR( "Can't move %s to %s; error %lu\n", pathname, backlog, GetLastError() ); /* compress new backlog */ if( destination->u.file.compresscmd ) { if( compress_backlog( destination->u.file.compresscmd, destination->u.file.compressoptions, backlog ) ) /* remove original uncompressed file */ DeleteFile( backlog ); } g_free( backlog ); g_free( dest_pathname ); TRACE_LEAVE( "done\n" ); } /****************************************************************************** * rotate_logfile */ void rotate_logfile( const gchar* pathname, struct destination* destination ) { struct stat fst; gboolean rotated = FALSE; time_t current_time; struct tm *tm; TRACE_ENTER( "pathname=%s\n", pathname ); if( 0 == destination->u.file.backlogs || (RP_UNDEFINED == destination->u.file.rotate && 0 == destination->u.file.size) ) { TRACE_LEAVE( "no conditions for rotation\n" ); return; } if( stat( pathname, &fst ) ) { /* most possible that file does not exist */ TRACE_2( "stat(%s) error %s\n", pathname, strerror( errno ) ); goto done; } if( destination->u.file.size ) { if( fst.st_size > destination->u.file.size ) { do_rotate( pathname, destination ); rotated = TRUE; goto done; } TRACE_2( "checked size: file=%d, max=%d\n", fst.st_size, destination->u.file.size ); } current_time = time(NULL); tm = gmtime( ¤t_time ); TRACE_2( "checking time: creation=%d, modification=%d\n", fst.st_ctime, fst.st_mtime ); switch( destination->u.file.rotate ) { case RP_DAILY: if( fst.st_mtime - (fst.st_ctime - fst.st_ctime % (24 * 3600)) > 24 * 3600 ) break; goto done; case RP_WEEKLY: { int current_weekday = tm->tm_wday; if( current_weekday < gmtime( &fst.st_ctime )->tm_wday || fst.st_mtime - fst.st_ctime > 7 * 24 * 3600 ) break; goto done; } case RP_MONTHLY: { int current_month = tm->tm_mon; if( current_month != gmtime( &fst.st_ctime )->tm_mon ) break; goto done; } default: goto done; } if( fst.st_size || destination->u.file.ifempty ) { do_rotate( pathname, destination ); rotated = TRUE; } else { TRACE_2( "do not rotate empty file\n" ); } done: TRACE_LEAVE( "done; %s\n", rotated? "rotated" : "not rotated" ); }