1 | /* |
---|
2 | * newsyslog - roll over selected logs at the appropriate time, |
---|
3 | * keeping the a specified number of backup files around. |
---|
4 | * |
---|
5 | * $Id: newsyslog.c,v 1.11 1999-10-19 20:22:28 danw Exp $ |
---|
6 | */ |
---|
7 | |
---|
8 | static const char rcsid[] = "$Id: newsyslog.c,v 1.11 1999-10-19 20:22:28 danw Exp $"; |
---|
9 | |
---|
10 | #include "config.h" |
---|
11 | |
---|
12 | #include <sys/types.h> |
---|
13 | #include <stdio.h> |
---|
14 | #include <stdlib.h> |
---|
15 | #include <unistd.h> |
---|
16 | #ifdef HAVE_STRING_H |
---|
17 | #include <string.h> |
---|
18 | #else |
---|
19 | #include <strings.h> /* only VAXen don't have string.h --bert 15mar96 */ |
---|
20 | #endif |
---|
21 | #include <ctype.h> |
---|
22 | #include <signal.h> |
---|
23 | #include <pwd.h> |
---|
24 | #include <grp.h> |
---|
25 | #include <sys/stat.h> |
---|
26 | #include <sys/param.h> |
---|
27 | #include <sys/wait.h> |
---|
28 | #include <fcntl.h> |
---|
29 | #include <sys/time.h> |
---|
30 | #include <time.h> |
---|
31 | #include <dirent.h> |
---|
32 | |
---|
33 | #include "signames.h" |
---|
34 | |
---|
35 | #ifdef sgi |
---|
36 | /* Note: Irix has the "killall" command, which is like kill except |
---|
37 | that it takes a process name instead of an id. This command is |
---|
38 | not to be confused with the Solaris killall command, which kills |
---|
39 | just about everything. It would probably be nice just to go |
---|
40 | ahead and read the process table instead of execing an external |
---|
41 | program, but... */ |
---|
42 | #define ALLOW_PROCNAME |
---|
43 | #define USE_PROCNAME |
---|
44 | #endif |
---|
45 | |
---|
46 | /*** defines ***/ |
---|
47 | |
---|
48 | #define CONF "/etc/athena/newsyslog.conf" /* Configuration file */ |
---|
49 | #define SYSLOG_PID "/etc/syslog.pid" /* Pidfile for syslogd */ |
---|
50 | #define SYSLOG_PNAME "syslogd" /* Process name for syslogd */ |
---|
51 | |
---|
52 | /* COMPRESS is now defined in config.h */ |
---|
53 | |
---|
54 | #ifndef SLEEP_DELAY |
---|
55 | #define SLEEP_DELAY 1 /* Default delay used after restarting each daemon, */ |
---|
56 | #endif /* to give it time to clean up (in seconds) */ |
---|
57 | |
---|
58 | #define kbytes(size) (((size) + 1023) >> 10) |
---|
59 | #ifdef _IBMR2 |
---|
60 | /* Calculates (db * DEV_BSIZE) */ |
---|
61 | #define dbtob(db) ((unsigned)(db) << UBSHIFT) |
---|
62 | #elif defined(__linux__) |
---|
63 | #define dbtob(db) (db * DEV_BSIZE) |
---|
64 | #endif |
---|
65 | |
---|
66 | /* special (i.e. non-configurable) flags for logfile entries */ |
---|
67 | #define CE_ACTIVE 1 /* Logfile needs to be turned over */ |
---|
68 | #define CE_BINARY 2 /* Logfile is in binary, don't add status messages */ |
---|
69 | #define CE_DATED 4 /* Mark the logfile with date, not number */ |
---|
70 | |
---|
71 | #define NONE -1 |
---|
72 | |
---|
73 | /* Definitions of the keywords for the logfile. |
---|
74 | * Should not be changed once we come up with good ones and document them. =) |
---|
75 | */ |
---|
76 | /* This is used to add another post-processing command, e.g. 'gzip -9' */ |
---|
77 | #define KEYWORD_EXEC "filter" |
---|
78 | /* This describes a process to restart, other than syslogd */ |
---|
79 | #define KEYWORD_PID "signal" |
---|
80 | /* This works as "signal," only takes a process name rather than a pid file. */ |
---|
81 | #define KEYWORD_PNAME "signame" |
---|
82 | |
---|
83 | /* Definitions of the predefined flag letters. */ |
---|
84 | #define FL_BINARY 'B' /* reserved */ |
---|
85 | #define FL_DATED 'D' /* reserved */ |
---|
86 | #define FL_COMPRESS 'Z' /* can be redefined */ |
---|
87 | |
---|
88 | /* Define the date format used for generating dated filenames. */ |
---|
89 | /***If you change the format, you must change the parsing in valid_date_ext***/ |
---|
90 | #define DATE_FORMAT "%Y%m%d" /* YYYYMMDD, as used by strftime */ |
---|
91 | #define DATE_LENGTH 8 |
---|
92 | |
---|
93 | struct log_entry { |
---|
94 | char *log; /* Name of the log */ |
---|
95 | int uid; /* Owner of log */ |
---|
96 | int gid; /* Group of log */ |
---|
97 | int numlogs; /* Number of logs to keep */ |
---|
98 | int size; /* Size cutoff to trigger trimming the log */ |
---|
99 | int hours; /* Hours between log trimming */ |
---|
100 | int permissions; /* File permissions on the log */ |
---|
101 | int flags; /* Flags (CE_ACTIVE , CE_BINARY, CE_DATED) */ |
---|
102 | char *exec_flags; /* Flag letters for things to run */ |
---|
103 | char *pid_flags; /* Flag letters for things to restart */ |
---|
104 | struct log_entry *next; /* Linked list pointer */ |
---|
105 | }; |
---|
106 | |
---|
107 | /* The same structure is used for different kinds of flags. */ |
---|
108 | /* This wastes a little run-time space, but makes my life easier. */ |
---|
109 | struct flag_entry { |
---|
110 | char option; /* Option character used for this program */ |
---|
111 | enum { EXEC, PID, PNAME } type; /* Type of flag ('run' vs. 'signal') */ |
---|
112 | |
---|
113 | /* EXEC flag options */ |
---|
114 | char *extension; /* Extension added by this program */ |
---|
115 | char **args; /* Program name and command-line arguments */ |
---|
116 | int nargs; /* Number of arguments not including args[0] */ |
---|
117 | |
---|
118 | /* PID/PNAME flag options */ |
---|
119 | char *pidident; /* Path to the PID file, or the process name */ |
---|
120 | int signal; /* Signal to restart the process */ |
---|
121 | int ref_count; /* Number of times used (if 0, no restart) */ |
---|
122 | |
---|
123 | struct flag_entry *next; /* Linked list pointer */ |
---|
124 | }; |
---|
125 | |
---|
126 | /* extern uid_t getuid(),geteuid(); */ |
---|
127 | /* extern time_t time(); */ |
---|
128 | |
---|
129 | /* extern's needed for getopt() processing */ |
---|
130 | extern int optind; |
---|
131 | extern char *optarg; |
---|
132 | |
---|
133 | /*** globals ***/ |
---|
134 | |
---|
135 | char *progname; /* name we were run as (ie, argv[0]) */ |
---|
136 | char hostname[64]; /* hostname of this machine */ |
---|
137 | |
---|
138 | /* variables which correspond to the command-line flags */ |
---|
139 | int verbose = 0; /* Print out what's going on */ |
---|
140 | int needroot = 1; /* Root privs are necessary */ |
---|
141 | int noaction = 0; /* Don't do anything, just show it */ |
---|
142 | char *conf = CONF; /* Configuration file to use */ |
---|
143 | int sleeptm = SLEEP_DELAY; /* Time to wait after restarting a daemon */ |
---|
144 | |
---|
145 | time_t timenow; /* Time of the start of newsyslog */ |
---|
146 | char *daytime; /* timenow in human readable form */ |
---|
147 | char datestamp[DATE_LENGTH+1]; /* timenow in the YYYYMMDD format */ |
---|
148 | |
---|
149 | struct flag_entry *flags; /* List of all config-file flags */ |
---|
150 | |
---|
151 | /*** support functions ***/ |
---|
152 | |
---|
153 | /* Skip Over Blanks */ |
---|
154 | static char *sob (register char *p) |
---|
155 | { |
---|
156 | while (p && *p && isspace((unsigned char)*p)) |
---|
157 | p++; |
---|
158 | return(p); |
---|
159 | } |
---|
160 | |
---|
161 | /* Skip Over Non-Blanks */ |
---|
162 | static char *son (register char *p) |
---|
163 | { |
---|
164 | while (p && *p && !isspace((unsigned char)*p)) |
---|
165 | p++; |
---|
166 | return(p); |
---|
167 | } |
---|
168 | |
---|
169 | /* Check if string is actually a number */ |
---|
170 | static int isnumber(char *string) |
---|
171 | { |
---|
172 | while (*string != '\0') { |
---|
173 | if (! isdigit((unsigned char)*string)) return(0); |
---|
174 | string++; |
---|
175 | } |
---|
176 | return(1); |
---|
177 | } |
---|
178 | |
---|
179 | #ifndef HAVE_STRDUP |
---|
180 | /* Duplicate a string using malloc */ |
---|
181 | static char *strdup (register char *strp) |
---|
182 | { |
---|
183 | register char *cp; |
---|
184 | |
---|
185 | if ((cp = malloc(strlen(strp) + 1)) == NULL) |
---|
186 | abort(); |
---|
187 | return(strcpy (cp, strp)); |
---|
188 | } |
---|
189 | #endif |
---|
190 | |
---|
191 | /* Tack a character to the end of a string. (Inefficient...) */ |
---|
192 | static char *grow_option_string (char **strp, char new) |
---|
193 | { |
---|
194 | size_t len; |
---|
195 | |
---|
196 | if (! strp) |
---|
197 | return NULL; |
---|
198 | else if (! *strp) { |
---|
199 | len = 0; |
---|
200 | (*strp) = malloc(2); |
---|
201 | } else { |
---|
202 | len = strlen(*strp); |
---|
203 | (*strp) = realloc(*strp, len + 2); |
---|
204 | } |
---|
205 | |
---|
206 | if (! *strp) abort(); |
---|
207 | |
---|
208 | (*strp)[len] = new; |
---|
209 | (*strp)[len+1] = '\0'; |
---|
210 | |
---|
211 | return *strp; |
---|
212 | } |
---|
213 | |
---|
214 | /*** finding the appropriate logfile, whatever its extension ***/ |
---|
215 | |
---|
216 | /* Check if the filename extension is composed of valid components */ |
---|
217 | static int valid_extension(char* ext) |
---|
218 | { |
---|
219 | struct flag_entry *cfl = flags; |
---|
220 | int len; |
---|
221 | |
---|
222 | if (ext && (*ext)) |
---|
223 | while (cfl) { |
---|
224 | if ((cfl->type == EXEC) && (cfl->extension)) { |
---|
225 | len = strlen(cfl->extension); |
---|
226 | |
---|
227 | /* if the current extension matches... */ |
---|
228 | if (!strncmp(ext, cfl->extension, len)) { |
---|
229 | /* exact match */ |
---|
230 | if (ext[len] == '\0') |
---|
231 | return 1; |
---|
232 | /* match, but we have more to check */ |
---|
233 | else if (ext[len] == '.') { |
---|
234 | if (valid_extension(ext + len + 1)) |
---|
235 | return 1; |
---|
236 | /* if valid_ext returns false, try other possibilities; |
---|
237 | that way, one proc can add >1 ext (.tar.gz) */ |
---|
238 | } |
---|
239 | } |
---|
240 | } |
---|
241 | cfl = cfl->next; |
---|
242 | } |
---|
243 | return 0; |
---|
244 | } |
---|
245 | |
---|
246 | /* Check if the string is empty, or a dot followed by a valid extension */ |
---|
247 | static int valid_dot_ext(char* dot) |
---|
248 | { |
---|
249 | if (!dot) |
---|
250 | return 0; |
---|
251 | else if (*dot == '\0') |
---|
252 | return 1; |
---|
253 | else if (*dot == '.') |
---|
254 | return valid_extension(dot + 1); |
---|
255 | |
---|
256 | return 0; |
---|
257 | } |
---|
258 | |
---|
259 | /* Check if the string is a valid date (YYYYMMDD) plus extension */ |
---|
260 | static int valid_date_ext(char* date) |
---|
261 | { |
---|
262 | /* if we're still compiling C code in 2099, something is horribly wrong */ |
---|
263 | return (((date[0]=='1' && date[1]=='9') |
---|
264 | || (date[0]=='2' && date[1]=='0')) && |
---|
265 | isdigit((unsigned char)date[2]) && |
---|
266 | isdigit((unsigned char)date[3]) && |
---|
267 | ((date[4]=='0' && (date[5]>='1' && date[5]<='9')) |
---|
268 | || (date[4]=='1' && (date[5]>='0' && date[5]<='2'))) && |
---|
269 | ((date[6]=='0' && (date[7]>='1' && date[7]<='9')) |
---|
270 | || ((date[6]=='1' || date[6]=='2') && |
---|
271 | isdigit((unsigned char)date[7])) |
---|
272 | || (date[6]=='3' && (date[7]>='0' && date[7]<='1'))) && |
---|
273 | valid_dot_ext(date + 8)) ; |
---|
274 | } |
---|
275 | |
---|
276 | /* Find the file whose name matches one given, with a valid extension, |
---|
277 | * subject to the following rules: |
---|
278 | * (1) If a filename of the form "${name}${root}valid_ext" exists, |
---|
279 | * then it is returned. If there is more than one such file, then |
---|
280 | * the file returned is the one whose entry occurs first in the directory. |
---|
281 | * (2) If not, and if cmp is non-NULL, then filenames of the form |
---|
282 | * "${name}YYYYMMDD.valid_ext" are considered. If there are less |
---|
283 | * than ${limit} files, NULL is returned; otherwise the 'cmp-most' |
---|
284 | * match is returned; i.e. the match for which cmp returned true |
---|
285 | * for all the files it was compared to. |
---|
286 | * Look below for more directly useful functions that call this one. |
---|
287 | */ |
---|
288 | static char *matching_file_compared (char* name, char* root, |
---|
289 | int cmp(char*,char*,size_t), int limit) |
---|
290 | { |
---|
291 | DIR *parent; |
---|
292 | struct dirent *dent; |
---|
293 | char *dirname, *namefrag, *fp, *filename; |
---|
294 | char *cmpmost = NULL; |
---|
295 | int fraglen, rootlen, have_path, mismatch, matches; |
---|
296 | |
---|
297 | if (root) |
---|
298 | rootlen = strlen(root); |
---|
299 | else |
---|
300 | rootlen = 0; |
---|
301 | |
---|
302 | /* extract the directory path and the filename fragment. */ |
---|
303 | fp = dirname = strdup(name); |
---|
304 | namefrag = strrchr(dirname, '/'); |
---|
305 | if (( have_path = (namefrag != NULL) )) { |
---|
306 | *(namefrag++) = '\0'; |
---|
307 | } else { |
---|
308 | namefrag = dirname; |
---|
309 | dirname = "."; |
---|
310 | } |
---|
311 | fraglen = strlen(namefrag); |
---|
312 | |
---|
313 | if (!(parent = opendir(dirname))) { |
---|
314 | free(fp); |
---|
315 | return (char*)NULL; |
---|
316 | } |
---|
317 | |
---|
318 | /* WARNING: on Solaris, -lucb will link with readdir() which |
---|
319 | returns a *different* struct dirent. libucb: just say no. */ |
---|
320 | |
---|
321 | matches = 0; |
---|
322 | /* find matching directory entry */ |
---|
323 | while ((dent=readdir(parent)) |
---|
324 | && ((mismatch = strncmp(dent->d_name, namefrag, fraglen)) |
---|
325 | || (root && strncmp(dent->d_name + fraglen, root, rootlen)) |
---|
326 | || !valid_dot_ext(dent->d_name + fraglen + rootlen))) { |
---|
327 | if (!mismatch && cmp && valid_date_ext(dent->d_name + fraglen)) { |
---|
328 | matches++; |
---|
329 | if ((cmpmost == NULL) |
---|
330 | || cmp(dent->d_name+fraglen, cmpmost+fraglen, DATE_LENGTH)) |
---|
331 | { |
---|
332 | /* current entry is cmp'er than previous value of cmpmost */ |
---|
333 | if (cmpmost) free(cmpmost); |
---|
334 | cmpmost = strdup(dent->d_name); |
---|
335 | } |
---|
336 | } |
---|
337 | } |
---|
338 | |
---|
339 | /* If we found an entry matching root, use that; if not, use a dated one */ |
---|
340 | if (dent && dent->d_name) |
---|
341 | filename = dent->d_name; |
---|
342 | else if (matches >= limit) |
---|
343 | filename = cmpmost; /* possibly NULL */ |
---|
344 | else |
---|
345 | filename = NULL; |
---|
346 | |
---|
347 | if (filename) { |
---|
348 | char *glued = (char*)malloc( strlen(dirname)+strlen(filename) + 3); |
---|
349 | if (!glued) |
---|
350 | abort(); |
---|
351 | |
---|
352 | /* reconstruct pathname+filename */ |
---|
353 | if (have_path) { |
---|
354 | sprintf(glued, "%s/%s", dirname, filename); |
---|
355 | } else |
---|
356 | strcpy(glued, filename); |
---|
357 | |
---|
358 | closedir(parent); |
---|
359 | if (cmpmost) free(cmpmost); |
---|
360 | free(fp); |
---|
361 | return glued; |
---|
362 | } else { |
---|
363 | closedir(parent); |
---|
364 | if (cmpmost) free(cmpmost); |
---|
365 | free(fp); |
---|
366 | return (char*)NULL; |
---|
367 | } |
---|
368 | } |
---|
369 | |
---|
370 | /* Comparison functions: true if file1's date is earlier than file2's */ |
---|
371 | static int cmp_earliest_date (char* file1, char* file2, size_t n) |
---|
372 | { |
---|
373 | return ( strncmp(file1, file2, n) < 0 ); |
---|
374 | } |
---|
375 | |
---|
376 | /* Comparison functions: true if file1's date is later than file2's */ |
---|
377 | static int cmp_latest_date (char* file1, char* file2, size_t n) |
---|
378 | { |
---|
379 | return ( strncmp(file1, file2, n) > 0 ); |
---|
380 | } |
---|
381 | |
---|
382 | /* Find the file whose name matches one given, with a valid extension */ |
---|
383 | static char *matching_file(char* name) |
---|
384 | { |
---|
385 | return matching_file_compared (name, NULL, NULL, -1); |
---|
386 | } |
---|
387 | |
---|
388 | /* Find the newest logfile: ".0" if it exists, or the latest-dated one */ |
---|
389 | static char *newest_log(char* file) |
---|
390 | { |
---|
391 | char tmp[MAXPATHLEN]; |
---|
392 | |
---|
393 | sprintf(tmp, "%s.", file); |
---|
394 | return matching_file_compared(tmp, "0", cmp_latest_date, -1); |
---|
395 | } |
---|
396 | |
---|
397 | /* Find the oldest logfile: ".${numlogs}" if it exists, or else the |
---|
398 | * first-dated file if there are more than ${numlogs} dated files. |
---|
399 | */ |
---|
400 | static char *oldest_log(char* file, int numlogs) |
---|
401 | { |
---|
402 | char tmp[MAXPATHLEN]; |
---|
403 | char num[MAXPATHLEN]; |
---|
404 | |
---|
405 | sprintf(tmp, "%s.", file); |
---|
406 | sprintf(num, "%d", numlogs); |
---|
407 | return matching_file_compared(tmp, num, cmp_earliest_date, numlogs); |
---|
408 | } |
---|
409 | |
---|
410 | |
---|
411 | /*** examining linked lists of flags ***/ |
---|
412 | |
---|
413 | /* return the flag_entry for option letter (if it's currently used), or NULL */ |
---|
414 | static struct flag_entry *get_flag_entry(char option, struct flag_entry *flags) |
---|
415 | { |
---|
416 | #ifdef DBG_FLAG_ENTRY |
---|
417 | { int debuggers_suck_when_breakpoint_is_a_while_loop = 1; } |
---|
418 | #endif |
---|
419 | |
---|
420 | /* go down the list until we find a match. */ |
---|
421 | while (flags) { |
---|
422 | if (option == flags->option) |
---|
423 | return flags; |
---|
424 | flags = flags->next; |
---|
425 | } |
---|
426 | |
---|
427 | return (struct flag_entry*)NULL; /* not found */ |
---|
428 | } |
---|
429 | |
---|
430 | /* check if an option letter is already used for a flag */ |
---|
431 | static int already_used (char option, struct flag_entry *flags) |
---|
432 | { |
---|
433 | /* these letters cannot be redefined! */ |
---|
434 | if ((option == FL_BINARY) || (option == FL_DATED)) |
---|
435 | return 1; |
---|
436 | |
---|
437 | /* otherwise check the list */ |
---|
438 | if ( get_flag_entry(option,flags) ) |
---|
439 | return 1; |
---|
440 | |
---|
441 | return 0; /* not found */ |
---|
442 | } |
---|
443 | |
---|
444 | /*** command-line parsing ***/ |
---|
445 | |
---|
446 | static void usage(void) |
---|
447 | { |
---|
448 | fprintf(stderr, |
---|
449 | "Usage: %s [-nrv] [-f config-file] [-t restart-time]\n", progname); |
---|
450 | exit(1); |
---|
451 | } |
---|
452 | |
---|
453 | /* Parse the command-line arguments */ |
---|
454 | static void PRS(int argc, char **argv) |
---|
455 | { |
---|
456 | int c; |
---|
457 | char *end; |
---|
458 | |
---|
459 | progname = argv[0]; |
---|
460 | timenow = time((time_t *) 0); |
---|
461 | daytime = ctime(&timenow); |
---|
462 | daytime[strlen(daytime)-1] = '\0'; |
---|
463 | if (!strftime(datestamp, DATE_LENGTH+1,DATE_FORMAT, localtime(&timenow))) { |
---|
464 | for (c=0; c<DATE_LENGTH; c++) |
---|
465 | datestamp[c] = '0'; |
---|
466 | datestamp[DATE_LENGTH] = '\0'; |
---|
467 | } |
---|
468 | |
---|
469 | /* Let's get our hostname */ |
---|
470 | /* WARNING: on Solaris, -lnsl has gethostname(). |
---|
471 | Using -lucb may be dangerous to your foot. --bert 7nov1995 */ |
---|
472 | if (gethostname(hostname, 64)) { |
---|
473 | perror("gethostname"); |
---|
474 | (void) strcpy(hostname,"Mystery Host"); |
---|
475 | } |
---|
476 | |
---|
477 | optind = 1; /* Start options parsing */ |
---|
478 | while ((c=getopt(argc,argv,"nrvf:t:")) != EOF) |
---|
479 | switch (c) { |
---|
480 | case 'n': |
---|
481 | noaction++; /* This implies needroot as off */ |
---|
482 | /* fall through */ |
---|
483 | case 'r': |
---|
484 | needroot = 0; |
---|
485 | break; |
---|
486 | case 'v': |
---|
487 | verbose++; |
---|
488 | break; |
---|
489 | case 'f': |
---|
490 | conf = optarg; |
---|
491 | break; |
---|
492 | case 't': |
---|
493 | sleeptm = strtol(optarg, &end, 0); |
---|
494 | if (end && *end) /* arg contains non-numeric chars */ |
---|
495 | usage(); |
---|
496 | break; |
---|
497 | default: |
---|
498 | usage(); |
---|
499 | } |
---|
500 | } |
---|
501 | |
---|
502 | /*** config file parsing ***/ |
---|
503 | |
---|
504 | /* Complain if the first argument is NULL, return it otherwise. */ |
---|
505 | static char *missing_field(char *p, char *errline) |
---|
506 | { |
---|
507 | if (!p || !*p) { |
---|
508 | fprintf(stderr,"Missing field in config file:\n"); |
---|
509 | fputs(errline,stderr); |
---|
510 | exit(1); |
---|
511 | } |
---|
512 | return(p); |
---|
513 | } |
---|
514 | |
---|
515 | /* Parse a logfile description from the configuration file and update |
---|
516 | * the linked list of logfiles |
---|
517 | */ |
---|
518 | static void parse_logfile_line(char *line, struct log_entry **first, |
---|
519 | struct flag_entry *flags_list) |
---|
520 | { |
---|
521 | char *parse, *q; |
---|
522 | char *errline, *group = NULL; |
---|
523 | struct passwd *pass; |
---|
524 | struct group *grp; |
---|
525 | struct log_entry *working = (*first); |
---|
526 | struct flag_entry *opt; |
---|
527 | |
---|
528 | errline = strdup(line); |
---|
529 | |
---|
530 | if (!working) { |
---|
531 | (*first) = working = |
---|
532 | (struct log_entry *) malloc(sizeof(struct log_entry)); |
---|
533 | if (!working) abort(); |
---|
534 | } else { |
---|
535 | while (working->next) |
---|
536 | working = working->next; |
---|
537 | |
---|
538 | working->next = (struct log_entry *)malloc(sizeof(struct log_entry)); |
---|
539 | working = working->next; |
---|
540 | if (!working) abort(); |
---|
541 | } |
---|
542 | |
---|
543 | working->next = (struct log_entry *)NULL; |
---|
544 | |
---|
545 | q = parse = missing_field(sob(line),errline); |
---|
546 | *(parse = son(line)) = '\0'; |
---|
547 | working->log = strdup(q); |
---|
548 | |
---|
549 | q = parse = missing_field(sob(++parse),errline); |
---|
550 | *(parse = son(parse)) = '\0'; |
---|
551 | if ((group = strchr(q, '.')) != NULL) { |
---|
552 | *group++ = '\0'; |
---|
553 | if (*q) { |
---|
554 | if (!(isnumber(q))) { |
---|
555 | if ((pass = getpwnam(q)) == NULL) { |
---|
556 | fprintf(stderr, |
---|
557 | "Error in config file; unknown user:\n"); |
---|
558 | fputs(errline,stderr); |
---|
559 | exit(1); |
---|
560 | } |
---|
561 | working->uid = pass->pw_uid; |
---|
562 | } else |
---|
563 | working->uid = atoi(q); |
---|
564 | } else |
---|
565 | working->uid = NONE; |
---|
566 | |
---|
567 | q = group; |
---|
568 | if (*q) { |
---|
569 | if (!(isnumber(q))) { |
---|
570 | if ((grp = getgrnam(q)) == NULL) { |
---|
571 | fprintf(stderr, |
---|
572 | "Error in config file; unknown group:\n"); |
---|
573 | fputs(errline,stderr); |
---|
574 | exit(1); |
---|
575 | } |
---|
576 | working->gid = grp->gr_gid; |
---|
577 | } else |
---|
578 | working->gid = atoi(q); |
---|
579 | } else |
---|
580 | working->gid = NONE; |
---|
581 | |
---|
582 | q = parse = missing_field(sob(++parse),errline); |
---|
583 | *(parse = son(parse)) = '\0'; |
---|
584 | } |
---|
585 | else /* next string does not contain a '.' */ |
---|
586 | working->uid = working->gid = NONE; |
---|
587 | |
---|
588 | if (!sscanf(q,"%o",&working->permissions)) { |
---|
589 | fprintf(stderr, |
---|
590 | "Error in config file; bad permissions:\n"); |
---|
591 | fputs(errline,stderr); |
---|
592 | exit(1); |
---|
593 | } |
---|
594 | |
---|
595 | q = parse = missing_field(sob(++parse),errline); |
---|
596 | *(parse = son(parse)) = '\0'; |
---|
597 | if (!sscanf(q,"%d",&working->numlogs)) { |
---|
598 | fprintf(stderr, |
---|
599 | "Error in config file; bad number:\n"); |
---|
600 | fputs(errline,stderr); |
---|
601 | exit(1); |
---|
602 | } |
---|
603 | |
---|
604 | q = parse = missing_field(sob(++parse),errline); |
---|
605 | *(parse = son(parse)) = '\0'; |
---|
606 | if (isdigit((unsigned char)*q)) |
---|
607 | working->size = atoi(q); |
---|
608 | else |
---|
609 | working->size = -1; |
---|
610 | |
---|
611 | q = parse = missing_field(sob(++parse),errline); |
---|
612 | *(parse = son(parse)) = '\0'; |
---|
613 | if (isdigit((unsigned char)*q)) |
---|
614 | working->hours = atoi(q); |
---|
615 | else |
---|
616 | working->hours = -1; |
---|
617 | |
---|
618 | q = parse = sob(++parse); /* Optional field */ |
---|
619 | *(parse = son(parse)) = '\0'; |
---|
620 | working->flags = 0; |
---|
621 | working->exec_flags = NULL; |
---|
622 | working->pid_flags = NULL; |
---|
623 | |
---|
624 | while (q && *q && !isspace((unsigned char)*q)) { |
---|
625 | char qq = toupper(*q); |
---|
626 | if (qq == FL_BINARY) |
---|
627 | working->flags |= CE_BINARY; |
---|
628 | else if (qq == FL_DATED) |
---|
629 | working->flags |= CE_DATED; |
---|
630 | else if (( opt = get_flag_entry(qq, flags_list) )) { |
---|
631 | if ( opt->type == EXEC ) |
---|
632 | grow_option_string( &(working->exec_flags), qq ); |
---|
633 | else if ( opt->type == PID || opt->type == PNAME ) |
---|
634 | grow_option_string( &(working->pid_flags), qq ); |
---|
635 | else abort(); |
---|
636 | } else { |
---|
637 | fprintf(stderr, |
---|
638 | "Illegal flag in config file -- %c\n", |
---|
639 | *q); |
---|
640 | exit(1); |
---|
641 | } |
---|
642 | q++; |
---|
643 | } |
---|
644 | |
---|
645 | free(errline); |
---|
646 | } |
---|
647 | |
---|
648 | /* Parse a program-to-run description from the configuration file and update |
---|
649 | * the linked list of flag entries |
---|
650 | */ |
---|
651 | static void parse_exec_line(char *line, struct flag_entry **first) |
---|
652 | { |
---|
653 | char *parse, *q; |
---|
654 | char *errline; |
---|
655 | struct flag_entry *working = (*first); |
---|
656 | int num_args, i; |
---|
657 | |
---|
658 | errline = strdup(line); |
---|
659 | |
---|
660 | if (!working) { |
---|
661 | (*first) = working = |
---|
662 | (struct flag_entry *) malloc(sizeof(struct flag_entry)); |
---|
663 | if (!working) abort(); |
---|
664 | } else { |
---|
665 | while (working->next) |
---|
666 | working = working->next; |
---|
667 | |
---|
668 | working->next = (struct flag_entry*) malloc(sizeof(struct flag_entry)); |
---|
669 | working = working->next; |
---|
670 | if (!working) abort(); |
---|
671 | } |
---|
672 | working->next = (struct flag_entry *)NULL; |
---|
673 | working->type = EXEC; |
---|
674 | |
---|
675 | parse = missing_field(sob(line),errline); |
---|
676 | parse = missing_field(son(parse),errline); /* skip over the keyword */ |
---|
677 | |
---|
678 | parse = missing_field(sob(parse),errline); |
---|
679 | working->option = 0; /* This is here so already_used doesn't break. */ |
---|
680 | if (already_used(toupper(*parse), *first)) { |
---|
681 | fprintf(stderr, |
---|
682 | "Error in config file; option letter already in use:\n"); |
---|
683 | fputs(errline,stderr); |
---|
684 | exit(1); |
---|
685 | } |
---|
686 | working->option = toupper(*parse); |
---|
687 | if (!isspace((unsigned char)*(++parse))) { |
---|
688 | fprintf(stderr, |
---|
689 | "Error in config file; more than one option letter:\n"); |
---|
690 | fputs(errline,stderr); |
---|
691 | exit(1); |
---|
692 | } |
---|
693 | |
---|
694 | q = parse = missing_field(sob(parse),errline); |
---|
695 | if (*q == '.') { /* extension */ |
---|
696 | *(parse = son(parse)) = '\0'; |
---|
697 | working->extension = strdup(++q); |
---|
698 | |
---|
699 | q = parse = missing_field(sob(++parse),errline); |
---|
700 | } |
---|
701 | else /* no extension */ |
---|
702 | working->extension = NULL; |
---|
703 | |
---|
704 | num_args = 0; |
---|
705 | while (q && (*q)) { |
---|
706 | num_args++; |
---|
707 | q = son(q); |
---|
708 | if (q && (*q)) |
---|
709 | q = sob(q); |
---|
710 | } |
---|
711 | working->args = (char**) malloc((num_args+2)*sizeof(char*)); |
---|
712 | if (! working->args) abort(); |
---|
713 | for (i=0; i<num_args; i++) { |
---|
714 | q = missing_field(parse,errline); |
---|
715 | *(parse = son(parse)) = '\0'; |
---|
716 | working->args[i] = strdup(q); |
---|
717 | parse = sob(++parse); |
---|
718 | } |
---|
719 | working->args[num_args] = NULL; /* placeholder for log file name */ |
---|
720 | working->args[num_args+1] = NULL; /* end of array */ |
---|
721 | working->nargs = num_args; |
---|
722 | |
---|
723 | free(errline); |
---|
724 | } |
---|
725 | |
---|
726 | /* Parse a process-to-restart description from the configuration file and |
---|
727 | * update the linked list of flag entries |
---|
728 | */ |
---|
729 | static void parse_pid_line(char *line, struct flag_entry **first) |
---|
730 | { |
---|
731 | char *parse, *q, *end; |
---|
732 | char *errline; |
---|
733 | struct flag_entry *working = (*first); |
---|
734 | char oops; |
---|
735 | |
---|
736 | errline = strdup(line); |
---|
737 | |
---|
738 | if (!working) { |
---|
739 | (*first) = working = |
---|
740 | (struct flag_entry *) malloc(sizeof(struct flag_entry)); |
---|
741 | if (!working) abort(); |
---|
742 | } else { |
---|
743 | while (working->next) |
---|
744 | working = working->next; |
---|
745 | |
---|
746 | working->next = (struct flag_entry *)malloc(sizeof(struct flag_entry)); |
---|
747 | working = working->next; |
---|
748 | if (!working) abort(); |
---|
749 | } |
---|
750 | |
---|
751 | working->next = (struct flag_entry *)NULL; |
---|
752 | |
---|
753 | parse = missing_field(sob(line),errline); |
---|
754 | |
---|
755 | if (!strncasecmp(parse, KEYWORD_PID, sizeof(KEYWORD_PID)-1) |
---|
756 | && isspace((unsigned char)parse[sizeof(KEYWORD_PID)-1])) |
---|
757 | working->type = PID; |
---|
758 | else |
---|
759 | working->type = PNAME; |
---|
760 | |
---|
761 | parse = missing_field(son(parse),errline); /* skip over the keyword */ |
---|
762 | |
---|
763 | parse = missing_field(sob(parse),errline); |
---|
764 | working->option = 0; /* This is here so already_used doesn't break. */ |
---|
765 | if (already_used(toupper(*parse), *first)) { |
---|
766 | fprintf(stderr, |
---|
767 | "Error in config file; option letter already in use:\n"); |
---|
768 | fputs(errline,stderr); |
---|
769 | exit(1); |
---|
770 | } |
---|
771 | working->option = toupper(*parse); |
---|
772 | if (!isspace((unsigned char)*(++parse))) { |
---|
773 | fprintf(stderr, |
---|
774 | "Error in config file; more than one option letter:\n"); |
---|
775 | fputs(errline,stderr); |
---|
776 | exit(1); |
---|
777 | } |
---|
778 | |
---|
779 | q = parse = missing_field(sob(++parse),errline); |
---|
780 | oops = *(parse = son(parse)); |
---|
781 | (*parse) = '\0'; |
---|
782 | working->pidident = strdup(q); |
---|
783 | (*parse) = oops; /* parse may have been pointing at the end of |
---|
784 | * the line already, so we can't just ++ it. |
---|
785 | * (Falling off the edge of the world would be bad.) */ |
---|
786 | |
---|
787 | /* the signal field is optional */ |
---|
788 | q = parse = sob(parse); |
---|
789 | if (q && (*q)) { |
---|
790 | *(parse = son(parse)) = '\0'; |
---|
791 | if (! (working->signal = signal_number(q))) { |
---|
792 | working->signal = strtol(q, &end, 0); |
---|
793 | if ((! working->signal) || (end != parse)) { |
---|
794 | fprintf(stderr, |
---|
795 | "Error in config file; bad signal number:\n"); |
---|
796 | fputs(errline,stderr); |
---|
797 | exit(1); |
---|
798 | } |
---|
799 | } |
---|
800 | } else { |
---|
801 | /* default signal value */ |
---|
802 | working->signal = SIGHUP; |
---|
803 | } |
---|
804 | |
---|
805 | working->ref_count = 0; |
---|
806 | |
---|
807 | free(errline); |
---|
808 | } |
---|
809 | |
---|
810 | /* Add support for default flags to the linked list of flag entries. |
---|
811 | */ |
---|
812 | static void add_default_flags(struct flag_entry **first) |
---|
813 | { |
---|
814 | struct flag_entry *working = (*first); |
---|
815 | |
---|
816 | /* we find out if Z flag is used *before* we change the linked list */ |
---|
817 | int have_Z = already_used(FL_COMPRESS, *first); |
---|
818 | |
---|
819 | /* we'll be adding at least one new entry. */ |
---|
820 | if (!working) { |
---|
821 | (*first) = working = |
---|
822 | (struct flag_entry *) malloc(sizeof(struct flag_entry)); |
---|
823 | if (!working) abort(); |
---|
824 | } else { |
---|
825 | while (working->next) |
---|
826 | working = working->next; |
---|
827 | working->next = (struct flag_entry*) malloc(sizeof(struct flag_entry)); |
---|
828 | working = working->next; |
---|
829 | if (!working) abort(); |
---|
830 | } |
---|
831 | |
---|
832 | /* add 'Z' flag for running "compress" on the logfile. */ |
---|
833 | if (! have_Z) { |
---|
834 | working->option = FL_COMPRESS; |
---|
835 | working->type = EXEC; |
---|
836 | working->extension = "Z"; |
---|
837 | |
---|
838 | working->args = (char**) malloc(4*sizeof(char*)); |
---|
839 | if (! working->args) abort(); |
---|
840 | working->args[0] = strdup(COMPRESS); |
---|
841 | working->args[1] = strdup("-f"); |
---|
842 | working->args[2] = NULL; /* placeholder for log file name */ |
---|
843 | working->args[3] = NULL; /* end of array */ |
---|
844 | working->nargs = 2; |
---|
845 | |
---|
846 | /* we used up an entry, so we allocate a new one. */ |
---|
847 | working->next = (struct flag_entry*) malloc(sizeof(struct flag_entry)); |
---|
848 | working = working->next; |
---|
849 | if (!working) abort(); |
---|
850 | } else { |
---|
851 | fprintf(stderr, "warning: '%c' flag has been redefined.\n", |
---|
852 | FL_COMPRESS); |
---|
853 | } |
---|
854 | |
---|
855 | /* add unnamed flag for restarting syslogd. */ |
---|
856 | #ifdef USE_PROCNAME |
---|
857 | working->type = PNAME; |
---|
858 | working->pidident = SYSLOG_PNAME; |
---|
859 | #else |
---|
860 | working->type = PID; |
---|
861 | working->pidident = SYSLOG_PID; |
---|
862 | #endif |
---|
863 | working->option = 0; |
---|
864 | working->signal = SIGHUP; |
---|
865 | working->ref_count = 0; |
---|
866 | |
---|
867 | working->next = (struct flag_entry *)NULL; |
---|
868 | } |
---|
869 | |
---|
870 | /* Parse a configuration file and return all relevant data in several |
---|
871 | * linked lists |
---|
872 | */ |
---|
873 | static void parse_file(struct log_entry **logfiles, struct flag_entry **flags) |
---|
874 | { |
---|
875 | FILE *f; |
---|
876 | char line[BUFSIZ]; |
---|
877 | int add_flags = 1; |
---|
878 | |
---|
879 | (*logfiles) = (struct log_entry *) NULL; |
---|
880 | (*flags) = (struct flag_entry *) NULL; |
---|
881 | |
---|
882 | if (strcmp(conf,"-")) |
---|
883 | f = fopen(conf,"r"); |
---|
884 | else |
---|
885 | f = stdin; |
---|
886 | if (!f) { |
---|
887 | (void) fprintf(stderr,"%s: ",progname); |
---|
888 | perror(conf); |
---|
889 | exit(1); |
---|
890 | } |
---|
891 | while (fgets(line,BUFSIZ,f)) { |
---|
892 | if ((line[0]== '\n') || (line[0] == '#')) |
---|
893 | continue; |
---|
894 | if (!strncasecmp(line, KEYWORD_EXEC, sizeof(KEYWORD_EXEC)-1) |
---|
895 | && isspace((unsigned char)line[sizeof(KEYWORD_EXEC)-1])) { |
---|
896 | parse_exec_line(line, flags); |
---|
897 | continue; |
---|
898 | } |
---|
899 | if ((!strncasecmp(line, KEYWORD_PID, sizeof(KEYWORD_PID)-1) |
---|
900 | && isspace((unsigned char)line[sizeof(KEYWORD_PID)-1])) |
---|
901 | #ifdef ALLOW_PROCNAME |
---|
902 | || (!strncasecmp(line, KEYWORD_PNAME, sizeof(KEYWORD_PNAME)-1) |
---|
903 | && isspace(line[sizeof(KEYWORD_PNAME)-1]))) { |
---|
904 | #else |
---|
905 | ) { |
---|
906 | #endif |
---|
907 | parse_pid_line(line, flags); |
---|
908 | continue; |
---|
909 | } |
---|
910 | |
---|
911 | /* Add records for some of the default flags. Do it before |
---|
912 | * the first file-to-turn-over line, so 'Z' can be redefined. |
---|
913 | * Also, make sure to do it only once. */ |
---|
914 | if (add_flags) { |
---|
915 | add_default_flags(flags); |
---|
916 | add_flags = 0; |
---|
917 | } |
---|
918 | |
---|
919 | parse_logfile_line (line, logfiles, *flags); |
---|
920 | } |
---|
921 | (void) fclose(f); |
---|
922 | } |
---|
923 | |
---|
924 | /*** support functions for turning logfiles over ***/ |
---|
925 | |
---|
926 | /* Return size in kilobytes of a file */ |
---|
927 | static int sizefile (char *file) |
---|
928 | { |
---|
929 | struct stat sb; |
---|
930 | |
---|
931 | if (stat(file,&sb) < 0) |
---|
932 | return(-1); |
---|
933 | return(kbytes(dbtob(sb.st_blocks))); |
---|
934 | } |
---|
935 | |
---|
936 | /* Return the age of old log file (file.0) */ |
---|
937 | static int age_old_log (char *file) |
---|
938 | { |
---|
939 | struct stat sb; |
---|
940 | char* last = newest_log(file); |
---|
941 | |
---|
942 | if (!last || (stat(last, &sb) < 0)) |
---|
943 | return(-1); |
---|
944 | free(last); |
---|
945 | |
---|
946 | #ifdef MINUTES /* this is for debugging: times in minutes instead of hrs */ |
---|
947 | return( (int) (timenow - sb.st_mtime + 30) / 60); |
---|
948 | #else |
---|
949 | return( (int) (timenow - sb.st_mtime + 1800) / 3600); |
---|
950 | #endif |
---|
951 | } |
---|
952 | |
---|
953 | /* Log the fact that the logs were turned over */ |
---|
954 | static int log_trim(char *log) |
---|
955 | { |
---|
956 | FILE *f; |
---|
957 | if ((f = fopen(log,"a")) == NULL) |
---|
958 | return(-1); |
---|
959 | fprintf(f,"%s %s newsyslog[%d]: logfile turned over\n", |
---|
960 | daytime, hostname, (int)getpid()); |
---|
961 | if (fclose(f) == EOF) { |
---|
962 | perror("log_trim: fclose:"); |
---|
963 | exit(1); |
---|
964 | } |
---|
965 | return(0); |
---|
966 | } |
---|
967 | |
---|
968 | /* Fork a program to compress or otherwise process the old log file */ |
---|
969 | static void compress_log(char *log, struct flag_entry *flag) |
---|
970 | { |
---|
971 | int pid; |
---|
972 | int i; |
---|
973 | |
---|
974 | if (noaction) { |
---|
975 | printf("flag '%c':\n ", flag->option); |
---|
976 | for (i=0; i < flag->nargs; i++) printf(" %s", flag->args[i]); |
---|
977 | printf(" %s\n", log); |
---|
978 | } else { |
---|
979 | pid = fork(); |
---|
980 | if (pid < 0) { |
---|
981 | fprintf(stderr,"%s: ",progname); |
---|
982 | perror("fork"); |
---|
983 | exit(1); |
---|
984 | } else if (!pid) { |
---|
985 | flag->args[flag->nargs] = log; |
---|
986 | #ifdef USE_EXECVP |
---|
987 | /* execvp() looks for the program in PATH (unless path is given) */ |
---|
988 | (void) execvp(flag->args[0], flag->args); |
---|
989 | #else |
---|
990 | (void) execv(flag->args[0], flag->args); |
---|
991 | #endif |
---|
992 | fprintf(stderr,"%s: ",progname); |
---|
993 | perror(flag->args[0]); |
---|
994 | exit(1); |
---|
995 | } |
---|
996 | } |
---|
997 | } |
---|
998 | |
---|
999 | /* Restart the process whose PID is given in the specified file */ |
---|
1000 | static void restart_proc(char *pidfile, int signum) |
---|
1001 | { |
---|
1002 | FILE *f; |
---|
1003 | char line[BUFSIZ]; |
---|
1004 | int pid = 0; |
---|
1005 | char *signame; |
---|
1006 | |
---|
1007 | /* Let's find the pid of syslogd */ |
---|
1008 | f = fopen(pidfile, "r"); |
---|
1009 | if (f && fgets(line,BUFSIZ,f)) |
---|
1010 | pid = atoi(line); |
---|
1011 | fclose(f); |
---|
1012 | |
---|
1013 | if (pid) { |
---|
1014 | if (noaction) { |
---|
1015 | if (( signame = signal_name(signum) )) |
---|
1016 | printf(" kill -%s %d (%s)\n", signame, pid, pidfile); |
---|
1017 | else |
---|
1018 | printf(" kill -%d %d (%s)\n", signum, pid, pidfile); |
---|
1019 | printf(" sleep %d\n", sleeptm); |
---|
1020 | } else { |
---|
1021 | if (kill(pid, signum)) { |
---|
1022 | fprintf(stderr,"%s: %s: ", progname, pidfile); |
---|
1023 | perror("warning - could not restart process"); |
---|
1024 | } |
---|
1025 | if (sleeptm > 0) |
---|
1026 | sleep(sleeptm); |
---|
1027 | } |
---|
1028 | } else { |
---|
1029 | fprintf(stderr,"%s: %s: ", progname, pidfile); |
---|
1030 | perror("warning - could not read pidfile"); |
---|
1031 | } |
---|
1032 | } |
---|
1033 | |
---|
1034 | #ifdef ALLOW_PROCNAME |
---|
1035 | static int run(char *what) |
---|
1036 | { |
---|
1037 | char cmd[1024], *args[20], *ptr; |
---|
1038 | int i = 0, status; |
---|
1039 | sigset_t mask, omask; |
---|
1040 | pid_t pid; |
---|
1041 | |
---|
1042 | if (what == NULL) |
---|
1043 | return -1; |
---|
1044 | |
---|
1045 | strcpy(cmd, what); |
---|
1046 | ptr = cmd; |
---|
1047 | while (*ptr != '\0') |
---|
1048 | { |
---|
1049 | args[i++] = ptr; |
---|
1050 | while (*ptr != '\0' && *ptr != ' ') ptr++; |
---|
1051 | if (*ptr == ' ') |
---|
1052 | *ptr++ = '\0'; |
---|
1053 | } |
---|
1054 | args[i] = NULL; |
---|
1055 | |
---|
1056 | /* Guard against kidnapping. */ |
---|
1057 | sigemptyset(&mask); |
---|
1058 | sigaddset(&mask, SIGCHLD); |
---|
1059 | sigprocmask(SIG_BLOCK, &mask, &omask); |
---|
1060 | |
---|
1061 | if ((pid = fork()) == 0) |
---|
1062 | { |
---|
1063 | sigprocmask(SIG_SETMASK, &omask, NULL); |
---|
1064 | execvp(args[0], args); |
---|
1065 | exit(-1); |
---|
1066 | } |
---|
1067 | |
---|
1068 | if (pid == -1) |
---|
1069 | { |
---|
1070 | sigprocmask(SIG_SETMASK, &omask, NULL); |
---|
1071 | return -1; |
---|
1072 | } |
---|
1073 | |
---|
1074 | while (pid != waitpid(pid, &status, 0)); |
---|
1075 | sigprocmask(SIG_SETMASK, &omask, NULL); |
---|
1076 | |
---|
1077 | if (WIFEXITED(status)) |
---|
1078 | return WEXITSTATUS(status); |
---|
1079 | |
---|
1080 | return -2; |
---|
1081 | } |
---|
1082 | |
---|
1083 | static int killproc(char *procname, int signum) |
---|
1084 | { |
---|
1085 | char line[BUFSIZ]; |
---|
1086 | |
---|
1087 | sprintf(line, "killall -%d %s", signum, procname); |
---|
1088 | return run(line); |
---|
1089 | } |
---|
1090 | |
---|
1091 | /* Restart the process whose name has been specified */ |
---|
1092 | static void restart_procname(char *procname, int signum) |
---|
1093 | { |
---|
1094 | char *signame; |
---|
1095 | |
---|
1096 | if (noaction) { |
---|
1097 | if (( signame = signal_name(signum) )) |
---|
1098 | printf(" killproc -%s %s\n", signame, procname); |
---|
1099 | else |
---|
1100 | printf(" killproc -%d %s\n", signum, procname); |
---|
1101 | printf(" sleep %d\n", sleeptm); |
---|
1102 | } else { |
---|
1103 | if (killproc(procname, signum)) { |
---|
1104 | fprintf(stderr,"%s: %s: ", progname, procname); |
---|
1105 | perror("warning - could not restart process"); |
---|
1106 | } |
---|
1107 | if (sleeptm > 0) |
---|
1108 | sleep(sleeptm); |
---|
1109 | } |
---|
1110 | } |
---|
1111 | #endif /* ALLOW_PROCNAME */ |
---|
1112 | |
---|
1113 | /*** various fun+games with logfile entries ***/ |
---|
1114 | |
---|
1115 | /* Examine an entry and figure out whether the logfile needs to be |
---|
1116 | * turned over; if it does, mark it with CE_ACTIVE flag. |
---|
1117 | */ |
---|
1118 | static void do_entry(struct log_entry *ent) |
---|
1119 | { |
---|
1120 | int size, modtime; |
---|
1121 | |
---|
1122 | if (verbose) { |
---|
1123 | printf("%s <%d>: ",ent->log,ent->numlogs); |
---|
1124 | } |
---|
1125 | size = sizefile(ent->log); |
---|
1126 | modtime = age_old_log(ent->log); |
---|
1127 | if (size < 0) { |
---|
1128 | if (verbose) |
---|
1129 | printf("does not exist.\n"); |
---|
1130 | } else { |
---|
1131 | if (verbose && (ent->size > 0)) |
---|
1132 | printf("size (Kb): %d [%d] ", size, ent->size); |
---|
1133 | if (verbose && (ent->hours > 0)) |
---|
1134 | printf(" age (hr): %d [%d] ", modtime, ent->hours); |
---|
1135 | if (((ent->size > 0) && (size >= ent->size)) || |
---|
1136 | ((ent->hours > 0) && ((modtime >= ent->hours) |
---|
1137 | || (modtime < 0)))) { |
---|
1138 | if (verbose) |
---|
1139 | printf("--> trimming log....\n"); |
---|
1140 | if (noaction && !verbose) { |
---|
1141 | printf("%s <%d>: trimming\n", |
---|
1142 | ent->log,ent->numlogs); |
---|
1143 | } |
---|
1144 | ent->flags |= CE_ACTIVE; |
---|
1145 | } else { |
---|
1146 | if (verbose) |
---|
1147 | printf("--> skipping\n"); |
---|
1148 | } |
---|
1149 | } |
---|
1150 | } |
---|
1151 | |
---|
1152 | /* Turn over the logfiles and create a new one, in preparation for a |
---|
1153 | * restart of the logging process(es) |
---|
1154 | */ |
---|
1155 | static void do_trim(struct log_entry *ent) |
---|
1156 | { |
---|
1157 | char file1[MAXPATHLEN], file2[MAXPATHLEN]; |
---|
1158 | char *zfile1, zfile2[MAXPATHLEN]; |
---|
1159 | char *freep = NULL; |
---|
1160 | int fd; |
---|
1161 | struct stat st; |
---|
1162 | int numdays = ent->numlogs; |
---|
1163 | int owner_uid = ent->uid; |
---|
1164 | struct flag_entry *flg; |
---|
1165 | |
---|
1166 | #ifdef _IBMR2 |
---|
1167 | /* AIX 3.1 has a broken fchown- if the owner_uid is -1, it will actually */ |
---|
1168 | /* change it to be owned by uid -1, instead of leaving it as is, as it is */ |
---|
1169 | /* supposed to. */ |
---|
1170 | if (owner_uid == -1) |
---|
1171 | owner_uid = geteuid(); |
---|
1172 | #endif |
---|
1173 | |
---|
1174 | /* Remove oldest log */ |
---|
1175 | zfile1 = freep = oldest_log(ent->log, numdays); |
---|
1176 | while (zfile1) { |
---|
1177 | if (noaction) { |
---|
1178 | printf(" rm -f %s ...\n", zfile1); |
---|
1179 | zfile1 = NULL; |
---|
1180 | } else { |
---|
1181 | (void) unlink(zfile1); |
---|
1182 | if (freep) { free(freep); freep = NULL; } |
---|
1183 | zfile1 = freep = oldest_log(ent->log, numdays); |
---|
1184 | } |
---|
1185 | } |
---|
1186 | |
---|
1187 | /* Note: if all of the logs are dated, the rolling-over code does nothing. |
---|
1188 | * However, it does no harm, and it is vaguely useful in the transition. |
---|
1189 | */ |
---|
1190 | |
---|
1191 | /* Move down log files */ |
---|
1192 | (void) sprintf(file1,"%s.%d", ent->log, numdays); |
---|
1193 | while (numdays--) { |
---|
1194 | (void) strcpy(file2,file1); |
---|
1195 | (void) sprintf(file1,"%s.%d",ent->log,numdays); |
---|
1196 | |
---|
1197 | if (!stat(file1, &st)) |
---|
1198 | zfile1 = file1; |
---|
1199 | else { |
---|
1200 | if (freep) { free(freep); freep = NULL; } |
---|
1201 | zfile1 = freep = matching_file(file1); |
---|
1202 | if (!zfile1) continue; |
---|
1203 | } |
---|
1204 | |
---|
1205 | /* the extension starts at (zfile1 + strlen(file1)) */ |
---|
1206 | (void) strcpy(zfile2, file2); |
---|
1207 | (void) strcat(zfile2, (zfile1 + strlen(file1))); |
---|
1208 | |
---|
1209 | if (noaction) { |
---|
1210 | printf(" mv %s %s\n",zfile1,zfile2); |
---|
1211 | printf(" chmod %o %s\n", ent->permissions, zfile2); |
---|
1212 | printf(" chown %d.%d %s\n", |
---|
1213 | owner_uid, ent->gid, zfile2); |
---|
1214 | } else { |
---|
1215 | (void) rename(zfile1, zfile2); |
---|
1216 | (void) chmod(zfile2, ent->permissions); |
---|
1217 | (void) chown(zfile2, owner_uid, ent->gid); |
---|
1218 | } |
---|
1219 | } |
---|
1220 | if (freep) { free(freep); freep = NULL; } |
---|
1221 | |
---|
1222 | if (ent->flags & CE_DATED) { /* if logs are dated, generate new name */ |
---|
1223 | (void) sprintf(file1, "%s.%s", ent->log, datestamp); |
---|
1224 | } |
---|
1225 | |
---|
1226 | if (noaction) |
---|
1227 | printf(" mv %s %s\n",ent->log, file1); |
---|
1228 | else |
---|
1229 | (void) rename(ent->log, file1); |
---|
1230 | if (noaction) |
---|
1231 | printf("Start new log...\n"); |
---|
1232 | else { |
---|
1233 | fd = creat(ent->log, ent->permissions); |
---|
1234 | if (fd < 0) { |
---|
1235 | perror("can't start new log"); |
---|
1236 | exit(1); |
---|
1237 | } |
---|
1238 | if (fchown(fd, owner_uid, ent->gid)) { |
---|
1239 | perror("can't chmod new log file"); |
---|
1240 | exit(1); |
---|
1241 | } |
---|
1242 | (void) close(fd); |
---|
1243 | if (!(ent->flags & CE_BINARY)) |
---|
1244 | if (log_trim(ent->log)) { /* Add status message */ |
---|
1245 | perror("can't add status message to log"); |
---|
1246 | exit(1); |
---|
1247 | } |
---|
1248 | } |
---|
1249 | if (noaction) |
---|
1250 | printf(" chmod %o %s\n",ent->permissions,ent->log); |
---|
1251 | else |
---|
1252 | (void) chmod(ent->log,ent->permissions); |
---|
1253 | |
---|
1254 | /* Everything on the pid_flags list should get tagged to be restarted. */ |
---|
1255 | if (ent->pid_flags) { |
---|
1256 | char* pid; |
---|
1257 | for (pid = ent->pid_flags; *pid; pid++) { |
---|
1258 | flg = get_flag_entry(*pid, flags); |
---|
1259 | if (!flg || ((flg->type != PID) && (flg->type != PNAME))) abort(); |
---|
1260 | |
---|
1261 | flg->ref_count++; |
---|
1262 | } |
---|
1263 | } else { |
---|
1264 | /* do the default restart. ('\0' is special.) */ |
---|
1265 | flg = get_flag_entry('\0', flags); |
---|
1266 | if (!flg || ((flg->type != PID) && flg->type != PNAME)) abort(); |
---|
1267 | |
---|
1268 | flg->ref_count++; |
---|
1269 | } |
---|
1270 | } |
---|
1271 | |
---|
1272 | /* Take the old logfile, mark it with current time, then run the |
---|
1273 | * apropriate program(s) on it. |
---|
1274 | */ |
---|
1275 | static void do_compress(struct log_entry *ent) |
---|
1276 | { |
---|
1277 | struct flag_entry *flg; |
---|
1278 | char* exec; |
---|
1279 | char old[MAXPATHLEN]; |
---|
1280 | |
---|
1281 | |
---|
1282 | if (ent->flags & CE_DATED) { |
---|
1283 | sprintf(old, "%s.%s", ent->log, datestamp); |
---|
1284 | } else { |
---|
1285 | sprintf(old, "%s.0", ent->log); |
---|
1286 | } |
---|
1287 | |
---|
1288 | if (!noaction && !(ent->flags & CE_BINARY)) { |
---|
1289 | (void) log_trim(old); /* add a date stamp to old log */ |
---|
1290 | } |
---|
1291 | |
---|
1292 | if ((ent->flags & CE_ACTIVE) && ent->exec_flags) { |
---|
1293 | for (exec = ent->exec_flags; *exec; exec++) { |
---|
1294 | flg = get_flag_entry(*exec, flags); |
---|
1295 | if (!flg || (flg->type != EXEC)) abort(); |
---|
1296 | |
---|
1297 | compress_log(old, flg); |
---|
1298 | |
---|
1299 | if (flg->extension) { |
---|
1300 | strcat(old, "."); |
---|
1301 | strcat(old, flg->extension); |
---|
1302 | } |
---|
1303 | } |
---|
1304 | } |
---|
1305 | } |
---|
1306 | |
---|
1307 | /*** main ***/ |
---|
1308 | |
---|
1309 | int main(int argc, char **argv) |
---|
1310 | { |
---|
1311 | struct log_entry *p, *q; |
---|
1312 | struct flag_entry *flg; |
---|
1313 | |
---|
1314 | PRS(argc,argv); |
---|
1315 | if (needroot && getuid() && geteuid()) { |
---|
1316 | fprintf(stderr,"%s: must have root privs\n",progname); |
---|
1317 | exit(1); |
---|
1318 | } |
---|
1319 | parse_file(&q, &flags); /* NB: 'flags' is a global variable */ |
---|
1320 | |
---|
1321 | for (p = q; p; p = p->next) |
---|
1322 | do_entry(p); |
---|
1323 | |
---|
1324 | for (p = q; p; p = p->next) |
---|
1325 | if (p->flags & CE_ACTIVE) { |
---|
1326 | if (verbose>1) printf("Trimming %s\n", p->log); |
---|
1327 | do_trim(p); |
---|
1328 | } |
---|
1329 | |
---|
1330 | for (flg = flags; flg; flg = flg->next) |
---|
1331 | if ((flg->type == PID) && flg->ref_count) { |
---|
1332 | if (verbose>1) printf("Restarting %s\n", flg->pidident); |
---|
1333 | restart_proc(flg->pidident, flg->signal); |
---|
1334 | } |
---|
1335 | |
---|
1336 | #ifdef ALLOW_PROCNAME |
---|
1337 | for (flg = flags; flg; flg = flg->next) |
---|
1338 | if ((flg->type == PNAME) && flg->ref_count) { |
---|
1339 | if (verbose>1) printf("Restarting %s\n", flg->pidident); |
---|
1340 | restart_procname(flg->pidident, flg->signal); |
---|
1341 | } |
---|
1342 | #endif |
---|
1343 | |
---|
1344 | for (p = q; p; p = p->next) { |
---|
1345 | if (p->flags & CE_ACTIVE) { |
---|
1346 | if (verbose>1) printf("Processing %s\n", p->log); |
---|
1347 | do_compress(p); |
---|
1348 | } |
---|
1349 | } |
---|
1350 | |
---|
1351 | return 0; |
---|
1352 | } |
---|
1353 | |
---|
1354 | /* |
---|
1355 | * Local Variables: |
---|
1356 | * mode: c |
---|
1357 | * c-indent-level: 4 |
---|
1358 | * c-continued-statement-offset: 4 |
---|
1359 | * c-label-offset: -3 |
---|
1360 | * End: |
---|
1361 | */ |
---|