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