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