Commit | Line | Data |
---|---|---|
059ec3d9 PH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
f9ba5e22 | 5 | /* Copyright (c) University of Cambridge 1995 - 2018 */ |
059ec3d9 PH |
6 | /* See the file NOTICE for conditions of use and distribution. */ |
7 | ||
8 | ||
9 | /* The main function: entry point, initialization, and high-level control. | |
10 | Also a few functions that don't naturally fit elsewhere. */ | |
11 | ||
12 | ||
13 | #include "exim.h" | |
14 | ||
98913c8e | 15 | #if defined(__GLIBC__) && !defined(__UCLIBC__) |
01f3091a JH |
16 | # include <gnu/libc-version.h> |
17 | #endif | |
18 | ||
f797c123 JH |
19 | #ifdef USE_GNUTLS |
20 | # include <gnutls/gnutls.h> | |
21 | # if GNUTLS_VERSION_NUMBER < 0x030103 && !defined(DISABLE_OCSP) | |
22 | # define DISABLE_OCSP | |
23 | # endif | |
24 | #endif | |
25 | ||
12815ef0 JH |
26 | #ifndef _TIME_H |
27 | # include <time.h> | |
28 | #endif | |
29 | ||
6545de78 PP |
30 | extern void init_lookup_list(void); |
31 | ||
059ec3d9 PH |
32 | |
33 | ||
34 | /************************************************* | |
35 | * Function interface to store functions * | |
36 | *************************************************/ | |
37 | ||
38 | /* We need some real functions to pass to the PCRE regular expression library | |
39 | for store allocation via Exim's store manager. The normal calls are actually | |
40 | macros that pass over location information to make tracing easier. These | |
41 | functions just interface to the standard macro calls. A good compiler will | |
42 | optimize out the tail recursion and so not make them too expensive. There | |
43 | are two sets of functions; one for use when we want to retain the compiled | |
44 | regular expression for a long time; the other for short-term use. */ | |
45 | ||
46 | static void * | |
47 | function_store_get(size_t size) | |
48 | { | |
f3ebb786 JH |
49 | /* For now, regard all RE results as potentially tainted. We might need |
50 | more intelligence on this point. */ | |
51 | return store_get((int)size, TRUE); | |
059ec3d9 PH |
52 | } |
53 | ||
54 | static void | |
55 | function_dummy_free(void *block) { block = block; } | |
56 | ||
57 | static void * | |
58 | function_store_malloc(size_t size) | |
59 | { | |
60 | return store_malloc((int)size); | |
61 | } | |
62 | ||
63 | static void | |
64 | function_store_free(void *block) | |
65 | { | |
66 | store_free(block); | |
67 | } | |
68 | ||
69 | ||
70 | ||
71 | ||
72 | /************************************************* | |
98a90c36 PP |
73 | * Enums for cmdline interface * |
74 | *************************************************/ | |
75 | ||
76 | enum commandline_info { CMDINFO_NONE=0, | |
36a3ae5f | 77 | CMDINFO_HELP, CMDINFO_SIEVE, CMDINFO_DSCP }; |
98a90c36 PP |
78 | |
79 | ||
80 | ||
81 | ||
82 | /************************************************* | |
059ec3d9 PH |
83 | * Compile regular expression and panic on fail * |
84 | *************************************************/ | |
85 | ||
86 | /* This function is called when failure to compile a regular expression leads | |
87 | to a panic exit. In other cases, pcre_compile() is called directly. In many | |
88 | cases where this function is used, the results of the compilation are to be | |
89 | placed in long-lived store, so we temporarily reset the store management | |
90 | functions that PCRE uses if the use_malloc flag is set. | |
91 | ||
92 | Argument: | |
93 | pattern the pattern to compile | |
94 | caseless TRUE if caseless matching is required | |
95 | use_malloc TRUE if compile into malloc store | |
96 | ||
97 | Returns: pointer to the compiled pattern | |
98 | */ | |
99 | ||
100 | const pcre * | |
476be7e2 | 101 | regex_must_compile(const uschar *pattern, BOOL caseless, BOOL use_malloc) |
059ec3d9 PH |
102 | { |
103 | int offset; | |
104 | int options = PCRE_COPT; | |
105 | const pcre *yield; | |
106 | const uschar *error; | |
107 | if (use_malloc) | |
108 | { | |
109 | pcre_malloc = function_store_malloc; | |
110 | pcre_free = function_store_free; | |
111 | } | |
112 | if (caseless) options |= PCRE_CASELESS; | |
3d2e82c5 | 113 | yield = pcre_compile(CCS pattern, options, CCSS &error, &offset, NULL); |
059ec3d9 PH |
114 | pcre_malloc = function_store_get; |
115 | pcre_free = function_dummy_free; | |
116 | if (yield == NULL) | |
117 | log_write(0, LOG_MAIN|LOG_PANIC_DIE, "regular expression error: " | |
118 | "%s at offset %d while compiling %s", error, offset, pattern); | |
119 | return yield; | |
120 | } | |
121 | ||
122 | ||
123 | ||
124 | ||
125 | /************************************************* | |
126 | * Execute regular expression and set strings * | |
127 | *************************************************/ | |
128 | ||
129 | /* This function runs a regular expression match, and sets up the pointers to | |
130 | the matched substrings. | |
131 | ||
132 | Arguments: | |
133 | re the compiled expression | |
134 | subject the subject string | |
135 | options additional PCRE options | |
136 | setup if < 0 do full setup | |
137 | if >= 0 setup from setup+1 onwards, | |
138 | excluding the full matched string | |
139 | ||
140 | Returns: TRUE or FALSE | |
141 | */ | |
142 | ||
143 | BOOL | |
1dc92d5a | 144 | regex_match_and_setup(const pcre *re, const uschar *subject, int options, int setup) |
059ec3d9 PH |
145 | { |
146 | int ovector[3*(EXPAND_MAXN+1)]; | |
1dc92d5a JH |
147 | uschar * s = string_copy(subject); /* de-constifying */ |
148 | int n = pcre_exec(re, NULL, CS s, Ustrlen(s), 0, | |
ee8b8090 | 149 | PCRE_EOPT | options, ovector, nelem(ovector)); |
059ec3d9 PH |
150 | BOOL yield = n >= 0; |
151 | if (n == 0) n = EXPAND_MAXN + 1; | |
152 | if (yield) | |
153 | { | |
ee8b8090 | 154 | expand_nmax = setup < 0 ? 0 : setup + 1; |
d7978c0f | 155 | for (int nn = setup < 0 ? 0 : 2; nn < n*2; nn += 2) |
059ec3d9 | 156 | { |
1dc92d5a | 157 | expand_nstring[expand_nmax] = s + ovector[nn]; |
059ec3d9 PH |
158 | expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn]; |
159 | } | |
160 | expand_nmax--; | |
161 | } | |
162 | return yield; | |
163 | } | |
164 | ||
165 | ||
166 | ||
167 | ||
168 | /************************************************* | |
921b12ca TF |
169 | * Set up processing details * |
170 | *************************************************/ | |
171 | ||
172 | /* Save a text string for dumping when SIGUSR1 is received. | |
173 | Do checks for overruns. | |
174 | ||
175 | Arguments: format and arguments, as for printf() | |
176 | Returns: nothing | |
177 | */ | |
178 | ||
179 | void | |
180 | set_process_info(const char *format, ...) | |
181 | { | |
d12746bc JH |
182 | gstring gs = { .size = PROCESS_INFO_SIZE - 2, .ptr = 0, .s = process_info }; |
183 | gstring * g; | |
184 | int len; | |
921b12ca | 185 | va_list ap; |
d12746bc JH |
186 | |
187 | g = string_fmt_append(&gs, "%5d ", (int)getpid()); | |
188 | len = g->ptr; | |
921b12ca | 189 | va_start(ap, format); |
f3ebb786 | 190 | if (!string_vformat(g, 0, format, ap)) |
d12746bc JH |
191 | { |
192 | gs.ptr = len; | |
193 | g = string_cat(&gs, US"**** string overflowed buffer ****"); | |
194 | } | |
195 | g = string_catn(g, US"\n", 1); | |
196 | string_from_gstring(g); | |
197 | process_info_len = g->ptr; | |
921b12ca TF |
198 | DEBUG(D_process_info) debug_printf("set_process_info: %s", process_info); |
199 | va_end(ap); | |
200 | } | |
201 | ||
830832c9 HSHR |
202 | /*********************************************** |
203 | * Handler for SIGTERM * | |
204 | ***********************************************/ | |
921b12ca | 205 | |
830832c9 HSHR |
206 | static void |
207 | term_handler(int sig) | |
208 | { | |
01446a56 | 209 | exit(1); |
830832c9 | 210 | } |
921b12ca TF |
211 | |
212 | ||
213 | /************************************************* | |
059ec3d9 PH |
214 | * Handler for SIGUSR1 * |
215 | *************************************************/ | |
216 | ||
217 | /* SIGUSR1 causes any exim process to write to the process log details of | |
218 | what it is currently doing. It will only be used if the OS is capable of | |
219 | setting up a handler that causes automatic restarting of any system call | |
220 | that is in progress at the time. | |
221 | ||
921b12ca TF |
222 | This function takes care to be signal-safe. |
223 | ||
059ec3d9 PH |
224 | Argument: the signal number (SIGUSR1) |
225 | Returns: nothing | |
226 | */ | |
227 | ||
228 | static void | |
229 | usr1_handler(int sig) | |
230 | { | |
921b12ca TF |
231 | int fd; |
232 | ||
233 | os_restarting_signal(sig, usr1_handler); | |
234 | ||
cab0c277 | 235 | if ((fd = Uopen(process_log_path, O_APPEND|O_WRONLY, LOG_MODE)) < 0) |
921b12ca TF |
236 | { |
237 | /* If we are already running as the Exim user, try to create it in the | |
238 | current process (assuming spool_directory exists). Otherwise, if we are | |
239 | root, do the creation in an exim:exim subprocess. */ | |
240 | ||
241 | int euid = geteuid(); | |
242 | if (euid == exim_uid) | |
243 | fd = Uopen(process_log_path, O_CREAT|O_APPEND|O_WRONLY, LOG_MODE); | |
244 | else if (euid == root_uid) | |
245 | fd = log_create_as_exim(process_log_path); | |
246 | } | |
247 | ||
248 | /* If we are neither exim nor root, or if we failed to create the log file, | |
249 | give up. There is not much useful we can do with errors, since we don't want | |
250 | to disrupt whatever is going on outside the signal handler. */ | |
251 | ||
252 | if (fd < 0) return; | |
253 | ||
2f21487f | 254 | (void)write(fd, process_info, process_info_len); |
921b12ca | 255 | (void)close(fd); |
059ec3d9 PH |
256 | } |
257 | ||
258 | ||
259 | ||
260 | /************************************************* | |
261 | * Timeout handler * | |
262 | *************************************************/ | |
263 | ||
264 | /* This handler is enabled most of the time that Exim is running. The handler | |
265 | doesn't actually get used unless alarm() has been called to set a timer, to | |
266 | place a time limit on a system call of some kind. When the handler is run, it | |
267 | re-enables itself. | |
268 | ||
269 | There are some other SIGALRM handlers that are used in special cases when more | |
270 | than just a flag setting is required; for example, when reading a message's | |
271 | input. These are normally set up in the code module that uses them, and the | |
272 | SIGALRM handler is reset to this one afterwards. | |
273 | ||
274 | Argument: the signal value (SIGALRM) | |
275 | Returns: nothing | |
276 | */ | |
277 | ||
278 | void | |
279 | sigalrm_handler(int sig) | |
280 | { | |
281 | sig = sig; /* Keep picky compilers happy */ | |
282 | sigalrm_seen = TRUE; | |
283 | os_non_restarting_signal(SIGALRM, sigalrm_handler); | |
284 | } | |
285 | ||
286 | ||
287 | ||
288 | /************************************************* | |
289 | * Sleep for a fractional time interval * | |
290 | *************************************************/ | |
291 | ||
292 | /* This function is called by millisleep() and exim_wait_tick() to wait for a | |
293 | period of time that may include a fraction of a second. The coding is somewhat | |
eb2c0248 PH |
294 | tedious. We do not expect setitimer() ever to fail, but if it does, the process |
295 | will wait for ever, so we panic in this instance. (There was a case of this | |
296 | when a bug in a function that calls milliwait() caused it to pass invalid data. | |
7086e875 | 297 | That's when I added the check. :-) |
059ec3d9 | 298 | |
6906c131 | 299 | We assume it to be not worth sleeping for under 50us; this value will |
0f8ba377 JH |
300 | require revisiting as hardware advances. This avoids the issue of |
301 | a zero-valued timer setting meaning "never fire". | |
302 | ||
059ec3d9 PH |
303 | Argument: an itimerval structure containing the interval |
304 | Returns: nothing | |
305 | */ | |
306 | ||
307 | static void | |
308 | milliwait(struct itimerval *itval) | |
309 | { | |
310 | sigset_t sigmask; | |
311 | sigset_t old_sigmask; | |
0f8ba377 | 312 | |
6906c131 | 313 | if (itval->it_value.tv_usec < 50 && itval->it_value.tv_sec == 0) |
0f8ba377 | 314 | return; |
059ec3d9 PH |
315 | (void)sigemptyset(&sigmask); /* Empty mask */ |
316 | (void)sigaddset(&sigmask, SIGALRM); /* Add SIGALRM */ | |
317 | (void)sigprocmask(SIG_BLOCK, &sigmask, &old_sigmask); /* Block SIGALRM */ | |
7086e875 | 318 | if (setitimer(ITIMER_REAL, itval, NULL) < 0) /* Start timer */ |
eb2c0248 PH |
319 | log_write(0, LOG_MAIN|LOG_PANIC_DIE, |
320 | "setitimer() failed: %s", strerror(errno)); | |
059ec3d9 PH |
321 | (void)sigfillset(&sigmask); /* All signals */ |
322 | (void)sigdelset(&sigmask, SIGALRM); /* Remove SIGALRM */ | |
323 | (void)sigsuspend(&sigmask); /* Until SIGALRM */ | |
324 | (void)sigprocmask(SIG_SETMASK, &old_sigmask, NULL); /* Restore mask */ | |
325 | } | |
326 | ||
327 | ||
328 | ||
329 | ||
330 | /************************************************* | |
331 | * Millisecond sleep function * | |
332 | *************************************************/ | |
333 | ||
334 | /* The basic sleep() function has a granularity of 1 second, which is too rough | |
335 | in some cases - for example, when using an increasing delay to slow down | |
336 | spammers. | |
337 | ||
338 | Argument: number of millseconds | |
339 | Returns: nothing | |
340 | */ | |
341 | ||
342 | void | |
343 | millisleep(int msec) | |
344 | { | |
345 | struct itimerval itval; | |
346 | itval.it_interval.tv_sec = 0; | |
347 | itval.it_interval.tv_usec = 0; | |
348 | itval.it_value.tv_sec = msec/1000; | |
349 | itval.it_value.tv_usec = (msec % 1000) * 1000; | |
350 | milliwait(&itval); | |
351 | } | |
352 | ||
353 | ||
354 | ||
355 | /************************************************* | |
356 | * Compare microsecond times * | |
357 | *************************************************/ | |
358 | ||
359 | /* | |
360 | Arguments: | |
361 | tv1 the first time | |
362 | tv2 the second time | |
363 | ||
364 | Returns: -1, 0, or +1 | |
365 | */ | |
366 | ||
32dfdf8b | 367 | static int |
059ec3d9 PH |
368 | exim_tvcmp(struct timeval *t1, struct timeval *t2) |
369 | { | |
370 | if (t1->tv_sec > t2->tv_sec) return +1; | |
371 | if (t1->tv_sec < t2->tv_sec) return -1; | |
372 | if (t1->tv_usec > t2->tv_usec) return +1; | |
373 | if (t1->tv_usec < t2->tv_usec) return -1; | |
374 | return 0; | |
375 | } | |
376 | ||
377 | ||
378 | ||
379 | ||
380 | /************************************************* | |
381 | * Clock tick wait function * | |
382 | *************************************************/ | |
383 | ||
6906c131 JH |
384 | #ifdef _POSIX_MONOTONIC_CLOCK |
385 | /* Amount CLOCK_MONOTONIC is behind realtime, at startup. */ | |
386 | static struct timespec offset_ts; | |
387 | ||
388 | static void | |
389 | exim_clock_init(void) | |
390 | { | |
391 | struct timeval tv; | |
392 | if (clock_gettime(CLOCK_MONOTONIC, &offset_ts) != 0) return; | |
393 | (void)gettimeofday(&tv, NULL); | |
394 | offset_ts.tv_sec = tv.tv_sec - offset_ts.tv_sec; | |
395 | offset_ts.tv_nsec = tv.tv_usec * 1000 - offset_ts.tv_nsec; | |
396 | if (offset_ts.tv_nsec >= 0) return; | |
397 | offset_ts.tv_sec--; | |
398 | offset_ts.tv_nsec += 1000*1000*1000; | |
399 | } | |
400 | #endif | |
401 | ||
402 | ||
059ec3d9 PH |
403 | /* Exim uses a time + a pid to generate a unique identifier in two places: its |
404 | message IDs, and in file names for maildir deliveries. Because some OS now | |
405 | re-use pids within the same second, sub-second times are now being used. | |
4c04137d | 406 | However, for absolute certainty, we must ensure the clock has ticked before |
059ec3d9 PH |
407 | allowing the relevant process to complete. At the time of implementation of |
408 | this code (February 2003), the speed of processors is such that the clock will | |
409 | invariably have ticked already by the time a process has done its job. This | |
410 | function prepares for the time when things are faster - and it also copes with | |
411 | clocks that go backwards. | |
412 | ||
413 | Arguments: | |
6906c131 | 414 | tgt_tv A timeval which was used to create uniqueness; its usec field |
059ec3d9 PH |
415 | has been rounded down to the value of the resolution. |
416 | We want to be sure the current time is greater than this. | |
417 | resolution The resolution that was used to divide the microseconds | |
418 | (1 for maildir, larger for message ids) | |
419 | ||
420 | Returns: nothing | |
421 | */ | |
422 | ||
423 | void | |
6906c131 | 424 | exim_wait_tick(struct timeval * tgt_tv, int resolution) |
059ec3d9 PH |
425 | { |
426 | struct timeval now_tv; | |
427 | long int now_true_usec; | |
428 | ||
6906c131 JH |
429 | #ifdef _POSIX_MONOTONIC_CLOCK |
430 | struct timespec now_ts; | |
431 | ||
432 | if (clock_gettime(CLOCK_MONOTONIC, &now_ts) == 0) | |
433 | { | |
434 | now_ts.tv_sec += offset_ts.tv_sec; | |
435 | if ((now_ts.tv_nsec += offset_ts.tv_nsec) >= 1000*1000*1000) | |
436 | { | |
437 | now_ts.tv_sec++; | |
438 | now_ts.tv_nsec -= 1000*1000*1000; | |
439 | } | |
440 | now_tv.tv_sec = now_ts.tv_sec; | |
441 | now_true_usec = (now_ts.tv_nsec / (resolution * 1000)) * resolution; | |
442 | now_tv.tv_usec = now_true_usec; | |
443 | } | |
444 | else | |
445 | #endif | |
446 | { | |
447 | (void)gettimeofday(&now_tv, NULL); | |
448 | now_true_usec = now_tv.tv_usec; | |
449 | now_tv.tv_usec = (now_true_usec/resolution) * resolution; | |
450 | } | |
059ec3d9 | 451 | |
6906c131 | 452 | while (exim_tvcmp(&now_tv, tgt_tv) <= 0) |
059ec3d9 PH |
453 | { |
454 | struct itimerval itval; | |
455 | itval.it_interval.tv_sec = 0; | |
456 | itval.it_interval.tv_usec = 0; | |
6906c131 JH |
457 | itval.it_value.tv_sec = tgt_tv->tv_sec - now_tv.tv_sec; |
458 | itval.it_value.tv_usec = tgt_tv->tv_usec + resolution - now_true_usec; | |
059ec3d9 PH |
459 | |
460 | /* We know that, overall, "now" is less than or equal to "then". Therefore, a | |
461 | negative value for the microseconds is possible only in the case when "now" | |
6906c131 | 462 | is more than a second less than "tgt". That means that itval.it_value.tv_sec |
059ec3d9 PH |
463 | is greater than zero. The following correction is therefore safe. */ |
464 | ||
465 | if (itval.it_value.tv_usec < 0) | |
466 | { | |
467 | itval.it_value.tv_usec += 1000000; | |
468 | itval.it_value.tv_sec -= 1; | |
469 | } | |
470 | ||
471 | DEBUG(D_transport|D_receive) | |
472 | { | |
8768d548 | 473 | if (!f.running_in_test_harness) |
059ec3d9 | 474 | { |
d0291a0a | 475 | debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n", |
6906c131 | 476 | tgt_tv->tv_sec, (long) tgt_tv->tv_usec, |
7437665e | 477 | now_tv.tv_sec, (long) now_tv.tv_usec); |
6906c131 | 478 | debug_printf("waiting " TIME_T_FMT ".%06lu sec\n", |
d0291a0a | 479 | itval.it_value.tv_sec, (long) itval.it_value.tv_usec); |
059ec3d9 PH |
480 | } |
481 | } | |
482 | ||
483 | milliwait(&itval); | |
fce5c23c ST |
484 | |
485 | /* Be prapared to go around if the kernel does not implement subtick | |
486 | granularity (GNU Hurd) */ | |
487 | ||
488 | (void)gettimeofday(&now_tv, NULL); | |
489 | now_true_usec = now_tv.tv_usec; | |
490 | now_tv.tv_usec = (now_true_usec/resolution) * resolution; | |
059ec3d9 PH |
491 | } |
492 | } | |
493 | ||
494 | ||
495 | ||
496 | ||
497 | /************************************************* | |
2632889e PH |
498 | * Call fopen() with umask 777 and adjust mode * |
499 | *************************************************/ | |
500 | ||
501 | /* Exim runs with umask(0) so that files created with open() have the mode that | |
502 | is specified in the open() call. However, there are some files, typically in | |
503 | the spool directory, that are created with fopen(). They end up world-writeable | |
504 | if no precautions are taken. Although the spool directory is not accessible to | |
505 | the world, this is an untidiness. So this is a wrapper function for fopen() | |
506 | that sorts out the mode of the created file. | |
507 | ||
508 | Arguments: | |
509 | filename the file name | |
510 | options the fopen() options | |
511 | mode the required mode | |
512 | ||
513 | Returns: the fopened FILE or NULL | |
514 | */ | |
515 | ||
516 | FILE * | |
1ba28e2b | 517 | modefopen(const uschar *filename, const char *options, mode_t mode) |
2632889e | 518 | { |
67d175de PH |
519 | mode_t saved_umask = umask(0777); |
520 | FILE *f = Ufopen(filename, options); | |
521 | (void)umask(saved_umask); | |
2632889e PH |
522 | if (f != NULL) (void)fchmod(fileno(f), mode); |
523 | return f; | |
524 | } | |
525 | ||
526 | ||
2632889e | 527 | /************************************************* |
059ec3d9 PH |
528 | * Ensure stdin, stdout, and stderr exist * |
529 | *************************************************/ | |
530 | ||
531 | /* Some operating systems grumble if an exec() happens without a standard | |
532 | input, output, and error (fds 0, 1, 2) being defined. The worry is that some | |
533 | file will be opened and will use these fd values, and then some other bit of | |
534 | code will assume, for example, that it can write error messages to stderr. | |
535 | This function ensures that fds 0, 1, and 2 are open if they do not already | |
536 | exist, by connecting them to /dev/null. | |
537 | ||
538 | This function is also used to ensure that std{in,out,err} exist at all times, | |
539 | so that if any library that Exim calls tries to use them, it doesn't crash. | |
540 | ||
541 | Arguments: None | |
542 | Returns: Nothing | |
543 | */ | |
544 | ||
545 | void | |
546 | exim_nullstd(void) | |
547 | { | |
059ec3d9 PH |
548 | int devnull = -1; |
549 | struct stat statbuf; | |
d7978c0f | 550 | for (int i = 0; i <= 2; i++) |
059ec3d9 PH |
551 | { |
552 | if (fstat(i, &statbuf) < 0 && errno == EBADF) | |
553 | { | |
554 | if (devnull < 0) devnull = open("/dev/null", O_RDWR); | |
555 | if (devnull < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", | |
f3ebb786 | 556 | string_open_failed(errno, "/dev/null", NULL)); |
1fe64dcc | 557 | if (devnull != i) (void)dup2(devnull, i); |
059ec3d9 PH |
558 | } |
559 | } | |
1fe64dcc | 560 | if (devnull > 2) (void)close(devnull); |
059ec3d9 PH |
561 | } |
562 | ||
563 | ||
564 | ||
565 | ||
566 | /************************************************* | |
567 | * Close unwanted file descriptors for delivery * | |
568 | *************************************************/ | |
569 | ||
570 | /* This function is called from a new process that has been forked to deliver | |
571 | an incoming message, either directly, or using exec. | |
572 | ||
573 | We want any smtp input streams to be closed in this new process. However, it | |
574 | has been observed that using fclose() here causes trouble. When reading in -bS | |
575 | input, duplicate copies of messages have been seen. The files will be sharing a | |
576 | file pointer with the parent process, and it seems that fclose() (at least on | |
577 | some systems - I saw this on Solaris 2.5.1) messes with that file pointer, at | |
578 | least sometimes. Hence we go for closing the underlying file descriptors. | |
579 | ||
580 | If TLS is active, we want to shut down the TLS library, but without molesting | |
581 | the parent's SSL connection. | |
582 | ||
583 | For delivery of a non-SMTP message, we want to close stdin and stdout (and | |
584 | stderr unless debugging) because the calling process might have set them up as | |
585 | pipes and be waiting for them to close before it waits for the submission | |
586 | process to terminate. If they aren't closed, they hold up the calling process | |
587 | until the initial delivery process finishes, which is not what we want. | |
588 | ||
589 | Exception: We do want it for synchronous delivery! | |
590 | ||
591 | And notwithstanding all the above, if D_resolver is set, implying resolver | |
592 | debugging, leave stdout open, because that's where the resolver writes its | |
593 | debugging output. | |
594 | ||
595 | When we close stderr (which implies we've also closed stdout), we also get rid | |
596 | of any controlling terminal. | |
597 | ||
598 | Arguments: None | |
599 | Returns: Nothing | |
600 | */ | |
601 | ||
602 | static void | |
603 | close_unwanted(void) | |
604 | { | |
605 | if (smtp_input) | |
606 | { | |
01603eec | 607 | #ifndef DISABLE_TLS |
74f1a423 JH |
608 | tls_close(NULL, TLS_NO_SHUTDOWN); /* Shut down the TLS library */ |
609 | #endif | |
1fe64dcc PH |
610 | (void)close(fileno(smtp_in)); |
611 | (void)close(fileno(smtp_out)); | |
059ec3d9 PH |
612 | smtp_in = NULL; |
613 | } | |
614 | else | |
615 | { | |
1fe64dcc PH |
616 | (void)close(0); /* stdin */ |
617 | if ((debug_selector & D_resolver) == 0) (void)close(1); /* stdout */ | |
618 | if (debug_selector == 0) /* stderr */ | |
059ec3d9 | 619 | { |
8768d548 | 620 | if (!f.synchronous_delivery) |
059ec3d9 | 621 | { |
1fe64dcc | 622 | (void)close(2); |
059ec3d9 PH |
623 | log_stderr = NULL; |
624 | } | |
625 | (void)setsid(); | |
626 | } | |
627 | } | |
628 | } | |
629 | ||
630 | ||
631 | ||
632 | ||
633 | /************************************************* | |
634 | * Set uid and gid * | |
635 | *************************************************/ | |
636 | ||
637 | /* This function sets a new uid and gid permanently, optionally calling | |
638 | initgroups() to set auxiliary groups. There are some special cases when running | |
639 | Exim in unprivileged modes. In these situations the effective uid will not be | |
640 | root; if we already have the right effective uid/gid, and don't need to | |
641 | initialize any groups, leave things as they are. | |
642 | ||
643 | Arguments: | |
644 | uid the uid | |
645 | gid the gid | |
646 | igflag TRUE if initgroups() wanted | |
647 | msg text to use in debugging output and failure log | |
648 | ||
649 | Returns: nothing; bombs out on failure | |
650 | */ | |
651 | ||
652 | void | |
653 | exim_setugid(uid_t uid, gid_t gid, BOOL igflag, uschar *msg) | |
654 | { | |
655 | uid_t euid = geteuid(); | |
656 | gid_t egid = getegid(); | |
657 | ||
658 | if (euid == root_uid || euid != uid || egid != gid || igflag) | |
659 | { | |
660 | /* At least one OS returns +1 for initgroups failure, so just check for | |
661 | non-zero. */ | |
662 | ||
663 | if (igflag) | |
664 | { | |
665 | struct passwd *pw = getpwuid(uid); | |
9af3c549 JH |
666 | if (!pw) |
667 | log_write(0, LOG_MAIN|LOG_PANIC_DIE, "cannot run initgroups(): " | |
668 | "no passwd entry for uid=%ld", (long int)uid); | |
669 | ||
670 | if (initgroups(pw->pw_name, gid) != 0) | |
671 | log_write(0,LOG_MAIN|LOG_PANIC_DIE,"initgroups failed for uid=%ld: %s", | |
672 | (long int)uid, strerror(errno)); | |
059ec3d9 PH |
673 | } |
674 | ||
675 | if (setgid(gid) < 0 || setuid(uid) < 0) | |
059ec3d9 PH |
676 | log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unable to set gid=%ld or uid=%ld " |
677 | "(euid=%ld): %s", (long int)gid, (long int)uid, (long int)euid, msg); | |
059ec3d9 PH |
678 | } |
679 | ||
680 | /* Debugging output included uid/gid and all groups */ | |
681 | ||
682 | DEBUG(D_uid) | |
683 | { | |
cd59ab18 | 684 | int group_count, save_errno; |
157d73b5 | 685 | gid_t group_list[EXIM_GROUPLIST_SIZE]; |
059ec3d9 PH |
686 | debug_printf("changed uid/gid: %s\n uid=%ld gid=%ld pid=%ld\n", msg, |
687 | (long int)geteuid(), (long int)getegid(), (long int)getpid()); | |
157d73b5 | 688 | group_count = getgroups(nelem(group_list), group_list); |
cd59ab18 | 689 | save_errno = errno; |
059ec3d9 PH |
690 | debug_printf(" auxiliary group list:"); |
691 | if (group_count > 0) | |
d7978c0f | 692 | for (int i = 0; i < group_count; i++) debug_printf(" %d", (int)group_list[i]); |
cd59ab18 PP |
693 | else if (group_count < 0) |
694 | debug_printf(" <error: %s>", strerror(save_errno)); | |
059ec3d9 PH |
695 | else debug_printf(" <none>"); |
696 | debug_printf("\n"); | |
697 | } | |
698 | } | |
699 | ||
700 | ||
701 | ||
702 | ||
703 | /************************************************* | |
704 | * Exit point * | |
705 | *************************************************/ | |
706 | ||
707 | /* Exim exits via this function so that it always clears up any open | |
708 | databases. | |
709 | ||
710 | Arguments: | |
711 | rc return code | |
712 | ||
713 | Returns: does not return | |
714 | */ | |
715 | ||
716 | void | |
9bfb7e1b | 717 | exim_exit(int rc, const uschar * process) |
059ec3d9 PH |
718 | { |
719 | search_tidyup(); | |
f3ebb786 | 720 | store_exit(); |
059ec3d9 | 721 | DEBUG(D_any) |
9bfb7e1b JH |
722 | debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d %s%s%sterminating with rc=%d " |
723 | ">>>>>>>>>>>>>>>>\n", (int)getpid(), | |
724 | process ? "(" : "", process, process ? ") " : "", rc); | |
059ec3d9 PH |
725 | exit(rc); |
726 | } | |
727 | ||
728 | ||
f3ebb786 JH |
729 | void |
730 | exim_underbar_exit(int rc) | |
731 | { | |
732 | store_exit(); | |
733 | _exit(rc); | |
734 | } | |
735 | ||
736 | ||
059ec3d9 | 737 | |
9af3c549 JH |
738 | /* Print error string, then die */ |
739 | static void | |
740 | exim_fail(const char * fmt, ...) | |
741 | { | |
742 | va_list ap; | |
743 | va_start(ap, fmt); | |
744 | vfprintf(stderr, fmt, ap); | |
745 | exit(EXIT_FAILURE); | |
746 | } | |
747 | ||
b66fecb4 HSHR |
748 | /* exim_chown_failure() called from exim_chown()/exim_fchown() on failure |
749 | of chown()/fchown(). See src/functions.h for more explanation */ | |
750 | int | |
751 | exim_chown_failure(int fd, const uschar *name, uid_t owner, gid_t group) | |
752 | { | |
ecf14f2a | 753 | int saved_errno = errno; /* from the preceeding chown call */ |
b66fecb4 HSHR |
754 | #if 1 |
755 | log_write(0, LOG_MAIN|LOG_PANIC, | |
756 | __FILE__ ":%d: chown(%s, %d:%d) failed (%s)." | |
757 | " Please contact the authors and refer to https://bugs.exim.org/show_bug.cgi?id=2391", | |
758 | __LINE__, name?name:US"<unknown>", owner, group, strerror(errno)); | |
759 | #else | |
760 | /* I leave this here, commented, in case the "bug"(?) comes up again. | |
761 | It is not an Exim bug, but we can provide a workaround. | |
762 | See Bug 2391 | |
763 | HS 2019-04-18 */ | |
764 | ||
b66fecb4 HSHR |
765 | struct stat buf; |
766 | ||
767 | if (0 == (fd < 0 ? stat(name, &buf) : fstat(fd, &buf))) | |
768 | { | |
769 | if (buf.st_uid == owner && buf.st_gid == group) return 0; | |
770 | log_write(0, LOG_MAIN|LOG_PANIC, "Wrong ownership on %s", name); | |
771 | } | |
772 | else log_write(0, LOG_MAIN|LOG_PANIC, "Stat failed on %s: %s", name, strerror(errno)); | |
773 | ||
ecf14f2a | 774 | #endif |
b66fecb4 HSHR |
775 | errno = saved_errno; |
776 | return -1; | |
b66fecb4 | 777 | } |
9af3c549 | 778 | |
059ec3d9 PH |
779 | |
780 | /************************************************* | |
781 | * Extract port from host address * | |
782 | *************************************************/ | |
783 | ||
784 | /* Called to extract the port from the values given to -oMa and -oMi. | |
b90c388a PH |
785 | It also checks the syntax of the address, and terminates it before the |
786 | port data when a port is extracted. | |
059ec3d9 PH |
787 | |
788 | Argument: | |
789 | address the address, with possible port on the end | |
790 | ||
791 | Returns: the port, or zero if there isn't one | |
792 | bombs out on a syntax error | |
793 | */ | |
794 | ||
795 | static int | |
796 | check_port(uschar *address) | |
797 | { | |
7cd1141b | 798 | int port = host_address_extract_port(address); |
8e669ac1 | 799 | if (string_is_ip_address(address, NULL) == 0) |
9af3c549 | 800 | exim_fail("exim abandoned: \"%s\" is not an IP address\n", address); |
059ec3d9 PH |
801 | return port; |
802 | } | |
803 | ||
804 | ||
805 | ||
806 | /************************************************* | |
807 | * Test/verify an address * | |
808 | *************************************************/ | |
809 | ||
810 | /* This function is called by the -bv and -bt code. It extracts a working | |
811 | address from a full RFC 822 address. This isn't really necessary per se, but it | |
812 | has the effect of collapsing source routes. | |
813 | ||
814 | Arguments: | |
815 | s the address string | |
816 | flags flag bits for verify_address() | |
817 | exit_value to be set for failures | |
818 | ||
a5a28604 | 819 | Returns: nothing |
059ec3d9 PH |
820 | */ |
821 | ||
822 | static void | |
823 | test_address(uschar *s, int flags, int *exit_value) | |
824 | { | |
825 | int start, end, domain; | |
826 | uschar *parse_error = NULL; | |
827 | uschar *address = parse_extract_address(s, &parse_error, &start, &end, &domain, | |
828 | FALSE); | |
829 | if (address == NULL) | |
830 | { | |
831 | fprintf(stdout, "syntax error: %s\n", parse_error); | |
832 | *exit_value = 2; | |
833 | } | |
834 | else | |
835 | { | |
836 | int rc = verify_address(deliver_make_addr(address,TRUE), stdout, flags, -1, | |
4deaf07d | 837 | -1, -1, NULL, NULL, NULL); |
059ec3d9 PH |
838 | if (rc == FAIL) *exit_value = 2; |
839 | else if (rc == DEFER && *exit_value == 0) *exit_value = 1; | |
840 | } | |
841 | } | |
842 | ||
843 | ||
844 | ||
845 | /************************************************* | |
059ec3d9 PH |
846 | * Show supported features * |
847 | *************************************************/ | |
848 | ||
059ec3d9 | 849 | static void |
96508de1 | 850 | show_db_version(FILE * f) |
059ec3d9 PH |
851 | { |
852 | #ifdef DB_VERSION_STRING | |
96508de1 JH |
853 | DEBUG(D_any) |
854 | { | |
855 | fprintf(f, "Library version: BDB: Compile: %s\n", DB_VERSION_STRING); | |
856 | fprintf(f, " Runtime: %s\n", | |
857 | db_version(NULL, NULL, NULL)); | |
858 | } | |
859 | else | |
860 | fprintf(f, "Berkeley DB: %s\n", DB_VERSION_STRING); | |
861 | ||
059ec3d9 PH |
862 | #elif defined(BTREEVERSION) && defined(HASHVERSION) |
863 | #ifdef USE_DB | |
864 | fprintf(f, "Probably Berkeley DB version 1.8x (native mode)\n"); | |
865 | #else | |
866 | fprintf(f, "Probably Berkeley DB version 1.8x (compatibility mode)\n"); | |
867 | #endif | |
96508de1 | 868 | |
059ec3d9 PH |
869 | #elif defined(_DBM_RDONLY) || defined(dbm_dirfno) |
870 | fprintf(f, "Probably ndbm\n"); | |
871 | #elif defined(USE_TDB) | |
872 | fprintf(f, "Using tdb\n"); | |
873 | #else | |
874 | #ifdef USE_GDBM | |
875 | fprintf(f, "Probably GDBM (native mode)\n"); | |
876 | #else | |
877 | fprintf(f, "Probably GDBM (compatibility mode)\n"); | |
878 | #endif | |
879 | #endif | |
96508de1 JH |
880 | } |
881 | ||
882 | ||
883 | /* This function is called for -bV/--version and for -d to output the optional | |
884 | features of the current Exim binary. | |
885 | ||
886 | Arguments: a FILE for printing | |
887 | Returns: nothing | |
888 | */ | |
889 | ||
890 | static void | |
8768d548 | 891 | show_whats_supported(FILE * fp) |
96508de1 | 892 | { |
8768d548 | 893 | DEBUG(D_any) {} else show_db_version(fp); |
059ec3d9 | 894 | |
8768d548 | 895 | fprintf(fp, "Support for:"); |
9cec981f | 896 | #ifdef SUPPORT_CRYPTEQ |
8768d548 | 897 | fprintf(fp, " crypteq"); |
9cec981f | 898 | #endif |
059ec3d9 | 899 | #if HAVE_ICONV |
8768d548 | 900 | fprintf(fp, " iconv()"); |
059ec3d9 PH |
901 | #endif |
902 | #if HAVE_IPV6 | |
8768d548 | 903 | fprintf(fp, " IPv6"); |
059ec3d9 | 904 | #endif |
79378e0f | 905 | #ifdef HAVE_SETCLASSRESOURCES |
8768d548 | 906 | fprintf(fp, " use_setclassresources"); |
929ba01c | 907 | #endif |
059ec3d9 | 908 | #ifdef SUPPORT_PAM |
8768d548 | 909 | fprintf(fp, " PAM"); |
059ec3d9 PH |
910 | #endif |
911 | #ifdef EXIM_PERL | |
8768d548 | 912 | fprintf(fp, " Perl"); |
059ec3d9 | 913 | #endif |
1a46a8c5 | 914 | #ifdef EXPAND_DLFUNC |
8768d548 | 915 | fprintf(fp, " Expand_dlfunc"); |
1a46a8c5 | 916 | #endif |
059ec3d9 | 917 | #ifdef USE_TCP_WRAPPERS |
8768d548 | 918 | fprintf(fp, " TCPwrappers"); |
059ec3d9 | 919 | #endif |
de517fd3 | 920 | #ifdef USE_GNUTLS |
8768d548 | 921 | fprintf(fp, " GnuTLS"); |
de517fd3 JH |
922 | #endif |
923 | #ifdef USE_OPENSSL | |
8768d548 | 924 | fprintf(fp, " OpenSSL"); |
059ec3d9 | 925 | #endif |
b2f5a032 | 926 | #ifdef SUPPORT_TRANSLATE_IP_ADDRESS |
8768d548 | 927 | fprintf(fp, " translate_ip_address"); |
b2f5a032 | 928 | #endif |
f174f16e | 929 | #ifdef SUPPORT_MOVE_FROZEN_MESSAGES |
8768d548 | 930 | fprintf(fp, " move_frozen_messages"); |
f174f16e | 931 | #endif |
8523533c | 932 | #ifdef WITH_CONTENT_SCAN |
8768d548 | 933 | fprintf(fp, " Content_Scanning"); |
8523533c | 934 | #endif |
c0635b6d | 935 | #ifdef SUPPORT_DANE |
8768d548 | 936 | fprintf(fp, " DANE"); |
c0635b6d | 937 | #endif |
74f150bf | 938 | #ifndef DISABLE_DKIM |
8768d548 | 939 | fprintf(fp, " DKIM"); |
74f150bf | 940 | #endif |
ef1bbb27 | 941 | #ifndef DISABLE_DNSSEC |
8768d548 | 942 | fprintf(fp, " DNSSEC"); |
ef1bbb27 | 943 | #endif |
0cbf2b82 | 944 | #ifndef DISABLE_EVENT |
8768d548 | 945 | fprintf(fp, " Event"); |
0cbf2b82 | 946 | #endif |
8c5d388a | 947 | #ifdef SUPPORT_I18N |
8768d548 | 948 | fprintf(fp, " I18N"); |
8c5d388a | 949 | #endif |
74f150bf | 950 | #ifndef DISABLE_OCSP |
8768d548 | 951 | fprintf(fp, " OCSP"); |
74f150bf | 952 | #endif |
81344b40 | 953 | #ifndef DISABLE_PIPE_CONNECT |
4e48d56c JH |
954 | fprintf(fp, " PIPE_CONNECT"); |
955 | #endif | |
4d832da1 | 956 | #ifndef DISABLE_PRDR |
8768d548 | 957 | fprintf(fp, " PRDR"); |
4d832da1 | 958 | #endif |
cee5f132 | 959 | #ifdef SUPPORT_PROXY |
8768d548 | 960 | fprintf(fp, " PROXY"); |
cee5f132 | 961 | #endif |
f0989ec0 | 962 | #ifdef SUPPORT_SOCKS |
8768d548 | 963 | fprintf(fp, " SOCKS"); |
f2de3a33 | 964 | #endif |
7952eef9 | 965 | #ifdef SUPPORT_SPF |
8768d548 | 966 | fprintf(fp, " SPF"); |
7952eef9 | 967 | #endif |
1a2e76e1 JH |
968 | #ifdef SUPPORT_DMARC |
969 | fprintf(fp, " DMARC"); | |
970 | #endif | |
1a2dfad5 | 971 | #ifdef TCP_FASTOPEN |
d85cdeb5 | 972 | tcp_init(); |
8768d548 | 973 | if (f.tcp_fastopen_ok) fprintf(fp, " TCP_Fast_Open"); |
1a2dfad5 | 974 | #endif |
617d3932 | 975 | #ifdef EXPERIMENTAL_ARC |
8768d548 | 976 | fprintf(fp, " Experimental_ARC"); |
617d3932 | 977 | #endif |
8523533c | 978 | #ifdef EXPERIMENTAL_BRIGHTMAIL |
8768d548 | 979 | fprintf(fp, " Experimental_Brightmail"); |
8523533c | 980 | #endif |
6a8f9482 | 981 | #ifdef EXPERIMENTAL_DCC |
8768d548 | 982 | fprintf(fp, " Experimental_DCC"); |
6a8f9482 | 983 | #endif |
895fbaf2 | 984 | #ifdef EXPERIMENTAL_DSN_INFO |
8768d548 | 985 | fprintf(fp, " Experimental_DSN_info"); |
895fbaf2 | 986 | #endif |
ff966302 JH |
987 | #ifdef EXPERIMENTAL_LMDB |
988 | fprintf(fp, " Experimental_LMDB"); | |
989 | #endif | |
990 | #ifdef EXPERIMENTAL_QUEUE_RAMP | |
991 | fprintf(fp, " Experimental_Queue_Ramp"); | |
992 | #endif | |
993 | #ifdef EXPERIMENTAL_QUEUEFILE | |
994 | fprintf(fp, " Experimental_QUEUEFILE"); | |
995 | #endif | |
996 | #if defined(EXPERIMENTAL_SRS) || defined(EXPERIMENTAL_SRS_NATIVE) | |
997 | fprintf(fp, " Experimental_SRS"); | |
998 | #endif | |
b10c87b3 JH |
999 | #ifdef EXPERIMENTAL_TLS_RESUME |
1000 | fprintf(fp, " Experimental_TLS_resume"); | |
1001 | #endif | |
8768d548 | 1002 | fprintf(fp, "\n"); |
059ec3d9 | 1003 | |
8768d548 | 1004 | fprintf(fp, "Lookups (built-in):"); |
e6d225ae | 1005 | #if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2 |
8768d548 | 1006 | fprintf(fp, " lsearch wildlsearch nwildlsearch iplsearch"); |
059ec3d9 | 1007 | #endif |
e6d225ae | 1008 | #if defined(LOOKUP_CDB) && LOOKUP_CDB!=2 |
8768d548 | 1009 | fprintf(fp, " cdb"); |
059ec3d9 | 1010 | #endif |
e6d225ae | 1011 | #if defined(LOOKUP_DBM) && LOOKUP_DBM!=2 |
8768d548 | 1012 | fprintf(fp, " dbm dbmjz dbmnz"); |
059ec3d9 | 1013 | #endif |
e6d225ae | 1014 | #if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2 |
8768d548 | 1015 | fprintf(fp, " dnsdb"); |
059ec3d9 | 1016 | #endif |
e6d225ae | 1017 | #if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2 |
8768d548 | 1018 | fprintf(fp, " dsearch"); |
059ec3d9 | 1019 | #endif |
e6d225ae | 1020 | #if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2 |
8768d548 | 1021 | fprintf(fp, " ibase"); |
059ec3d9 | 1022 | #endif |
ffc92d69 JH |
1023 | #if defined(LOOKUP_JSON) && LOOKUP_JSON!=2 |
1024 | fprintf(fp, " json"); | |
1025 | #endif | |
e6d225ae | 1026 | #if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2 |
8768d548 | 1027 | fprintf(fp, " ldap ldapdn ldapm"); |
059ec3d9 | 1028 | #endif |
5bde3efa | 1029 | #ifdef EXPERIMENTAL_LMDB |
8768d548 | 1030 | fprintf(fp, " lmdb"); |
5bde3efa | 1031 | #endif |
e6d225ae | 1032 | #if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2 |
8768d548 | 1033 | fprintf(fp, " mysql"); |
059ec3d9 | 1034 | #endif |
e6d225ae | 1035 | #if defined(LOOKUP_NIS) && LOOKUP_NIS!=2 |
8768d548 | 1036 | fprintf(fp, " nis nis0"); |
059ec3d9 | 1037 | #endif |
e6d225ae | 1038 | #if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2 |
8768d548 | 1039 | fprintf(fp, " nisplus"); |
059ec3d9 | 1040 | #endif |
e6d225ae | 1041 | #if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2 |
8768d548 | 1042 | fprintf(fp, " oracle"); |
059ec3d9 | 1043 | #endif |
e6d225ae | 1044 | #if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2 |
8768d548 | 1045 | fprintf(fp, " passwd"); |
059ec3d9 | 1046 | #endif |
e6d225ae | 1047 | #if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2 |
8768d548 | 1048 | fprintf(fp, " pgsql"); |
059ec3d9 | 1049 | #endif |
de78e2d5 | 1050 | #if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2 |
8768d548 | 1051 | fprintf(fp, " redis"); |
de78e2d5 | 1052 | #endif |
e6d225ae | 1053 | #if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2 |
8768d548 | 1054 | fprintf(fp, " sqlite"); |
13b685f9 | 1055 | #endif |
e6d225ae | 1056 | #if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2 |
8768d548 | 1057 | fprintf(fp, " testdb"); |
059ec3d9 | 1058 | #endif |
e6d225ae | 1059 | #if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2 |
8768d548 | 1060 | fprintf(fp, " whoson"); |
059ec3d9 | 1061 | #endif |
8768d548 | 1062 | fprintf(fp, "\n"); |
059ec3d9 | 1063 | |
8768d548 JH |
1064 | auth_show_supported(fp); |
1065 | route_show_supported(fp); | |
1066 | transport_show_supported(fp); | |
059ec3d9 | 1067 | |
c11d665d | 1068 | #ifdef WITH_CONTENT_SCAN |
8768d548 | 1069 | malware_show_supported(fp); |
c11d665d JH |
1070 | #endif |
1071 | ||
059ec3d9 PH |
1072 | if (fixed_never_users[0] > 0) |
1073 | { | |
1074 | int i; | |
8768d548 | 1075 | fprintf(fp, "Fixed never_users: "); |
059ec3d9 | 1076 | for (i = 1; i <= (int)fixed_never_users[0] - 1; i++) |
8768d548 JH |
1077 | fprintf(fp, "%d:", (unsigned int)fixed_never_users[i]); |
1078 | fprintf(fp, "%d\n", (unsigned int)fixed_never_users[i]); | |
059ec3d9 | 1079 | } |
21c28500 | 1080 | |
8768d548 | 1081 | fprintf(fp, "Configure owner: %d:%d\n", config_uid, config_gid); |
19bfe9e7 | 1082 | |
8768d548 | 1083 | fprintf(fp, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t)); |
36f12725 | 1084 | |
6545de78 PP |
1085 | /* Everything else is details which are only worth reporting when debugging. |
1086 | Perhaps the tls_version_report should move into this too. */ | |
1087 | DEBUG(D_any) do { | |
1088 | ||
b3c261f7 PP |
1089 | /* clang defines __GNUC__ (at least, for me) so test for it first */ |
1090 | #if defined(__clang__) | |
8768d548 | 1091 | fprintf(fp, "Compiler: CLang [%s]\n", __clang_version__); |
b3c261f7 | 1092 | #elif defined(__GNUC__) |
8768d548 | 1093 | fprintf(fp, "Compiler: GCC [%s]\n", |
b3c261f7 PP |
1094 | # ifdef __VERSION__ |
1095 | __VERSION__ | |
1096 | # else | |
1097 | "? unknown version ?" | |
1098 | # endif | |
1099 | ); | |
1100 | #else | |
8768d548 | 1101 | fprintf(fp, "Compiler: <unknown>\n"); |
b3c261f7 PP |
1102 | #endif |
1103 | ||
98913c8e | 1104 | #if defined(__GLIBC__) && !defined(__UCLIBC__) |
8768d548 | 1105 | fprintf(fp, "Library version: Glibc: Compile: %d.%d\n", |
01f3091a JH |
1106 | __GLIBC__, __GLIBC_MINOR__); |
1107 | if (__GLIBC_PREREQ(2, 1)) | |
8768d548 | 1108 | fprintf(fp, " Runtime: %s\n", |
01f3091a JH |
1109 | gnu_get_libc_version()); |
1110 | #endif | |
1111 | ||
8768d548 | 1112 | show_db_version(fp); |
96508de1 | 1113 | |
01603eec | 1114 | #ifndef DISABLE_TLS |
8768d548 | 1115 | tls_version_report(fp); |
754a0503 | 1116 | #endif |
8c5d388a | 1117 | #ifdef SUPPORT_I18N |
8768d548 | 1118 | utf8_version_report(fp); |
b04be5e7 | 1119 | #endif |
85e453f6 JH |
1120 | #ifdef SUPPORT_SPF |
1121 | spf_lib_version_report(fp); | |
1122 | #endif | |
754a0503 | 1123 | |
d7978c0f | 1124 | for (auth_info * authi = auths_available; *authi->driver_name != '\0'; ++authi) |
fc362fc5 | 1125 | if (authi->version_report) |
8768d548 | 1126 | (*authi->version_report)(fp); |
6545de78 | 1127 | |
decd95cb | 1128 | /* PCRE_PRERELEASE is either defined and empty or a bare sequence of |
6475bd82 PP |
1129 | characters; unless it's an ancient version of PCRE in which case it |
1130 | is not defined. */ | |
1131 | #ifndef PCRE_PRERELEASE | |
01f3091a | 1132 | # define PCRE_PRERELEASE |
6475bd82 PP |
1133 | #endif |
1134 | #define QUOTE(X) #X | |
1135 | #define EXPAND_AND_QUOTE(X) QUOTE(X) | |
8768d548 | 1136 | fprintf(fp, "Library version: PCRE: Compile: %d.%d%s\n" |
6545de78 PP |
1137 | " Runtime: %s\n", |
1138 | PCRE_MAJOR, PCRE_MINOR, | |
6475bd82 | 1139 | EXPAND_AND_QUOTE(PCRE_PRERELEASE) "", |
6545de78 | 1140 | pcre_version()); |
6475bd82 PP |
1141 | #undef QUOTE |
1142 | #undef EXPAND_AND_QUOTE | |
6545de78 PP |
1143 | |
1144 | init_lookup_list(); | |
d7978c0f | 1145 | for (int i = 0; i < lookup_list_count; i++) |
6545de78 | 1146 | if (lookup_list[i]->version_report) |
8768d548 | 1147 | lookup_list[i]->version_report(fp); |
6545de78 | 1148 | |
b70d2586 | 1149 | #ifdef WHITELIST_D_MACROS |
8768d548 | 1150 | fprintf(fp, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS); |
b70d2586 | 1151 | #else |
8768d548 | 1152 | fprintf(fp, "WHITELIST_D_MACROS unset\n"); |
b70d2586 PP |
1153 | #endif |
1154 | #ifdef TRUSTED_CONFIG_LIST | |
8768d548 | 1155 | fprintf(fp, "TRUSTED_CONFIG_LIST: \"%s\"\n", TRUSTED_CONFIG_LIST); |
b70d2586 | 1156 | #else |
8768d548 | 1157 | fprintf(fp, "TRUSTED_CONFIG_LIST unset\n"); |
b70d2586 PP |
1158 | #endif |
1159 | ||
6545de78 | 1160 | } while (0); |
059ec3d9 PH |
1161 | } |
1162 | ||
1163 | ||
98a90c36 PP |
1164 | /************************************************* |
1165 | * Show auxiliary information about Exim * | |
1166 | *************************************************/ | |
1167 | ||
1168 | static void | |
1169 | show_exim_information(enum commandline_info request, FILE *stream) | |
1170 | { | |
98a90c36 PP |
1171 | switch(request) |
1172 | { | |
1173 | case CMDINFO_NONE: | |
1174 | fprintf(stream, "Oops, something went wrong.\n"); | |
1175 | return; | |
1176 | case CMDINFO_HELP: | |
1177 | fprintf(stream, | |
1178 | "The -bI: flag takes a string indicating which information to provide.\n" | |
1179 | "If the string is not recognised, you'll get this help (on stderr).\n" | |
1180 | "\n" | |
1181 | " exim -bI:help this information\n" | |
030caf2a JS |
1182 | " exim -bI:dscp list of known dscp value keywords\n" |
1183 | " exim -bI:sieve list of supported sieve extensions\n" | |
98a90c36 PP |
1184 | ); |
1185 | return; | |
1186 | case CMDINFO_SIEVE: | |
d7978c0f | 1187 | for (const uschar ** pp = exim_sieve_extension_list; *pp; ++pp) |
98a90c36 PP |
1188 | fprintf(stream, "%s\n", *pp); |
1189 | return; | |
36a3ae5f PP |
1190 | case CMDINFO_DSCP: |
1191 | dscp_list_to_stream(stream); | |
1192 | return; | |
98a90c36 PP |
1193 | } |
1194 | } | |
059ec3d9 PH |
1195 | |
1196 | ||
1197 | /************************************************* | |
1198 | * Quote a local part * | |
1199 | *************************************************/ | |
1200 | ||
1201 | /* This function is used when a sender address or a From: or Sender: header | |
1202 | line is being created from the caller's login, or from an authenticated_id. It | |
1203 | applies appropriate quoting rules for a local part. | |
1204 | ||
1205 | Argument: the local part | |
1206 | Returns: the local part, quoted if necessary | |
1207 | */ | |
1208 | ||
1209 | uschar * | |
1210 | local_part_quote(uschar *lpart) | |
1211 | { | |
1212 | BOOL needs_quote = FALSE; | |
acec9514 | 1213 | gstring * g; |
059ec3d9 | 1214 | |
d7978c0f | 1215 | for (uschar * t = lpart; !needs_quote && *t != 0; t++) |
059ec3d9 PH |
1216 | { |
1217 | needs_quote = !isalnum(*t) && strchr("!#$%&'*+-/=?^_`{|}~", *t) == NULL && | |
1218 | (*t != '.' || t == lpart || t[1] == 0); | |
1219 | } | |
1220 | ||
1221 | if (!needs_quote) return lpart; | |
1222 | ||
acec9514 | 1223 | g = string_catn(NULL, US"\"", 1); |
059ec3d9 PH |
1224 | |
1225 | for (;;) | |
1226 | { | |
1227 | uschar *nq = US Ustrpbrk(lpart, "\\\""); | |
1228 | if (nq == NULL) | |
1229 | { | |
acec9514 | 1230 | g = string_cat(g, lpart); |
059ec3d9 PH |
1231 | break; |
1232 | } | |
acec9514 JH |
1233 | g = string_catn(g, lpart, nq - lpart); |
1234 | g = string_catn(g, US"\\", 1); | |
1235 | g = string_catn(g, nq, 1); | |
059ec3d9 PH |
1236 | lpart = nq + 1; |
1237 | } | |
1238 | ||
acec9514 JH |
1239 | g = string_catn(g, US"\"", 1); |
1240 | return string_from_gstring(g); | |
059ec3d9 PH |
1241 | } |
1242 | ||
1243 | ||
1244 | ||
1245 | #ifdef USE_READLINE | |
1246 | /************************************************* | |
1247 | * Load readline() functions * | |
1248 | *************************************************/ | |
1249 | ||
1250 | /* This function is called from testing executions that read data from stdin, | |
1251 | but only when running as the calling user. Currently, only -be does this. The | |
1252 | function loads the readline() function library and passes back the functions. | |
1253 | On some systems, it needs the curses library, so load that too, but try without | |
1254 | it if loading fails. All this functionality has to be requested at build time. | |
1255 | ||
1256 | Arguments: | |
1257 | fn_readline_ptr pointer to where to put the readline pointer | |
1258 | fn_addhist_ptr pointer to where to put the addhistory function | |
1259 | ||
1260 | Returns: the dlopen handle or NULL on failure | |
1261 | */ | |
1262 | ||
1263 | static void * | |
1ba28e2b PP |
1264 | set_readline(char * (**fn_readline_ptr)(const char *), |
1265 | void (**fn_addhist_ptr)(const char *)) | |
059ec3d9 PH |
1266 | { |
1267 | void *dlhandle; | |
e12f8c32 | 1268 | void *dlhandle_curses = dlopen("libcurses." DYNLIB_FN_EXT, RTLD_GLOBAL|RTLD_LAZY); |
059ec3d9 | 1269 | |
e12f8c32 | 1270 | dlhandle = dlopen("libreadline." DYNLIB_FN_EXT, RTLD_GLOBAL|RTLD_NOW); |
b1c673dd | 1271 | if (dlhandle_curses) dlclose(dlhandle_curses); |
059ec3d9 | 1272 | |
b1c673dd | 1273 | if (dlhandle) |
059ec3d9 | 1274 | { |
1ba28e2b PP |
1275 | /* Checked manual pages; at least in GNU Readline 6.1, the prototypes are: |
1276 | * char * readline (const char *prompt); | |
1277 | * void add_history (const char *string); | |
1278 | */ | |
1279 | *fn_readline_ptr = (char *(*)(const char*))dlsym(dlhandle, "readline"); | |
1280 | *fn_addhist_ptr = (void(*)(const char*))dlsym(dlhandle, "add_history"); | |
059ec3d9 PH |
1281 | } |
1282 | else | |
059ec3d9 | 1283 | DEBUG(D_any) debug_printf("failed to load readline: %s\n", dlerror()); |
059ec3d9 PH |
1284 | |
1285 | return dlhandle; | |
1286 | } | |
1287 | #endif | |
1288 | ||
1289 | ||
1290 | ||
1291 | /************************************************* | |
1292 | * Get a line from stdin for testing things * | |
1293 | *************************************************/ | |
1294 | ||
1295 | /* This function is called when running tests that can take a number of lines | |
1296 | of input (for example, -be and -bt). It handles continuations and trailing | |
1297 | spaces. And prompting and a blank line output on eof. If readline() is in use, | |
1298 | the arguments are non-NULL and provide the relevant functions. | |
1299 | ||
1300 | Arguments: | |
1301 | fn_readline readline function or NULL | |
1302 | fn_addhist addhist function or NULL | |
1303 | ||
1304 | Returns: pointer to dynamic memory, or NULL at end of file | |
1305 | */ | |
1306 | ||
1307 | static uschar * | |
1ba28e2b | 1308 | get_stdinput(char *(*fn_readline)(const char *), void(*fn_addhist)(const char *)) |
059ec3d9 | 1309 | { |
acec9514 | 1310 | gstring * g = NULL; |
059ec3d9 | 1311 | |
acec9514 | 1312 | if (!fn_readline) { printf("> "); fflush(stdout); } |
059ec3d9 | 1313 | |
d7978c0f | 1314 | for (int i = 0;; i++) |
059ec3d9 PH |
1315 | { |
1316 | uschar buffer[1024]; | |
1317 | uschar *p, *ss; | |
1318 | ||
1319 | #ifdef USE_READLINE | |
1320 | char *readline_line = NULL; | |
f3ebb786 | 1321 | if (fn_readline) |
059ec3d9 | 1322 | { |
f3ebb786 JH |
1323 | if (!(readline_line = fn_readline((i > 0)? "":"> "))) break; |
1324 | if (*readline_line != 0 && fn_addhist) fn_addhist(readline_line); | |
059ec3d9 PH |
1325 | p = US readline_line; |
1326 | } | |
1327 | else | |
1328 | #endif | |
1329 | ||
1330 | /* readline() not in use */ | |
1331 | ||
1332 | { | |
1333 | if (Ufgets(buffer, sizeof(buffer), stdin) == NULL) break; | |
1334 | p = buffer; | |
1335 | } | |
1336 | ||
1337 | /* Handle the line */ | |
1338 | ||
1339 | ss = p + (int)Ustrlen(p); | |
1340 | while (ss > p && isspace(ss[-1])) ss--; | |
1341 | ||
1342 | if (i > 0) | |
059ec3d9 | 1343 | while (p < ss && isspace(*p)) p++; /* leading space after cont */ |
059ec3d9 | 1344 | |
acec9514 | 1345 | g = string_catn(g, p, ss - p); |
059ec3d9 PH |
1346 | |
1347 | #ifdef USE_READLINE | |
acec9514 | 1348 | if (fn_readline) free(readline_line); |
059ec3d9 PH |
1349 | #endif |
1350 | ||
acec9514 JH |
1351 | /* g can only be NULL if ss==p */ |
1352 | if (ss == p || g->s[g->ptr-1] != '\\') | |
059ec3d9 | 1353 | break; |
acec9514 JH |
1354 | |
1355 | --g->ptr; | |
1356 | (void) string_from_gstring(g); | |
059ec3d9 PH |
1357 | } |
1358 | ||
acec9514 JH |
1359 | if (!g) printf("\n"); |
1360 | return string_from_gstring(g); | |
059ec3d9 PH |
1361 | } |
1362 | ||
1363 | ||
1364 | ||
1365 | /************************************************* | |
81ea09ca NM |
1366 | * Output usage information for the program * |
1367 | *************************************************/ | |
1368 | ||
1369 | /* This function is called when there are no recipients | |
1370 | or a specific --help argument was added. | |
1371 | ||
1372 | Arguments: | |
1373 | progname information on what name we were called by | |
1374 | ||
1375 | Returns: DOES NOT RETURN | |
1376 | */ | |
1377 | ||
1378 | static void | |
1379 | exim_usage(uschar *progname) | |
1380 | { | |
1381 | ||
4c04137d | 1382 | /* Handle specific program invocation variants */ |
81ea09ca | 1383 | if (Ustrcmp(progname, US"-mailq") == 0) |
9af3c549 | 1384 | exim_fail( |
e765a0f1 | 1385 | "mailq - list the contents of the mail queue\n\n" |
81ea09ca | 1386 | "For a list of options, see the Exim documentation.\n"); |
81ea09ca NM |
1387 | |
1388 | /* Generic usage - we output this whatever happens */ | |
9af3c549 | 1389 | exim_fail( |
81ea09ca NM |
1390 | "Exim is a Mail Transfer Agent. It is normally called by Mail User Agents,\n" |
1391 | "not directly from a shell command line. Options and/or arguments control\n" | |
1392 | "what it does when called. For a list of options, see the Exim documentation.\n"); | |
81ea09ca NM |
1393 | } |
1394 | ||
1395 | ||
1396 | ||
1397 | /************************************************* | |
a7cbbf50 PP |
1398 | * Validate that the macros given are okay * |
1399 | *************************************************/ | |
1400 | ||
1401 | /* Typically, Exim will drop privileges if macros are supplied. In some | |
1402 | cases, we want to not do so. | |
1403 | ||
a4034eb8 | 1404 | Arguments: opt_D_used - true if the commandline had a "-D" option |
a7cbbf50 PP |
1405 | Returns: true if trusted, false otherwise |
1406 | */ | |
1407 | ||
1408 | static BOOL | |
a4034eb8 | 1409 | macros_trusted(BOOL opt_D_used) |
a7cbbf50 PP |
1410 | { |
1411 | #ifdef WHITELIST_D_MACROS | |
d7978c0f | 1412 | uschar *whitelisted, *end, *p, **whites; |
a7cbbf50 PP |
1413 | int white_count, i, n; |
1414 | size_t len; | |
1415 | BOOL prev_char_item, found; | |
1416 | #endif | |
1417 | ||
a4034eb8 | 1418 | if (!opt_D_used) |
a7cbbf50 PP |
1419 | return TRUE; |
1420 | #ifndef WHITELIST_D_MACROS | |
1421 | return FALSE; | |
1422 | #else | |
1423 | ||
66581d1e PP |
1424 | /* We only trust -D overrides for some invoking users: |
1425 | root, the exim run-time user, the optional config owner user. | |
1426 | I don't know why config-owner would be needed, but since they can own the | |
1427 | config files anyway, there's no security risk to letting them override -D. */ | |
1428 | if ( ! ((real_uid == root_uid) | |
1429 | || (real_uid == exim_uid) | |
1430 | #ifdef CONFIGURE_OWNER | |
1431 | || (real_uid == config_uid) | |
1432 | #endif | |
1433 | )) | |
1434 | { | |
1435 | debug_printf("macros_trusted rejecting macros for uid %d\n", (int) real_uid); | |
1436 | return FALSE; | |
1437 | } | |
1438 | ||
a7cbbf50 | 1439 | /* Get a list of macros which are whitelisted */ |
f3ebb786 | 1440 | whitelisted = string_copy_perm(US WHITELIST_D_MACROS, FALSE); |
a7cbbf50 PP |
1441 | prev_char_item = FALSE; |
1442 | white_count = 0; | |
1443 | for (p = whitelisted; *p != '\0'; ++p) | |
1444 | { | |
1445 | if (*p == ':' || isspace(*p)) | |
1446 | { | |
1447 | *p = '\0'; | |
1448 | if (prev_char_item) | |
1449 | ++white_count; | |
1450 | prev_char_item = FALSE; | |
1451 | continue; | |
1452 | } | |
1453 | if (!prev_char_item) | |
1454 | prev_char_item = TRUE; | |
1455 | } | |
1456 | end = p; | |
1457 | if (prev_char_item) | |
1458 | ++white_count; | |
1459 | if (!white_count) | |
1460 | return FALSE; | |
1461 | whites = store_malloc(sizeof(uschar *) * (white_count+1)); | |
1462 | for (p = whitelisted, i = 0; (p != end) && (i < white_count); ++p) | |
1463 | { | |
1464 | if (*p != '\0') | |
1465 | { | |
1466 | whites[i++] = p; | |
1467 | if (i == white_count) | |
1468 | break; | |
1469 | while (*p != '\0' && p < end) | |
1470 | ++p; | |
1471 | } | |
1472 | } | |
1473 | whites[i] = NULL; | |
1474 | ||
1a7c9a48 JH |
1475 | /* The list of commandline macros should be very short. |
1476 | Accept the N*M complexity. */ | |
d7978c0f | 1477 | for (macro_item * m = macros_user; m; m = m->next) if (m->command_line) |
1a7c9a48 JH |
1478 | { |
1479 | found = FALSE; | |
d7978c0f | 1480 | for (uschar ** w = whites; *w; ++w) |
1a7c9a48 JH |
1481 | if (Ustrcmp(*w, m->name) == 0) |
1482 | { | |
1483 | found = TRUE; | |
1484 | break; | |
1485 | } | |
1486 | if (!found) | |
1487 | return FALSE; | |
1488 | if (!m->replacement) | |
1489 | continue; | |
1490 | if ((len = m->replen) == 0) | |
1491 | continue; | |
1492 | n = pcre_exec(regex_whitelisted_macro, NULL, CS m->replacement, len, | |
1493 | 0, PCRE_EOPT, NULL, 0); | |
1494 | if (n < 0) | |
1495 | { | |
1496 | if (n != PCRE_ERROR_NOMATCH) | |
1497 | debug_printf("macros_trusted checking %s returned %d\n", m->name, n); | |
1498 | return FALSE; | |
1499 | } | |
1500 | } | |
43236f35 | 1501 | DEBUG(D_any) debug_printf("macros_trusted overridden to true by whitelisting\n"); |
a7cbbf50 PP |
1502 | return TRUE; |
1503 | #endif | |
1504 | } | |
1505 | ||
1506 | ||
1507 | /************************************************* | |
9650d98a JH |
1508 | * Expansion testing * |
1509 | *************************************************/ | |
1510 | ||
1511 | /* Expand and print one item, doing macro-processing. | |
1512 | ||
1513 | Arguments: | |
1514 | item line for expansion | |
1515 | */ | |
1516 | ||
1517 | static void | |
1518 | expansion_test_line(uschar * line) | |
1519 | { | |
1520 | int len; | |
1521 | BOOL dummy_macexp; | |
1522 | ||
1523 | Ustrncpy(big_buffer, line, big_buffer_size); | |
1524 | big_buffer[big_buffer_size-1] = '\0'; | |
1525 | len = Ustrlen(big_buffer); | |
1526 | ||
1527 | (void) macros_expand(0, &len, &dummy_macexp); | |
1528 | ||
1529 | if (isupper(big_buffer[0])) | |
1530 | { | |
1531 | if (macro_read_assignment(big_buffer)) | |
1a7c9a48 | 1532 | printf("Defined macro '%s'\n", mlast->name); |
9650d98a JH |
1533 | } |
1534 | else | |
1535 | if ((line = expand_string(big_buffer))) printf("%s\n", CS line); | |
1536 | else printf("Failed: %s\n", expand_string_message); | |
1537 | } | |
1538 | ||
1539 | ||
9af3c549 | 1540 | |
9650d98a | 1541 | /************************************************* |
059ec3d9 PH |
1542 | * Entry point and high-level code * |
1543 | *************************************************/ | |
1544 | ||
1545 | /* Entry point for the Exim mailer. Analyse the arguments and arrange to take | |
1546 | the appropriate action. All the necessary functions are present in the one | |
1547 | binary. I originally thought one should split it up, but it turns out that so | |
1548 | much of the apparatus is needed in each chunk that one might as well just have | |
1549 | it all available all the time, which then makes the coding easier as well. | |
1550 | ||
1551 | Arguments: | |
1552 | argc count of entries in argv | |
1553 | argv argument strings, with argv[0] being the program name | |
1554 | ||
1555 | Returns: EXIT_SUCCESS if terminated successfully | |
1556 | EXIT_FAILURE otherwise, except when a message has been sent | |
1557 | to the sender, and -oee was given | |
1558 | */ | |
1559 | ||
1560 | int | |
1561 | main(int argc, char **cargv) | |
1562 | { | |
1563 | uschar **argv = USS cargv; | |
1564 | int arg_receive_timeout = -1; | |
1565 | int arg_smtp_receive_timeout = -1; | |
1566 | int arg_error_handling = error_handling; | |
f05da2e8 PH |
1567 | int filter_sfd = -1; |
1568 | int filter_ufd = -1; | |
059ec3d9 | 1569 | int group_count; |
1670ef10 | 1570 | int i, rv; |
059ec3d9 PH |
1571 | int list_queue_option = 0; |
1572 | int msg_action = 0; | |
1573 | int msg_action_arg = -1; | |
1574 | int namelen = (argv[0] == NULL)? 0 : Ustrlen(argv[0]); | |
1575 | int queue_only_reason = 0; | |
1576 | #ifdef EXIM_PERL | |
1577 | int perl_start_option = 0; | |
1578 | #endif | |
1579 | int recipients_arg = argc; | |
1580 | int sender_address_domain = 0; | |
1581 | int test_retry_arg = -1; | |
1582 | int test_rewrite_arg = -1; | |
3b7ac02c | 1583 | gid_t original_egid; |
059ec3d9 PH |
1584 | BOOL arg_queue_only = FALSE; |
1585 | BOOL bi_option = FALSE; | |
1586 | BOOL checking = FALSE; | |
1587 | BOOL count_queue = FALSE; | |
1588 | BOOL expansion_test = FALSE; | |
1589 | BOOL extract_recipients = FALSE; | |
f4ee74ac | 1590 | BOOL flag_G = FALSE; |
12f69989 | 1591 | BOOL flag_n = FALSE; |
059ec3d9 PH |
1592 | BOOL forced_delivery = FALSE; |
1593 | BOOL f_end_dot = FALSE; | |
1594 | BOOL deliver_give_up = FALSE; | |
1595 | BOOL list_queue = FALSE; | |
1596 | BOOL list_options = FALSE; | |
bf3c2c6b | 1597 | BOOL list_config = FALSE; |
059ec3d9 PH |
1598 | BOOL local_queue_only; |
1599 | BOOL more = TRUE; | |
1600 | BOOL one_msg_action = FALSE; | |
4ab69ec7 | 1601 | BOOL opt_D_used = FALSE; |
059ec3d9 PH |
1602 | BOOL queue_only_set = FALSE; |
1603 | BOOL receiving_message = TRUE; | |
33d73e3b | 1604 | BOOL sender_ident_set = FALSE; |
8669f003 | 1605 | BOOL session_local_queue_only; |
059ec3d9 PH |
1606 | BOOL unprivileged; |
1607 | BOOL removed_privilege = FALSE; | |
81ea09ca | 1608 | BOOL usage_wanted = FALSE; |
059ec3d9 PH |
1609 | BOOL verify_address_mode = FALSE; |
1610 | BOOL verify_as_sender = FALSE; | |
1611 | BOOL version_printed = FALSE; | |
1612 | uschar *alias_arg = NULL; | |
1613 | uschar *called_as = US""; | |
a3fb9793 | 1614 | uschar *cmdline_syslog_name = NULL; |
059ec3d9 PH |
1615 | uschar *start_queue_run_id = NULL; |
1616 | uschar *stop_queue_run_id = NULL; | |
328895cc | 1617 | uschar *expansion_test_message = NULL; |
059ec3d9 PH |
1618 | uschar *ftest_domain = NULL; |
1619 | uschar *ftest_localpart = NULL; | |
1620 | uschar *ftest_prefix = NULL; | |
1621 | uschar *ftest_suffix = NULL; | |
0ad2e0fc | 1622 | uschar *log_oneline = NULL; |
8544e77a | 1623 | uschar *malware_test_file = NULL; |
059ec3d9 PH |
1624 | uschar *real_sender_address; |
1625 | uschar *originator_home = US"/"; | |
a3fb9793 | 1626 | size_t sz; |
f3ebb786 | 1627 | rmark reset_point; |
059ec3d9 PH |
1628 | |
1629 | struct passwd *pw; | |
1630 | struct stat statbuf; | |
1631 | pid_t passed_qr_pid = (pid_t)0; | |
1632 | int passed_qr_pipe = -1; | |
157d73b5 | 1633 | gid_t group_list[EXIM_GROUPLIST_SIZE]; |
059ec3d9 | 1634 | |
98a90c36 PP |
1635 | /* For the -bI: flag */ |
1636 | enum commandline_info info_flag = CMDINFO_NONE; | |
1637 | BOOL info_stdout = FALSE; | |
1638 | ||
059ec3d9 PH |
1639 | /* Possible options for -R and -S */ |
1640 | ||
1641 | static uschar *rsopts[] = { US"f", US"ff", US"r", US"rf", US"rff" }; | |
1642 | ||
1643 | /* Need to define this in case we need to change the environment in order | |
1644 | to get rid of a bogus time zone. We have to make it char rather than uschar | |
1645 | because some OS define it in /usr/include/unistd.h. */ | |
1646 | ||
1647 | extern char **environ; | |
1648 | ||
9f01e50d JH |
1649 | #ifdef MEASURE_TIMING |
1650 | (void)gettimeofday(×tamp_startup, NULL); | |
1651 | #endif | |
1652 | ||
35edf2ff | 1653 | /* If the Exim user and/or group and/or the configuration file owner/group were |
059ec3d9 PH |
1654 | defined by ref:name at build time, we must now find the actual uid/gid values. |
1655 | This is a feature to make the lives of binary distributors easier. */ | |
1656 | ||
1657 | #ifdef EXIM_USERNAME | |
1658 | if (route_finduser(US EXIM_USERNAME, &pw, &exim_uid)) | |
1659 | { | |
10385c15 | 1660 | if (exim_uid == 0) |
9af3c549 JH |
1661 | exim_fail("exim: refusing to run with uid 0 for \"%s\"\n", EXIM_USERNAME); |
1662 | ||
084c1d8c PP |
1663 | /* If ref:name uses a number as the name, route_finduser() returns |
1664 | TRUE with exim_uid set and pw coerced to NULL. */ | |
1665 | if (pw) | |
1666 | exim_gid = pw->pw_gid; | |
1667 | #ifndef EXIM_GROUPNAME | |
1668 | else | |
9af3c549 | 1669 | exim_fail( |
084c1d8c PP |
1670 | "exim: ref:name should specify a usercode, not a group.\n" |
1671 | "exim: can't let you get away with it unless you also specify a group.\n"); | |
084c1d8c | 1672 | #endif |
059ec3d9 PH |
1673 | } |
1674 | else | |
9af3c549 | 1675 | exim_fail("exim: failed to find uid for user name \"%s\"\n", EXIM_USERNAME); |
059ec3d9 PH |
1676 | #endif |
1677 | ||
1678 | #ifdef EXIM_GROUPNAME | |
1679 | if (!route_findgroup(US EXIM_GROUPNAME, &exim_gid)) | |
9af3c549 | 1680 | exim_fail("exim: failed to find gid for group name \"%s\"\n", EXIM_GROUPNAME); |
059ec3d9 PH |
1681 | #endif |
1682 | ||
1683 | #ifdef CONFIGURE_OWNERNAME | |
1684 | if (!route_finduser(US CONFIGURE_OWNERNAME, NULL, &config_uid)) | |
9af3c549 | 1685 | exim_fail("exim: failed to find uid for user name \"%s\"\n", |
059ec3d9 | 1686 | CONFIGURE_OWNERNAME); |
059ec3d9 PH |
1687 | #endif |
1688 | ||
79d4bc3d PP |
1689 | /* We default the system_filter_user to be the Exim run-time user, as a |
1690 | sane non-root value. */ | |
1691 | system_filter_uid = exim_uid; | |
1692 | ||
35edf2ff PH |
1693 | #ifdef CONFIGURE_GROUPNAME |
1694 | if (!route_findgroup(US CONFIGURE_GROUPNAME, &config_gid)) | |
9af3c549 | 1695 | exim_fail("exim: failed to find gid for group name \"%s\"\n", |
35edf2ff | 1696 | CONFIGURE_GROUPNAME); |
35edf2ff PH |
1697 | #endif |
1698 | ||
92e6a3d9 JH |
1699 | /* In the Cygwin environment, some initialization used to need doing. |
1700 | It was fudged in by means of this macro; now no longer but we'll leave | |
1701 | it in case of others. */ | |
059ec3d9 PH |
1702 | |
1703 | #ifdef OS_INIT | |
1704 | OS_INIT | |
1705 | #endif | |
1706 | ||
1707 | /* Check a field which is patched when we are running Exim within its | |
1708 | testing harness; do a fast initial check, and then the whole thing. */ | |
1709 | ||
8768d548 | 1710 | f.running_in_test_harness = |
059ec3d9 | 1711 | *running_status == '<' && Ustrcmp(running_status, "<<<testing>>>") == 0; |
8768d548 | 1712 | if (f.running_in_test_harness) |
65a32f85 | 1713 | debug_store = TRUE; |
059ec3d9 PH |
1714 | |
1715 | /* The C standard says that the equivalent of setlocale(LC_ALL, "C") is obeyed | |
1716 | at the start of a program; however, it seems that some environments do not | |
1717 | follow this. A "strange" locale can affect the formatting of timestamps, so we | |
1718 | make quite sure. */ | |
1719 | ||
1720 | setlocale(LC_ALL, "C"); | |
1721 | ||
6906c131 JH |
1722 | /* Get the offset between CLOCK_MONOTONIC and wallclock */ |
1723 | ||
1724 | #ifdef _POSIX_MONOTONIC_CLOCK | |
1725 | exim_clock_init(); | |
1726 | #endif | |
1727 | ||
059ec3d9 PH |
1728 | /* Set up the default handler for timing using alarm(). */ |
1729 | ||
1730 | os_non_restarting_signal(SIGALRM, sigalrm_handler); | |
1731 | ||
1732 | /* Ensure we have a buffer for constructing log entries. Use malloc directly, | |
1733 | because store_malloc writes a log entry on failure. */ | |
1734 | ||
40c90bca | 1735 | if (!(log_buffer = US malloc(LOG_BUFFER_SIZE))) |
9af3c549 | 1736 | exim_fail("exim: failed to get store for log buffer\n"); |
059ec3d9 | 1737 | |
6c6d6e48 TF |
1738 | /* Initialize the default log options. */ |
1739 | ||
1740 | bits_set(log_selector, log_selector_size, log_default); | |
1741 | ||
059ec3d9 PH |
1742 | /* Set log_stderr to stderr, provided that stderr exists. This gets reset to |
1743 | NULL when the daemon is run and the file is closed. We have to use this | |
1744 | indirection, because some systems don't allow writing to the variable "stderr". | |
1745 | */ | |
1746 | ||
1747 | if (fstat(fileno(stderr), &statbuf) >= 0) log_stderr = stderr; | |
1748 | ||
1749 | /* Arrange for the PCRE regex library to use our store functions. Note that | |
1750 | the normal calls are actually macros that add additional arguments for | |
1751 | debugging purposes so we have to assign specially constructed functions here. | |
1752 | The default is to use store in the stacking pool, but this is overridden in the | |
1753 | regex_must_compile() function. */ | |
1754 | ||
1755 | pcre_malloc = function_store_get; | |
1756 | pcre_free = function_dummy_free; | |
1757 | ||
1758 | /* Ensure there is a big buffer for temporary use in several places. It is put | |
1759 | in malloc store so that it can be freed for enlargement if necessary. */ | |
1760 | ||
1761 | big_buffer = store_malloc(big_buffer_size); | |
1762 | ||
1763 | /* Set up the handler for the data request signal, and set the initial | |
1764 | descriptive text. */ | |
1765 | ||
f3ebb786 | 1766 | process_info = store_get(PROCESS_INFO_SIZE, TRUE); /* tainted */ |
059ec3d9 PH |
1767 | set_process_info("initializing"); |
1768 | os_restarting_signal(SIGUSR1, usr1_handler); | |
1769 | ||
830832c9 HSHR |
1770 | /* If running in a dockerized environment, the TERM signal is only |
1771 | delegated to the PID 1 if we request it by setting an signal handler */ | |
1772 | if (getpid() == 1) signal(SIGTERM, term_handler); | |
1773 | ||
059ec3d9 PH |
1774 | /* SIGHUP is used to get the daemon to reconfigure. It gets set as appropriate |
1775 | in the daemon code. For the rest of Exim's uses, we ignore it. */ | |
1776 | ||
1777 | signal(SIGHUP, SIG_IGN); | |
1778 | ||
1779 | /* We don't want to die on pipe errors as the code is written to handle | |
1780 | the write error instead. */ | |
1781 | ||
1782 | signal(SIGPIPE, SIG_IGN); | |
1783 | ||
1784 | /* Under some circumstance on some OS, Exim can get called with SIGCHLD | |
1785 | set to SIG_IGN. This causes subprocesses that complete before the parent | |
1786 | process waits for them not to hang around, so when Exim calls wait(), nothing | |
1787 | is there. The wait() code has been made robust against this, but let's ensure | |
1788 | that SIGCHLD is set to SIG_DFL, because it's tidier to wait and get a process | |
1789 | ending status. We use sigaction rather than plain signal() on those OS where | |
1790 | SA_NOCLDWAIT exists, because we want to be sure it is turned off. (There was a | |
1791 | problem on AIX with this.) */ | |
1792 | ||
1793 | #ifdef SA_NOCLDWAIT | |
1794 | { | |
1795 | struct sigaction act; | |
1796 | act.sa_handler = SIG_DFL; | |
1797 | sigemptyset(&(act.sa_mask)); | |
1798 | act.sa_flags = 0; | |
1799 | sigaction(SIGCHLD, &act, NULL); | |
1800 | } | |
1801 | #else | |
1802 | signal(SIGCHLD, SIG_DFL); | |
1803 | #endif | |
1804 | ||
1805 | /* Save the arguments for use if we re-exec exim as a daemon after receiving | |
1806 | SIGHUP. */ | |
1807 | ||
1808 | sighup_argv = argv; | |
1809 | ||
1810 | /* Set up the version number. Set up the leading 'E' for the external form of | |
1811 | message ids, set the pointer to the internal form, and initialize it to | |
1812 | indicate no message being processed. */ | |
1813 | ||
1814 | version_init(); | |
1815 | message_id_option[0] = '-'; | |
1816 | message_id_external = message_id_option + 1; | |
1817 | message_id_external[0] = 'E'; | |
1818 | message_id = message_id_external + 1; | |
1819 | message_id[0] = 0; | |
1820 | ||
67d175de | 1821 | /* Set the umask to zero so that any files Exim creates using open() are |
2632889e PH |
1822 | created with the modes that it specifies. NOTE: Files created with fopen() have |
1823 | a problem, which was not recognized till rather late (February 2006). With this | |
1824 | umask, such files will be world writeable. (They are all content scanning files | |
1825 | in the spool directory, which isn't world-accessible, so this is not a | |
1826 | disaster, but it's untidy.) I don't want to change this overall setting, | |
1827 | however, because it will interact badly with the open() calls. Instead, there's | |
1828 | now a function called modefopen() that fiddles with the umask while calling | |
1829 | fopen(). */ | |
059ec3d9 | 1830 | |
67d175de | 1831 | (void)umask(0); |
059ec3d9 PH |
1832 | |
1833 | /* Precompile the regular expression for matching a message id. Keep this in | |
1834 | step with the code that generates ids in the accept.c module. We need to do | |
1835 | this here, because the -M options check their arguments for syntactic validity | |
1836 | using mac_ismsgid, which uses this. */ | |
1837 | ||
1838 | regex_ismsgid = | |
1839 | regex_must_compile(US"^(?:[^\\W_]{6}-){2}[^\\W_]{2}$", FALSE, TRUE); | |
1840 | ||
a5bd321b | 1841 | /* Precompile the regular expression that is used for matching an SMTP error |
d6a96edc PH |
1842 | code, possibly extended, at the start of an error message. Note that the |
1843 | terminating whitespace character is included. */ | |
a5bd321b PH |
1844 | |
1845 | regex_smtp_code = | |
1846 | regex_must_compile(US"^\\d\\d\\d\\s(?:\\d\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\s)?", | |
1847 | FALSE, TRUE); | |
1848 | ||
a7cbbf50 PP |
1849 | #ifdef WHITELIST_D_MACROS |
1850 | /* Precompile the regular expression used to filter the content of macros | |
1851 | given to -D for permissibility. */ | |
1852 | ||
1853 | regex_whitelisted_macro = | |
1854 | regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE); | |
1855 | #endif | |
1856 | ||
f38917cc JH |
1857 | for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; |
1858 | ||
059ec3d9 PH |
1859 | /* If the program is called as "mailq" treat it as equivalent to "exim -bp"; |
1860 | this seems to be a generally accepted convention, since one finds symbolic | |
1861 | links called "mailq" in standard OS configurations. */ | |
1862 | ||
1863 | if ((namelen == 5 && Ustrcmp(argv[0], "mailq") == 0) || | |
1864 | (namelen > 5 && Ustrncmp(argv[0] + namelen - 6, "/mailq", 6) == 0)) | |
1865 | { | |
1866 | list_queue = TRUE; | |
1867 | receiving_message = FALSE; | |
1868 | called_as = US"-mailq"; | |
1869 | } | |
1870 | ||
1871 | /* If the program is called as "rmail" treat it as equivalent to | |
1872 | "exim -i -oee", thus allowing UUCP messages to be input using non-SMTP mode, | |
1873 | i.e. preventing a single dot on a line from terminating the message, and | |
1874 | returning with zero return code, even in cases of error (provided an error | |
1875 | message has been sent). */ | |
1876 | ||
1877 | if ((namelen == 5 && Ustrcmp(argv[0], "rmail") == 0) || | |
1878 | (namelen > 5 && Ustrncmp(argv[0] + namelen - 6, "/rmail", 6) == 0)) | |
1879 | { | |
8768d548 | 1880 | f.dot_ends = FALSE; |
059ec3d9 PH |
1881 | called_as = US"-rmail"; |
1882 | errors_sender_rc = EXIT_SUCCESS; | |
1883 | } | |
1884 | ||
1885 | /* If the program is called as "rsmtp" treat it as equivalent to "exim -bS"; | |
1886 | this is a smail convention. */ | |
1887 | ||
1888 | if ((namelen == 5 && Ustrcmp(argv[0], "rsmtp") == 0) || | |
1889 | (namelen > 5 && Ustrncmp(argv[0] + namelen - 6, "/rsmtp", 6) == 0)) | |
1890 | { | |
1891 | smtp_input = smtp_batched_input = TRUE; | |
1892 | called_as = US"-rsmtp"; | |
1893 | } | |
1894 | ||
1895 | /* If the program is called as "runq" treat it as equivalent to "exim -q"; | |
1896 | this is a smail convention. */ | |
1897 | ||
1898 | if ((namelen == 4 && Ustrcmp(argv[0], "runq") == 0) || | |
1899 | (namelen > 4 && Ustrncmp(argv[0] + namelen - 5, "/runq", 5) == 0)) | |
1900 | { | |
1901 | queue_interval = 0; | |
1902 | receiving_message = FALSE; | |
1903 | called_as = US"-runq"; | |
1904 | } | |
1905 | ||
1906 | /* If the program is called as "newaliases" treat it as equivalent to | |
1907 | "exim -bi"; this is a sendmail convention. */ | |
1908 | ||
1909 | if ((namelen == 10 && Ustrcmp(argv[0], "newaliases") == 0) || | |
1910 | (namelen > 10 && Ustrncmp(argv[0] + namelen - 11, "/newaliases", 11) == 0)) | |
1911 | { | |
1912 | bi_option = TRUE; | |
1913 | receiving_message = FALSE; | |
1914 | called_as = US"-newaliases"; | |
1915 | } | |
1916 | ||
1917 | /* Save the original effective uid for a couple of uses later. It should | |
1918 | normally be root, but in some esoteric environments it may not be. */ | |
1919 | ||
1920 | original_euid = geteuid(); | |
3b7ac02c | 1921 | original_egid = getegid(); |
059ec3d9 PH |
1922 | |
1923 | /* Get the real uid and gid. If the caller is root, force the effective uid/gid | |
1924 | to be the same as the real ones. This makes a difference only if Exim is setuid | |
1925 | (or setgid) to something other than root, which could be the case in some | |
1926 | special configurations. */ | |
1927 | ||
1928 | real_uid = getuid(); | |
1929 | real_gid = getgid(); | |
1930 | ||
1931 | if (real_uid == root_uid) | |
1932 | { | |
9af3c549 JH |
1933 | if ((rv = setgid(real_gid))) |
1934 | exim_fail("exim: setgid(%ld) failed: %s\n", | |
1670ef10 | 1935 | (long int)real_gid, strerror(errno)); |
9af3c549 JH |
1936 | if ((rv = setuid(real_uid))) |
1937 | exim_fail("exim: setuid(%ld) failed: %s\n", | |
1670ef10 | 1938 | (long int)real_uid, strerror(errno)); |
059ec3d9 PH |
1939 | } |
1940 | ||
1941 | /* If neither the original real uid nor the original euid was root, Exim is | |
1942 | running in an unprivileged state. */ | |
1943 | ||
1944 | unprivileged = (real_uid != root_uid && original_euid != root_uid); | |
1945 | ||
059ec3d9 PH |
1946 | /* Scan the program's arguments. Some can be dealt with right away; others are |
1947 | simply recorded for checking and handling afterwards. Do a high-level switch | |
1948 | on the second character (the one after '-'), to save some effort. */ | |
1949 | ||
1950 | for (i = 1; i < argc; i++) | |
1951 | { | |
1952 | BOOL badarg = FALSE; | |
1953 | uschar *arg = argv[i]; | |
1954 | uschar *argrest; | |
1955 | int switchchar; | |
1956 | ||
1957 | /* An argument not starting with '-' is the start of a recipients list; | |
1958 | break out of the options-scanning loop. */ | |
1959 | ||
1960 | if (arg[0] != '-') | |
1961 | { | |
1962 | recipients_arg = i; | |
1963 | break; | |
1964 | } | |
1965 | ||
4c04137d | 1966 | /* An option consisting of -- terminates the options */ |
059ec3d9 PH |
1967 | |
1968 | if (Ustrcmp(arg, "--") == 0) | |
1969 | { | |
1970 | recipients_arg = i + 1; | |
1971 | break; | |
1972 | } | |
1973 | ||
1974 | /* Handle flagged options */ | |
1975 | ||
1976 | switchchar = arg[1]; | |
1977 | argrest = arg+2; | |
1978 | ||
1979 | /* Make all -ex options synonymous with -oex arguments, since that | |
1980 | is assumed by various callers. Also make -qR options synonymous with -R | |
1981 | options, as that seems to be required as well. Allow for -qqR too, and | |
1982 | the same for -S options. */ | |
1983 | ||
1984 | if (Ustrncmp(arg+1, "oe", 2) == 0 || | |
1985 | Ustrncmp(arg+1, "qR", 2) == 0 || | |
1986 | Ustrncmp(arg+1, "qS", 2) == 0) | |
1987 | { | |
1988 | switchchar = arg[2]; | |
1989 | argrest++; | |
1990 | } | |
1991 | else if (Ustrncmp(arg+1, "qqR", 3) == 0 || Ustrncmp(arg+1, "qqS", 3) == 0) | |
1992 | { | |
1993 | switchchar = arg[3]; | |
1994 | argrest += 2; | |
8768d548 | 1995 | f.queue_2stage = TRUE; |
059ec3d9 PH |
1996 | } |
1997 | ||
1998 | /* Make -r synonymous with -f, since it is a documented alias */ | |
1999 | ||
2000 | else if (arg[1] == 'r') switchchar = 'f'; | |
2001 | ||
2002 | /* Make -ov synonymous with -v */ | |
2003 | ||
2004 | else if (Ustrcmp(arg, "-ov") == 0) | |
2005 | { | |
2006 | switchchar = 'v'; | |
2007 | argrest++; | |
2008 | } | |
2009 | ||
4b2241d2 PP |
2010 | /* deal with --option_aliases */ |
2011 | else if (switchchar == '-') | |
2012 | { | |
2013 | if (Ustrcmp(argrest, "help") == 0) | |
2014 | { | |
2015 | usage_wanted = TRUE; | |
2016 | break; | |
2017 | } | |
2018 | else if (Ustrcmp(argrest, "version") == 0) | |
2019 | { | |
2020 | switchchar = 'b'; | |
73a46702 | 2021 | argrest = US"V"; |
4b2241d2 PP |
2022 | } |
2023 | } | |
2024 | ||
059ec3d9 PH |
2025 | /* High-level switch on active initial letter */ |
2026 | ||
2027 | switch(switchchar) | |
2028 | { | |
a3fb9793 PP |
2029 | |
2030 | /* sendmail uses -Ac and -Am to control which .cf file is used; | |
2031 | we ignore them. */ | |
2032 | case 'A': | |
2033 | if (*argrest == '\0') { badarg = TRUE; break; } | |
2034 | else | |
2035 | { | |
2036 | BOOL ignore = FALSE; | |
2037 | switch (*argrest) | |
2038 | { | |
2039 | case 'c': | |
2040 | case 'm': | |
2041 | if (*(argrest + 1) == '\0') | |
2042 | ignore = TRUE; | |
2043 | break; | |
2044 | } | |
bdcc6f2b | 2045 | if (!ignore) badarg = TRUE; |
a3fb9793 PP |
2046 | } |
2047 | break; | |
2048 | ||
059ec3d9 PH |
2049 | /* -Btype is a sendmail option for 7bit/8bit setting. Exim is 8-bit clean |
2050 | so has no need of it. */ | |
2051 | ||
2052 | case 'B': | |
bdcc6f2b | 2053 | if (!*argrest) i++; /* Skip over the type */ |
059ec3d9 PH |
2054 | break; |
2055 | ||
2056 | ||
2057 | case 'b': | |
059ec3d9 | 2058 | { |
bdcc6f2b JH |
2059 | receiving_message = FALSE; /* Reset TRUE for -bm, -bS, -bs below */ |
2060 | ||
2061 | switch (*argrest++) | |
2062 | { | |
2063 | /* -bd: Run in daemon mode, awaiting SMTP connections. | |
2064 | -bdf: Ditto, but in the foreground. | |
2065 | */ | |
2066 | case 'd': | |
2067 | f.daemon_listen = TRUE; | |
2068 | if (*argrest == 'f') f.background_daemon = FALSE; | |
2069 | else if (*argrest) badarg = TRUE; | |
2070 | break; | |
2071 | ||
2072 | /* -be: Run in expansion test mode | |
2073 | -bem: Ditto, but read a message from a file first | |
2074 | */ | |
2075 | case 'e': | |
2076 | expansion_test = checking = TRUE; | |
2077 | if (*argrest == 'm') | |
2078 | { | |
2079 | if (++i >= argc) { badarg = TRUE; break; } | |
2080 | expansion_test_message = argv[i]; | |
2081 | argrest++; | |
2082 | } | |
2083 | if (*argrest) badarg = TRUE; | |
2084 | break; | |
2085 | ||
2086 | /* -bF: Run system filter test */ | |
2087 | case 'F': | |
2088 | filter_test |= checking = FTEST_SYSTEM; | |
2089 | if (*argrest) { badarg = TRUE; break; } | |
2090 | if (++i < argc) filter_test_sfile = argv[i]; else | |
2091 | exim_fail("exim: file name expected after %s\n", argv[i-1]); | |
2092 | break; | |
2093 | ||
2094 | /* -bf: Run user filter test | |
2095 | -bfd: Set domain for filter testing | |
2096 | -bfl: Set local part for filter testing | |
2097 | -bfp: Set prefix for filter testing | |
2098 | -bfs: Set suffix for filter testing | |
2099 | */ | |
2100 | case 'f': | |
2101 | if (!*argrest) | |
2102 | { | |
2103 | filter_test |= checking = FTEST_USER; | |
2104 | if (++i < argc) filter_test_ufile = argv[i]; | |
2105 | else exim_fail("exim: file name expected after %s\n", argv[i-1]); | |
2106 | } | |
2107 | else | |
2108 | { | |
2109 | if (++i >= argc) | |
2110 | exim_fail("exim: string expected after %s\n", arg); | |
2111 | if (Ustrcmp(argrest, "d") == 0) ftest_domain = argv[i]; | |
2112 | else if (Ustrcmp(argrest, "l") == 0) ftest_localpart = argv[i]; | |
2113 | else if (Ustrcmp(argrest, "p") == 0) ftest_prefix = argv[i]; | |
2114 | else if (Ustrcmp(argrest, "s") == 0) ftest_suffix = argv[i]; | |
2115 | else badarg = TRUE; | |
2116 | } | |
2117 | break; | |
2118 | ||
2119 | /* -bh: Host checking - an IP address must follow. */ | |
2120 | case 'h': | |
2121 | if (!*argrest || Ustrcmp(argrest, "c") == 0) | |
2122 | { | |
2123 | if (++i >= argc) { badarg = TRUE; break; } | |
2124 | sender_host_address = argv[i]; | |
2125 | host_checking = checking = f.log_testing_mode = TRUE; | |
2126 | f.host_checking_callout = *argrest == 'c'; | |
2127 | message_logs = FALSE; | |
2128 | } | |
2129 | else badarg = TRUE; | |
2130 | break; | |
2131 | ||
2132 | /* -bi: This option is used by sendmail to initialize *the* alias file, | |
2133 | though it has the -oA option to specify a different file. Exim has no | |
2134 | concept of *the* alias file, but since Sun's YP make script calls | |
2135 | sendmail this way, some support must be provided. */ | |
2136 | case 'i': | |
2137 | if (!*++argrest) bi_option = TRUE; | |
2138 | else badarg = TRUE; | |
2139 | break; | |
2140 | ||
2141 | /* -bI: provide information, of the type to follow after a colon. | |
2142 | This is an Exim flag. */ | |
2143 | case 'I': | |
2144 | if (Ustrlen(argrest) >= 1 && *argrest == ':') | |
2145 | { | |
2146 | uschar *p = argrest+1; | |
2147 | info_flag = CMDINFO_HELP; | |
2148 | if (Ustrlen(p)) | |
2149 | if (strcmpic(p, CUS"sieve") == 0) | |
2150 | { | |
2151 | info_flag = CMDINFO_SIEVE; | |
2152 | info_stdout = TRUE; | |
2153 | } | |
2154 | else if (strcmpic(p, CUS"dscp") == 0) | |
2155 | { | |
2156 | info_flag = CMDINFO_DSCP; | |
2157 | info_stdout = TRUE; | |
2158 | } | |
2159 | else if (strcmpic(p, CUS"help") == 0) | |
2160 | info_stdout = TRUE; | |
2161 | } | |
2162 | else badarg = TRUE; | |
2163 | break; | |
2164 | ||
2165 | /* -bm: Accept and deliver message - the default option. Reinstate | |
2166 | receiving_message, which got turned off for all -b options. | |
2167 | -bmalware: test the filename given for malware */ | |
2168 | case 'm': | |
2169 | if (!*argrest) receiving_message = TRUE; | |
2170 | else if (Ustrcmp(argrest, "alware") == 0) | |
2171 | { | |
2172 | if (++i >= argc) { badarg = TRUE; break; } | |
2173 | checking = TRUE; | |
2174 | malware_test_file = argv[i]; | |
2175 | } | |
2176 | else badarg = TRUE; | |
2177 | break; | |
2178 | ||
2179 | /* -bnq: For locally originating messages, do not qualify unqualified | |
2180 | addresses. In the envelope, this causes errors; in header lines they | |
2181 | just get left. */ | |
2182 | case 'n': | |
2183 | if (Ustrcmp(argrest, "q") == 0) | |
2184 | { | |
2185 | f.allow_unqualified_sender = FALSE; | |
2186 | f.allow_unqualified_recipient = FALSE; | |
2187 | } | |
2188 | else badarg = TRUE; | |
2189 | break; | |
2190 | ||
2191 | /* -bpxx: List the contents of the mail queue, in various forms. If | |
2192 | the option is -bpc, just a queue count is needed. Otherwise, if the | |
2193 | first letter after p is r, then order is random. */ | |
2194 | case 'p': | |
2195 | if (*argrest == 'c') | |
2196 | { | |
2197 | count_queue = TRUE; | |
2198 | if (*++argrest) badarg = TRUE; | |
2199 | break; | |
2200 | } | |
2201 | ||
2202 | if (*argrest == 'r') | |
2203 | { | |
2204 | list_queue_option = 8; | |
2205 | argrest++; | |
2206 | } | |
2207 | else list_queue_option = 0; | |
2208 | ||
2209 | list_queue = TRUE; | |
2210 | ||
2211 | /* -bp: List the contents of the mail queue, top-level only */ | |
2212 | ||
2213 | if (!*argrest) {} | |
2214 | ||
2215 | /* -bpu: List the contents of the mail queue, top-level undelivered */ | |
2216 | ||
2217 | else if (Ustrcmp(argrest, "u") == 0) list_queue_option += 1; | |
2218 | ||
2219 | /* -bpa: List the contents of the mail queue, including all delivered */ | |
2220 | ||
2221 | else if (Ustrcmp(argrest, "a") == 0) list_queue_option += 2; | |
2222 | ||
2223 | /* Unknown after -bp[r] */ | |
2224 | ||
2225 | else badarg = TRUE; | |
2226 | break; | |
2227 | ||
2228 | ||
2229 | /* -bP: List the configuration variables given as the address list. | |
2230 | Force -v, so configuration errors get displayed. */ | |
2231 | case 'P': | |
2232 | ||
2233 | /* -bP config: we need to setup here, because later, | |
2234 | * when list_options is checked, the config is read already */ | |
2235 | if (*argrest) | |
2236 | badarg = TRUE; | |
2237 | else if (argv[i+1] && Ustrcmp(argv[i+1], "config") == 0) | |
2238 | { | |
2239 | list_config = TRUE; | |
2240 | readconf_save_config(version_string); | |
2241 | } | |
2242 | else | |
2243 | { | |
2244 | list_options = TRUE; | |
2245 | debug_selector |= D_v; | |
2246 | debug_file = stderr; | |
2247 | } | |
2248 | break; | |
2249 | ||
2250 | /* -brt: Test retry configuration lookup */ | |
2251 | case 'r': | |
2252 | if (Ustrcmp(argrest, "t") == 0) | |
2253 | { | |
2254 | checking = TRUE; | |
2255 | test_retry_arg = i + 1; | |
2256 | goto END_ARG; | |
2257 | } | |
2258 | ||
2259 | /* -brw: Test rewrite configuration */ | |
2260 | ||
2261 | else if (Ustrcmp(argrest, "w") == 0) | |
2262 | { | |
2263 | checking = TRUE; | |
2264 | test_rewrite_arg = i + 1; | |
2265 | goto END_ARG; | |
2266 | } | |
2267 | else badarg = TRUE; | |
2268 | break; | |
2269 | ||
2270 | /* -bS: Read SMTP commands on standard input, but produce no replies - | |
2271 | all errors are reported by sending messages. */ | |
2272 | case 'S': | |
2273 | if (!*argrest) | |
2274 | smtp_input = smtp_batched_input = receiving_message = TRUE; | |
2275 | else badarg = TRUE; | |
2276 | break; | |
2277 | ||
2278 | /* -bs: Read SMTP commands on standard input and produce SMTP replies | |
2279 | on standard output. */ | |
2280 | case 's': | |
2281 | if (!*argrest) smtp_input = receiving_message = TRUE; | |
2282 | else badarg = TRUE; | |
2283 | break; | |
2284 | ||
2285 | /* -bt: address testing mode */ | |
2286 | case 't': | |
2287 | if (!*argrest) | |
2288 | f.address_test_mode = checking = f.log_testing_mode = TRUE; | |
2289 | else badarg = TRUE; | |
2290 | break; | |
2291 | ||
2292 | /* -bv: verify addresses */ | |
2293 | case 'v': | |
2294 | if (!*argrest) | |
2295 | verify_address_mode = checking = f.log_testing_mode = TRUE; | |
2296 | ||
2297 | /* -bvs: verify sender addresses */ | |
2298 | ||
2299 | else if (Ustrcmp(argrest, "s") == 0) | |
2300 | { | |
2301 | verify_address_mode = checking = f.log_testing_mode = TRUE; | |
2302 | verify_as_sender = TRUE; | |
2303 | } | |
2304 | else badarg = TRUE; | |
2305 | break; | |
2306 | ||
2307 | /* -bV: Print version string and support details */ | |
2308 | case 'V': | |
2309 | if (!*argrest) | |
2310 | { | |
2311 | printf("Exim version %s #%s built %s\n", version_string, | |
2312 | version_cnumber, version_date); | |
2313 | printf("%s\n", CS version_copyright); | |
2314 | version_printed = TRUE; | |
2315 | show_whats_supported(stdout); | |
2316 | f.log_testing_mode = TRUE; | |
2317 | } | |
2318 | else badarg = TRUE; | |
2319 | break; | |
2320 | ||
2321 | /* -bw: inetd wait mode, accept a listening socket as stdin */ | |
2322 | case 'w': | |
2323 | f.inetd_wait_mode = TRUE; | |
2324 | f.background_daemon = FALSE; | |
2325 | f.daemon_listen = TRUE; | |
2326 | if (*argrest) | |
2327 | if ((inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE)) <= 0) | |
2328 | exim_fail("exim: bad time value %s: abandoned\n", argv[i]); | |
2329 | break; | |
2330 | ||
2331 | default: | |
2332 | badarg = TRUE; | |
2333 | break; | |
2334 | } | |
2335 | break; | |
9ee44efb PP |
2336 | } |
2337 | ||
059ec3d9 PH |
2338 | |
2339 | /* -C: change configuration file list; ignore if it isn't really | |
2340 | a change! Enforce a prefix check if required. */ | |
2341 | ||
2342 | case 'C': | |
2343 | if (*argrest == 0) | |
2344 | { | |
2345 | if(++i < argc) argrest = argv[i]; else | |
2346 | { badarg = TRUE; break; } | |
2347 | } | |
2348 | if (Ustrcmp(config_main_filelist, argrest) != 0) | |
2349 | { | |
2350 | #ifdef ALT_CONFIG_PREFIX | |
2351 | int sep = 0; | |
2352 | int len = Ustrlen(ALT_CONFIG_PREFIX); | |
863bd541 | 2353 | const uschar *list = argrest; |
059ec3d9 PH |
2354 | uschar *filename; |
2355 | while((filename = string_nextinlist(&list, &sep, big_buffer, | |
2356 | big_buffer_size)) != NULL) | |
2357 | { | |
2358 | if ((Ustrlen(filename) < len || | |
2359 | Ustrncmp(filename, ALT_CONFIG_PREFIX, len) != 0 || | |
2360 | Ustrstr(filename, "/../") != NULL) && | |
2361 | (Ustrcmp(filename, "/dev/null") != 0 || real_uid != root_uid)) | |
9af3c549 | 2362 | exim_fail("-C Permission denied\n"); |
059ec3d9 PH |
2363 | } |
2364 | #endif | |
261dc43e DW |
2365 | if (real_uid != root_uid) |
2366 | { | |
90b6341f | 2367 | #ifdef TRUSTED_CONFIG_LIST |
261dc43e | 2368 | |
90b6341f DW |
2369 | if (real_uid != exim_uid |
2370 | #ifdef CONFIGURE_OWNER | |
2371 | && real_uid != config_uid | |
2372 | #endif | |
2373 | ) | |
8768d548 | 2374 | f.trusted_config = FALSE; |
261dc43e DW |
2375 | else |
2376 | { | |
90b6341f | 2377 | FILE *trust_list = Ufopen(TRUSTED_CONFIG_LIST, "rb"); |
261dc43e DW |
2378 | if (trust_list) |
2379 | { | |
2380 | struct stat statbuf; | |
2381 | ||
2382 | if (fstat(fileno(trust_list), &statbuf) != 0 || | |
2383 | (statbuf.st_uid != root_uid /* owner not root */ | |
2384 | #ifdef CONFIGURE_OWNER | |
2385 | && statbuf.st_uid != config_uid /* owner not the special one */ | |
2386 | #endif | |
2387 | ) || /* or */ | |
2388 | (statbuf.st_gid != root_gid /* group not root */ | |
2389 | #ifdef CONFIGURE_GROUP | |
2390 | && statbuf.st_gid != config_gid /* group not the special one */ | |
2391 | #endif | |
2392 | && (statbuf.st_mode & 020) != 0 /* group writeable */ | |
2393 | ) || /* or */ | |
2394 | (statbuf.st_mode & 2) != 0) /* world writeable */ | |
2395 | { | |
8768d548 | 2396 | f.trusted_config = FALSE; |
261dc43e DW |
2397 | fclose(trust_list); |
2398 | } | |
2399 | else | |
2400 | { | |
2401 | /* Well, the trust list at least is up to scratch... */ | |
f3ebb786 | 2402 | rmark reset_point = store_mark(); |
90b6341f DW |
2403 | uschar *trusted_configs[32]; |
2404 | int nr_configs = 0; | |
261dc43e DW |
2405 | int i = 0; |
2406 | ||
2407 | while (Ufgets(big_buffer, big_buffer_size, trust_list)) | |
2408 | { | |
2409 | uschar *start = big_buffer, *nl; | |
2410 | while (*start && isspace(*start)) | |
2411 | start++; | |
1e83d68b | 2412 | if (*start != '/') |
261dc43e DW |
2413 | continue; |
2414 | nl = Ustrchr(start, '\n'); | |
2415 | if (nl) | |
2416 | *nl = 0; | |
90b6341f DW |
2417 | trusted_configs[nr_configs++] = string_copy(start); |
2418 | if (nr_configs == 32) | |
261dc43e DW |
2419 | break; |
2420 | } | |
2421 | fclose(trust_list); | |
2422 | ||
90b6341f | 2423 | if (nr_configs) |
261dc43e DW |
2424 | { |
2425 | int sep = 0; | |
55414b25 | 2426 | const uschar *list = argrest; |
261dc43e | 2427 | uschar *filename; |
8768d548 | 2428 | while (f.trusted_config && (filename = string_nextinlist(&list, |
261dc43e DW |
2429 | &sep, big_buffer, big_buffer_size)) != NULL) |
2430 | { | |
90b6341f | 2431 | for (i=0; i < nr_configs; i++) |
90b6341f | 2432 | if (Ustrcmp(filename, trusted_configs[i]) == 0) |
261dc43e | 2433 | break; |
90b6341f | 2434 | if (i == nr_configs) |
261dc43e | 2435 | { |
8768d548 | 2436 | f.trusted_config = FALSE; |
261dc43e DW |
2437 | break; |
2438 | } | |
2439 | } | |
2440 | } | |
f3ebb786 | 2441 | else /* No valid prefixes found in trust_list file. */ |
8768d548 | 2442 | f.trusted_config = FALSE; |
f3ebb786 | 2443 | store_reset(reset_point); |
261dc43e DW |
2444 | } |
2445 | } | |
f3ebb786 | 2446 | else /* Could not open trust_list file. */ |
8768d548 | 2447 | f.trusted_config = FALSE; |
261dc43e DW |
2448 | } |
2449 | #else | |
2450 | /* Not root; don't trust config */ | |
8768d548 | 2451 | f.trusted_config = FALSE; |
261dc43e DW |
2452 | #endif |
2453 | } | |
059ec3d9 PH |
2454 | |
2455 | config_main_filelist = argrest; | |
8768d548 | 2456 | f.config_changed = TRUE; |
059ec3d9 PH |
2457 | } |
2458 | break; | |
2459 | ||
2460 | ||
2461 | /* -D: set up a macro definition */ | |
2462 | ||
2463 | case 'D': | |
9af3c549 JH |
2464 | #ifdef DISABLE_D_OPTION |
2465 | exim_fail("exim: -D is not available in this Exim binary\n"); | |
2466 | #else | |
059ec3d9 PH |
2467 | { |
2468 | int ptr = 0; | |
059ec3d9 PH |
2469 | macro_item *m; |
2470 | uschar name[24]; | |
2471 | uschar *s = argrest; | |
2472 | ||
4ab69ec7 | 2473 | opt_D_used = TRUE; |
059ec3d9 PH |
2474 | while (isspace(*s)) s++; |
2475 | ||
2476 | if (*s < 'A' || *s > 'Z') | |
9af3c549 | 2477 | exim_fail("exim: macro name set by -D must start with " |
059ec3d9 | 2478 | "an upper case letter\n"); |
059ec3d9 PH |
2479 | |
2480 | while (isalnum(*s) || *s == '_') | |
2481 | { | |
2482 | if (ptr < sizeof(name)-1) name[ptr++] = *s; | |
2483 | s++; | |
2484 | } | |
2485 | name[ptr] = 0; | |
2486 | if (ptr == 0) { badarg = TRUE; break; } | |
2487 | while (isspace(*s)) s++; | |
2488 | if (*s != 0) | |
2489 | { | |
2490 | if (*s++ != '=') { badarg = TRUE; break; } | |
2491 | while (isspace(*s)) s++; | |
2492 | } | |
2493 | ||
85e03244 | 2494 | for (m = macros_user; m; m = m->next) |
1a7c9a48 | 2495 | if (Ustrcmp(m->name, name) == 0) |
9af3c549 | 2496 | exim_fail("exim: duplicated -D in command line\n"); |
059ec3d9 | 2497 | |
85e03244 | 2498 | m = macro_create(name, s, TRUE); |
059ec3d9 PH |
2499 | |
2500 | if (clmacro_count >= MAX_CLMACROS) | |
9af3c549 | 2501 | exim_fail("exim: too many -D options on command line\n"); |
f3ebb786 JH |
2502 | clmacros[clmacro_count++] = |
2503 | string_sprintf("-D%s=%s", m->name, m->replacement); | |
059ec3d9 PH |
2504 | } |
2505 | #endif | |
2506 | break; | |
2507 | ||
2508 | /* -d: Set debug level (see also -v below) or set the drop_cr option. | |
8e669ac1 | 2509 | The latter is now a no-op, retained for compatibility only. If -dd is used, |
3d235903 | 2510 | debugging subprocesses of the daemon is disabled. */ |
059ec3d9 PH |
2511 | |
2512 | case 'd': | |
2513 | if (Ustrcmp(argrest, "ropcr") == 0) | |
2514 | { | |
2515 | /* drop_cr = TRUE; */ | |
2516 | } | |
2517 | ||
2518 | /* Use an intermediate variable so that we don't set debugging while | |
2519 | decoding the debugging bits. */ | |
2520 | ||
2521 | else | |
2522 | { | |
2523 | unsigned int selector = D_default; | |
2524 | debug_selector = 0; | |
2525 | debug_file = NULL; | |
3d235903 PH |
2526 | if (*argrest == 'd') |
2527 | { | |
8768d548 | 2528 | f.debug_daemon = TRUE; |
3d235903 PH |
2529 | argrest++; |
2530 | } | |
059ec3d9 | 2531 | if (*argrest != 0) |
6c6d6e48 TF |
2532 | decode_bits(&selector, 1, debug_notall, argrest, |
2533 | debug_options, debug_options_count, US"debug", 0); | |
059ec3d9 PH |
2534 | debug_selector = selector; |
2535 | } | |
2536 | break; | |
2537 | ||
2538 | ||
2539 | /* -E: This is a local error message. This option is not intended for | |
2540 | external use at all, but is not restricted to trusted callers because it | |
2541 | does no harm (just suppresses certain error messages) and if Exim is run | |
2542 | not setuid root it won't always be trusted when it generates error | |
2543 | messages using this option. If there is a message id following -E, point | |
2544 | message_reference at it, for logging. */ | |
2545 | ||
2546 | case 'E': | |
8768d548 | 2547 | f.local_error_message = TRUE; |
059ec3d9 PH |
2548 | if (mac_ismsgid(argrest)) message_reference = argrest; |
2549 | break; | |
2550 | ||
2551 | ||
2552 | /* -ex: The vacation program calls sendmail with the undocumented "-eq" | |
2553 | option, so it looks as if historically the -oex options are also callable | |
2554 | without the leading -o. So we have to accept them. Before the switch, | |
2555 | anything starting -oe has been converted to -e. Exim does not support all | |
2556 | of the sendmail error options. */ | |
2557 | ||
2558 | case 'e': | |
2559 | if (Ustrcmp(argrest, "e") == 0) | |
2560 | { | |
2561 | arg_error_handling = ERRORS_SENDER; | |
2562 | errors_sender_rc = EXIT_SUCCESS; | |
2563 | } | |
2564 | else if (Ustrcmp(argrest, "m") == 0) arg_error_handling = ERRORS_SENDER; | |
2565 | else if (Ustrcmp(argrest, "p") == 0) arg_error_handling = ERRORS_STDERR; | |
2566 | else if (Ustrcmp(argrest, "q") == 0) arg_error_handling = ERRORS_STDERR; | |
2567 | else if (Ustrcmp(argrest, "w") == 0) arg_error_handling = ERRORS_SENDER; | |
2568 | else badarg = TRUE; | |
2569 | break; | |
2570 | ||
2571 | ||
2572 | /* -F: Set sender's full name, used instead of the gecos entry from | |
2573 | the password file. Since users can usually alter their gecos entries, | |
2574 | there's no security involved in using this instead. The data can follow | |
2575 | the -F or be in the next argument. */ | |
2576 | ||
2577 | case 'F': | |
2578 | if (*argrest == 0) | |
2579 | { | |
2580 | if(++i < argc) argrest = argv[i]; else | |
2581 | { badarg = TRUE; break; } | |
2582 | } | |
2583 | originator_name = argrest; | |
8768d548 | 2584 | f.sender_name_forced = TRUE; |
059ec3d9 PH |
2585 | break; |
2586 | ||
2587 | ||
2588 | /* -f: Set sender's address - this value is only actually used if Exim is | |
2589 | run by a trusted user, or if untrusted_set_sender is set and matches the | |
2590 | address, except that the null address can always be set by any user. The | |
2591 | test for this happens later, when the value given here is ignored when not | |
2592 | permitted. For an untrusted user, the actual sender is still put in Sender: | |
2593 | if it doesn't match the From: header (unless no_local_from_check is set). | |
2594 | The data can follow the -f or be in the next argument. The -r switch is an | |
2595 | obsolete form of -f but since there appear to be programs out there that | |
2596 | use anything that sendmail has ever supported, better accept it - the | |
2597 | synonymizing is done before the switch above. | |
2598 | ||
2599 | At this stage, we must allow domain literal addresses, because we don't | |
2600 | know what the setting of allow_domain_literals is yet. Ditto for trailing | |
2601 | dots and strip_trailing_dot. */ | |
2602 | ||
2603 | case 'f': | |
2604 | { | |
250b6871 | 2605 | int dummy_start, dummy_end; |
059ec3d9 PH |
2606 | uschar *errmess; |
2607 | if (*argrest == 0) | |
2608 | { | |
2609 | if (i+1 < argc) argrest = argv[++i]; else | |
2610 | { badarg = TRUE; break; } | |
2611 | } | |
2612 | if (*argrest == 0) | |
f3ebb786 | 2613 | *(sender_address = store_get(1, FALSE)) = '\0'; /* Ensure writeable memory */ |
059ec3d9 PH |
2614 | else |
2615 | { | |
2616 | uschar *temp = argrest + Ustrlen(argrest) - 1; | |
2617 | while (temp >= argrest && isspace(*temp)) temp--; | |
2618 | if (temp >= argrest && *temp == '.') f_end_dot = TRUE; | |
2619 | allow_domain_literals = TRUE; | |
2620 | strip_trailing_dot = TRUE; | |
8c5d388a | 2621 | #ifdef SUPPORT_I18N |
250b6871 JH |
2622 | allow_utf8_domains = TRUE; |
2623 | #endif | |
2624 | sender_address = parse_extract_address(argrest, &errmess, | |
2625 | &dummy_start, &dummy_end, &sender_address_domain, TRUE); | |
f3ebb786 | 2626 | sender_address = string_copy_taint(sender_address, TRUE); |
8c5d388a | 2627 | #ifdef SUPPORT_I18N |
250b6871 JH |
2628 | message_smtputf8 = string_is_utf8(sender_address); |
2629 | allow_utf8_domains = FALSE; | |
2630 | #endif | |
059ec3d9 PH |
2631 | allow_domain_literals = FALSE; |
2632 | strip_trailing_dot = FALSE; | |
9af3c549 JH |
2633 | if (!sender_address) |
2634 | exim_fail("exim: bad -f address \"%s\": %s\n", argrest, errmess); | |
059ec3d9 | 2635 | } |
8768d548 | 2636 | f.sender_address_forced = TRUE; |
059ec3d9 PH |
2637 | } |
2638 | break; | |
2639 | ||
a3fb9793 | 2640 | /* -G: sendmail invocation to specify that it's a gateway submission and |
f4ee74ac PP |
2641 | sendmail may complain about problems instead of fixing them. |
2642 | We make it equivalent to an ACL "control = suppress_local_fixups" and do | |
2643 | not at this time complain about problems. */ | |
059ec3d9 PH |
2644 | |
2645 | case 'G': | |
f4ee74ac | 2646 | flag_G = TRUE; |
059ec3d9 PH |
2647 | break; |
2648 | ||
2649 | /* -h: Set the hop count for an incoming message. Exim does not currently | |
2650 | support this; it always computes it by counting the Received: headers. | |
2651 | To put it in will require a change to the spool header file format. */ | |
2652 | ||
2653 | case 'h': | |
2654 | if (*argrest == 0) | |
2655 | { | |
2656 | if(++i < argc) argrest = argv[i]; else | |
2657 | { badarg = TRUE; break; } | |
2658 | } | |
2659 | if (!isdigit(*argrest)) badarg = TRUE; | |
2660 | break; | |
2661 | ||
2662 | ||
2663 | /* -i: Set flag so dot doesn't end non-SMTP input (same as -oi, seems | |
2664 | not to be documented for sendmail but mailx (at least) uses it) */ | |
2665 | ||
2666 | case 'i': | |
8768d548 | 2667 | if (*argrest == 0) f.dot_ends = FALSE; else badarg = TRUE; |
059ec3d9 PH |
2668 | break; |
2669 | ||
2670 | ||
a3fb9793 PP |
2671 | /* -L: set the identifier used for syslog; equivalent to setting |
2672 | syslog_processname in the config file, but needs to be an admin option. */ | |
2673 | ||
2674 | case 'L': | |
2675 | if (*argrest == '\0') | |
2676 | { | |
2677 | if(++i < argc) argrest = argv[i]; else | |
2678 | { badarg = TRUE; break; } | |
2679 | } | |
9af3c549 JH |
2680 | if ((sz = Ustrlen(argrest)) > 32) |
2681 | exim_fail("exim: the -L syslog name is too long: \"%s\"\n", argrest); | |
a3fb9793 | 2682 | if (sz < 1) |
9af3c549 | 2683 | exim_fail("exim: the -L syslog name is too short\n"); |
a3fb9793 PP |
2684 | cmdline_syslog_name = argrest; |
2685 | break; | |
2686 | ||
059ec3d9 PH |
2687 | case 'M': |
2688 | receiving_message = FALSE; | |
2689 | ||
2690 | /* -MC: continue delivery of another message via an existing open | |
2691 | file descriptor. This option is used for an internal call by the | |
2692 | smtp transport when there is a pending message waiting to go to an | |
2693 | address to which it has got a connection. Five subsequent arguments are | |
2694 | required: transport name, host name, IP address, sequence number, and | |
2695 | message_id. Transports may decline to create new processes if the sequence | |
2696 | number gets too big. The channel is stdin. This (-MC) must be the last | |
2697 | argument. There's a subsequent check that the real-uid is privileged. | |
2698 | ||
2699 | If we are running in the test harness. delay for a bit, to let the process | |
2700 | that set this one up complete. This makes for repeatability of the logging, | |
2701 | etc. output. */ | |
2702 | ||
2703 | if (Ustrcmp(argrest, "C") == 0) | |
2704 | { | |
41c7c167 PH |
2705 | union sockaddr_46 interface_sock; |
2706 | EXIM_SOCKLEN_T size = sizeof(interface_sock); | |
2707 | ||
059ec3d9 | 2708 | if (argc != i + 6) |
9af3c549 | 2709 | exim_fail("exim: too many or too few arguments after -MC\n"); |
059ec3d9 PH |
2710 | |
2711 | if (msg_action_arg >= 0) | |
9af3c549 | 2712 | exim_fail("exim: incompatible arguments\n"); |
059ec3d9 PH |
2713 | |
2714 | continue_transport = argv[++i]; | |
2715 | continue_hostname = argv[++i]; | |
2716 | continue_host_address = argv[++i]; | |
2717 | continue_sequence = Uatoi(argv[++i]); | |
2718 | msg_action = MSG_DELIVER; | |
2719 | msg_action_arg = ++i; | |
2720 | forced_delivery = TRUE; | |
2721 | queue_run_pid = passed_qr_pid; | |
2722 | queue_run_pipe = passed_qr_pipe; | |
2723 | ||
2724 | if (!mac_ismsgid(argv[i])) | |
9af3c549 | 2725 | exim_fail("exim: malformed message id %s after -MC option\n", |
059ec3d9 | 2726 | argv[i]); |
059ec3d9 | 2727 | |
875512a3 JH |
2728 | /* Set up $sending_ip_address and $sending_port, unless proxied */ |
2729 | ||
5013d912 | 2730 | if (!continue_proxy_cipher) |
875512a3 JH |
2731 | if (getsockname(fileno(stdin), (struct sockaddr *)(&interface_sock), |
2732 | &size) == 0) | |
2733 | sending_ip_address = host_ntoa(-1, &interface_sock, NULL, | |
2734 | &sending_port); | |
2735 | else | |
9af3c549 | 2736 | exim_fail("exim: getsockname() failed after -MC option: %s\n", |
875512a3 | 2737 | strerror(errno)); |
41c7c167 | 2738 | |
9f01e50d | 2739 | testharness_pause_ms(500); |
059ec3d9 PH |
2740 | break; |
2741 | } | |
2742 | ||
2d14f397 JH |
2743 | else if (*argrest == 'C' && argrest[1] && !argrest[2]) |
2744 | { | |
875512a3 | 2745 | switch(argrest[1]) |
2d14f397 | 2746 | { |
059ec3d9 PH |
2747 | /* -MCA: set the smtp_authenticated flag; this is useful only when it |
2748 | precedes -MC (see above). The flag indicates that the host to which | |
2749 | Exim is connected has accepted an AUTH sequence. */ | |
2750 | ||
8768d548 | 2751 | case 'A': f.smtp_authenticated = TRUE; break; |
059ec3d9 | 2752 | |
6c1c3d1d WB |
2753 | /* -MCD: set the smtp_use_dsn flag; this indicates that the host |
2754 | that exim is connected to supports the esmtp extension DSN */ | |
28b3821f | 2755 | |
14de8063 | 2756 | case 'D': smtp_peer_options |= OPTION_DSN; break; |
6c1c3d1d | 2757 | |
e37f8a84 | 2758 | /* -MCG: set the queue name, to a non-default value */ |
28b3821f | 2759 | |
2d14f397 JH |
2760 | case 'G': if (++i < argc) queue_name = string_copy(argv[i]); |
2761 | else badarg = TRUE; | |
2762 | break; | |
2763 | ||
2764 | /* -MCK: the peer offered CHUNKING. Must precede -MC */ | |
2765 | ||
14de8063 | 2766 | case 'K': smtp_peer_options |= OPTION_CHUNKING; break; |
28b3821f | 2767 | |
059ec3d9 PH |
2768 | /* -MCP: set the smtp_use_pipelining flag; this is useful only when |
2769 | it preceded -MC (see above) */ | |
2770 | ||
14de8063 | 2771 | case 'P': smtp_peer_options |= OPTION_PIPE; break; |
059ec3d9 PH |
2772 | |
2773 | /* -MCQ: pass on the pid of the queue-running process that started | |
2774 | this chain of deliveries and the fd of its synchronizing pipe; this | |
2775 | is useful only when it precedes -MC (see above) */ | |
2776 | ||
2d14f397 JH |
2777 | case 'Q': if (++i < argc) passed_qr_pid = (pid_t)(Uatol(argv[i])); |
2778 | else badarg = TRUE; | |
2779 | if (++i < argc) passed_qr_pipe = (int)(Uatol(argv[i])); | |
2780 | else badarg = TRUE; | |
2781 | break; | |
059ec3d9 PH |
2782 | |
2783 | /* -MCS: set the smtp_use_size flag; this is useful only when it | |
2784 | precedes -MC (see above) */ | |
2785 | ||
14de8063 | 2786 | case 'S': smtp_peer_options |= OPTION_SIZE; break; |
059ec3d9 | 2787 | |
01603eec | 2788 | #ifndef DISABLE_TLS |
875512a3 | 2789 | /* -MCt: similar to -MCT below but the connection is still open |
aded2255 | 2790 | via a proxy process which handles the TLS context and coding. |
5013d912 JH |
2791 | Require three arguments for the proxied local address and port, |
2792 | and the TLS cipher. */ | |
875512a3 | 2793 | |
5013d912 | 2794 | case 't': if (++i < argc) sending_ip_address = argv[i]; |
875512a3 JH |
2795 | else badarg = TRUE; |
2796 | if (++i < argc) sending_port = (int)(Uatol(argv[i])); | |
2797 | else badarg = TRUE; | |
5013d912 JH |
2798 | if (++i < argc) continue_proxy_cipher = argv[i]; |
2799 | else badarg = TRUE; | |
875512a3 JH |
2800 | /*FALLTHROUGH*/ |
2801 | ||
059ec3d9 PH |
2802 | /* -MCT: set the tls_offered flag; this is useful only when it |
2803 | precedes -MC (see above). The flag indicates that the host to which | |
2804 | Exim is connected has offered TLS support. */ | |
2805 | ||
14de8063 | 2806 | case 'T': smtp_peer_options |= OPTION_TLS; break; |
2d14f397 JH |
2807 | #endif |
2808 | ||
2809 | default: badarg = TRUE; break; | |
2810 | } | |
8ac90765 JH |
2811 | break; |
2812 | } | |
2813 | ||
059ec3d9 PH |
2814 | /* -M[x]: various operations on the following list of message ids: |
2815 | -M deliver the messages, ignoring next retry times and thawing | |
2816 | -Mc deliver the messages, checking next retry times, no thawing | |
2817 | -Mf freeze the messages | |
2818 | -Mg give up on the messages | |
2819 | -Mt thaw the messages | |
2820 | -Mrm remove the messages | |
2821 | In the above cases, this must be the last option. There are also the | |
2822 | following options which are followed by a single message id, and which | |
2823 | act on that message. Some of them use the "recipient" addresses as well. | |
2824 | -Mar add recipient(s) | |
2825 | -Mmad mark all recipients delivered | |
2826 | -Mmd mark recipients(s) delivered | |
2827 | -Mes edit sender | |
0ef732d9 | 2828 | -Mset load a message for use with -be |
059ec3d9 | 2829 | -Mvb show body |
a96603a0 | 2830 | -Mvc show copy (of whole message, in RFC 2822 format) |
059ec3d9 PH |
2831 | -Mvh show header |
2832 | -Mvl show log | |
2833 | */ | |
2834 | ||
2835 | else if (*argrest == 0) | |
2836 | { | |
2837 | msg_action = MSG_DELIVER; | |
8768d548 | 2838 | forced_delivery = f.deliver_force_thaw = TRUE; |
059ec3d9 PH |
2839 | } |
2840 | else if (Ustrcmp(argrest, "ar") == 0) | |
2841 | { | |
2842 | msg_action = MSG_ADD_RECIPIENT; | |
2843 | one_msg_action = TRUE; | |
2844 | } | |
2845 | else if (Ustrcmp(argrest, "c") == 0) msg_action = MSG_DELIVER; | |
2846 | else if (Ustrcmp(argrest, "es") == 0) | |
2847 | { | |
2848 | msg_action = MSG_EDIT_SENDER; | |
2849 | one_msg_action = TRUE; | |
2850 | } | |
2851 | else if (Ustrcmp(argrest, "f") == 0) msg_action = MSG_FREEZE; | |
2852 | else if (Ustrcmp(argrest, "g") == 0) | |
2853 | { | |
2854 | msg_action = MSG_DELIVER; | |
2855 | deliver_give_up = TRUE; | |
2856 | } | |
fc7bae7f JH |
2857 | else if (Ustrcmp(argrest, "G") == 0) |
2858 | { | |
2859 | msg_action = MSG_SETQUEUE; | |
2860 | queue_name_dest = argv[++i]; | |
2861 | } | |
059ec3d9 PH |
2862 | else if (Ustrcmp(argrest, "mad") == 0) |
2863 | { | |
2864 | msg_action = MSG_MARK_ALL_DELIVERED; | |
2865 | } | |
2866 | else if (Ustrcmp(argrest, "md") == 0) | |
2867 | { | |
2868 | msg_action = MSG_MARK_DELIVERED; | |
2869 | one_msg_action = TRUE; | |
2870 | } | |
2871 | else if (Ustrcmp(argrest, "rm") == 0) msg_action = MSG_REMOVE; | |
0ef732d9 PH |
2872 | else if (Ustrcmp(argrest, "set") == 0) |
2873 | { | |
2874 | msg_action = MSG_LOAD; | |
2875 | one_msg_action = TRUE; | |
2876 | } | |
059ec3d9 PH |
2877 | else if (Ustrcmp(argrest, "t") == 0) msg_action = MSG_THAW; |
2878 | else if (Ustrcmp(argrest, "vb") == 0) | |
2879 | { | |
2880 | msg_action = MSG_SHOW_BODY; | |
2881 | one_msg_action = TRUE; | |
2882 | } | |
a96603a0 PH |
2883 | else if (Ustrcmp(argrest, "vc") == 0) |
2884 | { | |
2885 | msg_action = MSG_SHOW_COPY; | |
2886 | one_msg_action = TRUE; | |
2887 | } | |
059ec3d9 PH |
2888 | else if (Ustrcmp(argrest, "vh") == 0) |
2889 | { | |
2890 | msg_action = MSG_SHOW_HEADER; | |
2891 | one_msg_action = TRUE; | |
2892 | } | |
2893 | else if (Ustrcmp(argrest, "vl") == 0) | |
2894 | { | |
2895 | msg_action = MSG_SHOW_LOG; | |
2896 | one_msg_action = TRUE; | |
2897 | } | |
2898 | else { badarg = TRUE; break; } | |
2899 | ||
2900 | /* All the -Mxx options require at least one message id. */ | |
2901 | ||
2902 | msg_action_arg = i + 1; | |
2903 | if (msg_action_arg >= argc) | |
9af3c549 | 2904 | exim_fail("exim: no message ids given after %s option\n", arg); |
059ec3d9 PH |
2905 | |
2906 | /* Some require only message ids to follow */ | |
2907 | ||
2908 | if (!one_msg_action) | |
2909 | { | |
d7978c0f | 2910 | for (int j = msg_action_arg; j < argc; j++) if (!mac_ismsgid(argv[j])) |
9af3c549 | 2911 | exim_fail("exim: malformed message id %s after %s option\n", |
059ec3d9 | 2912 | argv[j], arg); |
059ec3d9 PH |
2913 | goto END_ARG; /* Remaining args are ids */ |
2914 | } | |
2915 | ||
2916 | /* Others require only one message id, possibly followed by addresses, | |
2917 | which will be handled as normal arguments. */ | |
2918 | ||
2919 | else | |
2920 | { | |
2921 | if (!mac_ismsgid(argv[msg_action_arg])) | |
9af3c549 | 2922 | exim_fail("exim: malformed message id %s after %s option\n", |
059ec3d9 | 2923 | argv[msg_action_arg], arg); |
059ec3d9 PH |
2924 | i++; |
2925 | } | |
2926 | break; | |
2927 | ||
2928 | ||
2929 | /* Some programs seem to call the -om option without the leading o; | |
2930 | for sendmail it askes for "me too". Exim always does this. */ | |
2931 | ||
2932 | case 'm': | |
2933 | if (*argrest != 0) badarg = TRUE; | |
2934 | break; | |
2935 | ||
2936 | ||
2937 | /* -N: don't do delivery - a debugging option that stops transports doing | |
2938 | their thing. It implies debugging at the D_v level. */ | |
2939 | ||
2940 | case 'N': | |
2941 | if (*argrest == 0) | |
2942 | { | |
8768d548 | 2943 | f.dont_deliver = TRUE; |
059ec3d9 PH |
2944 | debug_selector |= D_v; |
2945 | debug_file = stderr; | |
2946 | } | |
2947 | else badarg = TRUE; | |
2948 | break; | |
2949 | ||
2950 | ||
12f69989 PP |
2951 | /* -n: This means "don't alias" in sendmail, apparently. |
2952 | For normal invocations, it has no effect. | |
2953 | It may affect some other options. */ | |
059ec3d9 PH |
2954 | |
2955 | case 'n': | |
12f69989 | 2956 | flag_n = TRUE; |
059ec3d9 PH |
2957 | break; |
2958 | ||
2959 | /* -O: Just ignore it. In sendmail, apparently -O option=value means set | |
2960 | option to the specified value. This form uses long names. We need to handle | |
2961 | -O option=value and -Ooption=value. */ | |
2962 | ||
2963 | case 'O': | |
2964 | if (*argrest == 0) | |
059ec3d9 | 2965 | if (++i >= argc) |
9af3c549 | 2966 | exim_fail("exim: string expected after -O\n"); |
059ec3d9 PH |
2967 | break; |
2968 | ||
2969 | case 'o': | |
777cc748 | 2970 | switch (*argrest++) |
059ec3d9 | 2971 | { |
777cc748 JH |
2972 | /* -oA: Set an argument for the bi command (sendmail's "alternate alias |
2973 | file" option). */ | |
2974 | case 'A': | |
2975 | if (!*(alias_arg = argrest)) | |
2976 | if (i+1 < argc) alias_arg = argv[++i]; | |
2977 | else exim_fail("exim: string expected after -oA\n"); | |
2978 | break; | |
2979 | ||
2980 | /* -oB: Set a connection message max value for remote deliveries */ | |
2981 | case 'B': | |
2982 | { | |
2983 | uschar * p = argrest; | |
2984 | if (!*p) | |
2985 | if (i+1 < argc && isdigit((argv[i+1][0]))) | |
2986 | p = argv[++i]; | |
2987 | else | |
2988 | { | |
2989 | connection_max_messages = 1; | |
2990 | p = NULL; | |
2991 | } | |
059ec3d9 | 2992 | |
777cc748 JH |
2993 | if (p) |
2994 | { | |
2995 | if (!isdigit(*p)) | |
2996 | exim_fail("exim: number expected after -oB\n"); | |
2997 | connection_max_messages = Uatoi(p); | |
2998 | } | |
2999 | } | |
3000 | break; | |
3001 | ||
3002 | /* -odb: background delivery */ | |
3003 | ||
3004 | case 'd': | |
3005 | if (Ustrcmp(argrest, "b") == 0) | |
3006 | { | |
3007 | f.synchronous_delivery = FALSE; | |
3008 | arg_queue_only = FALSE; | |
3009 | queue_only_set = TRUE; | |
3010 | } | |
3011 | ||
ff966302 JH |
3012 | /* -odd: testsuite-only: add no inter-process delays */ |
3013 | ||
3014 | else if (Ustrcmp(argrest, "d") == 0) | |
3015 | f.testsuite_delays = FALSE; | |
3016 | ||
777cc748 JH |
3017 | /* -odf: foreground delivery (smail-compatible option); same effect as |
3018 | -odi: interactive (synchronous) delivery (sendmail-compatible option) | |
3019 | */ | |
3020 | ||
3021 | else if (Ustrcmp(argrest, "f") == 0 || Ustrcmp(argrest, "i") == 0) | |
3022 | { | |
3023 | f.synchronous_delivery = TRUE; | |
3024 | arg_queue_only = FALSE; | |
3025 | queue_only_set = TRUE; | |
3026 | } | |
3027 | ||
3028 | /* -odq: queue only */ | |
3029 | ||
3030 | else if (Ustrcmp(argrest, "q") == 0) | |
3031 | { | |
3032 | f.synchronous_delivery = FALSE; | |
3033 | arg_queue_only = TRUE; | |
3034 | queue_only_set = TRUE; | |
3035 | } | |
3036 | ||
3037 | /* -odqs: queue SMTP only - do local deliveries and remote routing, | |
3038 | but no remote delivery */ | |
3039 | ||
3040 | else if (Ustrcmp(argrest, "qs") == 0) | |
3041 | { | |
3042 | f.queue_smtp = TRUE; | |
3043 | arg_queue_only = FALSE; | |
3044 | queue_only_set = TRUE; | |
3045 | } | |
3046 | else badarg = TRUE; | |
3047 | break; | |
3048 | ||
3049 | /* -oex: Sendmail error flags. As these are also accepted without the | |
3050 | leading -o prefix, for compatibility with vacation and other callers, | |
3051 | they are handled with -e above. */ | |
3052 | ||
3053 | /* -oi: Set flag so dot doesn't end non-SMTP input (same as -i) | |
3054 | -oitrue: Another sendmail syntax for the same */ | |
3055 | ||
3056 | case 'i': | |
3057 | if (!*argrest || Ustrcmp(argrest, "true") == 0) | |
3058 | f.dot_ends = FALSE; | |
3059 | else badarg = TRUE; | |
3060 | break; | |
059ec3d9 PH |
3061 | |
3062 | /* -oM*: Set various characteristics for an incoming message; actually | |
3063 | acted on for trusted callers only. */ | |
3064 | ||
777cc748 JH |
3065 | case 'M': |
3066 | { | |
3067 | if (i+1 >= argc) | |
3068 | exim_fail("exim: data expected after -oM%s\n", argrest); | |
059ec3d9 | 3069 | |
777cc748 | 3070 | /* -oMa: Set sender host address */ |
059ec3d9 | 3071 | |
777cc748 | 3072 | if (Ustrcmp(argrest, "a") == 0) sender_host_address = argv[++i]; |
059ec3d9 | 3073 | |
777cc748 | 3074 | /* -oMaa: Set authenticator name */ |
059ec3d9 | 3075 | |
777cc748 JH |
3076 | else if (Ustrcmp(argrest, "aa") == 0) |
3077 | sender_host_authenticated = argv[++i]; | |
059ec3d9 | 3078 | |
777cc748 | 3079 | /* -oMas: setting authenticated sender */ |
059ec3d9 | 3080 | |
777cc748 JH |
3081 | else if (Ustrcmp(argrest, "as") == 0) |
3082 | authenticated_sender = string_copy_taint(argv[++i], TRUE); | |
059ec3d9 | 3083 | |
777cc748 | 3084 | /* -oMai: setting authenticated id */ |
059ec3d9 | 3085 | |
777cc748 JH |
3086 | else if (Ustrcmp(argrest, "ai") == 0) |
3087 | authenticated_id = string_copy_taint(argv[++i], TRUE); | |
059ec3d9 | 3088 | |
777cc748 | 3089 | /* -oMi: Set incoming interface address */ |
d2af03f4 | 3090 | |
777cc748 | 3091 | else if (Ustrcmp(argrest, "i") == 0) interface_address = argv[++i]; |
d2af03f4 | 3092 | |
777cc748 | 3093 | /* -oMm: Message reference */ |
059ec3d9 | 3094 | |
777cc748 JH |
3095 | else if (Ustrcmp(argrest, "m") == 0) |
3096 | { | |
3097 | if (!mac_ismsgid(argv[i+1])) | |
3098 | exim_fail("-oMm must be a valid message ID\n"); | |
3099 | if (!f.trusted_config) | |
3100 | exim_fail("-oMm must be called by a trusted user/config\n"); | |
3101 | message_reference = argv[++i]; | |
3102 | } | |
059ec3d9 | 3103 | |
777cc748 | 3104 | /* -oMr: Received protocol */ |
059ec3d9 | 3105 | |
777cc748 | 3106 | else if (Ustrcmp(argrest, "r") == 0) |
059ec3d9 | 3107 | |
777cc748 JH |
3108 | if (received_protocol) |
3109 | exim_fail("received_protocol is set already\n"); | |
3110 | else | |
3111 | received_protocol = argv[++i]; | |
059ec3d9 | 3112 | |
777cc748 | 3113 | /* -oMs: Set sender host name */ |
059ec3d9 | 3114 | |
777cc748 JH |
3115 | else if (Ustrcmp(argrest, "s") == 0) |
3116 | sender_host_name = string_copy_taint(argv[++i], TRUE); | |
059ec3d9 | 3117 | |
777cc748 | 3118 | /* -oMt: Set sender ident */ |
059ec3d9 | 3119 | |
777cc748 JH |
3120 | else if (Ustrcmp(argrest, "t") == 0) |
3121 | { | |
3122 | sender_ident_set = TRUE; | |
3123 | sender_ident = argv[++i]; | |
3124 | } | |
059ec3d9 | 3125 | |
777cc748 | 3126 | /* Else a bad argument */ |
059ec3d9 | 3127 | |
777cc748 JH |
3128 | else |
3129 | badarg = TRUE; | |
3130 | } | |
3131 | break; | |
3132 | ||
3133 | /* -om: Me-too flag for aliases. Exim always does this. Some programs | |
3134 | seem to call this as -m (undocumented), so that is also accepted (see | |
3135 | above). */ | |
3136 | /* -oo: An ancient flag for old-style addresses which still seems to | |
3137 | crop up in some calls (see in SCO). */ | |
3138 | ||
3139 | case 'm': | |
3140 | case 'o': | |
305e4fae | 3141 | if (*argrest) badarg = TRUE; |
777cc748 JH |
3142 | break; |
3143 | ||
3144 | /* -oP <name>: set pid file path for daemon | |
3145 | -oPX: delete pid file of daemon */ | |
3146 | ||
3147 | case 'P': | |
3148 | if (!*argrest) override_pid_file_path = argv[++i]; | |
3149 | else if (Ustrcmp(argrest, "X") == 0) delete_pid_file(); | |
3150 | else badarg = TRUE; | |
3151 | break; | |
3152 | ||
3153 | ||
3154 | /* -or <n>: set timeout for non-SMTP acceptance | |
3155 | -os <n>: set timeout for SMTP acceptance */ | |
3156 | ||
3157 | case 'r': | |
3158 | case 's': | |
305e4fae HSHR |
3159 | { |
3160 | int * tp = argrest[-1] == 'r' | |
3161 | ? &arg_receive_timeout : &arg_smtp_receive_timeout; | |
3162 | if (*argrest) | |
3163 | *tp = readconf_readtime(argrest, 0, FALSE); | |
3164 | else if (i+1 < argc) | |
3165 | *tp = readconf_readtime(argv[++i], 0, FALSE); | |
3166 | ||
3167 | if (*tp < 0) | |
3168 | exim_fail("exim: bad time value %s: abandoned\n", argv[i]); | |
3169 | } | |
777cc748 | 3170 | break; |
059ec3d9 | 3171 | |
777cc748 | 3172 | /* -oX <list>: Override local_interfaces and/or default daemon ports */ |
059ec3d9 | 3173 | |
777cc748 | 3174 | case 'X': |
305e4fae HSHR |
3175 | if (*argrest) badarg = TRUE; |
3176 | else override_local_interfaces = argv[++i]; | |
777cc748 | 3177 | break; |
01446a56 | 3178 | |
777cc748 | 3179 | /* Unknown -o argument */ |
059ec3d9 | 3180 | |
777cc748 JH |
3181 | default: |
3182 | badarg = TRUE; | |
059ec3d9 | 3183 | } |
059ec3d9 PH |
3184 | break; |
3185 | ||
3186 | ||
3187 | /* -ps: force Perl startup; -pd force delayed Perl startup */ | |
3188 | ||
3189 | case 'p': | |
3190 | #ifdef EXIM_PERL | |
3191 | if (*argrest == 's' && argrest[1] == 0) | |
3192 | { | |
3193 | perl_start_option = 1; | |
3194 | break; | |
3195 | } | |
3196 | if (*argrest == 'd' && argrest[1] == 0) | |
3197 | { | |
3198 | perl_start_option = -1; | |
3199 | break; | |
3200 | } | |
3201 | #endif | |
3202 | ||
3203 | /* -panythingelse is taken as the Sendmail-compatible argument -prval:sval, | |
3204 | which sets the host protocol and host name */ | |
3205 | ||
3206 | if (*argrest == 0) | |
5bfe3b35 JH |
3207 | if (i+1 < argc) |
3208 | argrest = argv[++i]; | |
3209 | else | |
059ec3d9 | 3210 | { badarg = TRUE; break; } |
059ec3d9 PH |
3211 | |
3212 | if (*argrest != 0) | |
3213 | { | |
65e061b7 HSHR |
3214 | uschar *hn; |
3215 | ||
3216 | if (received_protocol) | |
9af3c549 | 3217 | exim_fail("received_protocol is set already\n"); |
65e061b7 HSHR |
3218 | |
3219 | hn = Ustrchr(argrest, ':'); | |
059ec3d9 | 3220 | if (hn == NULL) |
059ec3d9 | 3221 | received_protocol = argrest; |
059ec3d9 PH |
3222 | else |
3223 | { | |
90341c71 JH |
3224 | int old_pool = store_pool; |
3225 | store_pool = POOL_PERM; | |
059ec3d9 | 3226 | received_protocol = string_copyn(argrest, hn - argrest); |
90341c71 | 3227 | store_pool = old_pool; |
059ec3d9 PH |
3228 | sender_host_name = hn + 1; |
3229 | } | |
3230 | } | |
3231 | break; | |
3232 | ||
3233 | ||
3234 | case 'q': | |
3235 | receiving_message = FALSE; | |
3cc66b45 | 3236 | if (queue_interval >= 0) |
9af3c549 | 3237 | exim_fail("exim: -q specified more than once\n"); |
059ec3d9 PH |
3238 | |
3239 | /* -qq...: Do queue runs in a 2-stage manner */ | |
3240 | ||
3241 | if (*argrest == 'q') | |
3242 | { | |
8768d548 | 3243 | f.queue_2stage = TRUE; |
059ec3d9 PH |
3244 | argrest++; |
3245 | } | |
3246 | ||
3247 | /* -qi...: Do only first (initial) deliveries */ | |
3248 | ||
3249 | if (*argrest == 'i') | |
3250 | { | |
8768d548 | 3251 | f.queue_run_first_delivery = TRUE; |
059ec3d9 PH |
3252 | argrest++; |
3253 | } | |
3254 | ||
3255 | /* -qf...: Run the queue, forcing deliveries | |
3256 | -qff..: Ditto, forcing thawing as well */ | |
3257 | ||
3258 | if (*argrest == 'f') | |
3259 | { | |
8768d548 | 3260 | f.queue_run_force = TRUE; |
55e70e76 | 3261 | if (*++argrest == 'f') |
059ec3d9 | 3262 | { |
8768d548 | 3263 | f.deliver_force_thaw = TRUE; |
059ec3d9 PH |
3264 | argrest++; |
3265 | } | |
3266 | } | |
3267 | ||
3268 | /* -q[f][f]l...: Run the queue only on local deliveries */ | |
3269 | ||
3270 | if (*argrest == 'l') | |
3271 | { | |
8768d548 | 3272 | f.queue_run_local = TRUE; |
059ec3d9 PH |
3273 | argrest++; |
3274 | } | |
3275 | ||
55e70e76 | 3276 | /* -q[f][f][l][G<name>]... Work on the named queue */ |
28b3821f JH |
3277 | |
3278 | if (*argrest == 'G') | |
3279 | { | |
fa665e0b JH |
3280 | int i; |
3281 | for (argrest++, i = 0; argrest[i] && argrest[i] != '/'; ) i++; | |
3282 | queue_name = string_copyn(argrest, i); | |
3283 | argrest += i; | |
3284 | if (*argrest == '/') argrest++; | |
28b3821f JH |
3285 | } |
3286 | ||
3287 | /* -q[f][f][l][G<name>]: Run the queue, optionally forced, optionally local | |
3288 | only, optionally named, optionally starting from a given message id. */ | |
059ec3d9 | 3289 | |
e5903596 JH |
3290 | if (!(list_queue || count_queue)) |
3291 | if (*argrest == 0 | |
3292 | && (i + 1 >= argc || argv[i+1][0] == '-' || mac_ismsgid(argv[i+1]))) | |
3293 | { | |
3294 | queue_interval = 0; | |
3295 | if (i+1 < argc && mac_ismsgid(argv[i+1])) | |
3296 | start_queue_run_id = argv[++i]; | |
3297 | if (i+1 < argc && mac_ismsgid(argv[i+1])) | |
3298 | stop_queue_run_id = argv[++i]; | |
3299 | } | |
059ec3d9 | 3300 | |
fa665e0b JH |
3301 | /* -q[f][f][l][G<name>/]<n>: Run the queue at regular intervals, optionally |
3302 | forced, optionally local only, optionally named. */ | |
059ec3d9 | 3303 | |
e5903596 JH |
3304 | else if ((queue_interval = readconf_readtime(*argrest ? argrest : argv[++i], |
3305 | 0, FALSE)) <= 0) | |
3306 | exim_fail("exim: bad time value %s: abandoned\n", argv[i]); | |
059ec3d9 PH |
3307 | break; |
3308 | ||
3309 | ||
3310 | case 'R': /* Synonymous with -qR... */ | |
3311 | receiving_message = FALSE; | |
3312 | ||
3313 | /* -Rf: As -R (below) but force all deliveries, | |
3314 | -Rff: Ditto, but also thaw all frozen messages, | |
3315 | -Rr: String is regex | |
3316 | -Rrf: Regex and force | |
3317 | -Rrff: Regex and force and thaw | |
3318 | ||
3319 | in all cases provided there are no further characters in this | |
3320 | argument. */ | |
3321 | ||
3322 | if (*argrest != 0) | |
d7978c0f | 3323 | for (int i = 0; i < nelem(rsopts); i++) |
059ec3d9 PH |
3324 | if (Ustrcmp(argrest, rsopts[i]) == 0) |
3325 | { | |
8768d548 JH |
3326 | if (i != 2) f.queue_run_force = TRUE; |
3327 | if (i >= 2) f.deliver_selectstring_regex = TRUE; | |
3328 | if (i == 1 || i == 4) f.deliver_force_thaw = TRUE; | |
059ec3d9 PH |
3329 | argrest += Ustrlen(rsopts[i]); |
3330 | } | |
059ec3d9 PH |
3331 | |
3332 | /* -R: Set string to match in addresses for forced queue run to | |
3333 | pick out particular messages. */ | |
3334 | ||
55e70e76 JH |
3335 | if (*argrest) |
3336 | deliver_selectstring = argrest; | |
3337 | else if (i+1 < argc) | |
3338 | deliver_selectstring = argv[++i]; | |
3339 | else | |
9af3c549 | 3340 | exim_fail("exim: string expected after -R\n"); |
059ec3d9 PH |
3341 | break; |
3342 | ||
3343 | ||
3344 | /* -r: an obsolete synonym for -f (see above) */ | |
3345 | ||
3346 | ||
3347 | /* -S: Like -R but works on sender. */ | |
3348 | ||
3349 | case 'S': /* Synonymous with -qS... */ | |
3350 | receiving_message = FALSE; | |
3351 | ||
3352 | /* -Sf: As -S (below) but force all deliveries, | |
3353 | -Sff: Ditto, but also thaw all frozen messages, | |
3354 | -Sr: String is regex | |
3355 | -Srf: Regex and force | |
3356 | -Srff: Regex and force and thaw | |
3357 | ||
3358 | in all cases provided there are no further characters in this | |
3359 | argument. */ | |
3360 | ||
55e70e76 | 3361 | if (*argrest) |
d7978c0f | 3362 | for (int i = 0; i < nelem(rsopts); i++) |
059ec3d9 PH |
3363 | if (Ustrcmp(argrest, rsopts[i]) == 0) |
3364 | { | |
8768d548 JH |
3365 | if (i != 2) f.queue_run_force = TRUE; |
3366 | if (i >= 2) f.deliver_selectstring_sender_regex = TRUE; | |
3367 | if (i == 1 || i == 4) f.deliver_force_thaw = TRUE; | |
059ec3d9 PH |
3368 | argrest += Ustrlen(rsopts[i]); |
3369 | } | |
059ec3d9 PH |
3370 | |
3371 | /* -S: Set string to match in addresses for forced queue run to | |
3372 | pick out particular messages. */ | |
3373 | ||
55e70e76 JH |
3374 | if (*argrest) |
3375 | deliver_selectstring_sender = argrest; | |
3376 | else if (i+1 < argc) | |
3377 | deliver_selectstring_sender = argv[++i]; | |
3378 | else | |
9af3c549 | 3379 | exim_fail("exim: string expected after -S\n"); |
059ec3d9 PH |
3380 | break; |
3381 | ||
3382 | /* -Tqt is an option that is exclusively for use by the testing suite. | |
3383 | It is not recognized in other circumstances. It allows for the setting up | |
3384 | of explicit "queue times" so that various warning/retry things can be | |
3385 | tested. Otherwise variability of clock ticks etc. cause problems. */ | |
3386 | ||
3387 | case 'T': | |
8768d548 | 3388 | if (f.running_in_test_harness && Ustrcmp(argrest, "qt") == 0) |
059ec3d9 PH |
3389 | fudged_queue_times = argv[++i]; |
3390 | else badarg = TRUE; | |
3391 | break; | |
3392 | ||
3393 | ||
3394 | /* -t: Set flag to extract recipients from body of message. */ | |
3395 | ||
3396 | case 't': | |
3397 | if (*argrest == 0) extract_recipients = TRUE; | |
3398 | ||
3399 | /* -ti: Set flag to extract recipients from body of message, and also | |
3400 | specify that dot does not end the message. */ | |
3401 | ||
3402 | else if (Ustrcmp(argrest, "i") == 0) | |
3403 | { | |
3404 | extract_recipients = TRUE; | |
8768d548 | 3405 | f.dot_ends = FALSE; |
059ec3d9 PH |
3406 | } |
3407 | ||
3408 | /* -tls-on-connect: don't wait for STARTTLS (for old clients) */ | |
3409 | ||
01603eec | 3410 | #ifndef DISABLE_TLS |
817d9f57 | 3411 | else if (Ustrcmp(argrest, "ls-on-connect") == 0) tls_in.on_connect = TRUE; |
059ec3d9 PH |
3412 | #endif |
3413 | ||
3414 | else badarg = TRUE; | |
3415 | break; | |
3416 | ||
3417 | ||
3418 | /* -U: This means "initial user submission" in sendmail, apparently. The | |
3419 | doc claims that in future sendmail may refuse syntactically invalid | |
3420 | messages instead of fixing them. For the moment, we just ignore it. */ | |
3421 | ||
3422 | case 'U': | |
3423 | break; | |
3424 | ||
3425 | ||
3426 | /* -v: verify things - this is a very low-level debugging */ | |
3427 | ||
3428 | case 'v': | |
3429 | if (*argrest == 0) | |
3430 | { | |
3431 | debug_selector |= D_v; | |
3432 | debug_file = stderr; | |
3433 | } | |
3434 | else badarg = TRUE; | |
3435 | break; | |
3436 | ||
3437 | ||
3438 | /* -x: AIX uses this to indicate some fancy 8-bit character stuff: | |
3439 | ||
3440 | The -x flag tells the sendmail command that mail from a local | |
3441 | mail program has National Language Support (NLS) extended characters | |
3442 | in the body of the mail item. The sendmail command can send mail with | |
3443 | extended NLS characters across networks that normally corrupts these | |
3444 | 8-bit characters. | |
3445 | ||
3446 | As Exim is 8-bit clean, it just ignores this flag. */ | |
3447 | ||
3448 | case 'x': | |
3449 | if (*argrest != 0) badarg = TRUE; | |
3450 | break; | |
3451 | ||
a3fb9793 PP |
3452 | /* -X: in sendmail: takes one parameter, logfile, and sends debugging |
3453 | logs to that file. We swallow the parameter and otherwise ignore it. */ | |
3454 | ||
3455 | case 'X': | |
3456 | if (*argrest == '\0') | |
a3fb9793 | 3457 | if (++i >= argc) |
9af3c549 | 3458 | exim_fail("exim: string expected after -X\n"); |
0ad2e0fc JH |
3459 | break; |
3460 | ||
3461 | case 'z': | |
3462 | if (*argrest == '\0') | |
9af3c549 JH |
3463 | if (++i < argc) |
3464 | log_oneline = argv[i]; | |
3465 | else | |
3466 | exim_fail("exim: file name expected after %s\n", argv[i-1]); | |
a3fb9793 PP |
3467 | break; |
3468 | ||
059ec3d9 PH |
3469 | /* All other initial characters are errors */ |
3470 | ||
3471 | default: | |
3472 | badarg = TRUE; | |
3473 | break; | |
3474 | } /* End of high-level switch statement */ | |
3475 | ||
3476 | /* Failed to recognize the option, or syntax error */ | |
3477 | ||
3478 | if (badarg) | |
9af3c549 | 3479 | exim_fail("exim abandoned: unknown, malformed, or incomplete " |
059ec3d9 | 3480 | "option %s\n", arg); |
059ec3d9 PH |
3481 | } |
3482 | ||
3483 | ||
3cc66b45 PH |
3484 | /* If -R or -S have been specified without -q, assume a single queue run. */ |
3485 | ||
55e70e76 JH |
3486 | if ( (deliver_selectstring || deliver_selectstring_sender) |
3487 | && queue_interval < 0) | |
3488 | queue_interval = 0; | |
3cc66b45 PH |
3489 | |
3490 | ||
059ec3d9 | 3491 | END_ARG: |
81ea09ca NM |
3492 | /* If usage_wanted is set we call the usage function - which never returns */ |
3493 | if (usage_wanted) exim_usage(called_as); | |
3494 | ||
3495 | /* Arguments have been processed. Check for incompatibilities. */ | |
059ec3d9 PH |
3496 | if (( |
3497 | (smtp_input || extract_recipients || recipients_arg < argc) && | |
8768d548 | 3498 | (f.daemon_listen || queue_interval >= 0 || bi_option || |
059ec3d9 | 3499 | test_retry_arg >= 0 || test_rewrite_arg >= 0 || |
f05da2e8 | 3500 | filter_test != FTEST_NONE || (msg_action_arg > 0 && !one_msg_action)) |
059ec3d9 PH |
3501 | ) || |
3502 | ( | |
3503 | msg_action_arg > 0 && | |
8768d548 | 3504 | (f.daemon_listen || queue_interval > 0 || list_options || |
0ef732d9 PH |
3505 | (checking && msg_action != MSG_LOAD) || |
3506 | bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0) | |
059ec3d9 PH |
3507 | ) || |
3508 | ( | |
8768d548 | 3509 | (f.daemon_listen || queue_interval > 0) && |
059ec3d9 | 3510 | (sender_address != NULL || list_options || list_queue || checking || |
0ef732d9 | 3511 | bi_option) |
059ec3d9 PH |
3512 | ) || |
3513 | ( | |
8768d548 | 3514 | f.daemon_listen && queue_interval == 0 |
059ec3d9 PH |
3515 | ) || |
3516 | ( | |
8768d548 | 3517 | f.inetd_wait_mode && queue_interval >= 0 |
9ee44efb PP |
3518 | ) || |
3519 | ( | |
059ec3d9 PH |
3520 | list_options && |
3521 | (checking || smtp_input || extract_recipients || | |
f05da2e8 | 3522 | filter_test != FTEST_NONE || bi_option) |
059ec3d9 PH |
3523 | ) || |
3524 | ( | |
3525 | verify_address_mode && | |
8768d548 | 3526 | (f.address_test_mode || smtp_input || extract_recipients || |
f05da2e8 | 3527 | filter_test != FTEST_NONE || bi_option) |
059ec3d9 PH |
3528 | ) || |
3529 | ( | |
8768d548 | 3530 | f.address_test_mode && (smtp_input || extract_recipients || |
f05da2e8 | 3531 | filter_test != FTEST_NONE || bi_option) |
059ec3d9 PH |
3532 | ) || |
3533 | ( | |
f05da2e8 | 3534 | smtp_input && (sender_address != NULL || filter_test != FTEST_NONE || |
059ec3d9 PH |
3535 | extract_recipients) |
3536 | ) || | |
3537 | ( | |
3538 | deliver_selectstring != NULL && queue_interval < 0 | |
328895cc PH |
3539 | ) || |
3540 | ( | |
3541 | msg_action == MSG_LOAD && | |
3542 | (!expansion_test || expansion_test_message != NULL) | |
059ec3d9 PH |
3543 | ) |
3544 | ) | |
9af3c549 | 3545 | exim_fail("exim: incompatible command-line options or arguments\n"); |
059ec3d9 PH |
3546 | |
3547 | /* If debugging is set up, set the file and the file descriptor to pass on to | |
3548 | child processes. It should, of course, be 2 for stderr. Also, force the daemon | |
3549 | to run in the foreground. */ | |
3550 | ||
3551 | if (debug_selector != 0) | |
3552 | { | |
3553 | debug_file = stderr; | |
3554 | debug_fd = fileno(debug_file); | |
8768d548 | 3555 | f.background_daemon = FALSE; |
9f01e50d | 3556 | testharness_pause_ms(100); /* lets caller finish */ |
059ec3d9 PH |
3557 | if (debug_selector != D_v) /* -v only doesn't show this */ |
3558 | { | |
3559 | debug_printf("Exim version %s uid=%ld gid=%ld pid=%d D=%x\n", | |
3560 | version_string, (long int)real_uid, (long int)real_gid, (int)getpid(), | |
3561 | debug_selector); | |
6545de78 PP |
3562 | if (!version_printed) |
3563 | show_whats_supported(stderr); | |
059ec3d9 PH |
3564 | } |
3565 | } | |
3566 | ||
3567 | /* When started with root privilege, ensure that the limits on the number of | |
3568 | open files and the number of processes (where that is accessible) are | |
3569 | sufficiently large, or are unset, in case Exim has been called from an | |
3570 | environment where the limits are screwed down. Not all OS have the ability to | |
3571 | change some of these limits. */ | |
3572 | ||
3573 | if (unprivileged) | |
3574 | { | |
3575 | DEBUG(D_any) debug_print_ids(US"Exim has no root privilege:"); | |
3576 | } | |
3577 | else | |
3578 | { | |
3579 | struct rlimit rlp; | |
3580 | ||
3581 | #ifdef RLIMIT_NOFILE | |
3582 | if (getrlimit(RLIMIT_NOFILE, &rlp) < 0) | |
3583 | { | |
3584 | log_write(0, LOG_MAIN|LOG_PANIC, "getrlimit(RLIMIT_NOFILE) failed: %s", | |
3585 | strerror(errno)); | |
3586 | rlp.rlim_cur = rlp.rlim_max = 0; | |
3587 | } | |
eb2c0248 PH |
3588 | |
3589 | /* I originally chose 1000 as a nice big number that was unlikely to | |
a494b1e1 PH |
3590 | be exceeded. It turns out that some older OS have a fixed upper limit of |
3591 | 256. */ | |
eb2c0248 | 3592 | |
059ec3d9 PH |
3593 | if (rlp.rlim_cur < 1000) |
3594 | { | |
3595 | rlp.rlim_cur = rlp.rlim_max = 1000; | |
3596 | if (setrlimit(RLIMIT_NOFILE, &rlp) < 0) | |
eb2c0248 | 3597 | { |
a494b1e1 PH |
3598 | rlp.rlim_cur = rlp.rlim_max = 256; |
3599 | if (setrlimit(RLIMIT_NOFILE, &rlp) < 0) | |
3600 | log_write(0, LOG_MAIN|LOG_PANIC, "setrlimit(RLIMIT_NOFILE) failed: %s", | |
3601 | strerror(errno)); | |
eb2c0248 | 3602 | } |
059ec3d9 PH |
3603 | } |
3604 | #endif | |
3605 | ||
3606 | #ifdef RLIMIT_NPROC | |
3607 | if (getrlimit(RLIMIT_NPROC, &rlp) < 0) | |
3608 | { | |
3609 | log_write(0, LOG_MAIN|LOG_PANIC, "getrlimit(RLIMIT_NPROC) failed: %s", | |
3610 | strerror(errno)); | |
3611 | rlp.rlim_cur = rlp.rlim_max = 0; | |
3612 | } | |
3613 | ||
3614 | #ifdef RLIM_INFINITY | |
3615 | if (rlp.rlim_cur != RLIM_INFINITY && rlp.rlim_cur < 1000) | |
3616 | { | |
3617 | rlp.rlim_cur = rlp.rlim_max = RLIM_INFINITY; | |
3618 | #else | |
3619 | if (rlp.rlim_cur < 1000) | |
3620 | { | |
3621 | rlp.rlim_cur = rlp.rlim_max = 1000; | |
3622 | #endif | |
3623 | if (setrlimit(RLIMIT_NPROC, &rlp) < 0) | |
3624 | log_write(0, LOG_MAIN|LOG_PANIC, "setrlimit(RLIMIT_NPROC) failed: %s", | |
3625 | strerror(errno)); | |
3626 | } | |
3627 | #endif | |
3628 | } | |
3629 | ||
3630 | /* Exim is normally entered as root (but some special configurations are | |
3631 | possible that don't do this). However, it always spins off sub-processes that | |
3632 | set their uid and gid as required for local delivery. We don't want to pass on | |
3633 | any extra groups that root may belong to, so we want to get rid of them all at | |
3634 | this point. | |
3635 | ||
3636 | We need to obey setgroups() at this stage, before possibly giving up root | |
3637 | privilege for a changed configuration file, but later on we might need to | |
3638 | check on the additional groups for the admin user privilege - can't do that | |
3639 | till after reading the config, which might specify the exim gid. Therefore, | |
3640 | save the group list here first. */ | |
3641 | ||
157d73b5 | 3642 | if ((group_count = getgroups(nelem(group_list), group_list)) < 0) |
9af3c549 | 3643 | exim_fail("exim: getgroups() failed: %s\n", strerror(errno)); |
059ec3d9 PH |
3644 | |
3645 | /* There is a fundamental difference in some BSD systems in the matter of | |
3646 | groups. FreeBSD and BSDI are known to be different; NetBSD and OpenBSD are | |
3647 | known not to be different. On the "different" systems there is a single group | |
3648 | list, and the first entry in it is the current group. On all other versions of | |
3649 | Unix there is a supplementary group list, which is in *addition* to the current | |
3650 | group. Consequently, to get rid of all extraneous groups on a "standard" system | |
3651 | you pass over 0 groups to setgroups(), while on a "different" system you pass | |
3652 | over a single group - the current group, which is always the first group in the | |
3653 | list. Calling setgroups() with zero groups on a "different" system results in | |
3654 | an error return. The following code should cope with both types of system. | |
3655 | ||