Linux Application Development (paperback) (2nd Edition)

   

12.5. Reopening Log Files

Most system daemons keep log files indicating what they have been busy doing. As many Unix systems stay up for months without interruption, these log files can grow quite large. Simply removing (or renaming) the log files occasionally is not a good solution because the daemons would simply keep writing to the files despite their inaccessibility, and having to stop and start each daemon while the log files are cleaned up would result in system downtime (albeit not much). A common way for daemons to manage this situation is to catch SIGHUP and reopen their log files. This allows log rotation (periodically starting new log files while keeping the old ones) to happen with a simple shell script like

cd /var/log mv messages messages.old killall -HUP syslogd

Logrotate[19] is one program that takes advantage of this feature to perform safe log rotation.

[19] ftp://ftp.redhat.com/pub/redhat/code/logrotate/

Including this ability in most daemons is straightforward. One of the easiest approaches is to include a global variable that indicates whether the logs need to be reopened. Then a SIGHUP signal handler sets this variable whenever it is invoked, and the main part of the program checks the variable as often as possible. The following is an example program that does this:

1: /* sighup.c */ 2: 3: #include <errno.h> 4: #include <signal.h> 5: #include <stdio.h> 6: #include <string.h> 7: #include <unistd.h> 8: 9: volatile int reopenLog = 0; /* volatile as it is modified 10: by a signal handler */ 11: 12: /* write a line to the log */ 13: void logstring(int logfd, char * str) { 14: write(logfd, str, strlen(str)); 15: } 16: 17: /* When SIGHUP occurs, make a note of it and continue. */ 18: void hupHandler(int signum) { 19: reopenLog = 1; 20: } 21: 22: int main() { 23: int done = 0; 24: struct sigaction sa; 25: int rc; 26: int logfd; 27: 28: logfd = STDOUT_FILENO; 29: 30: /* Set up a signal handler for SIGHUP. Use memset() to 31: initialize the struct sigaction to be sure we clear all 32: of it. */ 33: memset(&sa, 0, sizeof(sa)); 34: sa.sa_handler = hupHandler; 35: 36: if (sigaction(SIGHUP, &sa, NULL)) perror("sigaction"); 37: 38: /* Log a message every two seconds, and reopen the log file 39: as requested by SIGHUP. */ 40: while (!done) { 41: /* sleep() returns nonzero if it didn't sleep long enough */ 42: rc = sleep(2); 43: if (rc) { 44: if (reopenLog) { 45: logstring(logfd, 46: "* reopening log files at SIGHUP's request\n"); 47: reopenLog = 0; 48: } else { 49: logstring(logfd, 50: "* sleep interrupted by unknown signal " 51: "-- dying\n"); 52: done = 1; 53: } 54: } else { 55: logstring(logfd, "Periodic message\n"); 56: } 57: } 58: 59: return 0; 60: }

To test this program, run it in one xterm and send it SIGHUP from another. For each SIGHUP the program receives, it prints out a message where it would ordinarily rotate its logs. Remember that if a signal arrives while another instance of the signal is already pending, only one instance of the signal is delivered, so do not send the signals too quickly.


       
     

    Категории