*** stunnel-4.26/src/log.c 2008-03-26 20:06:05.000000000 +0100 --- stunnel-4.26-rotlog/src/log.c 2008-12-16 16:06:49.000000000 +0100 *************** *** 38,45 **** --- 38,51 ---- #include "common.h" #include "prototypes.h" + #ifdef HAVE_LIBZ + #include + #endif + static void log_raw(const int, const char *, const char *); static void get_timestamp(const int, char *); + static off_t size(DISK_FILE *file); + static int log_rotate(char *logfilename); static DISK_FILE *outfile=NULL; static struct LIST { *************** *** 127,132 **** --- 133,139 ---- static void log_raw(const int level, const char *stamp, const char *text) { char stamped[STRLEN]; + int logrotate_result; safecopy(stamped, stamp); safeconcat(stamped, text); *************** *** 138,145 **** syslog(level, "LOG%d[%lu:%lu]: %s", level, stunnel_process_id(), stunnel_thread_id(), text); #endif /* USE_WIN32, __vms */ ! if(outfile) file_putline(outfile, stamped); /* send log to file */ } #ifdef USE_WIN32 --- 145,164 ---- syslog(level, "LOG%d[%lu:%lu]: %s", level, stunnel_process_id(), stunnel_thread_id(), text); #endif /* USE_WIN32, __vms */ ! if(outfile) { ! if ((options.max_log_size>0) && ! (size(outfile)>options.max_log_size)) { ! file_putline(outfile, "Closing log file for rotation."); ! log_close(); ! logrotate_result=log_rotate(options.output_file); ! log_open(); ! if (logrotate_result<0) ! file_putline(outfile, "Error during log rotation."); ! else ! file_putline(outfile, "Opening log file after rotation."); ! } file_putline(outfile, stamped); /* send log to file */ + } } #ifdef USE_WIN32 *************** *** 305,308 **** --- 324,521 ---- } } + #ifdef HAVE_LIBZ + /** + * Given an open FILE * as the source of log data to save, write it into the + * given location, compressing with zlib. + * @return 0 on success, + * 1 on error for opening dest file + * 2 on error for reading source file + * 3 on error for writing dest file + **/ + static int gzip_stream(FILE *source, int fd) { + gzFile *dest; + int nread, nwritten; + static char buffer[2048]; + + /* Now, open this file descriptor as a maximally compressed file. */ + dest = gzdopen(fd, "wb9"); + if (dest == NULL) { + close(fd); + return 1; + } + + /* Copy the file over. Maybe some day add better error reporting. */ + while (!feof(source) && !ferror(source)) { + nread = fread(buffer, 1, 2047, source); + nwritten = gzwrite(dest, buffer, nread); + if (nread != nwritten || ferror(source)) { + gzclose(dest); + return 2; + } + } + + /* Close the destination file and then verify that the copy was + successful. */ + if (gzclose(dest) != 0) { + return 3; + } + return 0; + } + + + /** + * Given an open FILE * as the source of log data to save, write it into the + * given location, compressing as appropriate for the extension of the + * destination file. If mode is non-zero, fchmod the destination file to that + * mode. + * @return 0 on success, + * 1 if cannot create dest file. + * 2 on error for reading source file + * 3 on error for writing dest file + **/ + static int compress_stream(FILE *source, const char *destname, mode_t filemode) { + int fd; + + /* Open the destination file and try to chmod it to the right perms. */ + fd = open(destname, O_CREAT | O_EXCL | O_WRONLY, 0600); + if (fd < 0) { + return 1; + } + fchmod(fd, filemode); + return gzip_stream(source, fd); + } + + /** + * Copy a file from one location to another, compressing it in the process. + * @return 0 on success + * 1 if cannot create dest file. + * 2 on error for reading source file + * 3 on error for writing dest file + * 4 if cannot open source file + **/ + static int compress_file(const char *sourcename, const char *destname) + { + FILE *source; + struct stat statbuf; + int status; + mode_t filemode; + + /* Open the source file as a standard file and get its mode. */ + source = fopen(sourcename, "rb"); + if (!source) { + return 4; + } + if (fstat(fileno(source), &statbuf) == 0) { + filemode = statbuf.st_mode; + } else { + filemode = 0; + } + + /* Do the work and close the source file. */ + status = compress_stream(source, destname, filemode); + fclose(source); + return status; + } + #endif + + /** + * @return 0 on success + * <0 on failure + **/ + int log_rotate(char *logfilename) { + unsigned int i; + int res; + FILE *testfile; + pid_t ppid; + char actual[STRLEN]; + char new_one[STRLEN]; + char new_older[STRLEN]; + char extension[5]; + + #ifdef HAVE_SNPRINTF + snprintf( actual, STRLEN, + #else + sprintf( actual, + #endif + "%s.new", logfilename); + if (rename( logfilename, actual)!=0) { + fprintf( stderr, "Error while renaming log file.\n"); + return -1; + } + ppid=fork(); + if (ppid<0) /* error */ + return -1; + if (ppid>0) /* parent */ + return 0; + + /* child */ + nice(4); /* the log rotation process is not a priority */ + + if (options.option.useLogCompression) { + #ifdef HAVE_SNPRINTF + snprintf( new_one, STRLEN, + #else + sprintf( new_one, + #endif + "%s.1.gz.new", logfilename); + if ((res=compress_file( actual, new_one))!=0) { + unlink(new_one); + exit(res); + } + unlink(actual); + strcpy( extension, ".gz"); + } else { + #ifdef HAVE_SNPRINTF + snprintf( new_one, STRLEN, + #else + sprintf( new_one, + #endif + "%s.1.new", logfilename); + rename( actual, new_one); + strcpy( extension, ""); + } + i=1; + while (i<=options.max_log_files) { + #ifdef HAVE_SNPRINTF + snprintf( actual, STRLEN, + #else + sprintf( actual, + #endif + "%s.%u%s", logfilename, i, extension); + #ifdef HAVE_SNPRINTF + snprintf( new_one, STRLEN, + #else + sprintf( new_one, + #endif + "%s.%u%s.new", logfilename, i, extension); + if ((testfile=fopen( actual, "r"))==NULL) + break; + fclose(testfile); + #ifdef HAVE_SNPRINTF + snprintf( new_older, STRLEN, + #else + sprintf( new_older, + #endif + "%s.%u%s.new", logfilename, i + 1, extension); + rename( actual, new_older); + rename( new_one, actual); + i++; + } + if (i>options.max_log_files) { + unlink(new_older); + } else { + rename( new_one, actual); + } + exit(EXIT_SUCCESS); + } + + off_t size(DISK_FILE *file) { + struct stat file_stats; + + if (fstat( file->fd, &file_stats)) + return -1; + return file_stats.st_size; + } /* size */ + /* End of log.c */ *** stunnel-4.26/src/prototypes.h 2008-06-21 23:15:14.000000000 +0200 --- stunnel-4.26-rotlog/src/prototypes.h 2008-12-16 14:16:29.000000000 +0100 *************** *** 81,86 **** --- 81,87 ---- /**************************************** Prototypes for log.c */ + #define DEFAULT_LOG_FILES 7 void log_open(void); void log_close(void); void log_flush(void); *************** *** 151,156 **** --- 152,159 ---- int facility; /* debug facility for syslog */ #endif char *output_file; + off_t max_log_size; + int max_log_files; /* on/off switches */ struct { *************** *** 164,169 **** --- 167,173 ---- #ifdef USE_FIPS unsigned int fips:1; /* enable FIPS 140-2 mode */ #endif + unsigned int useLogCompression:1; } option; } GLOBAL_OPTIONS; *** stunnel-4.26/src/options.c 2008-06-21 23:18:23.000000000 +0200 --- stunnel-4.26-rotlog/src/options.c 2008-12-16 15:44:59.000000000 +0100 *************** *** 281,286 **** --- 281,364 ---- break; } + /* max_log_files */ + switch(cmd) { + case CMD_INIT: + options.max_log_files=DEFAULT_LOG_FILES; + break; + case CMD_EXEC: + if(strcasecmp(opt, "maxlogfiles")) + break; + options.max_log_files=atoi(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = %u", "maxLogFiles", DEFAULT_LOG_FILES); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = number of log files kept", "maxLogFiles"); + break; + } + + /* max_log_size */ + switch(cmd) { + unsigned int num; + char mult; + case CMD_INIT: + options.max_log_size=-1; + break; + case CMD_EXEC: + if(strcasecmp(opt, "maxlogsize")) + break; + if (sscanf(arg, "%u%c", &num, &mult)<2) + return "Error in specification of size"; + if ((mult=='k')||(mult=='K')) { + options.max_log_size=1024*num; + return NULL; + } + if ((mult=='m')||(mult=='M')) { + options.max_log_size=1024*1024*num; + return NULL; + } + if ((mult=='g')||(mult=='G')) { + options.max_log_size=1024*1024*1024*num; + return NULL; + } + return "Error in specification of size"; + case CMD_DEFAULT: + s_log(LOG_RAW, "%-15s = %s", "maxLogSize", "Unlimited"); + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = maximum size of log file", "maxLogSize"); + break; + } + + /* useLogCompression */ + switch(cmd) { + case CMD_INIT: + options.option.useLogCompression=1; + break; + case CMD_EXEC: + if(strcasecmp(opt, "useLogCompression")) + break; + if(!strcasecmp(arg, "yes")) { + #ifndef HAVE_LIBZ + return "Stunnel is not compiled with libz"; + #endif + options.option.useLogCompression=1; + } + else if(!strcasecmp(arg, "no")) + options.option.useLogCompression=0; + else + return "Argument should be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_RAW, "%-15s = yes|no compress log files using gzip", + "useLogCompression"); + break; + } + /* pid */ #ifndef USE_WIN32 switch(cmd) { *** stunnel-4.26/doc/stunnel.pod 2008-03-27 11:14:08.000000000 +0100 --- stunnel-4.26-rotlog/doc/stunnel.pod 2008-12-16 15:54:12.000000000 +0100 *************** *** 182,187 **** --- 182,210 ---- /dev/stdout device can be used to redirect log messages to the standard output (for example to log them with daemontools splogger). + =item B = size + + Maximum size of log file. B will rotate log file when the size + is reached. + + Size can be expressed in kilobytes (k), megabytes (M) or gigabytes (G). + + example : maxLogSize= 30M + + default : unlimited + + =item B = number + + Number of log files kept in log rotation in addition to current log file. + + default : 7 files are kept. + + =item B = yes | no + + Specify if log files kept must be compressed using gzip. + + default: yes + =item B = file (Unix only) pid file location *** stunnel-4.26/doc/stunnel.fr.pod 2007-09-23 17:29:19.000000000 +0200 --- stunnel-4.26-rotlog/doc/stunnel.fr.pod 2008-12-16 15:54:06.000000000 +0100 *************** *** 231,236 **** --- 231,260 ---- /dev/stdout peut être utilisé pour afficher les traces sur la sortie standard (par exemple pour les traiter avec les outils splogger). + =item B = taille + + Taille maximum du fichier de traces. B effectuera une rotation + des journaux de traces quand la taille spécifiée sera atteinte. + + La taille peut être exprimée en kilo-octets (k), mega-octets (M) ou giga-octets (G). + + Exemple : maxLogSize= 30M + + Par défaut : Pas de limite de taille + + =item B = nombre + + Nombre de fichiers de traces conservés, en plus du fichier en cours. + + Par défaut : 7 fichiers sont conservés. + + =item B = yes | no + + Spécifie si les fichiers de traces conservés doivent être compressés avec + l'algorithme gzip. + + Par défaut: yes + =item B = fichier (Unix seulement) Emplacement du fichier pid