SOURCES: cronolog-jumbo-patch.txt (NEW) - added

glen glen at pld-linux.org
Tue Dec 6 14:07:05 CET 2005


Author: glen                         Date: Tue Dec  6 13:07:05 2005 GMT
Module: SOURCES                       Tag: HEAD
---- Log message:
- added

---- Files affected:
SOURCES:
   cronolog-jumbo-patch.txt (NONE -> 1.1)  (NEW)

---- Diffs:

================================================================
Index: SOURCES/cronolog-jumbo-patch.txt
diff -u /dev/null SOURCES/cronolog-jumbo-patch.txt:1.1
--- /dev/null	Tue Dec  6 14:07:05 2005
+++ SOURCES/cronolog-jumbo-patch.txt	Tue Dec  6 14:07:00 2005
@@ -0,0 +1,663 @@
+From: Matthew Grosso < mgrosso at looksmart dot net>
+To: cronolog-users at icarus.demon.co.uk
+Subject: CRONOLOG: jumbo patch, includes launching external program to handle logs
+Date: Fri, 10 Oct 2003 13:02:14 -0400
+
+I've put together a jumbo patch with some features that are on the todo 
+list and most of the outstanding patches on the site. specifically:
+
+the todo list items completed (which motivated the work for my companies use) are:
+
+- ability to launch a helper program on every log file after it is closed 
+    if multiple cronologs writing to the same file, only one helper is  
+    launched.
+
+- signal handling to ensure that every log file has a chance to be handled
+    by that helper and that cronolog doesnt ever hang apache restarts
+
+- alarm() used so log files rotate on time period boundary even if there is no
+  traffic
+
+I also implemented (with tweaks) many of the outstanding patches, some of
+which are also on the todo list:
+
+- uid/gid patch from Isaac Wilcox <iwilcox at eatstatic dot net>
+    minus the configure changes which broke the build for me.
+
+- the large file patch from matt lanier <mlanier at danger dot .com>
+    with tweaks from me to work with my own changes above.
+
+- incorporated a slightly different implementation of
+    the SIGUSR1 patch from Prakash Kailasa <PKailasa at seisint dot com>
+
+- the s/stat/lstat/ fix from Victor Martinez <victor at kablinkteam dot com>
+
+the changes have only been tested on my Linux 2.4 box, and should be considered
+beta until more users have tried it. That said, we'll be starting to qa this at
+looksmart soon, but again, only on linux.
+
+I'm including a sample script that can be used as a helper. It will first
+compress, then scp a log to multiple destinations, then remove it.
+
+Still todo: I need to put my new options into the man page, not just the usage,
+and see if the configure.in needs modification.
+
+the patch is available below, and at this url:
+http://www.falconweb.com/~mattg/code/cronolog-1.6.2-jumbo-20031010.diff
+
+diff -Nur cronolog-1.6.2/src/cronolog.c cronolog-1.6.2-jumbo-20031008/src/cronolog.c
+--- cronolog-1.6.2/src/cronolog.c	2001-05-03 12:42:48.000000000 -0400
++++ cronolog-1.6.2-jumbo-20031008/src/cronolog.c	2003-10-08 00:22:10.000000000 -0400
+@@ -82,6 +82,18 @@
+  * written to "file" (e.g. /dev/console) or to stderr if "file" is "-".
+  */
+ 
++#ifndef _WIN32
++#define _GNU_SOURCE 1
++#define OPEN_EXCLUSIVE O_WRONLY|O_CREAT|O_EXCL|O_APPEND|O_LARGEFILE
++#define OPEN_SHARED O_WRONLY|O_CREAT|O_APPEND|O_LARGEFILE
++#else
++#define OPEN_EXCLUSIVE O_WRONLY|O_CREAT|O_EXCL|O_APPEND
++#define OPEN_SHARED O_WRONLY|O_CREAT|O_APPEND
++#endif
++
++#include <sys/types.h>
++#include <sys/wait.h>
++#include <signal.h>
+ #include "cronoutils.h"
+ #include "getopt.h"
+ 
+@@ -91,6 +103,16 @@
+ int	new_log_file(const char *, const char *, mode_t, const char *,
+ 		     PERIODICITY, int, int, char *, size_t, time_t, time_t *);
+ 
++void	cleanup(int );
++void	handle_file();
++void	fork_to_handle_file();
++
++int     openwrapper( const char *filename );
++
++#ifndef _WIN32
++void    setsig_handler( int signum,  void (*action)(int, siginfo_t *, void *));
++void    set_signal_handlers();
++#endif
+ 
+ /* Definition of version and usage messages */
+ 
+@@ -100,6 +122,12 @@
+ #define VERSION_MSG      "cronolog version 0.1\n"
+ #endif
+ 
++#ifndef _WIN32
++#define SETUGID_USAGE	"   -u USER,   --set-uid=USER  change to USER before doing anything (name or UID)\n" \
++			"   -g GROUP,  --set-gid=GROUP change to GROUP before doing anything (name or GID)\n"
++#else
++#define SETUGID_USAGE	""
++#endif
+ 
+ #define USAGE_MSG 	"usage: %s [OPTIONS] logfile-spec\n" \
+ 			"\n" \
+@@ -113,6 +141,11 @@
+ 			"   -o,        --once-only     create single output log from template (not rotated)\n" \
+ 			"   -x FILE,   --debug=FILE    write debug messages to FILE\n" \
+ 			"                              ( or to standard error if FILE is \"-\")\n" \
++			"   -r,        --helper=SCRIPT post rotation helper script to fork exec on old files\n" \
++			"                              ( will be called like \"SCRIPT <oldlog>\" )\n" \
++			"                              ( not tested on windows )\n" \
++			"   -G,        --helper-arg=ARG argument passed to rotation helper script\n" \
++            SETUGID_USAGE \
+ 			"   -a,        --american         American date formats\n" \
+ 			"   -e,        --european         European date formats (default)\n" \
+ 			"   -s,    --start-time=TIME   starting time\n" \
+@@ -122,7 +155,7 @@
+ 
+ /* Definition of the short and long program options */
+ 
+-char          *short_options = "ad:eop:s:z:H:P:S:l:hVx:";
++char          *short_options = "ad:eop:s:z:H:P:S:l:hVx:r:G:u:g:";
+ 
+ #ifndef _WIN32
+ struct option long_options[] =
+@@ -137,12 +170,23 @@
+     { "link",      	required_argument, 	NULL, 'l' },
+     { "period",		required_argument,	NULL, 'p' },
+     { "delay",		required_argument,	NULL, 'd' },
++    { "helper",		required_argument,	NULL, 'r' },
++    { "helper-arg",	required_argument,	NULL, 'G' },
++    { "set-uid",    required_argument,  NULL, 'u' },
++    { "set-gid",    required_argument,  NULL, 'g' },
+     { "once-only", 	no_argument,       	NULL, 'o' },
+     { "help",      	no_argument,       	NULL, 'h' },
+     { "version",   	no_argument,       	NULL, 'V' }
+ };
+ #endif
+ 
++static    char	handler[MAX_PATH];
++static    char	handler_arg[MAX_PATH];
++static    char	filename[MAX_PATH];
++static    int   use_handler =0;
++static    int   use_handler_arg =0;
++static    int   i_am_handler =0;
++
+ /* Main function.
+  */
+ int
+@@ -155,11 +199,16 @@
+     int		use_american_date_formats = 0;
+     char 	read_buf[BUFSIZE];
+     char 	tzbuf[BUFSIZE];
+-    char	filename[MAX_PATH];
+     char	*start_time = NULL;
+     char	*template;
+     char	*linkname = NULL;
+     char	*prevlinkname = NULL;
++#ifndef _WIN32
++    uid_t	new_uid = 0;
++    gid_t	new_gid = 0;
++    int		change_uid = 0;
++    int		change_gid = 0;
++#endif
+     mode_t	linktype = 0;
+     int 	n_bytes_read;
+     int		ch;
+@@ -167,6 +216,10 @@
+     time_t	time_offset = 0;
+     time_t	next_period = 0;
+     int 	log_fd = -1;
++    
++    memset( handler, '\0', MAX_PATH );
++    memset( handler_arg, '\0', MAX_PATH );
++    memset( filename, '\0', MAX_PATH );
+ 
+ #ifndef _WIN32
+     while ((ch = getopt_long(argc, argv, short_options, long_options, NULL)) != EOF)
+@@ -234,6 +287,16 @@
+ 	    }		
+ 	    break;
+ 	    
++#ifndef _WIN32
++	case 'u':
++	    new_uid = parse_uid(optarg, argv[0]);
++	    change_uid = 1;
++	    break;
++	case 'g':
++	    new_gid = parse_gid(optarg, argv[0]);
++	    change_gid = 1;
++	    break;
++#endif
+ 	case 'o':
+ 	    periodicity = ONCE_ONLY;
+ 	    break;
+@@ -248,7 +311,15 @@
+ 		debug_file = fopen(optarg, "a+");
+ 	    }
+ 	    break;
+-	    
++	case 'r':
++            strncat(handler, optarg, MAX_PATH );
++            use_handler=1;
++            break;
++	case 'G':
++            strncat(handler_arg, optarg, MAX_PATH );
++            use_handler_arg=1;
++            break;
++
+ 	case 'V':
+ 	    fprintf(stderr, VERSION_MSG);
+ 	    exit(0);
+@@ -266,6 +337,17 @@
+ 	exit(1);
+     }
+ 
++#ifndef _WIN32
++    if (change_gid && setgid(new_gid) == -1) {
++	fprintf(stderr, "setgid: unable to change to gid: %d\n", new_gid);
++       	exit(1);
++    }
++    if (change_uid && setuid(new_uid) == -1) {
++	fprintf(stderr, "setuid: unable to change to uid: %d\n", new_uid);
++       	exit(1);
++    }
++#endif
++
+     DEBUG((VERSION_MSG "\n"));
+ 
+     if (start_time)
+@@ -306,6 +388,10 @@
+     DEBUG(("Rotation period is per %d %s\n", period_multiple, periods[periodicity]));
+ 
+ 
++#ifndef _WIN32
++    set_signal_handlers();
++#endif
++
+     /* Loop, waiting for data on standard input */
+ 
+     for (;;)
+@@ -316,15 +402,17 @@
+ 	n_bytes_read = read(0, read_buf, sizeof read_buf);
+ 	if (n_bytes_read == 0)
+ 	{
+-	    exit(3);
++	    cleanup(3);
+ 	}
+ 	if (errno == EINTR)
+ 	{
+-	    continue;
++	    /* 
++             * fall through, it may have been alarm, in which case it will be time to rotate.
++             * */
+ 	}
+ 	else if (n_bytes_read < 0)
+ 	{
+-	    exit(4);
++	    cleanup(4);
+ 	}
+ 
+ 	time_now = time(NULL) + time_offset;
+@@ -336,6 +424,7 @@
+ 	{
+ 	    close(log_fd);
+ 	    log_fd = -1;
++            fork_to_handle_file();
+ 	}
+ 	
+ 	/* If there is no log file open then open a new one.
+@@ -345,6 +434,7 @@
+ 	    log_fd = new_log_file(template, linkname, linktype, prevlinkname,
+ 				  periodicity, period_multiple, period_delay,
+ 				  filename, sizeof (filename), time_now, &next_period);
++            alarm( next_period - time_now );
+ 	}
+ 
+ 	DEBUG(("%s (%d): wrote message; next period starts at %s (%d) in %d secs\n",
+@@ -354,10 +444,10 @@
+ 
+ 	/* Write out the log data to the current log file.
+ 	 */
+-	if (write(log_fd, read_buf, n_bytes_read) != n_bytes_read)
++	if (n_bytes_read && write(log_fd, read_buf, n_bytes_read) != n_bytes_read)
+ 	{
+ 	    perror(filename);
+-	    exit(5);
++	    cleanup(5);
+ 	}
+     }
+ 
+@@ -383,6 +473,7 @@
+     struct tm 	*tm;
+     int 	log_fd;
+ 
++
+     start_of_period = start_of_this_period(time_now, periodicity, period_multiple);
+     tm = localtime(&start_of_period);
+     strftime(pfilename, BUFSIZE, template, tm);
+@@ -394,13 +485,13 @@
+ 	   timestamp(*pnext_period), *pnext_period,
+ 	   *pnext_period - time_now));
+     
+-    log_fd = open(pfilename, O_WRONLY|O_CREAT|O_APPEND, FILE_MODE);
++    log_fd = openwrapper(pfilename);
+     
+ #ifndef DONT_CREATE_SUBDIRS
+     if ((log_fd < 0) && (errno == ENOENT))
+     {
+ 	create_subdirs(pfilename);
+-	log_fd = open(pfilename, O_WRONLY|O_CREAT|O_APPEND, FILE_MODE);
++	log_fd = openwrapper(pfilename);
+     }
+ #endif	    
+ 
+@@ -416,3 +507,179 @@
+     }
+     return log_fd;
+ }
++
++/* 
++ * fork, then exec an external handler to deal with rotated file.
++ */
++void
++fork_to_handle_file()
++{
++    int fk ;
++    static int childpid=0;
++
++    if( ! use_handler || !i_am_handler || handler[0] =='\0' || filename[0] == '\0' )
++    {
++        return;
++    }
++    fk=fork();
++    if( fk < 0 )
++    {
++        perror("couldnt fork");
++        exit(2);
++    }else if( fk > 0 )
++    {
++        if( childpid )
++        {
++            /* 
++             * collect zombies. run twice, in case one or more children took longer than
++             * the rotation period for a while, this will eventually clean them up.
++             * Of course, if handler children take longer than rotation period to handle
++             * things, you will eventually have a big problem.
++             * 
++             * */ 
++            (void) waitpid( 0, NULL, WNOHANG | WUNTRACED );
++            (void) waitpid( 0, NULL, WNOHANG | WUNTRACED );
++        }
++        childpid=fk;
++        return; /* parent */
++    }
++    /* child */
++    /* dont muck with stdin or out of parent, but allow stderr to be commingled */
++    close(0);
++    close(1);
++    handle_file();
++}
++
++/* 
++ * exec an external handler to deal with rotated file.
++ */
++void 
++handle_file()
++{
++    char **exec_argv ;
++    if( ! use_handler || !i_am_handler || handler[0] =='\0' || filename[0] == '\0' )
++    {
++        return;
++    }
++    if ( use_handler_arg == 0 )
++    { 
++        exec_argv = malloc( sizeof( char *)*3);
++        exec_argv[0] = strdup( handler );
++        exec_argv[1] = strdup( filename );
++        exec_argv[2] = NULL;
++    }else
++    {
++        exec_argv = malloc( sizeof( char *)*4);
++        exec_argv[0] = strdup( handler );
++        exec_argv[1] = strdup( handler_arg );
++        exec_argv[2] = strdup( filename );
++        exec_argv[3] = NULL ;
++    }
++    execvp( exec_argv[0], exec_argv );
++    perror("cant execvp");
++    exit(2);
++}
++
++
++
++#ifndef _WIN32
++/* 
++ * wrapper to be called as signal handler.
++ */
++void 
++handle_file_on_sig( int sig, siginfo_t *si, void *v )
++{
++    handle_file();
++    /* not reached */
++    exit( 3 );
++};
++
++/* 
++ * wrapper to be called for alarm signal
++ */
++void 
++alarm_signal_handler( int sig, siginfo_t *si, void *v )
++{
++        ;
++        /* 
++         * do nothing; the key thing is that the alarm will cause the read()
++         * to fail with errno=EINTR. this empty handler is required, because the
++         * default handler will exit(1)
++         *
++         */
++};
++
++void
++set_signal_handlers()
++{
++    /* 
++     * all signals which usually kill a process that can be caught are
++     * set to handle_file when received. This will make apache shutdowns more 
++     * graceful even if use_handler is false.
++     */
++    setsig_handler( SIGHUP, handle_file_on_sig );
++    setsig_handler( SIGINT, handle_file_on_sig );
++    setsig_handler( SIGQUIT, handle_file_on_sig );
++    setsig_handler( SIGILL, handle_file_on_sig );
++    setsig_handler( SIGABRT, handle_file_on_sig );
++    setsig_handler( SIGBUS, handle_file_on_sig );
++    setsig_handler( SIGFPE, handle_file_on_sig );
++    setsig_handler( SIGPIPE, handle_file_on_sig );
++    setsig_handler( SIGTERM, handle_file_on_sig );
++    setsig_handler( SIGUSR1, handle_file_on_sig );
++
++    /* sigalrm is used to break out of read() when it is time to rotate the log. */
++    setsig_handler( SIGALRM, alarm_signal_handler );
++}
++
++void
++setsig_handler( int signum,  void (*action)(int, siginfo_t *, void *))
++{
++    struct sigaction siga ;
++    memset( &siga, '\0', sizeof( struct sigaction ));
++    siga.sa_sigaction= action ;
++    siga.sa_flags = SA_SIGINFO ;
++    if( -1== sigaction( signum, &siga, NULL ))
++    {
++        perror( "cant set sigaction" );
++    }
++}
++#endif
++
++
++/* 
++ * cleanup
++ */
++void
++cleanup( int exit_status )
++{
++    handle_file();
++    exit(exit_status);
++}
++
++/* 
++ * only the first cronolog process to open a particular file is responsible
++ * for starting the cleanup process later. This wrapper sets i_am_handler
++ * according to that logic.
++ * */
++int 
++openwrapper( const char *ofilename )
++{
++    int ret;
++    if( use_handler !=1 )
++    {
++        return open(ofilename, OPEN_SHARED, S_IRWXU );
++    }
++    ret = open(ofilename, OPEN_EXCLUSIVE, S_IRWXU );
++    if( ret < 0 )
++    {
++        ret = open(ofilename, OPEN_SHARED, S_IRWXU );
++        i_am_handler= 0;
++    }
++    else
++    {
++        i_am_handler=1;
++    }
++    return ret;
++}
++
+diff -Nur cronolog-1.6.2/src/cronoutils.c cronolog-1.6.2-jumbo-20031008/src/cronoutils.c
+--- cronolog-1.6.2/src/cronoutils.c	2001-05-03 12:43:21.000000000 -0400
++++ cronolog-1.6.2-jumbo-20031008/src/cronoutils.c	2003-10-08 00:22:10.000000000 -0400
+@@ -195,11 +195,11 @@
+ {
+     struct stat		stat_buf;
+     
+-    if (stat(prevlinkname, &stat_buf) == 0)
++    if (lstat(prevlinkname, &stat_buf) == 0)
+     {
+ 	unlink(prevlinkname);
+     }
+-    if (stat(linkname, &stat_buf) == 0)
++    if (lstat(linkname, &stat_buf) == 0)
+     {
+ 	if (prevlinkname) {
+ 	    rename(linkname, prevlinkname);
+@@ -710,4 +710,50 @@
+     return retval;
+ }
+ 
+-    
++
++#ifndef _WIN32
++/* Turn a string specifying either a username or UID into an actual
++ * uid_t for use in setuid(). A string is assumed to be a UID if
++ * it contains only decimal digits. */
++uid_t
++parse_uid(char *user, char *argv0)
++{
++    char		*probe = user;
++    struct passwd	*ent;
++
++    while (*probe && isdigit(*probe)) {
++	probe++;
++    }
++    if (!(*probe)) {
++	return atoi(user);
++    }
++    if (!(ent = getpwnam(user))) {
++	fprintf(stderr, "%s: Bad username %s\n", argv0, user);
++	exit(1);
++    }
++    return (ent->pw_uid);
++}
++
++
++/* Turn a string specifying either a group name or GID into an actual
++ * gid_t for use in setgid(). A string is assumed to be a GID if
++ * it contains only decimal digits. */
++gid_t
++parse_gid(char *group, char *argv0)
++{
++    char		*probe = group;
++    struct group	*ent;
++
++    while (*probe && isdigit(*probe)) {
++	probe++;
++    }
++    if (!(*probe)) {
++	return atoi(group);
++    }
++    if (!(ent = getgrnam(group))) {
++	fprintf(stderr, "%s: Bad group name %s\n", argv0, group);
++	exit(1);
++    }
++    return (ent->gr_gid);
++}
++#endif /* _WIN32 */
+diff -Nur cronolog-1.6.2/src/cronoutils.h cronolog-1.6.2-jumbo-20031008/src/cronoutils.h
+--- cronolog-1.6.2/src/cronoutils.h	2001-05-03 12:40:12.000000000 -0400
++++ cronolog-1.6.2-jumbo-20031008/src/cronoutils.h	2003-10-08 00:22:10.000000000 -0400
+@@ -84,6 +84,8 @@
+ #include <limits.h>
+ #ifndef _WIN32
+ #include <unistd.h>
++#include <pwd.h>
++#include <grp.h>
+ #else
+ #include <io.h>
+ #include <direct.h>
+@@ -172,7 +174,8 @@
+ void		print_debug_msg(char *msg, ...);
+ time_t		parse_time(char *time_str, int);
+ char 		*timestamp(time_t thetime);
+-
++uid_t		parse_uid(char *user, char *argv0);
++gid_t		parse_gid(char *group, char *argv0);
+ 
+ /* Global variables */
+ 
+diff -Nur cronolog-1.6.2/src/zip_send_rm.sh cronolog-1.6.2-jumbo-20031008/src/zip_send_rm.sh
+--- cronolog-1.6.2/src/zip_send_rm.sh	1969-12-31 19:00:00.000000000 -0500
++++ cronolog-1.6.2-jumbo-20031008/src/zip_send_rm.sh	2003-10-10 12:37:22.000000000 -0400
+@@ -0,0 +1,64 @@
++#!/bin/bash
++## ----------------------------------------------------------------------
++## ----------------------------------------------------------------------
++##
++## File:      zip_send_rm
++## Author:    mgrosso 
<<Diff was trimmed, longer than 597 lines>>



More information about the pld-cvs-commit mailing list