Commit | Line | Data |
---|---|---|
059ec3d9 PH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
5a66c31b | 5 | /* Copyright (c) University of Cambridge 1995 - 2014 */ |
059ec3d9 PH |
6 | /* See the file NOTICE for conditions of use and distribution. */ |
7 | ||
8 | /* Functions for writing log files. The code for maintaining datestamped | |
9 | log files was originally contributed by Tony Sheen. */ | |
10 | ||
11 | ||
12 | #include "exim.h" | |
13 | ||
14 | #define LOG_NAME_SIZE 256 | |
15 | #define MAX_SYSLOG_LEN 870 | |
16 | ||
17 | #define LOG_MODE_FILE 1 | |
18 | #define LOG_MODE_SYSLOG 2 | |
19 | ||
921b12ca | 20 | enum { lt_main, lt_reject, lt_panic, lt_debug }; |
059ec3d9 | 21 | |
921b12ca | 22 | static uschar *log_names[] = { US"main", US"reject", US"panic", US"debug" }; |
059ec3d9 PH |
23 | |
24 | ||
25 | ||
26 | /************************************************* | |
27 | * Local static variables * | |
28 | *************************************************/ | |
29 | ||
30 | static uschar mainlog_name[LOG_NAME_SIZE]; | |
31 | static uschar rejectlog_name[LOG_NAME_SIZE]; | |
ed7f7860 | 32 | static uschar debuglog_name[LOG_NAME_SIZE]; |
059ec3d9 PH |
33 | |
34 | static uschar *mainlog_datestamp = NULL; | |
35 | static uschar *rejectlog_datestamp = NULL; | |
36 | ||
37 | static int mainlogfd = -1; | |
38 | static int rejectlogfd = -1; | |
39 | static ino_t mainlog_inode = 0; | |
40 | static ino_t rejectlog_inode = 0; | |
41 | ||
42 | static uschar *panic_save_buffer = NULL; | |
43 | static BOOL panic_recurseflag = FALSE; | |
44 | ||
45 | static BOOL syslog_open = FALSE; | |
46 | static BOOL path_inspected = FALSE; | |
47 | static int logging_mode = LOG_MODE_FILE; | |
48 | static uschar *file_path = US""; | |
49 | ||
50 | ||
51 | ||
52 | ||
53 | /************************************************* | |
54 | * Write to syslog * | |
55 | *************************************************/ | |
56 | ||
57 | /* The given string is split into sections according to length, or at embedded | |
58 | newlines, and syslogged as a numbered sequence if it is overlong or if there is | |
9675b384 PH |
59 | more than one line. However, if we are running in the test harness, do not do |
60 | anything. (The test harness doesn't use syslog - for obvious reasons - but we | |
61 | can get here if there is a failure to open the panic log.) | |
059ec3d9 PH |
62 | |
63 | Arguments: | |
64 | priority syslog priority | |
65 | s the string to be written | |
66 | ||
67 | Returns: nothing | |
68 | */ | |
69 | ||
70 | static void | |
71 | write_syslog(int priority, uschar *s) | |
72 | { | |
73 | int len, pass; | |
74 | int linecount = 0; | |
75 | ||
9675b384 PH |
76 | if (running_in_test_harness) return; |
77 | ||
059ec3d9 PH |
78 | if (!syslog_timestamp) s += log_timezone? 26 : 20; |
79 | ||
80 | len = Ustrlen(s); | |
81 | ||
82 | #ifndef NO_OPENLOG | |
83 | if (!syslog_open) | |
84 | { | |
85 | #ifdef SYSLOG_LOG_PID | |
86 | openlog(CS syslog_processname, LOG_PID|LOG_CONS, syslog_facility); | |
87 | #else | |
88 | openlog(CS syslog_processname, LOG_CONS, syslog_facility); | |
89 | #endif | |
90 | syslog_open = TRUE; | |
91 | } | |
92 | #endif | |
93 | ||
94 | /* First do a scan through the message in order to determine how many lines | |
95 | it is going to end up as. Then rescan to output it. */ | |
96 | ||
97 | for (pass = 0; pass < 2; pass++) | |
98 | { | |
99 | int i; | |
100 | int tlen; | |
101 | uschar *ss = s; | |
102 | for (i = 1, tlen = len; tlen > 0; i++) | |
103 | { | |
104 | int plen = tlen; | |
105 | uschar *nlptr = Ustrchr(ss, '\n'); | |
106 | if (nlptr != NULL) plen = nlptr - ss; | |
107 | #ifndef SYSLOG_LONG_LINES | |
108 | if (plen > MAX_SYSLOG_LEN) plen = MAX_SYSLOG_LEN; | |
109 | #endif | |
110 | tlen -= plen; | |
111 | if (ss[plen] == '\n') tlen--; /* chars left */ | |
112 | ||
113 | if (pass == 0) linecount++; else | |
114 | { | |
115 | if (linecount == 1) | |
116 | syslog(priority, "%.*s", plen, ss); | |
117 | else | |
118 | syslog(priority, "[%d%c%d] %.*s", i, | |
119 | (ss[plen] == '\n' && tlen != 0)? '\\' : '/', | |
120 | linecount, plen, ss); | |
121 | } | |
122 | ss += plen; | |
123 | if (*ss == '\n') ss++; | |
124 | } | |
125 | } | |
126 | } | |
127 | ||
128 | ||
129 | ||
130 | /************************************************* | |
131 | * Die tidily * | |
132 | *************************************************/ | |
133 | ||
134 | /* This is called when Exim is dying as a result of something going wrong in | |
135 | the logging, or after a log call with LOG_PANIC_DIE set. Optionally write a | |
136 | message to debug_file or a stderr file, if they exist. Then, if in the middle | |
8f128379 PH |
137 | of accepting a message, throw it away tidily by calling receive_bomb_out(); |
138 | this will attempt to send an SMTP response if appropriate. Passing NULL as the | |
139 | first argument stops it trying to run the NOTQUIT ACL (which might try further | |
140 | logging and thus cause problems). Otherwise, try to close down an outstanding | |
141 | SMTP call tidily. | |
059ec3d9 PH |
142 | |
143 | Arguments: | |
144 | s1 Error message to write to debug_file and/or stderr and syslog | |
145 | s2 Error message for any SMTP call that is in progress | |
146 | Returns: The function does not return | |
147 | */ | |
148 | ||
149 | static void | |
150 | die(uschar *s1, uschar *s2) | |
151 | { | |
152 | if (s1 != NULL) | |
153 | { | |
154 | write_syslog(LOG_CRIT, s1); | |
155 | if (debug_file != NULL) debug_printf("%s\n", s1); | |
156 | if (log_stderr != NULL && log_stderr != debug_file) | |
157 | fprintf(log_stderr, "%s\n", s1); | |
158 | } | |
8f128379 | 159 | if (receive_call_bombout) receive_bomb_out(NULL, s2); /* does not return */ |
059ec3d9 PH |
160 | if (smtp_input) smtp_closedown(s2); |
161 | exim_exit(EXIT_FAILURE); | |
162 | } | |
163 | ||
164 | ||
165 | ||
166 | /************************************************* | |
167 | * Create a log file * | |
168 | *************************************************/ | |
169 | ||
170 | /* This function is called to create and open a log file. It may be called in a | |
171 | subprocess when the original process is root. | |
172 | ||
173 | Arguments: | |
174 | name the file name | |
175 | ||
176 | The file name has been build in a working buffer, so it is permissible to | |
177 | overwrite it temporarily if it is necessary to create the directory. | |
178 | ||
179 | Returns: a file descriptor, or < 0 on failure (errno set) | |
180 | */ | |
181 | ||
4840604e | 182 | int |
921b12ca | 183 | log_create(uschar *name) |
059ec3d9 PH |
184 | { |
185 | int fd = Uopen(name, O_CREAT|O_APPEND|O_WRONLY, LOG_MODE); | |
186 | ||
187 | /* If creation failed, attempt to build a log directory in case that is the | |
188 | problem. */ | |
189 | ||
190 | if (fd < 0 && errno == ENOENT) | |
191 | { | |
192 | BOOL created; | |
193 | uschar *lastslash = Ustrrchr(name, '/'); | |
194 | *lastslash = 0; | |
195 | created = directory_make(NULL, name, LOG_DIRECTORY_MODE, FALSE); | |
196 | DEBUG(D_any) debug_printf("%s log directory %s\n", | |
197 | created? "created" : "failed to create", name); | |
198 | *lastslash = '/'; | |
199 | if (created) fd = Uopen(name, O_CREAT|O_APPEND|O_WRONLY, LOG_MODE); | |
200 | } | |
201 | ||
202 | return fd; | |
203 | } | |
204 | ||
205 | ||
206 | ||
921b12ca TF |
207 | /************************************************* |
208 | * Create a log file as the exim user * | |
209 | *************************************************/ | |
210 | ||
211 | /* This function is called when we are root to spawn an exim:exim subprocess | |
212 | in which we can create a log file. It must be signal-safe since it is called | |
213 | by the usr1_handler(). | |
214 | ||
215 | Arguments: | |
216 | name the file name | |
217 | ||
218 | Returns: a file descriptor, or < 0 on failure (errno set) | |
219 | */ | |
220 | ||
221 | int | |
222 | log_create_as_exim(uschar *name) | |
223 | { | |
224 | pid_t pid = fork(); | |
225 | int status = 1; | |
226 | int fd = -1; | |
227 | ||
228 | /* In the subprocess, change uid/gid and do the creation. Return 0 from the | |
229 | subprocess on success. If we don't check for setuid failures, then the file | |
230 | can be created as root, so vulnerabilities which cause setuid to fail mean | |
231 | that the Exim user can use symlinks to cause a file to be opened/created as | |
232 | root. We always open for append, so can't nuke existing content but it would | |
233 | still be Rather Bad. */ | |
234 | ||
235 | if (pid == 0) | |
236 | { | |
237 | if (setgid(exim_gid) < 0) | |
238 | die(US"exim: setgid for log-file creation failed, aborting", | |
239 | US"Unexpected log failure, please try later"); | |
240 | if (setuid(exim_uid) < 0) | |
241 | die(US"exim: setuid for log-file creation failed, aborting", | |
242 | US"Unexpected log failure, please try later"); | |
243 | _exit((log_create(name) < 0)? 1 : 0); | |
244 | } | |
245 | ||
246 | /* If we created a subprocess, wait for it. If it succeeded, try the open. */ | |
247 | ||
248 | while (pid > 0 && waitpid(pid, &status, 0) != pid); | |
249 | if (status == 0) fd = Uopen(name, O_APPEND|O_WRONLY, LOG_MODE); | |
250 | ||
251 | /* If we failed to create a subprocess, we are in a bad way. We return | |
252 | with fd still < 0, and errno set, letting the caller handle the error. */ | |
253 | ||
254 | return fd; | |
255 | } | |
256 | ||
257 | ||
258 | ||
059ec3d9 PH |
259 | |
260 | /************************************************* | |
261 | * Open a log file * | |
262 | *************************************************/ | |
263 | ||
921b12ca TF |
264 | /* This function opens one of a number of logs, creating the log directory if |
265 | it does not exist. This may be called recursively on failure, in order to open | |
266 | the panic log. | |
059ec3d9 PH |
267 | |
268 | The directory is in the static variable file_path. This is static so that it | |
269 | the work of sorting out the path is done just once per Exim process. | |
270 | ||
271 | Exim is normally configured to avoid running as root wherever possible, the log | |
272 | files must be owned by the non-privileged exim user. To ensure this, first try | |
273 | an open without O_CREAT - most of the time this will succeed. If it fails, try | |
274 | to create the file; if running as root, this must be done in a subprocess to | |
275 | avoid races. | |
276 | ||
277 | Arguments: | |
278 | fd where to return the resulting file descriptor | |
921b12ca | 279 | type lt_main, lt_reject, lt_panic, or lt_debug |
ed7f7860 | 280 | tag optional tag to include in the name (only hooked up for debug) |
059ec3d9 PH |
281 | |
282 | Returns: nothing | |
283 | */ | |
284 | ||
285 | static void | |
ed7f7860 | 286 | open_log(int *fd, int type, uschar *tag) |
059ec3d9 PH |
287 | { |
288 | uid_t euid; | |
ed7f7860 | 289 | BOOL ok, ok2; |
059ec3d9 PH |
290 | uschar buffer[LOG_NAME_SIZE]; |
291 | ||
921b12ca TF |
292 | /* The names of the log files are controlled by file_path. The panic log is |
293 | written to the same directory as the main and reject logs, but its name does | |
f1e5fef5 PP |
294 | not have a datestamp. The use of datestamps is indicated by %D/%M in file_path. |
295 | When opening the panic log, if %D or %M is present, we remove the datestamp | |
296 | from the generated name; if it is at the start, remove a following | |
297 | non-alphanumeric character as well; otherwise, remove a preceding | |
298 | non-alphanumeric character. This is definitely kludgy, but it sort of does what | |
299 | people want, I hope. */ | |
059ec3d9 | 300 | |
921b12ca TF |
301 | ok = string_format(buffer, sizeof(buffer), CS file_path, log_names[type]); |
302 | ||
303 | /* Save the name of the mainlog for rollover processing. Without a datestamp, | |
304 | it gets statted to see if it has been cycled. With a datestamp, the datestamp | |
305 | will be compared. The static slot for saving it is the same size as buffer, | |
306 | and the text has been checked above to fit, so this use of strcpy() is OK. */ | |
307 | ||
308 | if (type == lt_main) | |
059ec3d9 | 309 | { |
921b12ca TF |
310 | Ustrcpy(mainlog_name, buffer); |
311 | mainlog_datestamp = mainlog_name + string_datestamp_offset; | |
312 | } | |
059ec3d9 | 313 | |
921b12ca | 314 | /* Ditto for the reject log */ |
059ec3d9 | 315 | |
921b12ca TF |
316 | else if (type == lt_reject) |
317 | { | |
318 | Ustrcpy(rejectlog_name, buffer); | |
319 | rejectlog_datestamp = rejectlog_name + string_datestamp_offset; | |
320 | } | |
059ec3d9 | 321 | |
921b12ca TF |
322 | /* and deal with the debug log (which keeps the datestamp, but does not |
323 | update it) */ | |
059ec3d9 | 324 | |
921b12ca TF |
325 | else if (type == lt_debug) |
326 | { | |
327 | Ustrcpy(debuglog_name, buffer); | |
328 | if (tag) | |
059ec3d9 | 329 | { |
921b12ca TF |
330 | /* this won't change the offset of the datestamp */ |
331 | ok2 = string_format(buffer, sizeof(buffer), "%s%s", | |
332 | debuglog_name, tag); | |
333 | if (ok2) | |
334 | Ustrcpy(debuglog_name, buffer); | |
059ec3d9 | 335 | } |
921b12ca | 336 | } |
059ec3d9 | 337 | |
921b12ca TF |
338 | /* Remove any datestamp if this is the panic log. This is rare, so there's no |
339 | need to optimize getting the datestamp length. We remove one non-alphanumeric | |
340 | char afterwards if at the start, otherwise one before. */ | |
ed7f7860 | 341 | |
921b12ca TF |
342 | else if (string_datestamp_offset >= 0) |
343 | { | |
344 | uschar *from = buffer + string_datestamp_offset; | |
345 | uschar *to = from + string_datestamp_length; | |
346 | if (from == buffer || from[-1] == '/') | |
ed7f7860 | 347 | { |
921b12ca | 348 | if (!isalnum(*to)) to++; |
ed7f7860 | 349 | } |
921b12ca | 350 | else |
059ec3d9 | 351 | { |
921b12ca TF |
352 | if (!isalnum(from[-1])) from--; |
353 | } | |
059ec3d9 | 354 | |
921b12ca | 355 | /* This strcpy is ok, because we know that to is a substring of from. */ |
059ec3d9 | 356 | |
921b12ca | 357 | Ustrcpy(from, to); |
059ec3d9 PH |
358 | } |
359 | ||
360 | /* If the file name is too long, it is an unrecoverable disaster */ | |
361 | ||
362 | if (!ok) | |
363 | { | |
364 | die(US"exim: log file path too long: aborting", | |
365 | US"Logging failure; please try later"); | |
366 | } | |
367 | ||
368 | /* We now have the file name. Try to open an existing file. After a successful | |
369 | open, arrange for automatic closure on exec(), and then return. */ | |
370 | ||
371 | *fd = Uopen(buffer, O_APPEND|O_WRONLY, LOG_MODE); | |
372 | ||
373 | if (*fd >= 0) | |
374 | { | |
ff790e47 | 375 | (void)fcntl(*fd, F_SETFD, fcntl(*fd, F_GETFD) | FD_CLOEXEC); |
059ec3d9 PH |
376 | return; |
377 | } | |
378 | ||
379 | /* Open was not successful: try creating the file. If this is a root process, | |
380 | we must do the creating in a subprocess set to exim:exim in order to ensure | |
381 | that the file is created with the right ownership. Otherwise, there can be a | |
901f42cb PH |
382 | race if another Exim process is trying to write to the log at the same time. |
383 | The use of SIGUSR1 by the exiwhat utility can provoke a lot of simultaneous | |
384 | writing. */ | |
059ec3d9 PH |
385 | |
386 | euid = geteuid(); | |
387 | ||
388 | /* If we are already running as the Exim user (even if that user is root), | |
389 | we can go ahead and create in the current process. */ | |
390 | ||
921b12ca | 391 | if (euid == exim_uid) *fd = log_create(buffer); |
059ec3d9 PH |
392 | |
393 | /* Otherwise, if we are root, do the creation in an exim:exim subprocess. If we | |
394 | are neither exim nor root, creation is not attempted. */ | |
395 | ||
921b12ca | 396 | else if (euid == root_uid) *fd = log_create_as_exim(buffer); |
059ec3d9 PH |
397 | |
398 | /* If we now have an open file, set the close-on-exec flag and return. */ | |
399 | ||
400 | if (*fd >= 0) | |
401 | { | |
ff790e47 | 402 | (void)fcntl(*fd, F_SETFD, fcntl(*fd, F_GETFD) | FD_CLOEXEC); |
059ec3d9 PH |
403 | return; |
404 | } | |
405 | ||
406 | /* Creation failed. There are some circumstances in which we get here when | |
407 | the effective uid is not root or exim, which is the problem. (For example, a | |
408 | non-setuid binary with log_arguments set, called in certain ways.) Rather than | |
409 | just bombing out, force the log to stderr and carry on if stderr is available. | |
410 | */ | |
411 | ||
412 | if (euid != root_uid && euid != exim_uid && log_stderr != NULL) | |
413 | { | |
414 | *fd = fileno(log_stderr); | |
415 | return; | |
416 | } | |
417 | ||
418 | /* Otherwise this is a disaster. This call is deliberately ONLY to the panic | |
419 | log. If possible, save a copy of the original line that was being logged. If we | |
420 | are recursing (can't open the panic log either), the pointer will already be | |
421 | set. */ | |
422 | ||
423 | if (panic_save_buffer == NULL) | |
424 | { | |
425 | panic_save_buffer = (uschar *)malloc(LOG_BUFFER_SIZE); | |
426 | if (panic_save_buffer != NULL) | |
427 | memcpy(panic_save_buffer, log_buffer, LOG_BUFFER_SIZE); | |
428 | } | |
429 | ||
430 | log_write(0, LOG_PANIC_DIE, "Cannot open %s log file \"%s\": %s: " | |
431 | "euid=%d egid=%d", log_names[type], buffer, strerror(errno), euid, getegid()); | |
432 | /* Never returns */ | |
433 | } | |
434 | ||
435 | ||
436 | ||
437 | /************************************************* | |
438 | * Add configuration file info to log line * | |
439 | *************************************************/ | |
440 | ||
441 | /* This is put in a function because it's needed twice (once for debugging, | |
442 | once for real). | |
443 | ||
444 | Arguments: | |
445 | ptr pointer to the end of the line we are building | |
446 | flags log flags | |
447 | ||
448 | Returns: updated pointer | |
449 | */ | |
450 | ||
451 | static uschar * | |
452 | log_config_info(uschar *ptr, int flags) | |
453 | { | |
454 | Ustrcpy(ptr, "Exim configuration error"); | |
455 | ptr += 24; | |
456 | ||
457 | if ((flags & (LOG_CONFIG_FOR & ~LOG_CONFIG)) != 0) | |
458 | { | |
459 | Ustrcpy(ptr, " for "); | |
460 | return ptr + 5; | |
461 | } | |
462 | ||
463 | if ((flags & (LOG_CONFIG_IN & ~LOG_CONFIG)) != 0) | |
464 | { | |
465 | sprintf(CS ptr, " in line %d of %s", config_lineno, config_filename); | |
466 | while (*ptr) ptr++; | |
467 | } | |
468 | ||
469 | Ustrcpy(ptr, ":\n "); | |
470 | return ptr + 4; | |
471 | } | |
472 | ||
473 | ||
474 | /************************************************* | |
475 | * A write() operation failed * | |
476 | *************************************************/ | |
477 | ||
478 | /* This function is called when write() fails on anything other than the panic | |
479 | log, which can happen if a disk gets full or a file gets too large or whatever. | |
480 | We try to save the relevant message in the panic_save buffer before crashing | |
481 | out. | |
482 | ||
23ecb73d PP |
483 | The potential invoker should probably not call us for EINTR -1 writes. But |
484 | otherwise, short writes are bad as we don't do non-blocking writes to fds | |
485 | subject to flow control. (If we do, that's new and the logic of this should | |
486 | be reconsidered). | |
487 | ||
059ec3d9 PH |
488 | Arguments: |
489 | name the name of the log being written | |
490 | length the string length being written | |
491 | rc the return value from write() | |
492 | ||
493 | Returns: does not return | |
494 | */ | |
495 | ||
496 | static void | |
497 | log_write_failed(uschar *name, int length, int rc) | |
498 | { | |
499 | int save_errno = errno; | |
500 | ||
501 | if (panic_save_buffer == NULL) | |
502 | { | |
503 | panic_save_buffer = (uschar *)malloc(LOG_BUFFER_SIZE); | |
504 | if (panic_save_buffer != NULL) | |
505 | memcpy(panic_save_buffer, log_buffer, LOG_BUFFER_SIZE); | |
506 | } | |
507 | ||
508 | log_write(0, LOG_PANIC_DIE, "failed to write to %s: length=%d result=%d " | |
509 | "errno=%d (%s)", name, length, rc, save_errno, | |
510 | (save_errno == 0)? "write incomplete" : strerror(save_errno)); | |
511 | /* Never returns */ | |
512 | } | |
513 | ||
514 | ||
515 | ||
0599f9cf PP |
516 | /************************************************* |
517 | * Write to an fd, retrying after signals * | |
518 | *************************************************/ | |
519 | ||
520 | /* Basic write to fd for logs, handling EINTR. | |
521 | ||
522 | Arguments: | |
523 | fd the fd to write to | |
524 | buf the string to write | |
525 | length the string length being written | |
526 | ||
527 | Returns: | |
528 | length actually written, persisting an errno from write() | |
529 | */ | |
530 | ssize_t | |
17c76198 | 531 | write_to_fd_buf(int fd, const uschar *buf, size_t length) |
0599f9cf PP |
532 | { |
533 | ssize_t wrote; | |
534 | size_t total_written = 0; | |
17c76198 | 535 | const uschar *p = buf; |
0599f9cf PP |
536 | size_t left = length; |
537 | ||
538 | while (1) | |
539 | { | |
540 | wrote = write(fd, p, left); | |
541 | if (wrote == (ssize_t)-1) | |
542 | { | |
543 | if (errno == EINTR) continue; | |
544 | return wrote; | |
545 | } | |
546 | total_written += wrote; | |
547 | if (wrote == left) | |
548 | break; | |
549 | else | |
550 | { | |
551 | p += wrote; | |
552 | left -= wrote; | |
553 | } | |
554 | } | |
555 | return total_written; | |
556 | } | |
557 | ||
558 | ||
059ec3d9 PH |
559 | /************************************************* |
560 | * Write message to log file * | |
561 | *************************************************/ | |
562 | ||
563 | /* Exim can be configured to log to local files, or use syslog, or both. This | |
564 | is controlled by the setting of log_file_path. The following cases are | |
565 | recognized: | |
566 | ||
567 | log_file_path = "" write files in the spool/log directory | |
568 | log_file_path = "xxx" write files in the xxx directory | |
569 | log_file_path = "syslog" write to syslog | |
570 | log_file_path = "syslog : xxx" write to syslog and to files (any order) | |
571 | ||
059ec3d9 PH |
572 | The message always gets '\n' added on the end of it, since more than one |
573 | process may be writing to the log at once and we don't want intermingling to | |
574 | happen in the middle of lines. To be absolutely sure of this we write the data | |
575 | into a private buffer and then put it out in a single write() call. | |
576 | ||
577 | The flags determine which log(s) the message is written to, or for syslogging, | |
578 | which priority to use, and in the case of the panic log, whether the process | |
579 | should die afterwards. | |
580 | ||
581 | The variable really_exim is TRUE only when exim is running in privileged state | |
582 | (i.e. not with a changed configuration or with testing options such as -brw). | |
583 | If it is not, don't try to write to the log because permission will probably be | |
584 | denied. | |
585 | ||
586 | Avoid actually writing to the logs when exim is called with -bv or -bt to | |
587 | test an address, but take other actions, such as panicing. | |
588 | ||
589 | In Exim proper, the buffer for building the message is got at start-up, so that | |
590 | nothing gets done if it can't be got. However, some functions that are also | |
591 | used in utilities occasionally obey log_write calls in error situations, and it | |
592 | is simplest to put a single malloc() here rather than put one in each utility. | |
593 | Malloc is used directly because the store functions may call log_write(). | |
594 | ||
595 | If a message_id exists, we include it after the timestamp. | |
596 | ||
597 | Arguments: | |
598 | selector write to main log or LOG_INFO only if this value is zero, or if | |
599 | its bit is set in log_write_selector | |
600 | flags each bit indicates some independent action: | |
601 | LOG_SENDER add raw sender to the message | |
602 | LOG_RECIPIENTS add raw recipients list to message | |
603 | LOG_CONFIG add "Exim configuration error" | |
604 | LOG_CONFIG_FOR add " for " instead of ":\n " | |
605 | LOG_CONFIG_IN add " in line x[ of file y]" | |
606 | LOG_MAIN write to main log or syslog LOG_INFO | |
607 | LOG_REJECT write to reject log or syslog LOG_NOTICE | |
608 | LOG_PANIC write to panic log or syslog LOG_ALERT | |
609 | LOG_PANIC_DIE write to panic log or LOG_ALERT and then crash | |
059ec3d9 PH |
610 | format a printf() format |
611 | ... arguments for format | |
612 | ||
613 | Returns: nothing | |
614 | */ | |
615 | ||
616 | void | |
1ba28e2b | 617 | log_write(unsigned int selector, int flags, const char *format, ...) |
059ec3d9 PH |
618 | { |
619 | uschar *ptr; | |
0599f9cf | 620 | int length; |
059ec3d9 | 621 | int paniclogfd; |
23ecb73d | 622 | ssize_t written_len; |
059ec3d9 PH |
623 | va_list ap; |
624 | ||
625 | /* If panic_recurseflag is set, we have failed to open the panic log. This is | |
626 | the ultimate disaster. First try to write the message to a debug file and/or | |
627 | stderr and also to syslog. If panic_save_buffer is not NULL, it contains the | |
628 | original log line that caused the problem. Afterwards, expire. */ | |
629 | ||
630 | if (panic_recurseflag) | |
631 | { | |
632 | uschar *extra = (panic_save_buffer == NULL)? US"" : panic_save_buffer; | |
633 | if (debug_file != NULL) debug_printf("%s%s", extra, log_buffer); | |
634 | if (log_stderr != NULL && log_stderr != debug_file) | |
635 | fprintf(log_stderr, "%s%s", extra, log_buffer); | |
636 | if (*extra != 0) write_syslog(LOG_CRIT, extra); | |
637 | write_syslog(LOG_CRIT, log_buffer); | |
638 | die(US"exim: could not open panic log - aborting: see message(s) above", | |
639 | US"Unexpected log failure, please try later"); | |
640 | } | |
641 | ||
642 | /* Ensure we have a buffer (see comment above); this should never be obeyed | |
643 | when running Exim proper, only when running utilities. */ | |
644 | ||
645 | if (log_buffer == NULL) | |
646 | { | |
647 | log_buffer = (uschar *)malloc(LOG_BUFFER_SIZE); | |
648 | if (log_buffer == NULL) | |
649 | { | |
650 | fprintf(stderr, "exim: failed to get store for log buffer\n"); | |
651 | exim_exit(EXIT_FAILURE); | |
652 | } | |
653 | } | |
654 | ||
655 | /* If we haven't already done so, inspect the setting of log_file_path to | |
656 | determine whether to log to files and/or to syslog. Bits in logging_mode | |
657 | control this, and for file logging, the path must end up in file_path. This | |
658 | variable must be in permanent store because it may be required again later in | |
659 | the process. */ | |
660 | ||
661 | if (!path_inspected) | |
662 | { | |
663 | BOOL multiple = FALSE; | |
664 | int old_pool = store_pool; | |
665 | ||
666 | store_pool = POOL_PERM; | |
667 | ||
668 | /* If nothing has been set, don't waste effort... the default values for the | |
669 | statics are file_path="" and logging_mode = LOG_MODE_FILE. */ | |
670 | ||
671 | if (log_file_path[0] != 0) | |
672 | { | |
673 | int sep = ':'; /* Fixed separator - outside use */ | |
674 | uschar *s; | |
675 | uschar *ss = log_file_path; | |
676 | logging_mode = 0; | |
677 | while ((s = string_nextinlist(&ss,&sep,log_buffer,LOG_BUFFER_SIZE)) != NULL) | |
678 | { | |
679 | if (Ustrcmp(s, "syslog") == 0) | |
680 | logging_mode |= LOG_MODE_SYSLOG; | |
681 | else if ((logging_mode & LOG_MODE_FILE) != 0) multiple = TRUE; | |
682 | else | |
683 | { | |
684 | logging_mode |= LOG_MODE_FILE; | |
685 | ||
686 | /* If a non-empty path is given, use it */ | |
687 | ||
688 | if (s[0] != 0) | |
689 | { | |
690 | file_path = string_copy(s); | |
691 | } | |
692 | ||
693 | /* If the path is empty, we want to use the first non-empty, non- | |
694 | syslog item in LOG_FILE_PATH, if there is one, since the value of | |
695 | log_file_path may have been set at runtime. If there is no such item, | |
696 | use the ultimate default in the spool directory. */ | |
697 | ||
698 | else | |
699 | { | |
700 | uschar *t; | |
701 | uschar *tt = US LOG_FILE_PATH; | |
702 | while ((t = string_nextinlist(&tt,&sep,log_buffer,LOG_BUFFER_SIZE)) | |
703 | != NULL) | |
704 | { | |
705 | if (Ustrcmp(t, "syslog") == 0 || t[0] == 0) continue; | |
706 | file_path = string_copy(t); | |
707 | break; | |
708 | } | |
709 | } /* Empty item in log_file_path */ | |
710 | } /* First non-syslog item in log_file_path */ | |
711 | } /* Scan of log_file_path */ | |
712 | } | |
713 | ||
714 | /* If no modes have been selected, it is a major disaster */ | |
715 | ||
716 | if (logging_mode == 0) | |
717 | die(US"Neither syslog nor file logging set in log_file_path", | |
718 | US"Unexpected logging failure"); | |
719 | ||
720 | /* Set up the ultimate default if necessary. Then revert to the old store | |
721 | pool, and record that we've sorted out the path. */ | |
722 | ||
723 | if ((logging_mode & LOG_MODE_FILE) != 0 && file_path[0] == 0) | |
724 | file_path = string_sprintf("%s/log/%%slog", spool_directory); | |
725 | store_pool = old_pool; | |
726 | path_inspected = TRUE; | |
727 | ||
728 | /* If more than one file path was given, log a complaint. This recursive call | |
729 | should work since we have now set up the routing. */ | |
730 | ||
731 | if (multiple) | |
732 | { | |
733 | log_write(0, LOG_MAIN|LOG_PANIC, | |
734 | "More than one path given in log_file_path: using %s", file_path); | |
735 | } | |
736 | } | |
737 | ||
738 | /* If debugging, show all log entries, but don't show headers. Do it all | |
739 | in one go so that it doesn't get split when multi-processing. */ | |
740 | ||
741 | DEBUG(D_any|D_v) | |
742 | { | |
743 | int i; | |
744 | ptr = log_buffer; | |
745 | ||
746 | Ustrcpy(ptr, "LOG:"); | |
747 | ptr += 4; | |
748 | ||
749 | /* Show the options that were passed into the call. These are those whose | |
750 | flag values do not have the 0x80000000 bit in them. Note that this | |
751 | automatically exclude the "all" setting. */ | |
752 | ||
753 | for (i = 0; i < log_options_count; i++) | |
754 | { | |
755 | unsigned int bit = log_options[i].bit; | |
756 | if ((bit & 0x80000000) != 0) continue; | |
757 | if ((selector & bit) != 0) | |
758 | { | |
759 | *ptr++ = ' '; | |
760 | Ustrcpy(ptr, log_options[i].name); | |
761 | while (*ptr) ptr++; | |
762 | } | |
763 | } | |
764 | ||
d690cbdc | 765 | sprintf(CS ptr, "%s%s%s%s\n ", |
059ec3d9 PH |
766 | ((flags & LOG_MAIN) != 0)? " MAIN" : "", |
767 | ((flags & LOG_PANIC) != 0)? " PANIC" : "", | |
768 | ((flags & LOG_PANIC_DIE) == LOG_PANIC_DIE)? " DIE" : "", | |
059ec3d9 PH |
769 | ((flags & LOG_REJECT) != 0)? " REJECT" : ""); |
770 | ||
771 | while(*ptr) ptr++; | |
772 | if ((flags & LOG_CONFIG) != 0) ptr = log_config_info(ptr, flags); | |
773 | ||
774 | va_start(ap, format); | |
775 | if (!string_vformat(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer)-1, format, ap)) | |
776 | Ustrcpy(ptr, "**** log string overflowed log buffer ****"); | |
777 | va_end(ap); | |
778 | ||
779 | while(*ptr) ptr++; | |
780 | Ustrcat(ptr, "\n"); | |
781 | debug_printf("%s", log_buffer); | |
782 | } | |
783 | ||
784 | /* If no log file is specified, we are in a mess. */ | |
785 | ||
921b12ca | 786 | if ((flags & (LOG_MAIN|LOG_PANIC|LOG_REJECT)) == 0) |
059ec3d9 PH |
787 | log_write(0, LOG_MAIN|LOG_PANIC_DIE, "log_write called with no log " |
788 | "flags set"); | |
789 | ||
790 | /* There are some weird circumstances in which logging is disabled. */ | |
791 | ||
792 | if (disable_logging) | |
793 | { | |
794 | DEBUG(D_any) debug_printf("log writing disabled\n"); | |
795 | return; | |
796 | } | |
797 | ||
a7d7aa58 PH |
798 | /* Handle disabled reject log */ |
799 | ||
800 | if (!write_rejectlog) flags &= ~LOG_REJECT; | |
801 | ||
921b12ca TF |
802 | /* Create the main message in the log buffer. Do not include the message id |
803 | when called by a utility. */ | |
059ec3d9 PH |
804 | |
805 | ptr = log_buffer; | |
f3f065bb PH |
806 | sprintf(CS ptr, "%s ", tod_stamp(tod_log)); |
807 | while(*ptr) ptr++; | |
808 | ||
809 | if ((log_extra_selector & LX_pid) != 0) | |
810 | { | |
811 | sprintf(CS ptr, "[%d] ", (int)getpid()); | |
812 | while (*ptr) ptr++; | |
813 | } | |
814 | ||
921b12ca | 815 | if (really_exim && message_id[0] != 0) |
f3f065bb PH |
816 | { |
817 | sprintf(CS ptr, "%s ", message_id); | |
818 | while(*ptr) ptr++; | |
819 | } | |
059ec3d9 | 820 | |
059ec3d9 PH |
821 | if ((flags & LOG_CONFIG) != 0) ptr = log_config_info(ptr, flags); |
822 | ||
823 | va_start(ap, format); | |
824 | if (!string_vformat(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer)-1, format, ap)) | |
825 | Ustrcpy(ptr, "**** log string overflowed log buffer ****\n"); | |
826 | while(*ptr) ptr++; | |
827 | va_end(ap); | |
828 | ||
829 | /* Add the raw, unrewritten, sender to the message if required. This is done | |
830 | this way because it kind of fits with LOG_RECIPIENTS. */ | |
831 | ||
832 | if ((flags & LOG_SENDER) != 0 && | |
ccfdb010 | 833 | ptr < log_buffer + LOG_BUFFER_SIZE - 10 - Ustrlen(raw_sender)) |
059ec3d9 PH |
834 | { |
835 | sprintf(CS ptr, " from <%s>", raw_sender); | |
836 | while (*ptr) ptr++; | |
837 | } | |
838 | ||
839 | /* Add list of recipients to the message if required; the raw list, | |
840 | before rewriting, was saved in raw_recipients. There may be none, if an ACL | |
841 | discarded them all. */ | |
842 | ||
843 | if ((flags & LOG_RECIPIENTS) != 0 && ptr < log_buffer + LOG_BUFFER_SIZE - 6 && | |
844 | raw_recipients_count > 0) | |
845 | { | |
846 | int i; | |
847 | sprintf(CS ptr, " for"); | |
848 | while (*ptr) ptr++; | |
849 | for (i = 0; i < raw_recipients_count; i++) | |
850 | { | |
851 | uschar *s = raw_recipients[i]; | |
852 | if (log_buffer + LOG_BUFFER_SIZE - ptr < Ustrlen(s) + 3) break; | |
853 | sprintf(CS ptr, " %s", s); | |
854 | while (*ptr) ptr++; | |
855 | } | |
856 | } | |
857 | ||
858 | sprintf(CS ptr, "\n"); | |
859 | while(*ptr) ptr++; | |
860 | length = ptr - log_buffer; | |
861 | ||
862 | /* Handle loggable errors when running a utility, or when address testing. | |
863 | Write to log_stderr unless debugging (when it will already have been written), | |
864 | or unless there is no log_stderr (expn called from daemon, for example). */ | |
865 | ||
866 | if (!really_exim || log_testing_mode) | |
867 | { | |
868 | if (debug_selector == 0 && log_stderr != NULL && | |
869 | (selector == 0 || (selector & log_write_selector) != 0)) | |
870 | { | |
871 | if (host_checking) | |
872 | fprintf(log_stderr, "LOG: %s", CS(log_buffer + 20)); /* no timestamp */ | |
873 | else | |
874 | fprintf(log_stderr, "%s", CS log_buffer); | |
875 | } | |
876 | if ((flags & LOG_PANIC_DIE) == LOG_PANIC_DIE) exim_exit(EXIT_FAILURE); | |
877 | return; | |
878 | } | |
879 | ||
880 | /* Handle the main log. We know that either syslog or file logging (or both) is | |
881 | set up. A real file gets left open during reception or delivery once it has | |
882 | been opened, but we don't want to keep on writing to it for too long after it | |
883 | has been renamed. Therefore, do a stat() and see if the inode has changed, and | |
884 | if so, re-open. */ | |
885 | ||
886 | if ((flags & LOG_MAIN) != 0 && | |
887 | (selector == 0 || (selector & log_write_selector) != 0)) | |
888 | { | |
889 | if ((logging_mode & LOG_MODE_SYSLOG) != 0 && | |
890 | (syslog_duplication || (flags & (LOG_REJECT|LOG_PANIC)) == 0)) | |
891 | write_syslog(LOG_INFO, log_buffer); | |
892 | ||
893 | if ((logging_mode & LOG_MODE_FILE) != 0) | |
894 | { | |
895 | struct stat statbuf; | |
896 | ||
897 | /* Check for a change to the mainlog file name when datestamping is in | |
898 | operation. This happens at midnight, at which point we want to roll over | |
899 | the file. Closing it has the desired effect. */ | |
900 | ||
901 | if (mainlog_datestamp != NULL) | |
902 | { | |
f1e5fef5 | 903 | uschar *nowstamp = tod_stamp(string_datestamp_type); |
059ec3d9 PH |
904 | if (Ustrncmp (mainlog_datestamp, nowstamp, Ustrlen(nowstamp)) != 0) |
905 | { | |
f1e894f3 | 906 | (void)close(mainlogfd); /* Close the file */ |
059ec3d9 PH |
907 | mainlogfd = -1; /* Clear the file descriptor */ |
908 | mainlog_inode = 0; /* Unset the inode */ | |
909 | mainlog_datestamp = NULL; /* Clear the datestamp */ | |
910 | } | |
911 | } | |
912 | ||
913 | /* Otherwise, we want to check whether the file has been renamed by a | |
914 | cycling script. This could be "if else", but for safety's sake, leave it as | |
915 | "if" so that renaming the log starts a new file even when datestamping is | |
916 | happening. */ | |
917 | ||
918 | if (mainlogfd >= 0) | |
919 | { | |
920 | if (Ustat(mainlog_name, &statbuf) < 0 || statbuf.st_ino != mainlog_inode) | |
921 | { | |
f1e894f3 | 922 | (void)close(mainlogfd); |
059ec3d9 PH |
923 | mainlogfd = -1; |
924 | mainlog_inode = 0; | |
925 | } | |
926 | } | |
927 | ||
928 | /* If the log is closed, open it. Then write the line. */ | |
929 | ||
930 | if (mainlogfd < 0) | |
931 | { | |
ed7f7860 | 932 | open_log(&mainlogfd, lt_main, NULL); /* No return on error */ |
059ec3d9 PH |
933 | if (fstat(mainlogfd, &statbuf) >= 0) mainlog_inode = statbuf.st_ino; |
934 | } | |
935 | ||
936 | /* Failing to write to the log is disastrous */ | |
937 | ||
0599f9cf | 938 | written_len = write_to_fd_buf(mainlogfd, log_buffer, length); |
23ecb73d | 939 | if (written_len != length) |
059ec3d9 | 940 | { |
23ecb73d | 941 | log_write_failed(US"main log", length, written_len); |
059ec3d9 PH |
942 | /* That function does not return */ |
943 | } | |
944 | } | |
945 | } | |
946 | ||
a7d7aa58 PH |
947 | /* Handle the log for rejected messages. This can be globally disabled, in |
948 | which case the flags are altered above. If there are any header lines (i.e. if | |
949 | the rejection is happening after the DATA phase), log the recipients and the | |
950 | headers. */ | |
059ec3d9 | 951 | |
a7d7aa58 | 952 | if ((flags & LOG_REJECT) != 0) |
059ec3d9 PH |
953 | { |
954 | header_line *h; | |
955 | ||
956 | if (header_list != NULL && (log_extra_selector & LX_rejected_header) != 0) | |
957 | { | |
958 | if (recipients_count > 0) | |
959 | { | |
960 | int i; | |
961 | ||
962 | /* List the sender */ | |
963 | ||
964 | string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), | |
965 | "Envelope-from: <%s>\n", sender_address); | |
966 | while (*ptr) ptr++; | |
967 | ||
968 | /* List up to 5 recipients */ | |
969 | ||
970 | string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), | |
971 | "Envelope-to: <%s>\n", recipients_list[0].address); | |
972 | while (*ptr) ptr++; | |
973 | ||
974 | for (i = 1; i < recipients_count && i < 5; i++) | |
975 | { | |
976 | string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), " <%s>\n", | |
977 | recipients_list[i].address); | |
978 | while (*ptr) ptr++; | |
979 | } | |
980 | ||
981 | if (i < recipients_count) | |
982 | { | |
983 | (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), | |
984 | " ...\n"); | |
985 | while (*ptr) ptr++; | |
986 | } | |
987 | } | |
988 | ||
989 | /* A header with a NULL text is an unfilled in Received: header */ | |
990 | ||
991 | for (h = header_list; h != NULL; h = h->next) | |
992 | { | |
993 | BOOL fitted; | |
994 | if (h->text == NULL) continue; | |
995 | fitted = string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), | |
996 | "%c %s", h->type, h->text); | |
997 | while(*ptr) ptr++; | |
998 | if (!fitted) /* Buffer is full; truncate */ | |
999 | { | |
1000 | ptr -= 100; /* For message and separator */ | |
1001 | if (ptr[-1] == '\n') ptr--; | |
1002 | Ustrcpy(ptr, "\n*** truncated ***\n"); | |
1003 | while (*ptr) ptr++; | |
1004 | break; | |
1005 | } | |
1006 | } | |
1007 | ||
1008 | length = ptr - log_buffer; | |
1009 | } | |
1010 | ||
1011 | /* Write to syslog or to a log file */ | |
1012 | ||
1013 | if ((logging_mode & LOG_MODE_SYSLOG) != 0 && | |
1014 | (syslog_duplication || (flags & LOG_PANIC) == 0)) | |
1015 | write_syslog(LOG_NOTICE, log_buffer); | |
1016 | ||
1017 | /* Check for a change to the rejectlog file name when datestamping is in | |
1018 | operation. This happens at midnight, at which point we want to roll over | |
1019 | the file. Closing it has the desired effect. */ | |
1020 | ||
1021 | if ((logging_mode & LOG_MODE_FILE) != 0) | |
1022 | { | |
1023 | struct stat statbuf; | |
1024 | ||
1025 | if (rejectlog_datestamp != NULL) | |
1026 | { | |
f1e5fef5 | 1027 | uschar *nowstamp = tod_stamp(string_datestamp_type); |
059ec3d9 PH |
1028 | if (Ustrncmp (rejectlog_datestamp, nowstamp, Ustrlen(nowstamp)) != 0) |
1029 | { | |
f1e894f3 | 1030 | (void)close(rejectlogfd); /* Close the file */ |
059ec3d9 PH |
1031 | rejectlogfd = -1; /* Clear the file descriptor */ |
1032 | rejectlog_inode = 0; /* Unset the inode */ | |
1033 | rejectlog_datestamp = NULL; /* Clear the datestamp */ | |
1034 | } | |
1035 | } | |
1036 | ||
1037 | /* Otherwise, we want to check whether the file has been renamed by a | |
1038 | cycling script. This could be "if else", but for safety's sake, leave it as | |
1039 | "if" so that renaming the log starts a new file even when datestamping is | |
1040 | happening. */ | |
1041 | ||
1042 | if (rejectlogfd >= 0) | |
1043 | { | |
1044 | if (Ustat(rejectlog_name, &statbuf) < 0 || | |
1045 | statbuf.st_ino != rejectlog_inode) | |
1046 | { | |
f1e894f3 | 1047 | (void)close(rejectlogfd); |
059ec3d9 PH |
1048 | rejectlogfd = -1; |
1049 | rejectlog_inode = 0; | |
1050 | } | |
1051 | } | |
1052 | ||
1053 | /* Open the file if necessary, and write the data */ | |
1054 | ||
1055 | if (rejectlogfd < 0) | |
1056 | { | |
ed7f7860 | 1057 | open_log(&rejectlogfd, lt_reject, NULL); /* No return on error */ |
059ec3d9 PH |
1058 | if (fstat(rejectlogfd, &statbuf) >= 0) rejectlog_inode = statbuf.st_ino; |
1059 | } | |
1060 | ||
0599f9cf PP |
1061 | written_len = write_to_fd_buf(rejectlogfd, log_buffer, length); |
1062 | if (written_len != length) | |
059ec3d9 | 1063 | { |
0599f9cf | 1064 | log_write_failed(US"reject log", length, written_len); |
059ec3d9 PH |
1065 | /* That function does not return */ |
1066 | } | |
1067 | } | |
1068 | } | |
1069 | ||
1070 | ||
059ec3d9 PH |
1071 | /* Handle the panic log, which is not kept open like the others. If it fails to |
1072 | open, there will be a recursive call to log_write(). We detect this above and | |
1073 | attempt to write to the system log as a last-ditch try at telling somebody. In | |
47c7a64a | 1074 | all cases except mua_wrapper, try to write to log_stderr. */ |
059ec3d9 PH |
1075 | |
1076 | if ((flags & LOG_PANIC) != 0) | |
1077 | { | |
47c7a64a | 1078 | if (log_stderr != NULL && log_stderr != debug_file && !mua_wrapper) |
059ec3d9 PH |
1079 | fprintf(log_stderr, "%s", CS log_buffer); |
1080 | ||
1081 | if ((logging_mode & LOG_MODE_SYSLOG) != 0) | |
1082 | { | |
1083 | write_syslog(LOG_ALERT, log_buffer); | |
1084 | } | |
1085 | ||
1086 | /* If this panic logging was caused by a failure to open the main log, | |
1087 | the original log line is in panic_save_buffer. Make an attempt to write it. */ | |
1088 | ||
1089 | if ((logging_mode & LOG_MODE_FILE) != 0) | |
1090 | { | |
1091 | panic_recurseflag = TRUE; | |
ed7f7860 | 1092 | open_log(&paniclogfd, lt_panic, NULL); /* Won't return on failure */ |
059ec3d9 PH |
1093 | panic_recurseflag = FALSE; |
1094 | ||
1095 | if (panic_save_buffer != NULL) | |
1ac6b2e7 JH |
1096 | { |
1097 | int i = write(paniclogfd, panic_save_buffer, Ustrlen(panic_save_buffer)); | |
1098 | i = i; /* compiler quietening */ | |
1099 | } | |
059ec3d9 | 1100 | |
0599f9cf PP |
1101 | written_len = write_to_fd_buf(paniclogfd, log_buffer, length); |
1102 | if (written_len != length) | |
059ec3d9 PH |
1103 | { |
1104 | int save_errno = errno; | |
1105 | write_syslog(LOG_CRIT, log_buffer); | |
1106 | sprintf(CS log_buffer, "write failed on panic log: length=%d result=%d " | |
0599f9cf | 1107 | "errno=%d (%s)", length, (int)written_len, save_errno, strerror(save_errno)); |
059ec3d9 PH |
1108 | write_syslog(LOG_CRIT, log_buffer); |
1109 | flags |= LOG_PANIC_DIE; | |
1110 | } | |
1111 | ||
f1e894f3 | 1112 | (void)close(paniclogfd); |
059ec3d9 PH |
1113 | } |
1114 | ||
1115 | /* Give up if the DIE flag is set */ | |
1116 | ||
1117 | if ((flags & LOG_PANIC_DIE) != LOG_PANIC) | |
1118 | die(NULL, US"Unexpected failure, please try later"); | |
1119 | } | |
1120 | } | |
1121 | ||
1122 | ||
1123 | ||
1124 | /************************************************* | |
1125 | * Close any open log files * | |
1126 | *************************************************/ | |
1127 | ||
1128 | void | |
1129 | log_close_all(void) | |
1130 | { | |
1131 | if (mainlogfd >= 0) | |
f1e894f3 | 1132 | { (void)close(mainlogfd); mainlogfd = -1; } |
059ec3d9 | 1133 | if (rejectlogfd >= 0) |
f1e894f3 | 1134 | { (void)close(rejectlogfd); rejectlogfd = -1; } |
059ec3d9 PH |
1135 | closelog(); |
1136 | syslog_open = FALSE; | |
1137 | } | |
1138 | ||
ed7f7860 PP |
1139 | |
1140 | ||
1141 | /************************************************* | |
1142 | * Decode bit settings for log/debug * | |
1143 | *************************************************/ | |
1144 | ||
1145 | /* This function decodes a string containing bit settings in the form of +name | |
1146 | and/or -name sequences, and sets/unsets bits in a bit string accordingly. It | |
1147 | also recognizes a numeric setting of the form =<number>, but this is not | |
1148 | intended for user use. It's an easy way for Exim to pass the debug settings | |
1149 | when it is re-exec'ed. | |
1150 | ||
1151 | The log options are held in two unsigned ints (because there became too many | |
1152 | for one). The top bit in the table means "put in 2nd selector". This does not | |
1153 | yet apply to debug options, so the "=" facility sets only the first selector. | |
1154 | ||
1155 | The "all" selector, which must be equal to 0xffffffff, is recognized specially. | |
1156 | It sets all the bits in both selectors. However, there is a facility for then | |
1157 | unsetting certain bits, because we want to turn off "memory" in the debug case. | |
1158 | ||
1159 | The action taken for bad values varies depending upon why we're here. | |
1160 | For log messages, or if the debugging is triggered from config, then we write | |
1161 | to the log on the way out. For debug setting triggered from the command-line, | |
1162 | we treat it as an unknown option: error message to stderr and die. | |
1163 | ||
1164 | Arguments: | |
1165 | selector1 address of the first bit string | |
1166 | selector2 address of the second bit string, or NULL | |
1167 | notall1 bits to exclude from "all" for selector1 | |
1168 | notall2 bits to exclude from "all" for selector2 | |
1169 | string the configured string | |
1170 | options the table of option names | |
1171 | count size of table | |
1172 | which "log" or "debug" | |
1173 | flags DEBUG_FROM_CONFIG | |
1174 | ||
1175 | Returns: nothing on success - bomb out on failure | |
1176 | */ | |
1177 | ||
1178 | void | |
1179 | decode_bits(unsigned int *selector1, unsigned int *selector2, int notall1, | |
1180 | int notall2, uschar *string, bit_table *options, int count, uschar *which, | |
1181 | int flags) | |
1182 | { | |
1183 | uschar *errmsg; | |
1184 | if (string == NULL) return; | |
1185 | ||
1186 | if (*string == '=') | |
1187 | { | |
1188 | char *end; /* Not uschar */ | |
1189 | *selector1 = strtoul(CS string+1, &end, 0); | |
1190 | if (*end == 0) return; | |
1191 | errmsg = string_sprintf("malformed numeric %s_selector setting: %s", which, | |
1192 | string); | |
1193 | goto ERROR_RETURN; | |
1194 | } | |
1195 | ||
1196 | /* Handle symbolic setting */ | |
1197 | ||
1198 | else for(;;) | |
1199 | { | |
1200 | BOOL adding; | |
1201 | uschar *s; | |
1202 | int len; | |
1203 | bit_table *start, *end; | |
1204 | ||
1205 | while (isspace(*string)) string++; | |
1206 | if (*string == 0) return; | |
1207 | ||
1208 | if (*string != '+' && *string != '-') | |
1209 | { | |
1210 | errmsg = string_sprintf("malformed %s_selector setting: " | |
1211 | "+ or - expected but found \"%s\"", which, string); | |
1212 | goto ERROR_RETURN; | |
1213 | } | |
1214 | ||
1215 | adding = *string++ == '+'; | |
1216 | s = string; | |
1217 | while (isalnum(*string) || *string == '_') string++; | |
1218 | len = string - s; | |
1219 | ||
1220 | start = options; | |
1221 | end = options + count; | |
1222 | ||
1223 | while (start < end) | |
1224 | { | |
1225 | bit_table *middle = start + (end - start)/2; | |
1226 | int c = Ustrncmp(s, middle->name, len); | |
1227 | if (c == 0) | |
1228 | { | |
1229 | if (middle->name[len] != 0) c = -1; else | |
1230 | { | |
1231 | unsigned int bit = middle->bit; | |
1232 | unsigned int *selector; | |
1233 | ||
1234 | /* The value with all bits set means "force all bits in both selectors" | |
1235 | in the case where two are being handled. However, the top bit in the | |
1236 | second selector is never set. When setting, some bits can be excluded. | |
1237 | */ | |
1238 | ||
1239 | if (bit == 0xffffffff) | |
1240 | { | |
1241 | if (adding) | |
1242 | { | |
1243 | *selector1 = 0xffffffff ^ notall1; | |
1244 | if (selector2 != NULL) *selector2 = 0x7fffffff ^ notall2; | |
1245 | } | |
1246 | else | |
1247 | { | |
1248 | *selector1 = 0; | |
1249 | if (selector2 != NULL) *selector2 = 0; | |
1250 | } | |
1251 | } | |
1252 | ||
1253 | /* Otherwise, the 0x80000000 bit means "this value, without the top | |
1254 | bit, belongs in the second selector". */ | |
1255 | ||
1256 | else | |
1257 | { | |
1258 | if ((bit & 0x80000000) != 0) | |
1259 | { | |
1260 | selector = selector2; | |
1261 | bit &= 0x7fffffff; | |
1262 | } | |
1263 | else selector = selector1; | |
1264 | if (adding) *selector |= bit; else *selector &= ~bit; | |
1265 | } | |
1266 | break; /* Out of loop to match selector name */ | |
1267 | } | |
1268 | } | |
1269 | if (c < 0) end = middle; else start = middle + 1; | |
1270 | } /* Loop to match selector name */ | |
1271 | ||
1272 | if (start >= end) | |
1273 | { | |
1274 | errmsg = string_sprintf("unknown %s_selector setting: %c%.*s", which, | |
1275 | adding? '+' : '-', len, s); | |
1276 | goto ERROR_RETURN; | |
1277 | } | |
1278 | } /* Loop for selector names */ | |
1279 | ||
1280 | /* Handle disasters */ | |
1281 | ||
1282 | ERROR_RETURN: | |
1283 | if (Ustrcmp(which, "debug") == 0) | |
1284 | { | |
1285 | if (flags & DEBUG_FROM_CONFIG) | |
1286 | { | |
1287 | log_write(0, LOG_CONFIG|LOG_PANIC, "%s", errmsg); | |
1288 | return; | |
1289 | } | |
1290 | fprintf(stderr, "exim: %s\n", errmsg); | |
1291 | exit(EXIT_FAILURE); | |
1292 | } | |
1293 | else log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "%s", errmsg); | |
1294 | } | |
1295 | ||
1296 | ||
1297 | ||
1298 | /************************************************* | |
1299 | * Activate a debug logfile (late) * | |
1300 | *************************************************/ | |
1301 | ||
1302 | /* Normally, debugging is activated from the command-line; it may be useful | |
1303 | within the configuration to activate debugging later, based on certain | |
1304 | conditions. If debugging is already in progress, we return early, no action | |
1305 | taken (besides debug-logging that we wanted debug-logging). | |
1306 | ||
1307 | Failures in options are not fatal but will result in paniclog entries for the | |
1308 | misconfiguration. | |
1309 | ||
1310 | The first use of this is in ACL logic, "control = debug/tag=foo/opts=+expand" | |
1311 | which can be combined with conditions, etc, to activate extra logging only | |
9ee44efb | 1312 | for certain sources. The second use is inetd wait mode debug preservation. */ |
ed7f7860 PP |
1313 | |
1314 | void | |
1315 | debug_logging_activate(uschar *tag_name, uschar *opts) | |
1316 | { | |
1317 | int fd = -1; | |
1318 | ||
1319 | if (debug_file) | |
1320 | { | |
1321 | debug_printf("DEBUGGING ACTIVATED FROM WITHIN CONFIG.\n" | |
9ee44efb | 1322 | "DEBUG: Tag=\"%s\" Opts=\"%s\"\n", tag_name, opts ? opts : US""); |
ed7f7860 PP |
1323 | return; |
1324 | } | |
1325 | ||
1326 | if (tag_name != NULL && (Ustrchr(tag_name, '/') != NULL)) | |
1327 | { | |
1328 | log_write(0, LOG_MAIN|LOG_PANIC, "debug tag may not contain a '/' in: %s", | |
1329 | tag_name); | |
1330 | return; | |
1331 | } | |
1332 | ||
1333 | debug_selector = D_default; | |
1334 | if (opts) | |
1335 | { | |
1336 | decode_bits(&debug_selector, NULL, D_memory, 0, opts, | |
1337 | debug_options, debug_options_count, US"debug", DEBUG_FROM_CONFIG); | |
1338 | } | |
1339 | ||
1340 | open_log(&fd, lt_debug, tag_name); | |
1341 | ||
1342 | if (fd != -1) | |
1343 | debug_file = fdopen(fd, "w"); | |
1344 | else | |
1345 | log_write(0, LOG_MAIN|LOG_PANIC, "unable to open debug log"); | |
1346 | } | |
1347 | ||
1348 | ||
059ec3d9 | 1349 | /* End of log.c */ |