c77c60ec47369eddcf2f0ffbe1dfdf7146adf9b1
[openafs.git] / src / util / serverLog.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*  serverLog.c     - Server logging                                      */
11 /*                                                                        */
12 /*  Information Technology Center                                         */
13 /*  Date: 05/21/97                                                        */
14 /*                                                                        */
15 /*  Function    - These routines implement logging from the servers.      */
16 /*                                                                        */
17 /* ********************************************************************** */
18
19 #include <afsconfig.h>
20 #include <afs/param.h>
21 #include <afs/stds.h>
22
23 #include <afs/procmgmt.h>       /* signal(), kill(), wait(), etc. */
24
25 #include <roken.h>              /* Must come after procmgmt.h */
26 #ifdef AFS_PTHREAD_ENV
27  #include <opr/softsig.h>
28  #include <afs/procmgmt_softsig.h>      /* Must come after softsig.h */
29 #endif
30 #include <afs/opr.h>
31 #include "afsutil.h"
32 #include "fileutil.h"
33 #include <lwp.h>
34
35 #if defined(AFS_PTHREAD_ENV)
36 #include <pthread.h>
37 static pthread_once_t serverLogOnce = PTHREAD_ONCE_INIT;
38 static pthread_mutex_t serverLogMutex;
39 #define LOCK_SERVERLOG() opr_Verify(pthread_mutex_lock(&serverLogMutex) == 0)
40 #define UNLOCK_SERVERLOG() opr_Verify(pthread_mutex_unlock(&serverLogMutex) == 0)
41
42 #ifdef AFS_NT40_ENV
43 #define NULLDEV "NUL"
44 #else
45 #define NULLDEV "/dev/null"
46 #endif
47
48 #else /* AFS_PTHREAD_ENV */
49 #define LOCK_SERVERLOG()
50 #define UNLOCK_SERVERLOG()
51 #endif /* AFS_PTHREAD_ENV */
52
53 #ifdef AFS_NT40_ENV
54 #define F_OK 0
55 #define O_NONBLOCK 0
56 #endif
57
58 /*!
59  * Placeholder function to return dummy thread number.
60  */
61 static int
62 dummyThreadNum(void)
63 {
64     return -1;
65 }
66 static int (*threadNumProgram) (void) = dummyThreadNum;
67
68 /* After single-threaded startup, accesses to serverlogFD and
69  * serverLogSyslog* are protected by LOCK_SERVERLOG(). */
70 static int serverLogFD = -1;    /*!< The log file descriptor. */
71 static struct logOptions serverLogOpts; /*!< logging options */
72
73 int LogLevel;                   /*!< The current logging level. */
74 static int threadIdLogs = 0;    /*!< Include the thread id in log messages when true. */
75 static int resetSignals = 0;    /*!< Reset signal handlers for the next signal when true. */
76 static char *ourName = NULL;    /*!< The fully qualified log file path, saved for reopens. */
77
78 static int OpenLogFile(const char *fileName);
79 static void RotateLogFile(void);
80
81 /*!
82  * Determine if the file is a named pipe.
83  *
84  * This check is performed to support named pipes as logs by not rotating them
85  * and opening them with a non-blocking flags.
86  *
87  * \param[in] fileName  log file name
88  *
89  * \returns non-zero if log file is a named pipe.
90  */
91 static int
92 IsFIFO(const char *fileName)
93 {
94     struct stat statbuf;
95     return (lstat(fileName, &statbuf) == 0) && (S_ISFIFO(statbuf.st_mode));
96 }
97
98 /*!
99  * Return the current logging level.
100  */
101 int
102 GetLogLevel(void)
103 {
104     return LogLevel;
105 }
106
107 /*!
108  * Return the log destination.
109  */
110 enum logDest
111 GetLogDest(void)
112 {
113     return serverLogOpts.lopt_dest;
114 }
115
116 /*!
117  * Get the log filename for file based logging.
118  *
119  * An empty string is returned if the log destination is not
120  * file based.  The caller must make a copy of the string
121  * if it is accessed after the CloseLog.
122  */
123 const char *
124 GetLogFilename(void)
125 {
126     return serverLogOpts.lopt_dest == logDest_file ? (const char*)ourName : "";
127 }
128
129 /*!
130  * Set the function to log thread numbers.
131  */
132 void
133 SetLogThreadNumProgram(int (*func) (void) )
134 {
135     threadNumProgram = func;
136 }
137
138 /*!
139  * Write a block of bytes to the log.
140  *
141  * Write a block of bytes directly to the log without formatting
142  * or prepending a timestamp.
143  *
144  * \param[in] buf  pointer to bytes to write
145  * \param[in] len  number of bytes to write
146  */
147 void
148 WriteLogBuffer(char *buf, afs_uint32 len)
149 {
150     LOCK_SERVERLOG();
151     if (serverLogFD >= 0) {
152         if (write(serverLogFD, buf, len) < 0)
153             ; /* don't care */
154     }
155     UNLOCK_SERVERLOG();
156 }
157
158 /*!
159  * Get the current thread number.
160  */
161 int
162 LogThreadNum(void)
163 {
164   return (*threadNumProgram) ();
165 }
166
167 /*!
168  * Write a message to the log.
169  *
170  * \param[in] format  printf-style format string
171  * \param[in] args    variable list of arguments
172  */
173 void
174 vFSLog(const char *format, va_list args)
175 {
176     time_t currenttime;
177     char tbuffer[1024];
178     char *info;
179     size_t len;
180     struct tm tm;
181     int num;
182
183     currenttime = time(NULL);
184     len = strftime(tbuffer, sizeof(tbuffer), "%a %b %d %H:%M:%S %Y ",
185                    localtime_r(&currenttime, &tm));
186     info = &tbuffer[len];
187
188     if (threadIdLogs) {
189         num = (*threadNumProgram) ();
190         if (num > -1) {
191             snprintf(info, (sizeof tbuffer) - strlen(tbuffer), "[%d] ",
192                      num);
193             info += strlen(info);
194         }
195     }
196
197     vsnprintf(info, (sizeof tbuffer) - strlen(tbuffer), format, args);
198
199     len = strlen(tbuffer);
200     LOCK_SERVERLOG();
201 #ifdef HAVE_SYSLOG
202     if (serverLogOpts.dest == logDest_syslog) {
203         syslog(LOG_INFO, "%s", info);
204     } else
205 #endif
206     if (serverLogFD >= 0) {
207         if (write(serverLogFD, tbuffer, len) < 0)
208             ; /* don't care */
209     }
210     UNLOCK_SERVERLOG();
211
212 #if !defined(AFS_PTHREAD_ENV) && !defined(AFS_NT40_ENV)
213     if (serverLogOpts.dest == logDest_file) {
214         fflush(stdout);
215         fflush(stderr);         /* in case they're sharing the same FD */
216     }
217 #endif
218 }                               /*vFSLog */
219
220 /*!
221  * Write a message to the log.
222  *
223  * \param[in] format  printf-style format specification
224  * \param[in] ...     arguments for format specification
225  */
226 void
227 FSLog(const char *format, ...)
228 {
229     va_list args;
230
231     va_start(args, format);
232     vFSLog(format, args);
233     va_end(args);
234 }                               /*FSLog */
235
236 /*!
237  * Write the command-line invocation to the log.
238  *
239  * \param[in] argc      argument count from main()
240  * \param[in] argv      argument vector from main()
241  * \param[in] progname  program name
242  * \param[in] version   program version
243  * \param[in] logstring log message string
244  * \param[in] log       printf-style log function
245  */
246 void
247 LogCommandLine(int argc, char **argv, const char *progname,
248                const char *version, const char *logstring,
249                void (*log) (const char *format, ...))
250 {
251     int i, l;
252     char *commandLine, *cx;
253
254     opr_Assert(argc > 0);
255
256     for (l = i = 0; i < argc; i++)
257         l += strlen(argv[i]) + 1;
258     if ((commandLine = malloc(l))) {
259         for (cx = commandLine, i = 0; i < argc; i++) {
260             strcpy(cx, argv[i]);
261             cx += strlen(cx);
262             *(cx++) = ' ';
263         }
264         commandLine[l-1] = '\0';
265         (*log)("%s %s %s%s(%s)\n", logstring, progname,
266                     version, strlen(version)>0?" ":"", commandLine);
267         free(commandLine);
268     } else {
269         /* What, we're out of memory already!? */
270         (*log)("%s %s%s%s\n", logstring,
271               progname, strlen(version)>0?" ":"", version);
272     }
273 }
274
275 /*!
276  * Write the single-DES deprecation warning to the log.
277  */
278 void
279 LogDesWarning(void)
280 {
281     /* The blank newlines help this stand out a bit more in the log. */
282     ViceLog(0, ("\n"));
283     ViceLog(0, ("WARNING: You are using single-DES keys in a KeyFile. Using single-DES\n"));
284     ViceLog(0, ("WARNING: long-term keys is considered insecure, and it is strongly\n"));
285     ViceLog(0, ("WARNING: recommended that you migrate to stronger encryption. See\n"));
286     ViceLog(0, ("WARNING: OPENAFS-SA-2013-003 on http://www.openafs.org/security/\n"));
287     ViceLog(0, ("WARNING: for details.\n"));
288     ViceLog(0, ("\n"));
289 }
290
291 /*!
292  * Move the current log file out of the way so a new one can be started.
293  *
294  * The format of the new name depends on the logging style.  The traditional
295  * Transarc style appends ".old" to the log file name.  When MR-AFS style
296  * logging is in effect, a time stamp is appended to the log file name instead
297  * of ".old".
298  *
299  * \bug  Unfortunately, no check is made to avoid overwriting
300  *       old logs in the traditional Transarc mode.
301  *
302  * \param fileName  fully qualified log file path
303  */
304 static void
305 RenameLogFile(const char *fileName)
306 {
307     int code;
308     char *nextName = NULL;
309     int tries;
310     time_t t;
311     struct stat buf;
312     struct tm *timeFields;
313
314     switch (serverLogOpts.lopt_rotateStyle) {
315     case logRotate_none:
316         break;
317     case logRotate_old:
318         code = asprintf(&nextName, "%s.old", fileName);
319         if (code < 0) {
320             nextName = NULL;
321         }
322         break;
323     case logRotate_timestamp:
324         time(&t);
325         for (tries = 0; nextName == NULL && tries < 100; t++, tries++) {
326             timeFields = localtime(&t);
327             code = asprintf(&nextName, "%s.%d%02d%02d%02d%02d%02d",
328                             fileName, timeFields->tm_year + 1900,
329                             timeFields->tm_mon + 1, timeFields->tm_mday,
330                             timeFields->tm_hour, timeFields->tm_min,
331                             timeFields->tm_sec);
332             if (code < 0) {
333                 nextName = NULL;
334                 break;
335             }
336             if (lstat(nextName, &buf) == 0) {
337                 /* Avoid clobbering a log. */
338                 free(nextName);
339                 nextName = NULL;
340             }
341         }
342         break;
343     default:
344         opr_Assert(0);
345     }
346     if (nextName != NULL) {
347         rk_rename(fileName, nextName);  /* Don't check the error code. */
348         free(nextName);
349     }
350 }
351
352 /*!
353  * Write message to the log to indicate the log level.
354  *
355  * This helper function is called by the signal handlers when the log level is
356  * changed, to write a message to the log to indicate the log level has been
357  * changed.
358  */
359 static void*
360 DebugOn(void *param)
361 {
362     int loglevel = (intptr_t)param;
363     if (loglevel == 0) {
364         ViceLog(0, ("Reset Debug levels to 0\n"));
365     } else {
366         ViceLog(0, ("Set Debug On level = %d\n", loglevel));
367     }
368     return 0;
369 }                               /*DebugOn */
370
371 /*!
372  * Signal handler to increase the logging level.
373  *
374  * Increase the current logging level to 1 if it in currently 0,
375  * otherwise, increase the current logging level by a factor of 5 if it
376  * is currently non-zero.
377  *
378  * Enables thread id logging when the log level is greater than 1.
379  */
380 void
381 SetDebug_Signal(int signo)
382 {
383     if (LogLevel > 0) {
384         LogLevel *= 5;
385
386 #if defined(AFS_PTHREAD_ENV)
387         if (LogLevel > 1 && threadNumProgram != NULL &&
388             threadIdLogs == 0) {
389             threadIdLogs = 1;
390         }
391 #endif
392     } else {
393         LogLevel = 1;
394
395 #if defined(AFS_PTHREAD_ENV)
396         if (threadIdLogs == 1)
397             threadIdLogs = 0;
398 #endif
399     }
400 #if defined(AFS_PTHREAD_ENV)
401     DebugOn((void *)(intptr_t)LogLevel);
402 #else /* AFS_PTHREAD_ENV */
403     IOMGR_SoftSig(DebugOn, (void *)(intptr_t)LogLevel);
404 #endif /* AFS_PTHREAD_ENV */
405
406     if (resetSignals) {
407         /* When pthreaded softsig handlers are not in use, some platforms
408          * require this signal handler to be set again. */
409         (void)signal(signo, SetDebug_Signal);
410     }
411 }                               /*SetDebug_Signal */
412
413 /*!
414  * Signal handler to reset the logging level.
415  *
416  * Reset the logging level and disable thread id logging.
417  *
418  * \note This handler has the side-effect of rotating and reopening
419  *       MR-AFS style logs.
420  */
421 void
422 ResetDebug_Signal(int signo)
423 {
424     LogLevel = 0;
425
426 #if defined(AFS_PTHREAD_ENV)
427     DebugOn((void *)(intptr_t)LogLevel);
428 #else /* AFS_PTHREAD_ENV */
429     IOMGR_SoftSig(DebugOn, (void *)(intptr_t)LogLevel);
430 #endif /* AFS_PTHREAD_ENV */
431
432     if (resetSignals) {
433         /* When pthreaded softsig handlers are not in use, some platforms
434          * require this signal handler to be set again. */
435         (void)signal(signo, ResetDebug_Signal);
436     }
437 #if defined(AFS_PTHREAD_ENV)
438     if (threadIdLogs == 1)
439         threadIdLogs = 0;
440 #endif
441     if (serverLogOpts.lopt_rotateOnReset) {
442         RotateLogFile();
443     }
444 }                               /*ResetDebug_Signal */
445
446
447 #ifdef AFS_PTHREAD_ENV
448 /*!
449  * Register pthread-safe signal handlers for server log management.
450  *
451  * \note opr_softsig_Init() must be called before this function.
452  */
453 void
454 SetupLogSoftSignals(void)
455 {
456     opr_softsig_Register(SIGHUP, ResetDebug_Signal);
457     opr_softsig_Register(SIGTSTP, SetDebug_Signal);
458 #ifndef AFS_NT40_ENV
459     (void)signal(SIGPIPE, SIG_IGN);
460 #endif
461 }
462 #endif /* AFS_PTHREAD_ENV */
463
464 /*!
465  * Register signal handlers for server log management.
466  *
467  * \note This function is deprecated and should not be used
468  *       in new code. This function should be removed when
469  *       all the servers have been converted to pthreads
470  *       and lwp has been removed.
471  */
472 void
473 SetupLogSignals(void)
474 {
475     resetSignals = 1;
476     (void)signal(SIGHUP, ResetDebug_Signal);
477     /* Note that we cannot use SIGUSR1 -- Linux stole it for pthreads! */
478     (void)signal(SIGTSTP, SetDebug_Signal);
479 #ifndef AFS_NT40_ENV
480     (void)signal(SIGPIPE, SIG_IGN);
481 #endif
482 }
483
484 #if defined(AFS_PTHREAD_ENV)
485 static void
486 InitServerLogMutex(void)
487 {
488     opr_Verify(pthread_mutex_init(&serverLogMutex, NULL) == 0);
489 }
490 #endif /* AFS_PTHREAD_ENV */
491
492 /*!
493  * Redirect stdout and stderr to the log file.
494  *
495  * \note Call directly after opening the log file.
496  *
497  * \param[in] fileName  log file name
498  */
499 static void
500 RedirectStdStreams(const char *fileName)
501 {
502     if (freopen(fileName, "a", stdout) == NULL)
503         ; /* don't care */
504     if (freopen(fileName, "a", stderr) != NULL) {
505 #ifdef HAVE_SETVBUF
506         setvbuf(stderr, NULL, _IONBF, 0);
507 #else
508         setbuf(stderr, NULL);
509 #endif
510     }
511 }
512
513 /*!
514  * Open the log file.
515  *
516  * Open the log file using the options given in OpenLog().
517  *
518  * \returns 0 on success
519  */
520 static int
521 OpenLogFile(const char *fileName)
522 {
523     /*
524      * This function should allow various libraries that inconsistently
525      * use stdout/stderr to all go to the same place
526      */
527     int tempfd;
528     int flags = O_WRONLY | O_CREAT | O_APPEND;
529
530     opr_Assert(serverLogOpts.dest == logDest_file);
531
532     opr_Assert(fileName != NULL);
533
534     if (IsFIFO(fileName)) {
535         /* Support named pipes as logs by not rotating them. */
536         flags |= O_NONBLOCK;
537     } else if (serverLogOpts.lopt_rotateOnOpen) {
538         /* Old style logging always started a new log file. */
539         flags |= O_TRUNC;
540         RenameLogFile(fileName);
541     }
542
543     tempfd = open(fileName, flags, 0666);
544     if (tempfd < 0) {
545         printf("Unable to open log file %s\n", fileName);
546         return -1;
547     }
548     RedirectStdStreams(fileName);
549
550     /* Save our name for reopening. */
551     free(ourName);
552     ourName = strdup(fileName);
553     opr_Assert(ourName != NULL);
554
555     serverLogFD = tempfd;
556
557     return 0;
558 }
559
560 /*!
561  * Open the log file descriptor or a connection to the system log.
562  *
563  * This function should be called once during program initialization and
564  * must be called before calling FSLog() or WriteLogBuffer().  The
565  * fields of the given argument specify the logging destination and
566  * various optional features.
567  *
568  * The lopt_logLevel value specifies the initial logging level.
569  *
570  * The lopt_dest enum specifies the logging destination; either
571  * file based (logDest_file) or the system log (logDest_syslog).
572  *
573  * File Based Logging
574  * ------------------
575  *
576  * A file will be opened for log messages when the lopt_dest enum is set
577  * to logDest_file.  The file specified by lopt_filename will be opened
578  * for appending log messages.  A new file will be created if the log
579  * file does not exist.
580  *
581  * The lopt_rotateOnOpen flag specifies whether an existing log file is
582  * to be renamed and a new log file created during the call to OpenLog.
583  * The lopt_rotateOnOpen flag has no effect if the file given by
584  * lopt_filename is a named pipe (fifo).
585  *
586  * The lopt_rotateOnReset flag specifies whether the log file is renamed
587  * and then reopened when the reset signal (SIGHUP) is caught.
588  *
589  * The lopt_rotateStyle enum specifies how the new log file is renamed when
590  * lopt_rotateOnOpen or lopt_rotateOnReset are set. The lopt_rotateStyle
591  * may be the traditional Transarc style (logRotate_old) or the MR-AFS
592  * style (logRotate_timestamp).
593  *
594  * When lopt_rotateStyle is set to logRotate_old, the suffix ".old" is
595  * appended to the log file name. The existing ".old" log file is
596  * removed.
597  *
598  * When lopt_rotateStyle is set to logRotate_timestamp, a timestamp
599  * string is appended to the log file name and existing files are not
600  * removed.
601  *
602  * \note  Messages written to stdout and stderr are redirected to the log
603  *        file when file-based logging is in effect.
604  *
605  * System Logging
606  * --------------
607  *
608  * A connection to the system log (syslog) will be established for log
609  * messages when the lopt_dest enum is set to logDest_syslog.
610  *
611  * The lopt_facility specifies the system log facility to be used when
612  * writing messages to the system log.
613  *
614  * The lopt_tag string specifies the indentification string to be used
615  * when writing messages to the system log.
616  *
617  * \param opts  logging options. A copy of the logging
618  *              options will be made before returning to
619  *              the caller.
620  *
621  * \returns 0 on success
622  */
623 int
624 OpenLog(struct logOptions *opts)
625 {
626     int code;
627
628 #if defined(AFS_PTHREAD_ENV)
629     opr_Verify(pthread_once(&serverLogOnce, InitServerLogMutex) == 0);
630 #endif /* AFS_PTHREAD_ENV */
631
632     LogLevel = serverLogOpts.logLevel = opts->logLevel;
633     serverLogOpts.dest = opts->dest;
634     switch (serverLogOpts.dest) {
635     case logDest_file:
636         serverLogOpts.lopt_rotateOnOpen = opts->lopt_rotateOnOpen;
637         serverLogOpts.lopt_rotateOnReset = opts->lopt_rotateOnReset;
638         serverLogOpts.lopt_rotateStyle = opts->lopt_rotateStyle;
639         /* OpenLogFile() sets ourName; don't cache filename here. */
640         code = OpenLogFile(opts->lopt_filename);
641         break;
642 #ifdef HAVE_SYSLOG
643     case logDest_syslog:
644         serverLogOpts.lopt_rotateOnOpen = 0;
645         serverLogOpts.lopt_rotateOnReset = 0;
646         serverLogOpts.lopt_rotateStyle = logRotate_none;
647         openlog(opts->lopt_tag, LOG_PID, opts->lopt_facility);
648         code = 0;
649         break;
650 #endif
651     default:
652         opr_Assert(0);
653     }
654     return code;
655 }                               /*OpenLog */
656
657 /*!
658  * Reopen the log file descriptor.
659  *
660  * Reopen the log file descriptor in order to support rotation
661  * of the log files.  Has no effect when logging to the syslog.
662  *
663  * \returns  0 on success
664  */
665 int
666 ReOpenLog(void)
667 {
668     int flags = O_WRONLY | O_APPEND | O_CREAT;
669
670 #ifdef HAVE_SYSLOG
671     if (serverLogOpts.dest == logDest_syslog) {
672         return 0;
673     }
674 #endif
675
676     LOCK_SERVERLOG();
677     if (ourName == NULL) {
678         UNLOCK_SERVERLOG();
679         return -1;
680     }
681     if (IsFIFO(ourName)) {
682         flags |= O_NONBLOCK;
683     }
684     if (serverLogFD >= 0)
685         close(serverLogFD);
686     serverLogFD = open(ourName, flags, 0666);
687     if (serverLogFD >= 0) {
688         RedirectStdStreams(ourName);
689     }
690     UNLOCK_SERVERLOG();
691     return serverLogFD < 0 ? -1 : 0;
692 }
693
694 /*!
695  * Rotate the log file by renaming then truncating.
696  */
697 static void
698 RotateLogFile(void)
699 {
700     LOCK_SERVERLOG();
701     if (ourName != NULL) {
702         if (serverLogFD >= 0) {
703             close(serverLogFD);
704             serverLogFD = -1;
705         }
706         OpenLogFile(ourName);
707     }
708     UNLOCK_SERVERLOG();
709 }
710
711 /*!
712  * Close the server log file.
713  *
714  * \note Must be preceeded by OpenLog().
715  */
716 void
717 CloseLog(void)
718 {
719     LOCK_SERVERLOG();
720
721 #ifdef HAVE_SYSLOG
722     if (serverLogOpts.dest == logDest_syslog) {
723         closelog();
724     } else
725 #endif
726     {
727         if (serverLogFD >= 0) {
728             close(serverLogFD);
729             serverLogFD = -1;
730         }
731         free(ourName);
732         ourName = NULL;
733     }
734     UNLOCK_SERVERLOG();
735 }