*** 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 <zlib.h>
+ #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<maxLogSize> = size
+ 
+ Maximum size of log file. B<Stunnel> 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<maxLogFiles> = number
+ 
+ Number of log files kept in log rotation in addition to current log file.
+ 
+ default : 7 files are kept.
+ 
+ =item B<useLogCompression> = yes | no
+ 
+ Specify if log files kept must be compressed using gzip.
+ 
+ default: yes
+ 
  =item B<pid> = 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<maxLogSize> = taille
+ 
+ Taille maximum du fichier de traces. B<Stunnel> 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<maxLogFiles> = nombre
+ 
+ Nombre de fichiers de traces conservés, en plus du fichier en cours.
+ 
+ Par défaut : 7 fichiers sont conservés.
+ 
+ =item B<useLogCompression> = 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<pid> = fichier (Unix seulement)
  
  Emplacement du fichier pid
