newsyslog.c revision 1.3 1 /*
2 * This file contains changes from the Open Software Foundation.
3 * The RCS history log will appear at the end of this file.
4 * @(#)newsyslog.c $Revision: 1.3 $ $Date: 1993/05/22 03:52:20 $ $Locker: $
5 */
6
7 /*
8
9 Copyright 1988, 1989 by the Massachusetts Institute of Technology
10
11 Permission to use, copy, modify, and distribute this software
12 and its documentation for any purpose and without fee is
13 hereby granted, provided that the above copyright notice
14 appear in all copies and that both that copyright notice and
15 this permission notice appear in supporting documentation,
16 and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
17 used in advertising or publicity pertaining to distribution
18 of the software without specific, written prior permission.
19 M.I.T. and the M.I.T. S.I.P.B. make no representations about
20 the suitability of this software for any purpose. It is
21 provided "as is" without express or implied warranty.
22
23 */
24
25 /*
26 * newsyslog - roll over selected logs at the appropriate time,
27 * keeping the a specified number of backup files around.
28 *
29 * $Source: /tank/opengrok/rsync2/NetBSD/src/usr.bin/newsyslog/newsyslog.c,v $
30 * $Author: cgd $
31 */
32
33 #if !defined(lint) && !defined(_NOIDENT)
34 static char rcsid[] = "@(#)$RCSfile: newsyslog.c,v $ $Revision: 1.3 $ (OSF) $Date: 1993/05/22 03:52:20 $";
35 #endif
36
37 #ifndef CONF
38 #define CONF "/etc/athena/newsyslog.conf" /* Configuration file */
39 #endif
40 #ifndef PIDFILE
41 #define PIDFILE "/etc/syslog.pid"
42 #endif
43 #ifndef COMPRESS
44 #define COMPRESS "/usr/ucb/compress" /* File compression program */
45 #endif
46 #ifndef COMPRESS_POSTFIX
47 #define COMPRESS_POSTFIX ".Z"
48 #endif
49
50 #include <stdio.h>
51 #include <strings.h>
52 #include <ctype.h>
53 #include <signal.h>
54 #include <pwd.h>
55 #include <grp.h>
56 #include <sys/types.h>
57 #include <sys/time.h>
58 #include <sys/stat.h>
59 #include <sys/param.h>
60 #include <sys/wait.h>
61
62 #define kbytes(size) (((size) + 1023) >> 10)
63 #ifdef _IBMR2
64 /* Calculates (db * DEV_BSIZE) */
65 #define dbtob(db) ((unsigned)(db) << UBSHIFT)
66 #endif
67
68 #define CE_COMPACT 1 /* Compact the achived log files */
69 #define CE_BINARY 2 /* Logfile is in binary, don't add */
70 /* status messages */
71 #define NONE -1
72
73 struct conf_entry {
74 char *log; /* Name of the log */
75 int uid; /* Owner of log */
76 int gid; /* Group of log */
77 int numlogs; /* Number of logs to keep */
78 int size; /* Size cutoff to trigger trimming the log */
79 int hours; /* Hours between log trimming */
80 int permissions; /* File permissions on the log */
81 int flags; /* Flags (CE_COMPACT & CE_BINARY) */
82 struct conf_entry *next; /* Linked list pointer */
83 };
84
85 extern int optind;
86 extern char *optarg;
87 extern char *malloc();
88 extern uid_t getuid(),geteuid();
89 extern time_t time();
90
91 char *progname; /* contains argv[0] */
92 int verbose = 0; /* Print out what's going on */
93 int needroot = 1; /* Root privs are necessary */
94 int noaction = 0; /* Don't do anything, just show it */
95 char *conf = CONF; /* Configuration file to use */
96 time_t timenow;
97 int syslog_pid; /* read in from /etc/syslog.pid */
98 char hostname[64]; /* hostname */
99 char *daytime; /* timenow in human readable form */
100
101
102 struct conf_entry *parse_file();
103 char *sob(), *son(), *strdup(), *missing_field();
104
105 main(argc,argv)
106 int argc;
107 char **argv;
108 {
109 struct conf_entry *p, *q;
110
111 PRS(argc,argv);
112 if (needroot && getuid() && geteuid()) {
113 fprintf(stderr,"%s: must have root privs\n",progname);
114 exit(1);
115 }
116 p = q = parse_file();
117 while (p) {
118 do_entry(p);
119 p=p->next;
120 free((char *) q);
121 q=p;
122 }
123 exit(0);
124 }
125
126 do_entry(ent)
127 struct conf_entry *ent;
128
129 {
130 int size, modtime;
131
132 if (verbose) {
133 if (ent->flags & CE_COMPACT)
134 printf("%s <%dZ>: ",ent->log,ent->numlogs);
135 else
136 printf("%s <%d>: ",ent->log,ent->numlogs);
137 }
138 size = sizefile(ent->log);
139 modtime = age_old_log(ent->log);
140 if (size < 0) {
141 if (verbose)
142 printf("does not exist.\n");
143 } else {
144 if (verbose && (ent->size > 0))
145 printf("size (Kb): %d [%d] ", size, ent->size);
146 if (verbose && (ent->hours > 0))
147 printf(" age (hr): %d [%d] ", modtime, ent->hours);
148 if (((ent->size > 0) && (size >= ent->size)) ||
149 ((ent->hours > 0) && ((modtime >= ent->hours)
150 || (modtime < 0)))) {
151 if (verbose)
152 printf("--> trimming log....\n");
153 if (noaction && !verbose) {
154 if (ent->flags & CE_COMPACT)
155 printf("%s <%dZ>: trimming",
156 ent->log,ent->numlogs);
157 else
158 printf("%s <%d>: trimming",
159 ent->log,ent->numlogs);
160 }
161 dotrim(ent->log, ent->numlogs, ent->flags,
162 ent->permissions, ent->uid, ent->gid);
163 } else {
164 if (verbose)
165 printf("--> skipping\n");
166 }
167 }
168 }
169
170 PRS(argc,argv)
171 int argc;
172 char **argv;
173 {
174 int c;
175 FILE *f;
176 char line[BUFSIZ];
177
178 progname = argv[0];
179 timenow = time((time_t *) 0);
180 daytime = ctime(&timenow);
181 daytime[strlen(daytime)-1] = '\0';
182
183 /* Let's find the pid of syslogd */
184 syslog_pid = 0;
185 f = fopen(PIDFILE,"r");
186 if (f && fgets(line,BUFSIZ,f))
187 syslog_pid = atoi(line);
188
189 /* Let's get our hostname */
190 if (gethostname(hostname, 64)) {
191 perror("gethostname");
192 (void) strcpy(hostname,"Mystery Host");
193 }
194
195 optind = 1; /* Start options parsing */
196 while ((c=getopt(argc,argv,"nrvf:t:")) != EOF)
197 switch (c) {
198 case 'n':
199 noaction++; /* This implies needroot as off */
200 /* fall through */
201 case 'r':
202 needroot = 0;
203 break;
204 case 'v':
205 verbose++;
206 break;
207 case 'f':
208 conf = optarg;
209 break;
210 default:
211 usage();
212 }
213 }
214
215 usage()
216 {
217 fprintf(stderr,
218 "Usage: %s <-nrv> <-f config-file>\n");
219 exit(1);
220 }
221
222 /* Parse a configuration file and return a linked list of all the logs
223 * to process
224 */
225 struct conf_entry *parse_file()
226 {
227 FILE *f;
228 char line[BUFSIZ], *parse, *q;
229 char *errline, *group;
230 struct conf_entry *first = NULL;
231 struct conf_entry *working;
232 struct passwd *pass;
233 struct group *grp;
234
235 if (strcmp(conf,"-"))
236 f = fopen(conf,"r");
237 else
238 f = stdin;
239 if (!f) {
240 (void) fprintf(stderr,"%s: ",progname);
241 perror(conf);
242 exit(1);
243 }
244 while (fgets(line,BUFSIZ,f)) {
245 if ((line[0]== '\n') || (line[0] == '#'))
246 continue;
247 errline = strdup(line);
248 if (!first) {
249 working = (struct conf_entry *) malloc(sizeof(struct conf_entry));
250 first = working;
251 } else {
252 working->next = (struct conf_entry *) malloc(sizeof(struct conf_entry));
253 working = working->next;
254 }
255
256 q = parse = missing_field(sob(line),errline);
257 *(parse = son(line)) = '\0';
258 working->log = strdup(q);
259
260 q = parse = missing_field(sob(++parse),errline);
261 *(parse = son(parse)) = '\0';
262 if ((group = index(q, '.')) != NULL) {
263 *group++ = '\0';
264 if (*q) {
265 if (!(isnumber(q))) {
266 if ((pass = getpwnam(q)) == NULL) {
267 fprintf(stderr,
268 "Error in config file; unknown user:\n");
269 fputs(errline,stderr);
270 exit(1);
271 }
272 working->uid = pass->pw_uid;
273 } else
274 working->uid = atoi(q);
275 } else
276 working->uid = NONE;
277
278 q = group;
279 if (*q) {
280 if (!(isnumber(q))) {
281 if ((grp = getgrnam(q)) == NULL) {
282 fprintf(stderr,
283 "Error in config file; unknown group:\n");
284 fputs(errline,stderr);
285 exit(1);
286 }
287 working->gid = grp->gr_gid;
288 } else
289 working->gid = atoi(q);
290 } else
291 working->gid = NONE;
292
293 q = parse = missing_field(sob(++parse),errline);
294 *(parse = son(parse)) = '\0';
295 }
296 else
297 working->uid = working->gid = NONE;
298
299 if (!sscanf(q,"%o",&working->permissions)) {
300 fprintf(stderr,
301 "Error in config file; bad permissions:\n");
302 fputs(errline,stderr);
303 exit(1);
304 }
305
306 q = parse = missing_field(sob(++parse),errline);
307 *(parse = son(parse)) = '\0';
308 if (!sscanf(q,"%d",&working->numlogs)) {
309 fprintf(stderr,
310 "Error in config file; bad number:\n");
311 fputs(errline,stderr);
312 exit(1);
313 }
314
315 q = parse = missing_field(sob(++parse),errline);
316 *(parse = son(parse)) = '\0';
317 if (isdigit(*q))
318 working->size = atoi(q);
319 else
320 working->size = -1;
321
322 q = parse = missing_field(sob(++parse),errline);
323 *(parse = son(parse)) = '\0';
324 if (isdigit(*q))
325 working->hours = atoi(q);
326 else
327 working->hours = -1;
328
329 q = parse = sob(++parse); /* Optional field */
330 *(parse = son(parse)) = '\0';
331 working->flags = 0;
332 while (q && *q && !isspace(*q)) {
333 if ((*q == 'Z') || (*q == 'z'))
334 working->flags |= CE_COMPACT;
335 else if ((*q == 'B') || (*q == 'b'))
336 working->flags |= CE_BINARY;
337 else {
338 fprintf(stderr,
339 "Illegal flag in config file -- %c\n",
340 *q);
341 exit(1);
342 }
343 q++;
344 }
345
346 free(errline);
347 }
348 if (working)
349 working->next = (struct conf_entry *) NULL;
350 (void) fclose(f);
351 return(first);
352 }
353
354 char *missing_field(p,errline)
355 char *p,*errline;
356 {
357 if (!p || !*p) {
358 fprintf(stderr,"Missing field in config file:\n");
359 fputs(errline,stderr);
360 exit(1);
361 }
362 return(p);
363 }
364
365 dotrim(log,numdays,flags,perm,owner_uid,group_gid)
366 char *log;
367 int numdays;
368 int flags;
369 int perm;
370 int owner_uid;
371 int group_gid;
372 {
373 char file1[128], file2[128];
374 char zfile1[128], zfile2[128];
375 int fd;
376 struct stat st;
377
378 #ifdef _IBMR2
379 /* AIX 3.1 has a broken fchown- if the owner_uid is -1, it will actually */
380 /* change it to be owned by uid -1, instead of leaving it as is, as it is */
381 /* supposed to. */
382 if (owner_uid == -1)
383 owner_uid = geteuid();
384 #endif
385
386 /* Remove oldest log */
387 (void) sprintf(file1,"%s.%d",log,numdays);
388 (void) strcpy(zfile1, file1);
389 (void) strcat(zfile1, COMPRESS_POSTFIX);
390
391 if (noaction) {
392 printf("rm -f %s\n", file1);
393 printf("rm -f %s\n", zfile1);
394 } else {
395 (void) unlink(file1);
396 (void) unlink(zfile1);
397 }
398
399 /* Move down log files */
400 while (numdays--) {
401 (void) strcpy(file2,file1);
402 (void) sprintf(file1,"%s.%d",log,numdays);
403 (void) strcpy(zfile1, file1);
404 (void) strcpy(zfile2, file2);
405 if (lstat(file1, &st)) {
406 (void) strcat(zfile1, COMPRESS_POSTFIX);
407 (void) strcat(zfile2, COMPRESS_POSTFIX);
408 if (lstat(zfile1, &st)) continue;
409 }
410 if (noaction) {
411 printf("mv %s %s\n",zfile1,zfile2);
412 printf("chmod %o %s\n", perm, zfile2);
413 printf("chown %d.%d %s\n",
414 owner_uid, group_gid, zfile2);
415 } else {
416 (void) rename(zfile1, zfile2);
417 (void) chmod(zfile2, perm);
418 (void) chown(zfile2, owner_uid, group_gid);
419 }
420 }
421 if (!noaction && !(flags & CE_BINARY))
422 (void) log_trim(log); /* Report the trimming to the old log */
423
424 if (noaction)
425 printf("mv %s to %s\n",log,file1);
426 else
427 (void) rename(log,file1);
428 if (noaction)
429 printf("Start new log...");
430 else {
431 fd = creat(log,perm);
432 if (fd < 0) {
433 perror("can't start new log");
434 exit(1);
435 }
436 if (fchown(fd, owner_uid, group_gid)) {
437 perror("can't chmod new log file");
438 exit(1);
439 }
440 (void) close(fd);
441 if (!(flags & CE_BINARY))
442 if (log_trim(log)) { /* Add status message */
443 perror("can't add status message to log");
444 exit(1);
445 }
446 }
447 if (noaction)
448 printf("chmod %o %s...",perm,log);
449 else
450 (void) chmod(log,perm);
451 if (noaction)
452 printf("kill -HUP %d (syslogd)\n",syslog_pid);
453 else
454 if (kill(syslog_pid,SIGHUP)) {
455 fprintf(stderr,"%s: ",progname);
456 perror("warning - could not restart syslogd");
457 }
458 if (flags & CE_COMPACT) {
459 if (noaction)
460 printf("Compress %s.0\n",log);
461 else
462 compress_log(log);
463 }
464 }
465
466 /* Log the fact that the logs were turned over */
467 log_trim(log)
468 char *log;
469 {
470 FILE *f;
471 if ((f = fopen(log,"a")) == NULL)
472 return(-1);
473 fprintf(f,"%s %s newsyslog[%d]: logfile turned over\n",
474 daytime, hostname, getpid());
475 if (fclose(f) == EOF) {
476 perror("log_trim: fclose:");
477 exit(1);
478 }
479 return(0);
480 }
481
482 /* Fork of /usr/ucb/compress to compress the old log file */
483 compress_log(log)
484 char *log;
485 {
486 int pid;
487 char tmp[128];
488
489 pid = fork();
490 (void) sprintf(tmp,"%s.0",log);
491 if (pid < 0) {
492 fprintf(stderr,"%s: ",progname);
493 perror("fork");
494 exit(1);
495 } else if (!pid) {
496 (void) execl(COMPRESS,"compress","-f",tmp,0);
497 fprintf(stderr,"%s: ",progname);
498 perror(COMPRESS);
499 exit(1);
500 }
501 }
502
503 /* Return size in kilobytes of a file */
504 int sizefile(file)
505 char *file;
506 {
507 struct stat sb;
508
509 if (stat(file,&sb) < 0)
510 return(-1);
511 return(kbytes(dbtob(sb.st_blocks)));
512 }
513
514 /* Return the age of old log file (file.0) */
515 int age_old_log(file)
516 char *file;
517 {
518 struct stat sb;
519 char tmp[80];
520
521 (void) strcpy(tmp,file);
522 if (stat(strcat(tmp,".0"),&sb) < 0)
523 if (stat(strcat(tmp,COMPRESS_POSTFIX), &sb) < 0)
524 return(-1);
525 return( (int) (timenow - sb.st_mtime + 1800) / 3600);
526 }
527
528
529 #ifndef OSF
530 /* Duplicate a string using malloc */
531
532 char *strdup(strp)
533 register char *strp;
534 {
535 register char *cp;
536
537 if ((cp = malloc((unsigned) strlen(strp) + 1)) == NULL)
538 abort();
539 return(strcpy (cp, strp));
540 }
541 #endif
542
543 /* Skip Over Blanks */
544 char *sob(p)
545 register char *p;
546 {
547 while (p && *p && isspace(*p))
548 p++;
549 return(p);
550 }
551
552 /* Skip Over Non-Blanks */
553 char *son(p)
554 register char *p;
555 {
556 while (p && *p && !isspace(*p))
557 p++;
558 return(p);
559 }
560
561
562 /* Check if string is actually a number */
563
564 isnumber(string)
565 char *string;
566 {
567 while (*string != '\0') {
568 if (*string < '0' || *string > '9') return(0);
569 string++;
570 }
571 return(1);
572 }
573
574 #if !defined(__SABER__) && !defined(lint) && !defined(NO_WHAT_STRINGS)
575 static char *what_string = "@(#)newsyslog.c\t$Revision: 1.3 $ $Date: 1993/05/22 03:52:20 $ $Locker: $";
576 #endif
577
578 /*
579 * HISTORY
580 * $Log: newsyslog.c,v $
581 * Revision 1.3 1993/05/22 03:52:20 cgd
582 * add use of "COMPRESS_POSTFIX" define, to determine what postfix
583 * (e.g. ".Z", ".z", etc.) to append to compressed log files.
584 *
585 * Revision 1.2 1993/05/21 14:47:30 cgd
586 * use the correct (or so john brezak says) copyright.
587 *
588 * Revision 1.1 1993/05/21 14:44:02 cgd
589 * initial import of this log-rotation program to NetBSD
590 *
591 * Revision 3.0.2.2 1993/02/06 04:14:51 brezak
592 * Fix up rcsid.
593 * [1993/02/06 04:14:03 brezak]
594 *
595 * Revision 3.0 1993/01/01 07:39:17 ede
596 * Initial revision for OSF/1 R1.3
597 *
598 * Revision 1.2 1991/08/16 09:50:26 devrcs
599 * From John Brezak, brezak (at) apollo.com, originally from Project Athena
600 * [91/07/24 09:33:18 meissner]
601 *
602 * $EndLog$
603 */
604