1 /*
2  * FCRON - periodic command scheduler
3  *
4  *  Copyright 2000-2016 Thibault Godouet <fcron@free.fr>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  The GNU General Public License can also be found in the file
21  *  `LICENSE' that comes with the fcron source distribution.
22  */
23 
24 
25 /* This code was originally inspired by Anacron's sources of
26    Itai Tzur <itzur@actcom.co.il> */
27 
28 
29 #include "fcron.h"
30 
31 #include "log.h"
32 
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 
36 #ifdef DEBUG
37 char debug_opt = 1;             /* set to 1 if we are in debug mode */
38 #else
39 char debug_opt = 0;             /* set to 1 if we are in debug mode */
40 #endif
41 int dosyslog = 1;
42 char *logfile_path = NULL;
43 
44 
45 char *make_msg(const char *append, char *fmt, va_list args);
46 void log_syslog_str(int priority, char *msg);
47 void log_file_str(int priority, char *msg);
48 void log_console_str(int priority, char *msg);
49 void log_fd_str(int fd, char *msg);
50 static void print_line_prefix(FILE * logfile, int priority);
51 static void xlog(int priority, int fd, char *fmt, va_list args);
52 static void xlog_e(int priority, int fd, char *fmt, va_list args);
53 #ifdef HAVE_LIBPAM
54 static void log_pame(int priority, pam_handle_t * pamh, int pamerrno,
55                      char *fmt, va_list args);
56 #endif
57 
58 static char truncated[] = " (truncated)";
59 static int log_open = 0;
60 static FILE *logfile = NULL;
61 
62 
63 /* Initialise logging to either syslog or a file specified in fcron.conf,
64  * or take no action if logging is suppressed.
65  * This function will be called automatically if you attempt to log something,
66  * however you may have to call it explicitely as it needs to run before the
67  * program becomes a daemon so as it can print an error on the console
68  * if it can't open the logs correctly. */
69 void
xopenlog(void)70 xopenlog(void)
71 {
72     if (log_open)
73         return;
74 
75     /* we MUST set log_open to 1 before doing anything else. That way,
76      * if we call a function that logs something, which calls xopenlog,
77      * then we won't end up in a nasty loop */
78     log_open = 1;
79 
80     // are we logging to a file or using syslog or not logging at all?
81     if (dosyslog) {
82         openlog(prog_name, LOG_PID, SYSLOG_FACILITY);
83     }
84 
85     if (logfile_path != NULL) {
86         logfile = fopen(logfile_path, "a");
87         if (logfile == NULL) {
88             int saved_errno = errno;
89 
90             if (dosyslog) {
91                 /* we have already called openlog() which cannot fail */
92                 syslog(COMPLAIN_LEVEL, "Could not fopen log file '%s': %s",
93                        logfile_path, strerror(saved_errno));
94             }
95 
96             print_line_prefix(stderr, COMPLAIN_LEVEL);
97             fprintf(stderr, "Could not fopen log file '%s': %s\n", logfile_path,
98                     strerror(saved_errno));
99         }
100     }
101 
102 }
103 
104 
105 void
xcloselog()106 xcloselog()
107 {
108     if (!log_open)
109         return;
110 
111     // check whether we need to close syslog, or a file.
112     if (logfile != NULL) {
113         /* we must NOT use xfclose_check() in log.c to avoid infinite loops */
114         if (xfclose(&logfile) != 0) {
115             int saved_errno = errno;
116 
117             syslog(COMPLAIN_LEVEL, "Error while closing log file '%s': %s",
118                    logfile_path, strerror(saved_errno));
119 
120             if (foreground == 1) {
121                 print_line_prefix(stderr, COMPLAIN_LEVEL);
122                 fprintf(stderr, "Error while closing log file '%s': %s\n",
123                         logfile_path, strerror(saved_errno));
124             }
125         }
126     }
127 
128     if (dosyslog) {
129         closelog();
130     }
131 
132     log_open = 0;
133 }
134 
135 
136 /* Construct the message string from its parts, and append a string to it */
137 char *
make_msg(const char * append,char * fmt,va_list args)138 make_msg(const char *append, char *fmt, va_list args)
139 {
140     int len;
141     char *msg = NULL;
142 
143     if ((msg = calloc(1, MAX_MSG + 1)) == NULL)
144         return NULL;
145     /* There's some confusion in the documentation about what vsnprintf
146      * returns when the buffer overflows.  Hmmm... */
147     len = vsnprintf(msg, MAX_MSG + 1, fmt, args);
148     if (append != NULL) {
149         size_t size_to_cat = ((MAX_MSG - len) > 0) ? (MAX_MSG - len) : 0;
150         strncat(msg, ": ", size_to_cat);
151         strncat(msg, append, size_to_cat);
152         len += 2 + strlen(append);
153     }
154     if (len >= MAX_MSG)
155         strcpy(msg + (MAX_MSG - 1) - sizeof(truncated), truncated);
156 
157     return msg;
158 }
159 
160 
161 /* log a simple string to syslog if needed */
162 void
log_syslog_str(int priority,char * msg)163 log_syslog_str(int priority, char *msg)
164 {
165     if (dosyslog) {
166         xopenlog();
167         syslog(priority, "%s", msg);
168     }
169 }
170 
171 /* log a simple string to a log file if needed */
172 void
log_file_str(int priority,char * msg)173 log_file_str(int priority, char *msg)
174 {
175     xopenlog();
176 
177     /* we may have failed to open the logfile - check if
178      * it does exist *after* xopenlog() */
179     if (logfile != NULL) {
180         print_line_prefix(logfile, priority);
181         fprintf(logfile, "%s\n", msg);
182         fflush(logfile);
183     }
184 
185 }
186 
187 /* log a simple string to console if needed */
188 void
log_console_str(int priority,char * msg)189 log_console_str(int priority, char *msg)
190 {
191     if (foreground == 1) {
192         print_line_prefix(stderr, priority);
193         fprintf(stderr, "%s\n", msg);
194     }
195 }
196 
197 /* log a simple string to fd if needed */
198 void
log_fd_str(int fd,char * msg)199 log_fd_str(int fd, char *msg)
200 {
201     if (fd >= 0) {
202         send(fd, msg, strlen(msg), 0);
203         send(fd, "\n", strlen("\n"), 0);
204     }
205 }
206 
207 /* Log a message, described by "fmt" and "args", with the specified
208  * "priority". */
209 /* write it also to fd if positive, and to stderr if foreground==1 */
210 static void
xlog(int priority,int fd,char * fmt,va_list args)211 xlog(int priority, int fd, char *fmt, va_list args)
212 {
213     char *msg;
214 
215     if ((msg = make_msg(NULL, fmt, args)) == NULL)
216         return;
217 
218     log_syslog_str(priority, msg);
219     log_file_str(priority, msg);
220     log_console_str(priority, msg);
221     log_fd_str(fd, msg);
222 
223     Free_safe(msg);
224 }
225 
226 /* Same as xlog(), but also appends an error description corresponding
227  * to "errno". */
228 static void
xlog_e(int priority,int fd,char * fmt,va_list args)229 xlog_e(int priority, int fd, char *fmt, va_list args)
230 {
231     int saved_errno;
232     char *msg;
233 
234     saved_errno = errno;
235 
236     if ((msg = make_msg(strerror(saved_errno), fmt, args)) == NULL)
237         return;
238 
239     log_syslog_str(priority, msg);
240     log_file_str(priority, msg);
241     log_console_str(priority, msg);
242     log_fd_str(fd, msg);
243 
244     Free_safe(msg);
245 }
246 
247 /* write a message to the file specified by logfile. */
248 static void
print_line_prefix(FILE * logfile,int priority)249 print_line_prefix(FILE * logfile, int priority)
250 {
251     time_t t = time(NULL);
252     struct tm *ft;
253     char date[30];
254     char *type;
255 
256     // print the current time as a string.
257     ft = localtime(&t);
258     date[0] = '\0';
259     strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", ft);
260 
261     // is it an info/warning/error/debug message?
262     switch (priority) {
263     case EXPLAIN_LEVEL:
264         type = " INFO";
265         break;
266     case WARNING_LEVEL:
267         type = " WARN";
268         break;
269     case COMPLAIN_LEVEL:
270         type = "ERROR";
271         break;
272     case DEBUG_LEVEL:
273         type = "DEBUG";
274         break;
275     default:
276         type = "UNKNOWN_SEVERITY";
277     }
278 
279     // print the log message.
280     fprintf(logfile, "%s %s ", date, type);
281 }
282 
283 
284 #ifdef HAVE_LIBPAM
285 /* Same as log_syslog(), but also appends an error description corresponding
286  * to the pam_error. */
287 static void
log_pame(int priority,pam_handle_t * pamh,int pamerrno,char * fmt,va_list args)288 log_pame(int priority, pam_handle_t * pamh, int pamerrno, char *fmt,
289          va_list args)
290 {
291     char *msg;
292 
293     if ((msg = make_msg(pam_strerror(pamh, pamerrno), fmt, args)) == NULL)
294         return;
295 
296     log_syslog_str(priority, msg);
297     log_console_str(priority, msg);
298 
299     xcloselog();
300 
301     Free_safe(msg);
302 }
303 #endif
304 
305 
306 /* Log an "explain" level message */
307 void
explain(char * fmt,...)308 explain(char *fmt, ...)
309 {
310     va_list args;
311 
312     va_start(args, fmt);
313     xlog(EXPLAIN_LEVEL, -1, fmt, args);
314     va_end(args);
315 }
316 
317 /* as explain(), but also write message to fd if positive */
318 void
explain_fd(int fd,char * fmt,...)319 explain_fd(int fd, char *fmt, ...)
320 {
321     va_list args;
322 
323     va_start(args, fmt);
324     xlog(EXPLAIN_LEVEL, fd, fmt, args);
325     va_end(args);
326 }
327 
328 
329 /* Log an "explain" level message, with an error description */
330 void
explain_e(char * fmt,...)331 explain_e(char *fmt, ...)
332 {
333     va_list args;
334 
335     va_start(args, fmt);
336     xlog_e(EXPLAIN_LEVEL, -1, fmt, args);
337     va_end(args);
338 }
339 
340 
341 /* Log a "warning" level message */
342 void
warn(char * fmt,...)343 warn(char *fmt, ...)
344 {
345     va_list args;
346 
347     va_start(args, fmt);
348     xlog(WARNING_LEVEL, -1, fmt, args);
349     va_end(args);
350 }
351 
352 /* as warn(), but also write message to fd if positive */
353 void
warn_fd(int fd,char * fmt,...)354 warn_fd(int fd, char *fmt, ...)
355 {
356     va_list args;
357 
358     va_start(args, fmt);
359     xlog(WARNING_LEVEL, fd, fmt, args);
360     va_end(args);
361 }
362 
363 
364 /* Log a "warning" level message, with an error description */
365 void
warn_e(char * fmt,...)366 warn_e(char *fmt, ...)
367 {
368     va_list args;
369 
370     va_start(args, fmt);
371     xlog_e(WARNING_LEVEL, -1, fmt, args);
372     va_end(args);
373 }
374 
375 
376 /* Log a "complain" level message */
377 void
error(char * fmt,...)378 error(char *fmt, ...)
379 {
380     va_list args;
381 
382     va_start(args, fmt);
383     xlog(COMPLAIN_LEVEL, -1, fmt, args);
384     va_end(args);
385 }
386 
387 /* as error(), but also write message to fd if positive */
388 void
error_fd(int fd,char * fmt,...)389 error_fd(int fd, char *fmt, ...)
390 {
391     va_list args;
392 
393     va_start(args, fmt);
394     xlog(COMPLAIN_LEVEL, fd, fmt, args);
395     va_end(args);
396 }
397 
398 
399 /* Log a "complain" level message, with an error description */
400 void
error_e(char * fmt,...)401 error_e(char *fmt, ...)
402 {
403     va_list args;
404 
405     va_start(args, fmt);
406     xlog_e(COMPLAIN_LEVEL, -1, fmt, args);
407     va_end(args);
408 }
409 
410 
411 #ifdef HAVE_LIBPAM
412 /* Log a "complain" level message, with a PAM error description */
413 void
error_pame(pam_handle_t * pamh,int pamerrno,char * fmt,...)414 error_pame(pam_handle_t * pamh, int pamerrno, char *fmt, ...)
415 {
416     va_list args;
417 
418     xcloselog();                /* PAM is likely to have used openlog() */
419 
420     va_start(args, fmt);
421     log_pame(COMPLAIN_LEVEL, pamh, pamerrno, fmt, args);
422     va_end(args);
423 }
424 #endif
425 
426 /* Log a "complain" level message, and exit */
427 void
die(char * fmt,...)428 die(char *fmt, ...)
429 {
430     va_list args;
431 
432     va_start(args, fmt);
433     xlog(COMPLAIN_LEVEL, -1, fmt, args);
434     va_end(args);
435     if (getpid() == daemon_pid) {
436         error("Aborted");
437     }
438     else {
439         error("fcron child aborted: this does not affect the main fcron daemon,"
440               " but this may prevent a job from being run or an email from being sent.");
441     }
442 
443     exit(EXIT_ERR);
444 
445 }
446 
447 
448 /* Log a "complain" level message, with an error description, and exit */
449 void
die_e(char * fmt,...)450 die_e(char *fmt, ...)
451 {
452     va_list args;
453     int err_no = 0;
454 
455     err_no = errno;
456 
457     va_start(args, fmt);
458     xlog_e(COMPLAIN_LEVEL, -1, fmt, args);
459     va_end(args);
460     if (getpid() == daemon_pid) {
461         error("Aborted");
462     }
463     else {
464         error("fcron child aborted: this does not affect the main fcron daemon,"
465               " but this may prevent a job from being run or an email from being sent.");
466     }
467 
468 
469     exit(err_no);
470 
471 }
472 
473 #ifdef HAVE_LIBPAM
474 /* Log a "complain" level message, with a PAM error description, and exit */
475 void
die_pame(pam_handle_t * pamh,int pamerrno,char * fmt,...)476 die_pame(pam_handle_t * pamh, int pamerrno, char *fmt, ...)
477 {
478     va_list args;
479 
480     xcloselog();                /* PAM is likely to have used openlog() */
481 
482     va_start(args, fmt);
483     log_pame(COMPLAIN_LEVEL, pamh, pamerrno, fmt, args);
484     va_end(args);
485     pam_end(pamh, pamerrno);
486     if (getpid() == daemon_pid)
487         error("Aborted");
488 
489     exit(EXIT_ERR);
490 
491 }
492 #endif
493 
494 /* Log a "debug" level message */
495 void
Debug(char * fmt,...)496 Debug(char *fmt, ...)
497 {
498     va_list args;
499 
500     va_start(args, fmt);
501     xlog(DEBUG_LEVEL, -1, fmt, args);
502     va_end(args);
503 }
504 
505 /* write message to fd, and to syslog in "debug" level message if debug_opt */
506 void
send_msg_fd_debug(int fd,char * fmt,...)507 send_msg_fd_debug(int fd, char *fmt, ...)
508 {
509     char *msg;
510 
511     va_list args;
512 
513     va_start(args, fmt);
514 
515     if ((msg = make_msg(NULL, fmt, args)) == NULL)
516         return;
517 
518     if (debug_opt)
519         log_syslog_str(DEBUG_LEVEL, msg);
520 
521     log_fd_str(fd, msg);
522 
523     Free_safe(msg);
524 
525     va_end(args);
526 }
527 
528 /* write message to fd */
529 void
send_msg_fd(int fd,char * fmt,...)530 send_msg_fd(int fd, char *fmt, ...)
531 {
532     char *msg;
533 
534     va_list args;
535 
536     va_start(args, fmt);
537 
538     if ((msg = make_msg(NULL, fmt, args)) == NULL)
539         return;
540 
541     log_fd_str(fd, msg);
542 
543     Free_safe(msg);
544 
545     va_end(args);
546 }
547