]> git.pld-linux.org Git - packages/cronolog.git/blame - cronolog-jumbo-patch.txt
- invoke fix-info-dir via /sbin/postshell where possible to avoid extra /bin/sh dep
[packages/cronolog.git] / cronolog-jumbo-patch.txt
CommitLineData
41873642
ER
1From: Matthew Grosso < mgrosso at looksmart dot net>
2To: cronolog-users@icarus.demon.co.uk
3Subject: CRONOLOG: jumbo patch, includes launching external program to handle logs
4Date: Fri, 10 Oct 2003 13:02:14 -0400
5
6I've put together a jumbo patch with some features that are on the todo
7list and most of the outstanding patches on the site. specifically:
8
9the todo list items completed (which motivated the work for my companies use) are:
10
11- ability to launch a helper program on every log file after it is closed
12 if multiple cronologs writing to the same file, only one helper is
13 launched.
14
15- signal handling to ensure that every log file has a chance to be handled
16 by that helper and that cronolog doesnt ever hang apache restarts
17
18- alarm() used so log files rotate on time period boundary even if there is no
19 traffic
20
21I also implemented (with tweaks) many of the outstanding patches, some of
22which are also on the todo list:
23
24- uid/gid patch from Isaac Wilcox <iwilcox at eatstatic dot net>
25 minus the configure changes which broke the build for me.
26
27- the large file patch from matt lanier <mlanier at danger dot .com>
28 with tweaks from me to work with my own changes above.
29
30- incorporated a slightly different implementation of
31 the SIGUSR1 patch from Prakash Kailasa <PKailasa at seisint dot com>
32
33- the s/stat/lstat/ fix from Victor Martinez <victor at kablinkteam dot com>
34
35the changes have only been tested on my Linux 2.4 box, and should be considered
36beta until more users have tried it. That said, we'll be starting to qa this at
37looksmart soon, but again, only on linux.
38
39I'm including a sample script that can be used as a helper. It will first
40compress, then scp a log to multiple destinations, then remove it.
41
42Still todo: I need to put my new options into the man page, not just the usage,
43and see if the configure.in needs modification.
44
45the patch is available below, and at this url:
46http://www.falconweb.com/~mattg/code/cronolog-1.6.2-jumbo-20031010.diff
47
48diff -Nur cronolog-1.6.2/src/cronolog.c cronolog-1.6.2-jumbo-20031008/src/cronolog.c
49--- cronolog-1.6.2/src/cronolog.c 2001-05-03 12:42:48.000000000 -0400
50+++ cronolog-1.6.2-jumbo-20031008/src/cronolog.c 2003-10-08 00:22:10.000000000 -0400
51@@ -82,6 +82,18 @@
52 * written to "file" (e.g. /dev/console) or to stderr if "file" is "-".
53 */
54
55+#ifndef _WIN32
56+#define _GNU_SOURCE 1
57+#define OPEN_EXCLUSIVE O_WRONLY|O_CREAT|O_EXCL|O_APPEND|O_LARGEFILE
58+#define OPEN_SHARED O_WRONLY|O_CREAT|O_APPEND|O_LARGEFILE
59+#else
60+#define OPEN_EXCLUSIVE O_WRONLY|O_CREAT|O_EXCL|O_APPEND
61+#define OPEN_SHARED O_WRONLY|O_CREAT|O_APPEND
62+#endif
63+
64+#include <sys/types.h>
65+#include <sys/wait.h>
66+#include <signal.h>
67 #include "cronoutils.h"
68 #include "getopt.h"
69
70@@ -91,6 +103,16 @@
71 int new_log_file(const char *, const char *, mode_t, const char *,
72 PERIODICITY, int, int, char *, size_t, time_t, time_t *);
73
74+void cleanup(int );
75+void handle_file();
76+void fork_to_handle_file();
77+
78+int openwrapper( const char *filename );
79+
80+#ifndef _WIN32
81+void setsig_handler( int signum, void (*action)(int, siginfo_t *, void *));
82+void set_signal_handlers();
83+#endif
84
85 /* Definition of version and usage messages */
86
87@@ -100,6 +122,12 @@
88 #define VERSION_MSG "cronolog version 0.1\n"
89 #endif
90
91+#ifndef _WIN32
92+#define SETUGID_USAGE " -u USER, --set-uid=USER change to USER before doing anything (name or UID)\n" \
93+ " -g GROUP, --set-gid=GROUP change to GROUP before doing anything (name or GID)\n"
94+#else
95+#define SETUGID_USAGE ""
96+#endif
97
98 #define USAGE_MSG "usage: %s [OPTIONS] logfile-spec\n" \
99 "\n" \
100@@ -113,6 +141,11 @@
101 " -o, --once-only create single output log from template (not rotated)\n" \
102 " -x FILE, --debug=FILE write debug messages to FILE\n" \
103 " ( or to standard error if FILE is \"-\")\n" \
104+ " -r, --helper=SCRIPT post rotation helper script to fork exec on old files\n" \
105+ " ( will be called like \"SCRIPT <oldlog>\" )\n" \
106+ " ( not tested on windows )\n" \
107+ " -G, --helper-arg=ARG argument passed to rotation helper script\n" \
108+ SETUGID_USAGE \
109 " -a, --american American date formats\n" \
110 " -e, --european European date formats (default)\n" \
111 " -s, --start-time=TIME starting time\n" \
112@@ -122,7 +155,7 @@
113
114 /* Definition of the short and long program options */
115
116-char *short_options = "ad:eop:s:z:H:P:S:l:hVx:";
117+char *short_options = "ad:eop:s:z:H:P:S:l:hVx:r:G:u:g:";
118
119 #ifndef _WIN32
120 struct option long_options[] =
121@@ -137,12 +170,23 @@
122 { "link", required_argument, NULL, 'l' },
123 { "period", required_argument, NULL, 'p' },
124 { "delay", required_argument, NULL, 'd' },
125+ { "helper", required_argument, NULL, 'r' },
126+ { "helper-arg", required_argument, NULL, 'G' },
127+ { "set-uid", required_argument, NULL, 'u' },
128+ { "set-gid", required_argument, NULL, 'g' },
129 { "once-only", no_argument, NULL, 'o' },
130 { "help", no_argument, NULL, 'h' },
131 { "version", no_argument, NULL, 'V' }
132 };
133 #endif
134
135+static char handler[MAX_PATH];
136+static char handler_arg[MAX_PATH];
137+static char filename[MAX_PATH];
138+static int use_handler =0;
139+static int use_handler_arg =0;
140+static int i_am_handler =0;
141+
142 /* Main function.
143 */
144 int
145@@ -155,11 +199,16 @@
146 int use_american_date_formats = 0;
147 char read_buf[BUFSIZE];
148 char tzbuf[BUFSIZE];
149- char filename[MAX_PATH];
150 char *start_time = NULL;
151 char *template;
152 char *linkname = NULL;
153 char *prevlinkname = NULL;
154+#ifndef _WIN32
155+ uid_t new_uid = 0;
156+ gid_t new_gid = 0;
157+ int change_uid = 0;
158+ int change_gid = 0;
159+#endif
160 mode_t linktype = 0;
161 int n_bytes_read;
162 int ch;
163@@ -167,6 +216,10 @@
164 time_t time_offset = 0;
165 time_t next_period = 0;
166 int log_fd = -1;
167+
168+ memset( handler, '\0', MAX_PATH );
169+ memset( handler_arg, '\0', MAX_PATH );
170+ memset( filename, '\0', MAX_PATH );
171
172 #ifndef _WIN32
173 while ((ch = getopt_long(argc, argv, short_options, long_options, NULL)) != EOF)
174@@ -234,6 +287,16 @@
175 }
176 break;
177
178+#ifndef _WIN32
179+ case 'u':
180+ new_uid = parse_uid(optarg, argv[0]);
181+ change_uid = 1;
182+ break;
183+ case 'g':
184+ new_gid = parse_gid(optarg, argv[0]);
185+ change_gid = 1;
186+ break;
187+#endif
188 case 'o':
189 periodicity = ONCE_ONLY;
190 break;
191@@ -248,7 +311,15 @@
192 debug_file = fopen(optarg, "a+");
193 }
194 break;
195-
196+ case 'r':
197+ strncat(handler, optarg, MAX_PATH );
198+ use_handler=1;
199+ break;
200+ case 'G':
201+ strncat(handler_arg, optarg, MAX_PATH );
202+ use_handler_arg=1;
203+ break;
204+
205 case 'V':
206 fprintf(stderr, VERSION_MSG);
207 exit(0);
208@@ -266,6 +337,17 @@
209 exit(1);
210 }
211
212+#ifndef _WIN32
213+ if (change_gid && setgid(new_gid) == -1) {
214+ fprintf(stderr, "setgid: unable to change to gid: %d\n", new_gid);
215+ exit(1);
216+ }
217+ if (change_uid && setuid(new_uid) == -1) {
218+ fprintf(stderr, "setuid: unable to change to uid: %d\n", new_uid);
219+ exit(1);
220+ }
221+#endif
222+
223 DEBUG((VERSION_MSG "\n"));
224
225 if (start_time)
226@@ -306,6 +388,10 @@
227 DEBUG(("Rotation period is per %d %s\n", period_multiple, periods[periodicity]));
228
229
230+#ifndef _WIN32
231+ set_signal_handlers();
232+#endif
233+
234 /* Loop, waiting for data on standard input */
235
236 for (;;)
237@@ -316,15 +402,17 @@
238 n_bytes_read = read(0, read_buf, sizeof read_buf);
239 if (n_bytes_read == 0)
240 {
241- exit(3);
242+ cleanup(3);
243 }
244 if (errno == EINTR)
245 {
246- continue;
247+ /*
248+ * fall through, it may have been alarm, in which case it will be time to rotate.
249+ * */
250 }
251 else if (n_bytes_read < 0)
252 {
253- exit(4);
254+ cleanup(4);
255 }
256
257 time_now = time(NULL) + time_offset;
258@@ -336,6 +424,7 @@
259 {
260 close(log_fd);
261 log_fd = -1;
262+ fork_to_handle_file();
263 }
264
265 /* If there is no log file open then open a new one.
266@@ -345,6 +434,7 @@
267 log_fd = new_log_file(template, linkname, linktype, prevlinkname,
268 periodicity, period_multiple, period_delay,
269 filename, sizeof (filename), time_now, &next_period);
270+ alarm( next_period - time_now );
271 }
272
273 DEBUG(("%s (%d): wrote message; next period starts at %s (%d) in %d secs\n",
274@@ -354,10 +444,10 @@
275
276 /* Write out the log data to the current log file.
277 */
278- if (write(log_fd, read_buf, n_bytes_read) != n_bytes_read)
279+ if (n_bytes_read && write(log_fd, read_buf, n_bytes_read) != n_bytes_read)
280 {
281 perror(filename);
282- exit(5);
283+ cleanup(5);
284 }
285 }
286
287@@ -383,6 +473,7 @@
288 struct tm *tm;
289 int log_fd;
290
291+
292 start_of_period = start_of_this_period(time_now, periodicity, period_multiple);
293 tm = localtime(&start_of_period);
294 strftime(pfilename, BUFSIZE, template, tm);
295@@ -394,13 +485,13 @@
296 timestamp(*pnext_period), *pnext_period,
297 *pnext_period - time_now));
298
299- log_fd = open(pfilename, O_WRONLY|O_CREAT|O_APPEND, FILE_MODE);
300+ log_fd = openwrapper(pfilename);
301
302 #ifndef DONT_CREATE_SUBDIRS
303 if ((log_fd < 0) && (errno == ENOENT))
304 {
305 create_subdirs(pfilename);
306- log_fd = open(pfilename, O_WRONLY|O_CREAT|O_APPEND, FILE_MODE);
307+ log_fd = openwrapper(pfilename);
308 }
309 #endif
310
311@@ -416,3 +507,179 @@
312 }
313 return log_fd;
314 }
315+
316+/*
317+ * fork, then exec an external handler to deal with rotated file.
318+ */
319+void
320+fork_to_handle_file()
321+{
322+ int fk ;
323+ static int childpid=0;
324+
325+ if( ! use_handler || !i_am_handler || handler[0] =='\0' || filename[0] == '\0' )
326+ {
327+ return;
328+ }
329+ fk=fork();
330+ if( fk < 0 )
331+ {
332+ perror("couldnt fork");
333+ exit(2);
334+ }else if( fk > 0 )
335+ {
336+ if( childpid )
337+ {
338+ /*
339+ * collect zombies. run twice, in case one or more children took longer than
340+ * the rotation period for a while, this will eventually clean them up.
341+ * Of course, if handler children take longer than rotation period to handle
342+ * things, you will eventually have a big problem.
343+ *
344+ * */
345+ (void) waitpid( 0, NULL, WNOHANG | WUNTRACED );
346+ (void) waitpid( 0, NULL, WNOHANG | WUNTRACED );
347+ }
348+ childpid=fk;
349+ return; /* parent */
350+ }
351+ /* child */
352+ /* dont muck with stdin or out of parent, but allow stderr to be commingled */
353+ close(0);
354+ close(1);
355+ handle_file();
356+}
357+
358+/*
359+ * exec an external handler to deal with rotated file.
360+ */
361+void
362+handle_file()
363+{
364+ char **exec_argv ;
365+ if( ! use_handler || !i_am_handler || handler[0] =='\0' || filename[0] == '\0' )
366+ {
367+ return;
368+ }
369+ if ( use_handler_arg == 0 )
370+ {
371+ exec_argv = malloc( sizeof( char *)*3);
372+ exec_argv[0] = strdup( handler );
373+ exec_argv[1] = strdup( filename );
374+ exec_argv[2] = NULL;
375+ }else
376+ {
377+ exec_argv = malloc( sizeof( char *)*4);
378+ exec_argv[0] = strdup( handler );
379+ exec_argv[1] = strdup( handler_arg );
380+ exec_argv[2] = strdup( filename );
381+ exec_argv[3] = NULL ;
382+ }
383+ execvp( exec_argv[0], exec_argv );
384+ perror("cant execvp");
385+ exit(2);
386+}
387+
388+
389+
390+#ifndef _WIN32
391+/*
392+ * wrapper to be called as signal handler.
393+ */
394+void
395+handle_file_on_sig( int sig, siginfo_t *si, void *v )
396+{
397+ handle_file();
398+ /* not reached */
399+ exit( 3 );
400+};
401+
402+/*
403+ * wrapper to be called for alarm signal
404+ */
405+void
406+alarm_signal_handler( int sig, siginfo_t *si, void *v )
407+{
408+ ;
409+ /*
410+ * do nothing; the key thing is that the alarm will cause the read()
411+ * to fail with errno=EINTR. this empty handler is required, because the
412+ * default handler will exit(1)
413+ *
414+ */
415+};
416+
417+void
418+set_signal_handlers()
419+{
420+ /*
421+ * all signals which usually kill a process that can be caught are
422+ * set to handle_file when received. This will make apache shutdowns more
423+ * graceful even if use_handler is false.
424+ */
425+ setsig_handler( SIGHUP, handle_file_on_sig );
426+ setsig_handler( SIGINT, handle_file_on_sig );
427+ setsig_handler( SIGQUIT, handle_file_on_sig );
428+ setsig_handler( SIGILL, handle_file_on_sig );
429+ setsig_handler( SIGABRT, handle_file_on_sig );
430+ setsig_handler( SIGBUS, handle_file_on_sig );
431+ setsig_handler( SIGFPE, handle_file_on_sig );
432+ setsig_handler( SIGPIPE, handle_file_on_sig );
433+ setsig_handler( SIGTERM, handle_file_on_sig );
434+ setsig_handler( SIGUSR1, handle_file_on_sig );
435+
436+ /* sigalrm is used to break out of read() when it is time to rotate the log. */
437+ setsig_handler( SIGALRM, alarm_signal_handler );
438+}
439+
440+void
441+setsig_handler( int signum, void (*action)(int, siginfo_t *, void *))
442+{
443+ struct sigaction siga ;
444+ memset( &siga, '\0', sizeof( struct sigaction ));
445+ siga.sa_sigaction= action ;
446+ siga.sa_flags = SA_SIGINFO ;
447+ if( -1== sigaction( signum, &siga, NULL ))
448+ {
449+ perror( "cant set sigaction" );
450+ }
451+}
452+#endif
453+
454+
455+/*
456+ * cleanup
457+ */
458+void
459+cleanup( int exit_status )
460+{
461+ handle_file();
462+ exit(exit_status);
463+}
464+
465+/*
466+ * only the first cronolog process to open a particular file is responsible
467+ * for starting the cleanup process later. This wrapper sets i_am_handler
468+ * according to that logic.
469+ * */
470+int
471+openwrapper( const char *ofilename )
472+{
473+ int ret;
474+ if( use_handler !=1 )
475+ {
476+ return open(ofilename, OPEN_SHARED, S_IRWXU );
477+ }
478+ ret = open(ofilename, OPEN_EXCLUSIVE, S_IRWXU );
479+ if( ret < 0 )
480+ {
481+ ret = open(ofilename, OPEN_SHARED, S_IRWXU );
482+ i_am_handler= 0;
483+ }
484+ else
485+ {
486+ i_am_handler=1;
487+ }
488+ return ret;
489+}
490+
491diff -Nur cronolog-1.6.2/src/cronoutils.c cronolog-1.6.2-jumbo-20031008/src/cronoutils.c
492--- cronolog-1.6.2/src/cronoutils.c 2001-05-03 12:43:21.000000000 -0400
493+++ cronolog-1.6.2-jumbo-20031008/src/cronoutils.c 2003-10-08 00:22:10.000000000 -0400
494@@ -195,11 +195,11 @@
495 {
496 struct stat stat_buf;
497
498- if (stat(prevlinkname, &stat_buf) == 0)
499+ if (lstat(prevlinkname, &stat_buf) == 0)
500 {
501 unlink(prevlinkname);
502 }
503- if (stat(linkname, &stat_buf) == 0)
504+ if (lstat(linkname, &stat_buf) == 0)
505 {
506 if (prevlinkname) {
507 rename(linkname, prevlinkname);
508@@ -710,4 +710,50 @@
509 return retval;
510 }
511
512-
513+
514+#ifndef _WIN32
515+/* Turn a string specifying either a username or UID into an actual
516+ * uid_t for use in setuid(). A string is assumed to be a UID if
517+ * it contains only decimal digits. */
518+uid_t
519+parse_uid(char *user, char *argv0)
520+{
521+ char *probe = user;
522+ struct passwd *ent;
523+
524+ while (*probe && isdigit(*probe)) {
525+ probe++;
526+ }
527+ if (!(*probe)) {
528+ return atoi(user);
529+ }
530+ if (!(ent = getpwnam(user))) {
531+ fprintf(stderr, "%s: Bad username %s\n", argv0, user);
532+ exit(1);
533+ }
534+ return (ent->pw_uid);
535+}
536+
537+
538+/* Turn a string specifying either a group name or GID into an actual
539+ * gid_t for use in setgid(). A string is assumed to be a GID if
540+ * it contains only decimal digits. */
541+gid_t
542+parse_gid(char *group, char *argv0)
543+{
544+ char *probe = group;
545+ struct group *ent;
546+
547+ while (*probe && isdigit(*probe)) {
548+ probe++;
549+ }
550+ if (!(*probe)) {
551+ return atoi(group);
552+ }
553+ if (!(ent = getgrnam(group))) {
554+ fprintf(stderr, "%s: Bad group name %s\n", argv0, group);
555+ exit(1);
556+ }
557+ return (ent->gr_gid);
558+}
559+#endif /* _WIN32 */
560diff -Nur cronolog-1.6.2/src/cronoutils.h cronolog-1.6.2-jumbo-20031008/src/cronoutils.h
561--- cronolog-1.6.2/src/cronoutils.h 2001-05-03 12:40:12.000000000 -0400
562+++ cronolog-1.6.2-jumbo-20031008/src/cronoutils.h 2003-10-08 00:22:10.000000000 -0400
563@@ -84,6 +84,8 @@
564 #include <limits.h>
565 #ifndef _WIN32
566 #include <unistd.h>
567+#include <pwd.h>
568+#include <grp.h>
569 #else
570 #include <io.h>
571 #include <direct.h>
572@@ -172,7 +174,8 @@
573 void print_debug_msg(char *msg, ...);
574 time_t parse_time(char *time_str, int);
575 char *timestamp(time_t thetime);
576-
577+uid_t parse_uid(char *user, char *argv0);
578+gid_t parse_gid(char *group, char *argv0);
579
580 /* Global variables */
581
582diff -Nur cronolog-1.6.2/src/zip_send_rm.sh cronolog-1.6.2-jumbo-20031008/src/zip_send_rm.sh
583--- cronolog-1.6.2/src/zip_send_rm.sh 1969-12-31 19:00:00.000000000 -0500
584+++ cronolog-1.6.2-jumbo-20031008/src/zip_send_rm.sh 2003-10-10 12:37:22.000000000 -0400
585@@ -0,0 +1,64 @@
586+#!/bin/bash
587+## ----------------------------------------------------------------------
588+## ----------------------------------------------------------------------
589+##
590+## File: zip_send_rm
591+## Author: mgrosso
592+## Created: Fri Oct 3 18:18:38 EDT 2003 on dhcp-172-18-102-101.looksmart.com
593+## Project: apache, cronolog
594+## Purpose: make damn sure the log file arrives at its destinations
595+##
596+## Copyright (c) 2003 LookSmart. All Rights Reserved.
597+##
598+## $Id$
599+## ----------------------------------------------------------------------
600+## ----------------------------------------------------------------------
601+
602+
603+function do_or_die()
604+{
605+ $@
606+ if [ $? -ne 0 ] ; then
607+ logger -s -p local0.crit "$0 puking on [ $@ ]"
608+ exit 1
609+ fi
610+}
611+
612+if [ $# == 2 ] ; then
613+ SCPDESTLIST=$1
614+ shift
615+fi
616+FILE=$1
617+
618+
619+
620+do_or_die gzip $FILE
621+
622+if [ -z $SCPDESTLIST ] ; then
623+ exit 0
624+fi
625+
626+FILE=${FILE}.gz
627+
628+# spawn subshells, so that each scp can suceed or fail on its own.
629+# while this is less efficient from a bandwidth and cpu perspective, it
630+# is safer because it ensures that a hanging dest cant prevent the other
631+# dests from working.
632+
633+SCPDESTINATIONS=$( echo $SCPDESTLIST | sed -e 's/,/ /g' )
634+for DEST in $SCPDESTINATIONS ; do
635+ (
636+ HDEST=$( echo $DEST | cut -d ':' -f 1 )
637+ BACKUP_LINK=${HDEST}-$FILE
638+ do_or_die ln $FILE $BACKUP_LINK
639+ do_or_die scp $FILE $DEST
640+ do_or_die rm $BACKUP_LINK
641+ ) &
642+done
643+wait
644+# we dont delete the file unless it arrive at all destinations
645+# because if one destination failed, then ${DEST}-$FILE will still exist
646+# even after we rm $FILE
647+do_or_die rm $FILE
648+exit 0
649+
650
651
652
653
654
655--
656#######################################################################
657# Matt Grosso cell (201)780-9592 mgrosso@looksmart.net #
658# work ny (212)324-1904 fax ny (212)324-1910 work sf (415)348-7756 #
659# pgp public key is at http://www.falconweb.com/~mattg/publickey.pgp #
660#######################################################################
661
662
663
This page took 0.137331 seconds and 4 git commands to generate.