Print this page
6136 sysmacros.h unnecessarily polutes the namespace
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/sendmail/src/queue.c
+++ new/usr/src/cmd/sendmail/src/queue.c
1 1 /*
2 2 * Copyright (c) 1998-2009 Sendmail, Inc. and its suppliers.
3 3 * All rights reserved.
4 4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
5 5 * Copyright (c) 1988, 1993
6 6 * The Regents of the University of California. All rights reserved.
7 7 *
8 8 * By using this file, you agree to the terms and conditions set
↓ open down ↓ |
8 lines elided |
↑ open up ↑ |
9 9 * forth in the LICENSE file which can be found at the top level of
10 10 * the sendmail distribution.
11 11 *
12 12 */
13 13
14 14 #include <sendmail.h>
15 15 #include <sm/sem.h>
16 16
17 17 SM_RCSID("@(#)$Id: queue.c,v 8.987 2009/12/18 17:08:01 ca Exp $")
18 18
19 +#include <sys/types.h>
20 +#include <sys/mkdev.h>
19 21 #include <dirent.h>
20 22
21 23 # define RELEASE_QUEUE (void) 0
22 24 # define ST_INODE(st) (st).st_ino
23 25
24 26 # define sm_file_exists(errno) ((errno) == EEXIST)
25 27
26 28 # if HASFLOCK && defined(O_EXLOCK)
27 29 # define SM_OPEN_EXLOCK 1
28 30 # define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK)
29 31 # else /* HASFLOCK && defined(O_EXLOCK) */
30 32 # define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL)
31 33 # endif /* HASFLOCK && defined(O_EXLOCK) */
32 34
33 35 #ifndef SM_OPEN_EXLOCK
34 36 # define SM_OPEN_EXLOCK 0
35 37 #endif /* ! SM_OPEN_EXLOCK */
36 38
37 39 /*
38 40 ** Historical notes:
39 41 ** QF_VERSION == 4 was sendmail 8.10/8.11 without _FFR_QUEUEDELAY
40 42 ** QF_VERSION == 5 was sendmail 8.10/8.11 with _FFR_QUEUEDELAY
41 43 ** QF_VERSION == 6 was sendmail 8.12 without _FFR_QUEUEDELAY
42 44 ** QF_VERSION == 7 was sendmail 8.12 with _FFR_QUEUEDELAY
43 45 ** QF_VERSION == 8 is sendmail 8.13
44 46 */
45 47
46 48 #define QF_VERSION 8 /* version number of this queue format */
47 49
48 50 static char queue_letter __P((ENVELOPE *, int));
49 51 static bool quarantine_queue_item __P((int, int, ENVELOPE *, char *));
50 52
51 53 /* Naming convention: qgrp: index of queue group, qg: QUEUEGROUP */
52 54
53 55 /*
54 56 ** Work queue.
55 57 */
56 58
57 59 struct work
58 60 {
59 61 char *w_name; /* name of control file */
60 62 char *w_host; /* name of recipient host */
61 63 bool w_lock; /* is message locked? */
62 64 bool w_tooyoung; /* is it too young to run? */
63 65 long w_pri; /* priority of message, see below */
64 66 time_t w_ctime; /* creation time */
65 67 time_t w_mtime; /* modification time */
66 68 int w_qgrp; /* queue group located in */
67 69 int w_qdir; /* queue directory located in */
68 70 struct work *w_next; /* next in queue */
69 71 };
70 72
71 73 typedef struct work WORK;
72 74
73 75 static WORK *WorkQ; /* queue of things to be done */
74 76 static int NumWorkGroups; /* number of work groups */
75 77 static time_t Current_LA_time = 0;
76 78
77 79 /* Get new load average every 30 seconds. */
78 80 #define GET_NEW_LA_TIME 30
79 81
80 82 #define SM_GET_LA(now) \
81 83 do \
82 84 { \
83 85 now = curtime(); \
84 86 if (Current_LA_time < now - GET_NEW_LA_TIME) \
85 87 { \
86 88 sm_getla(); \
87 89 Current_LA_time = now; \
88 90 } \
89 91 } while (0)
90 92
91 93 /*
92 94 ** DoQueueRun indicates that a queue run is needed.
93 95 ** Notice: DoQueueRun is modified in a signal handler!
94 96 */
95 97
96 98 static bool volatile DoQueueRun; /* non-interrupt time queue run needed */
97 99
98 100 /*
99 101 ** Work group definition structure.
100 102 ** Each work group contains one or more queue groups. This is done
101 103 ** to manage the number of queue group runners active at the same time
102 104 ** to be within the constraints of MaxQueueChildren (if it is set).
103 105 ** The number of queue groups that can be run on the next work run
104 106 ** is kept track of. The queue groups are run in a round robin.
105 107 */
106 108
107 109 struct workgrp
108 110 {
109 111 int wg_numqgrp; /* number of queue groups in work grp */
110 112 int wg_runners; /* total runners */
111 113 int wg_curqgrp; /* current queue group */
112 114 QUEUEGRP **wg_qgs; /* array of queue groups */
113 115 int wg_maxact; /* max # of active runners */
114 116 time_t wg_lowqintvl; /* lowest queue interval */
115 117 int wg_restart; /* needs restarting? */
116 118 int wg_restartcnt; /* count of times restarted */
117 119 };
118 120
119 121 typedef struct workgrp WORKGRP;
120 122
121 123 static WORKGRP volatile WorkGrp[MAXWORKGROUPS + 1]; /* work groups */
122 124
123 125 #if SM_HEAP_CHECK
124 126 static SM_DEBUG_T DebugLeakQ = SM_DEBUG_INITIALIZER("leak_q",
125 127 "@(#)$Debug: leak_q - trace memory leaks during queue processing $");
126 128 #endif /* SM_HEAP_CHECK */
127 129
128 130 /*
129 131 ** We use EmptyString instead of "" to avoid
130 132 ** 'zero-length format string' warnings from gcc
131 133 */
132 134
133 135 static const char EmptyString[] = "";
134 136
135 137 static void grow_wlist __P((int, int));
136 138 static int multiqueue_cache __P((char *, int, QUEUEGRP *, int, unsigned int *));
137 139 static int gatherq __P((int, int, bool, bool *, bool *, int *));
138 140 static int sortq __P((int));
139 141 static void printctladdr __P((ADDRESS *, SM_FILE_T *));
140 142 static bool readqf __P((ENVELOPE *, bool));
141 143 static void restart_work_group __P((int));
142 144 static void runner_work __P((ENVELOPE *, int, bool, int, int));
143 145 static void schedule_queue_runs __P((bool, int, bool));
144 146 static char *strrev __P((char *));
145 147 static ADDRESS *setctluser __P((char *, int, ENVELOPE *));
146 148 #if _FFR_RHS
147 149 static int sm_strshufflecmp __P((char *, char *));
148 150 static void init_shuffle_alphabet __P(());
149 151 #endif /* _FFR_RHS */
150 152
151 153 /*
152 154 ** Note: workcmpf?() don't use a prototype because it will cause a conflict
153 155 ** with the qsort() call (which expects something like
154 156 ** int (*compar)(const void *, const void *), not (WORK *, WORK *))
155 157 */
156 158
157 159 static int workcmpf0();
158 160 static int workcmpf1();
159 161 static int workcmpf2();
160 162 static int workcmpf3();
161 163 static int workcmpf4();
162 164 static int randi = 3; /* index for workcmpf5() */
163 165 static int workcmpf5();
164 166 static int workcmpf6();
165 167 #if _FFR_RHS
166 168 static int workcmpf7();
167 169 #endif /* _FFR_RHS */
168 170
169 171 #if RANDOMSHIFT
170 172 # define get_rand_mod(m) ((get_random() >> RANDOMSHIFT) % (m))
171 173 #else /* RANDOMSHIFT */
172 174 # define get_rand_mod(m) (get_random() % (m))
173 175 #endif /* RANDOMSHIFT */
174 176
175 177 /*
176 178 ** File system definition.
177 179 ** Used to keep track of how much free space is available
178 180 ** on a file system in which one or more queue directories reside.
179 181 */
180 182
181 183 typedef struct filesys_shared FILESYS;
182 184
183 185 struct filesys_shared
184 186 {
185 187 dev_t fs_dev; /* unique device id */
186 188 long fs_avail; /* number of free blocks available */
187 189 long fs_blksize; /* block size, in bytes */
188 190 };
189 191
190 192 /* probably kept in shared memory */
191 193 static FILESYS FileSys[MAXFILESYS]; /* queue file systems */
192 194 static const char *FSPath[MAXFILESYS]; /* pathnames for file systems */
193 195
194 196 #if SM_CONF_SHM
195 197
196 198 /*
197 199 ** Shared memory data
198 200 **
199 201 ** Current layout:
200 202 ** size -- size of shared memory segment
201 203 ** pid -- pid of owner, should be a unique id to avoid misinterpretations
202 204 ** by other processes.
203 205 ** tag -- should be a unique id to avoid misinterpretations by others.
204 206 ** idea: hash over configuration data that will be stored here.
205 207 ** NumFileSys -- number of file systems.
206 208 ** FileSys -- (arrary of) structure for used file systems.
207 209 ** RSATmpCnt -- counter for number of uses of ephemeral RSA key.
208 210 ** QShm -- (array of) structure for information about queue directories.
209 211 */
210 212
211 213 /*
212 214 ** Queue data in shared memory
213 215 */
214 216
215 217 typedef struct queue_shared QUEUE_SHM_T;
216 218
217 219 struct queue_shared
218 220 {
219 221 int qs_entries; /* number of entries */
220 222 /* XXX more to follow? */
221 223 };
222 224
223 225 static void *Pshm; /* pointer to shared memory */
224 226 static FILESYS *PtrFileSys; /* pointer to queue file system array */
225 227 int ShmId = SM_SHM_NO_ID; /* shared memory id */
226 228 static QUEUE_SHM_T *QShm; /* pointer to shared queue data */
227 229 static size_t shms;
228 230
229 231 # define SHM_OFF_PID(p) (((char *) (p)) + sizeof(int))
230 232 # define SHM_OFF_TAG(p) (((char *) (p)) + sizeof(pid_t) + sizeof(int))
231 233 # define SHM_OFF_HEAD (sizeof(pid_t) + sizeof(int) * 2)
232 234
233 235 /* how to access FileSys */
234 236 # define FILE_SYS(i) (PtrFileSys[i])
235 237
236 238 /* first entry is a tag, for now just the size */
237 239 # define OFF_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD)
238 240
239 241 /* offset for PNumFileSys */
240 242 # define OFF_NUM_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys))
241 243
242 244 /* offset for PRSATmpCnt */
243 245 # define OFF_RSA_TMP_CNT(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int))
244 246 int *PRSATmpCnt;
245 247
246 248 /* offset for queue_shm */
247 249 # define OFF_QUEUE_SHM(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
248 250
249 251 # define QSHM_ENTRIES(i) QShm[i].qs_entries
250 252
251 253 /* basic size of shared memory segment */
252 254 # define SM_T_SIZE (SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
253 255
254 256 static unsigned int hash_q __P((char *, unsigned int));
255 257
256 258 /*
257 259 ** HASH_Q -- simple hash function
258 260 **
259 261 ** Parameters:
260 262 ** p -- string to hash.
261 263 ** h -- hash start value (from previous run).
262 264 **
263 265 ** Returns:
264 266 ** hash value.
265 267 */
266 268
267 269 static unsigned int
268 270 hash_q(p, h)
269 271 char *p;
270 272 unsigned int h;
271 273 {
272 274 int c, d;
273 275
274 276 while (*p != '\0')
275 277 {
276 278 d = *p++;
277 279 c = d;
278 280 c ^= c<<6;
279 281 h += (c<<11) ^ (c>>1);
280 282 h ^= (d<<14) + (d<<7) + (d<<4) + d;
281 283 }
282 284 return h;
283 285 }
284 286
285 287
286 288 #else /* SM_CONF_SHM */
287 289 # define FILE_SYS(i) FileSys[i]
288 290 #endif /* SM_CONF_SHM */
289 291
290 292 /* access to the various components of file system data */
291 293 #define FILE_SYS_NAME(i) FSPath[i]
292 294 #define FILE_SYS_AVAIL(i) FILE_SYS(i).fs_avail
293 295 #define FILE_SYS_BLKSIZE(i) FILE_SYS(i).fs_blksize
294 296 #define FILE_SYS_DEV(i) FILE_SYS(i).fs_dev
295 297
296 298
297 299 /*
298 300 ** Current qf file field assignments:
299 301 **
300 302 ** A AUTH= parameter
301 303 ** B body type
302 304 ** C controlling user
303 305 ** D data file name
304 306 ** d data file directory name (added in 8.12)
305 307 ** E error recipient
306 308 ** F flag bits
307 309 ** G free (was: queue delay algorithm if _FFR_QUEUEDELAY)
308 310 ** H header
309 311 ** I data file's inode number
310 312 ** K time of last delivery attempt
311 313 ** L Solaris Content-Length: header (obsolete)
312 314 ** M message
313 315 ** N number of delivery attempts
314 316 ** P message priority
315 317 ** q quarantine reason
316 318 ** Q original recipient (ORCPT=)
317 319 ** r final recipient (Final-Recipient: DSN field)
318 320 ** R recipient
319 321 ** S sender
320 322 ** T init time
321 323 ** V queue file version
322 324 ** X free (was: character set if _FFR_SAVE_CHARSET)
323 325 ** Y free (was: current delay if _FFR_QUEUEDELAY)
324 326 ** Z original envelope id from ESMTP
325 327 ** ! deliver by (added in 8.12)
326 328 ** $ define macro
327 329 ** . terminate file
328 330 */
329 331
330 332 /*
331 333 ** QUEUEUP -- queue a message up for future transmission.
332 334 **
333 335 ** Parameters:
334 336 ** e -- the envelope to queue up.
335 337 ** announce -- if true, tell when you are queueing up.
336 338 ** msync -- if true, then fsync() if SuperSafe interactive mode.
337 339 **
338 340 ** Returns:
339 341 ** none.
340 342 **
341 343 ** Side Effects:
342 344 ** The current request is saved in a control file.
343 345 ** The queue file is left locked.
344 346 */
345 347
346 348 void
347 349 queueup(e, announce, msync)
348 350 register ENVELOPE *e;
349 351 bool announce;
350 352 bool msync;
351 353 {
352 354 register SM_FILE_T *tfp;
353 355 register HDR *h;
354 356 register ADDRESS *q;
355 357 int tfd = -1;
356 358 int i;
357 359 bool newid;
358 360 register char *p;
359 361 MAILER nullmailer;
360 362 MCI mcibuf;
361 363 char qf[MAXPATHLEN];
362 364 char tf[MAXPATHLEN];
363 365 char df[MAXPATHLEN];
364 366 char buf[MAXLINE];
365 367
366 368 /*
367 369 ** Create control file.
368 370 */
369 371
370 372 #define OPEN_TF do \
371 373 { \
372 374 MODE_T oldumask = 0; \
373 375 \
374 376 if (bitset(S_IWGRP, QueueFileMode)) \
375 377 oldumask = umask(002); \
376 378 tfd = open(tf, TF_OPEN_FLAGS, QueueFileMode); \
377 379 if (bitset(S_IWGRP, QueueFileMode)) \
378 380 (void) umask(oldumask); \
379 381 } while (0)
380 382
381 383
382 384 newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);
383 385 (void) sm_strlcpy(tf, queuename(e, NEWQFL_LETTER), sizeof(tf));
384 386 tfp = e->e_lockfp;
385 387 if (tfp == NULL && newid)
386 388 {
387 389 /*
388 390 ** open qf file directly: this will give an error if the file
389 391 ** already exists and hence prevent problems if a queue-id
390 392 ** is reused (e.g., because the clock is set back).
391 393 */
392 394
393 395 (void) sm_strlcpy(tf, queuename(e, ANYQFL_LETTER), sizeof(tf));
394 396 OPEN_TF;
395 397 if (tfd < 0 ||
396 398 #if !SM_OPEN_EXLOCK
397 399 !lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB) ||
398 400 #endif /* !SM_OPEN_EXLOCK */
399 401 (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
400 402 (void *) &tfd, SM_IO_WRONLY,
401 403 NULL)) == NULL)
402 404 {
403 405 int save_errno = errno;
404 406
405 407 printopenfds(true);
406 408 errno = save_errno;
407 409 syserr("!queueup: cannot create queue file %s, euid=%d, fd=%d, fp=%p",
408 410 tf, (int) geteuid(), tfd, tfp);
409 411 /* NOTREACHED */
410 412 }
411 413 e->e_lockfp = tfp;
412 414 upd_qs(e, 1, 0, "queueup");
413 415 }
414 416
415 417 /* if newid, write the queue file directly (instead of temp file) */
416 418 if (!newid)
417 419 {
418 420 /* get a locked tf file */
419 421 for (i = 0; i < 128; i++)
420 422 {
421 423 if (tfd < 0)
422 424 {
423 425 OPEN_TF;
424 426 if (tfd < 0)
425 427 {
426 428 if (errno != EEXIST)
427 429 break;
428 430 if (LogLevel > 0 && (i % 32) == 0)
429 431 sm_syslog(LOG_ALERT, e->e_id,
430 432 "queueup: cannot create %s, euid=%d: %s",
431 433 tf, (int) geteuid(),
432 434 sm_errstring(errno));
433 435 }
434 436 #if SM_OPEN_EXLOCK
435 437 else
436 438 break;
437 439 #endif /* SM_OPEN_EXLOCK */
438 440 }
439 441 if (tfd >= 0)
440 442 {
441 443 #if SM_OPEN_EXLOCK
442 444 /* file is locked by open() */
443 445 break;
444 446 #else /* SM_OPEN_EXLOCK */
445 447 if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB))
446 448 break;
447 449 else
448 450 #endif /* SM_OPEN_EXLOCK */
449 451 if (LogLevel > 0 && (i % 32) == 0)
450 452 sm_syslog(LOG_ALERT, e->e_id,
451 453 "queueup: cannot lock %s: %s",
452 454 tf, sm_errstring(errno));
453 455 if ((i % 32) == 31)
454 456 {
455 457 (void) close(tfd);
456 458 tfd = -1;
457 459 }
458 460 }
459 461
460 462 if ((i % 32) == 31)
461 463 {
462 464 /* save the old temp file away */
463 465 (void) rename(tf, queuename(e, TEMPQF_LETTER));
464 466 }
465 467 else
466 468 (void) sleep(i % 32);
467 469 }
468 470 if (tfd < 0 || (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
469 471 (void *) &tfd, SM_IO_WRONLY_B,
470 472 NULL)) == NULL)
471 473 {
472 474 int save_errno = errno;
473 475
474 476 printopenfds(true);
475 477 errno = save_errno;
476 478 syserr("!queueup: cannot create queue temp file %s, uid=%d",
477 479 tf, (int) geteuid());
478 480 }
479 481 }
480 482
481 483 if (tTd(40, 1))
482 484 sm_dprintf("\n>>>>> queueing %s/%s%s >>>>>\n",
483 485 qid_printqueue(e->e_qgrp, e->e_qdir),
484 486 queuename(e, ANYQFL_LETTER),
485 487 newid ? " (new id)" : "");
486 488 if (tTd(40, 3))
487 489 {
488 490 sm_dprintf(" e_flags=");
489 491 printenvflags(e);
490 492 }
491 493 if (tTd(40, 32))
492 494 {
493 495 sm_dprintf(" sendq=");
494 496 printaddr(sm_debug_file(), e->e_sendqueue, true);
495 497 }
496 498 if (tTd(40, 9))
497 499 {
498 500 sm_dprintf(" tfp=");
499 501 dumpfd(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL), true, false);
500 502 sm_dprintf(" lockfp=");
501 503 if (e->e_lockfp == NULL)
502 504 sm_dprintf("NULL\n");
503 505 else
504 506 dumpfd(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL),
505 507 true, false);
506 508 }
507 509
508 510 /*
509 511 ** If there is no data file yet, create one.
510 512 */
511 513
512 514 (void) sm_strlcpy(df, queuename(e, DATAFL_LETTER), sizeof(df));
513 515 if (bitset(EF_HAS_DF, e->e_flags))
514 516 {
515 517 if (e->e_dfp != NULL &&
516 518 SuperSafe != SAFE_REALLY &&
517 519 SuperSafe != SAFE_REALLY_POSTMILTER &&
518 520 sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
519 521 errno != EINVAL)
520 522 {
521 523 syserr("!queueup: cannot commit data file %s, uid=%d",
522 524 queuename(e, DATAFL_LETTER), (int) geteuid());
523 525 }
524 526 if (e->e_dfp != NULL &&
525 527 SuperSafe == SAFE_INTERACTIVE && msync)
526 528 {
527 529 if (tTd(40,32))
528 530 sm_syslog(LOG_INFO, e->e_id,
529 531 "queueup: fsync(e->e_dfp)");
530 532
531 533 if (fsync(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD,
532 534 NULL)) < 0)
533 535 {
534 536 if (newid)
535 537 syserr("!552 Error writing data file %s",
536 538 df);
537 539 else
538 540 syserr("!452 Error writing data file %s",
539 541 df);
540 542 }
541 543 }
542 544 }
543 545 else
544 546 {
545 547 int dfd;
546 548 MODE_T oldumask = 0;
547 549 register SM_FILE_T *dfp = NULL;
548 550 struct stat stbuf;
549 551
550 552 if (e->e_dfp != NULL &&
551 553 sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
552 554 syserr("committing over bf file");
553 555
554 556 if (bitset(S_IWGRP, QueueFileMode))
555 557 oldumask = umask(002);
556 558 dfd = open(df, O_WRONLY|O_CREAT|O_TRUNC|QF_O_EXTRA,
557 559 QueueFileMode);
558 560 if (bitset(S_IWGRP, QueueFileMode))
559 561 (void) umask(oldumask);
560 562 if (dfd < 0 || (dfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
561 563 (void *) &dfd, SM_IO_WRONLY_B,
562 564 NULL)) == NULL)
563 565 syserr("!queueup: cannot create data temp file %s, uid=%d",
564 566 df, (int) geteuid());
565 567 if (fstat(dfd, &stbuf) < 0)
566 568 e->e_dfino = -1;
567 569 else
568 570 {
569 571 e->e_dfdev = stbuf.st_dev;
570 572 e->e_dfino = ST_INODE(stbuf);
571 573 }
572 574 e->e_flags |= EF_HAS_DF;
573 575 memset(&mcibuf, '\0', sizeof(mcibuf));
574 576 mcibuf.mci_out = dfp;
575 577 mcibuf.mci_mailer = FileMailer;
576 578 (*e->e_putbody)(&mcibuf, e, NULL);
577 579
578 580 if (SuperSafe == SAFE_REALLY ||
579 581 SuperSafe == SAFE_REALLY_POSTMILTER ||
580 582 (SuperSafe == SAFE_INTERACTIVE && msync))
581 583 {
582 584 if (tTd(40,32))
583 585 sm_syslog(LOG_INFO, e->e_id,
584 586 "queueup: fsync(dfp)");
585 587
586 588 if (fsync(sm_io_getinfo(dfp, SM_IO_WHAT_FD, NULL)) < 0)
587 589 {
588 590 if (newid)
589 591 syserr("!552 Error writing data file %s",
590 592 df);
591 593 else
592 594 syserr("!452 Error writing data file %s",
593 595 df);
594 596 }
595 597 }
596 598
597 599 if (sm_io_close(dfp, SM_TIME_DEFAULT) < 0)
598 600 syserr("!queueup: cannot save data temp file %s, uid=%d",
599 601 df, (int) geteuid());
600 602 e->e_putbody = putbody;
601 603 }
602 604
603 605 /*
604 606 ** Output future work requests.
605 607 ** Priority and creation time should be first, since
606 608 ** they are required by gatherq.
607 609 */
608 610
609 611 /* output queue version number (must be first!) */
610 612 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "V%d\n", QF_VERSION);
611 613
612 614 /* output creation time */
613 615 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "T%ld\n", (long) e->e_ctime);
614 616
615 617 /* output last delivery time */
616 618 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime);
617 619
618 620 /* output number of delivery attempts */
619 621 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "N%d\n", e->e_ntries);
620 622
621 623 /* output message priority */
622 624 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "P%ld\n", e->e_msgpriority);
623 625
624 626 /*
625 627 ** If data file is in a different directory than the queue file,
626 628 ** output a "d" record naming the directory of the data file.
627 629 */
628 630
629 631 if (e->e_dfqgrp != e->e_qgrp)
630 632 {
631 633 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "d%s\n",
632 634 Queue[e->e_dfqgrp]->qg_qpaths[e->e_dfqdir].qp_name);
633 635 }
634 636
635 637 /* output inode number of data file */
636 638 /* XXX should probably include device major/minor too */
637 639 if (e->e_dfino != -1)
638 640 {
639 641 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "I%ld/%ld/%llu\n",
640 642 (long) major(e->e_dfdev),
641 643 (long) minor(e->e_dfdev),
642 644 (ULONGLONG_T) e->e_dfino);
643 645 }
644 646
645 647 /* output body type */
646 648 if (e->e_bodytype != NULL)
647 649 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "B%s\n",
648 650 denlstring(e->e_bodytype, true, false));
649 651
650 652 /* quarantine reason */
651 653 if (e->e_quarmsg != NULL)
652 654 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "q%s\n",
653 655 denlstring(e->e_quarmsg, true, false));
654 656
655 657 /* message from envelope, if it exists */
656 658 if (e->e_message != NULL)
657 659 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
658 660 denlstring(e->e_message, true, false));
659 661
660 662 /* send various flag bits through */
661 663 p = buf;
662 664 if (bitset(EF_WARNING, e->e_flags))
663 665 *p++ = 'w';
664 666 if (bitset(EF_RESPONSE, e->e_flags))
665 667 *p++ = 'r';
666 668 if (bitset(EF_HAS8BIT, e->e_flags))
667 669 *p++ = '8';
668 670 if (bitset(EF_DELETE_BCC, e->e_flags))
669 671 *p++ = 'b';
670 672 if (bitset(EF_RET_PARAM, e->e_flags))
671 673 *p++ = 'd';
672 674 if (bitset(EF_NO_BODY_RETN, e->e_flags))
673 675 *p++ = 'n';
674 676 if (bitset(EF_SPLIT, e->e_flags))
675 677 *p++ = 's';
676 678 *p++ = '\0';
677 679 if (buf[0] != '\0')
678 680 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "F%s\n", buf);
679 681
680 682 /* save $={persistentMacros} macro values */
681 683 queueup_macros(macid("{persistentMacros}"), tfp, e);
682 684
683 685 /* output name of sender */
684 686 if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
685 687 p = e->e_sender;
686 688 else
687 689 p = e->e_from.q_paddr;
688 690 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "S%s\n",
689 691 denlstring(p, true, false));
690 692
691 693 /* output ESMTP-supplied "original" information */
692 694 if (e->e_envid != NULL)
693 695 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Z%s\n",
694 696 denlstring(e->e_envid, true, false));
695 697
696 698 /* output AUTH= parameter */
697 699 if (e->e_auth_param != NULL)
698 700 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "A%s\n",
699 701 denlstring(e->e_auth_param, true, false));
700 702 if (e->e_dlvr_flag != 0)
701 703 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "!%c %ld\n",
702 704 (char) e->e_dlvr_flag, e->e_deliver_by);
703 705
704 706 /* output list of recipient addresses */
705 707 printctladdr(NULL, NULL);
706 708 for (q = e->e_sendqueue; q != NULL; q = q->q_next)
707 709 {
708 710 if (!QS_IS_UNDELIVERED(q->q_state))
709 711 continue;
710 712
711 713 /* message for this recipient, if it exists */
712 714 if (q->q_message != NULL)
713 715 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
714 716 denlstring(q->q_message, true,
715 717 false));
716 718
717 719 printctladdr(q, tfp);
718 720 if (q->q_orcpt != NULL)
719 721 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Q%s\n",
720 722 denlstring(q->q_orcpt, true,
721 723 false));
722 724 if (q->q_finalrcpt != NULL)
723 725 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "r%s\n",
724 726 denlstring(q->q_finalrcpt, true,
725 727 false));
726 728 (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'R');
727 729 if (bitset(QPRIMARY, q->q_flags))
728 730 (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'P');
729 731 if (bitset(QHASNOTIFY, q->q_flags))
730 732 (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'N');
731 733 if (bitset(QPINGONSUCCESS, q->q_flags))
732 734 (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'S');
733 735 if (bitset(QPINGONFAILURE, q->q_flags))
734 736 (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'F');
735 737 if (bitset(QPINGONDELAY, q->q_flags))
736 738 (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'D');
737 739 if (q->q_alias != NULL &&
738 740 bitset(QALIAS, q->q_alias->q_flags))
739 741 (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'A');
740 742 (void) sm_io_putc(tfp, SM_TIME_DEFAULT, ':');
741 743 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s\n",
742 744 denlstring(q->q_paddr, true, false));
743 745 if (announce)
744 746 {
745 747 char *tag = "queued";
746 748
747 749 if (e->e_quarmsg != NULL)
748 750 tag = "quarantined";
749 751
750 752 e->e_to = q->q_paddr;
751 753 message(tag);
752 754 if (LogLevel > 8)
753 755 logdelivery(q->q_mailer, NULL, q->q_status,
754 756 tag, NULL, (time_t) 0, e);
755 757 e->e_to = NULL;
756 758 }
757 759 if (tTd(40, 1))
758 760 {
759 761 sm_dprintf("queueing ");
760 762 printaddr(sm_debug_file(), q, false);
761 763 }
762 764 }
763 765
764 766 /*
765 767 ** Output headers for this message.
766 768 ** Expand macros completely here. Queue run will deal with
767 769 ** everything as absolute headers.
768 770 ** All headers that must be relative to the recipient
769 771 ** can be cracked later.
770 772 ** We set up a "null mailer" -- i.e., a mailer that will have
771 773 ** no effect on the addresses as they are output.
772 774 */
773 775
774 776 memset((char *) &nullmailer, '\0', sizeof(nullmailer));
775 777 nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
776 778 nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1;
777 779 nullmailer.m_eol = "\n";
778 780 memset(&mcibuf, '\0', sizeof(mcibuf));
779 781 mcibuf.mci_mailer = &nullmailer;
780 782 mcibuf.mci_out = tfp;
781 783
782 784 macdefine(&e->e_macro, A_PERM, 'g', "\201f");
783 785 for (h = e->e_header; h != NULL; h = h->h_link)
784 786 {
785 787 if (h->h_value == NULL)
786 788 continue;
787 789
788 790 /* don't output resent headers on non-resent messages */
789 791 if (bitset(H_RESENT, h->h_flags) &&
790 792 !bitset(EF_RESENT, e->e_flags))
791 793 continue;
792 794
793 795 /* expand macros; if null, don't output header at all */
794 796 if (bitset(H_DEFAULT, h->h_flags))
795 797 {
796 798 (void) expand(h->h_value, buf, sizeof(buf), e);
797 799 if (buf[0] == '\0')
798 800 continue;
799 801 if (buf[0] == ' ' && buf[1] == '\0')
800 802 continue;
801 803 }
802 804
803 805 /* output this header */
804 806 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "H?");
805 807
806 808 /* output conditional macro if present */
807 809 if (h->h_macro != '\0')
808 810 {
809 811 if (bitset(0200, h->h_macro))
810 812 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
811 813 "${%s}",
812 814 macname(bitidx(h->h_macro)));
813 815 else
814 816 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
815 817 "$%c", h->h_macro);
816 818 }
817 819 else if (!bitzerop(h->h_mflags) &&
818 820 bitset(H_CHECK|H_ACHECK, h->h_flags))
819 821 {
820 822 int j;
821 823
822 824 /* if conditional, output the set of conditions */
823 825 for (j = '\0'; j <= '\177'; j++)
824 826 if (bitnset(j, h->h_mflags))
825 827 (void) sm_io_putc(tfp, SM_TIME_DEFAULT,
826 828 j);
827 829 }
828 830 (void) sm_io_putc(tfp, SM_TIME_DEFAULT, '?');
829 831
830 832 /* output the header: expand macros, convert addresses */
831 833 if (bitset(H_DEFAULT, h->h_flags) &&
832 834 !bitset(H_BINDLATE, h->h_flags))
833 835 {
834 836 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s:%s\n",
835 837 h->h_field,
836 838 denlstring(buf, false, true));
837 839 }
838 840 else if (bitset(H_FROM|H_RCPT, h->h_flags) &&
839 841 !bitset(H_BINDLATE, h->h_flags))
840 842 {
841 843 bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
842 844 SM_FILE_T *savetrace = TrafficLogFile;
843 845
844 846 TrafficLogFile = NULL;
845 847
846 848 if (bitset(H_FROM, h->h_flags))
847 849 oldstyle = false;
848 850 commaize(h, h->h_value, oldstyle, &mcibuf, e,
849 851 PXLF_HEADER);
850 852
851 853 TrafficLogFile = savetrace;
852 854 }
853 855 else
854 856 {
855 857 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s:%s\n",
856 858 h->h_field,
857 859 denlstring(h->h_value, false,
858 860 true));
859 861 }
860 862 }
861 863
862 864 /*
863 865 ** Clean up.
864 866 **
865 867 ** Write a terminator record -- this is to prevent
866 868 ** scurrilous crackers from appending any data.
867 869 */
868 870
869 871 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ".\n");
870 872
871 873 if (sm_io_flush(tfp, SM_TIME_DEFAULT) != 0 ||
872 874 ((SuperSafe == SAFE_REALLY ||
873 875 SuperSafe == SAFE_REALLY_POSTMILTER ||
874 876 (SuperSafe == SAFE_INTERACTIVE && msync)) &&
875 877 fsync(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL)) < 0) ||
876 878 sm_io_error(tfp))
877 879 {
878 880 if (newid)
879 881 syserr("!552 Error writing control file %s", tf);
880 882 else
881 883 syserr("!452 Error writing control file %s", tf);
882 884 }
883 885
884 886 if (!newid)
885 887 {
886 888 char new = queue_letter(e, ANYQFL_LETTER);
887 889
888 890 /* rename (locked) tf to be (locked) [qh]f */
889 891 (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER),
890 892 sizeof(qf));
891 893 if (rename(tf, qf) < 0)
892 894 syserr("cannot rename(%s, %s), uid=%d",
893 895 tf, qf, (int) geteuid());
894 896 else
895 897 {
896 898 /*
897 899 ** Check if type has changed and only
898 900 ** remove the old item if the rename above
899 901 ** succeeded.
900 902 */
901 903
902 904 if (e->e_qfletter != '\0' &&
903 905 e->e_qfletter != new)
904 906 {
905 907 if (tTd(40, 5))
906 908 {
907 909 sm_dprintf("type changed from %c to %c\n",
908 910 e->e_qfletter, new);
909 911 }
910 912
911 913 if (unlink(queuename(e, e->e_qfletter)) < 0)
912 914 {
913 915 /* XXX: something more drastic? */
914 916 if (LogLevel > 0)
915 917 sm_syslog(LOG_ERR, e->e_id,
916 918 "queueup: unlink(%s) failed: %s",
917 919 queuename(e, e->e_qfletter),
918 920 sm_errstring(errno));
919 921 }
920 922 }
921 923 }
922 924 e->e_qfletter = new;
923 925
924 926 /*
925 927 ** fsync() after renaming to make sure metadata is
926 928 ** written to disk on filesystems in which renames are
927 929 ** not guaranteed.
928 930 */
929 931
930 932 if (SuperSafe != SAFE_NO)
931 933 {
932 934 /* for softupdates */
933 935 if (tfd >= 0 && fsync(tfd) < 0)
934 936 {
935 937 syserr("!queueup: cannot fsync queue temp file %s",
936 938 tf);
937 939 }
938 940 SYNC_DIR(qf, true);
939 941 }
940 942
941 943 /* close and unlock old (locked) queue file */
942 944 if (e->e_lockfp != NULL)
943 945 (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
944 946 e->e_lockfp = tfp;
945 947
946 948 /* save log info */
947 949 if (LogLevel > 79)
948 950 sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", qf);
949 951 }
950 952 else
951 953 {
952 954 /* save log info */
953 955 if (LogLevel > 79)
954 956 sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", tf);
955 957
956 958 e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
957 959 }
958 960
959 961 errno = 0;
960 962 e->e_flags |= EF_INQUEUE;
961 963
962 964 if (tTd(40, 1))
963 965 sm_dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
964 966 return;
965 967 }
966 968
967 969 /*
968 970 ** PRINTCTLADDR -- print control address to file.
969 971 **
970 972 ** Parameters:
971 973 ** a -- address.
972 974 ** tfp -- file pointer.
973 975 **
974 976 ** Returns:
975 977 ** none.
976 978 **
977 979 ** Side Effects:
978 980 ** The control address (if changed) is printed to the file.
979 981 ** The last control address and uid are saved.
980 982 */
981 983
982 984 static void
983 985 printctladdr(a, tfp)
984 986 register ADDRESS *a;
985 987 SM_FILE_T *tfp;
986 988 {
987 989 char *user;
988 990 register ADDRESS *q;
989 991 uid_t uid;
990 992 gid_t gid;
991 993 static ADDRESS *lastctladdr = NULL;
992 994 static uid_t lastuid;
993 995
994 996 /* initialization */
995 997 if (a == NULL || a->q_alias == NULL || tfp == NULL)
996 998 {
997 999 if (lastctladdr != NULL && tfp != NULL)
998 1000 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C\n");
999 1001 lastctladdr = NULL;
1000 1002 lastuid = 0;
1001 1003 return;
1002 1004 }
1003 1005
1004 1006 /* find the active uid */
1005 1007 q = getctladdr(a);
1006 1008 if (q == NULL)
1007 1009 {
1008 1010 user = NULL;
1009 1011 uid = 0;
1010 1012 gid = 0;
1011 1013 }
1012 1014 else
1013 1015 {
1014 1016 user = q->q_ruser != NULL ? q->q_ruser : q->q_user;
1015 1017 uid = q->q_uid;
1016 1018 gid = q->q_gid;
1017 1019 }
1018 1020 a = a->q_alias;
1019 1021
1020 1022 /* check to see if this is the same as last time */
1021 1023 if (lastctladdr != NULL && uid == lastuid &&
1022 1024 strcmp(lastctladdr->q_paddr, a->q_paddr) == 0)
1023 1025 return;
1024 1026 lastuid = uid;
1025 1027 lastctladdr = a;
1026 1028
1027 1029 if (uid == 0 || user == NULL || user[0] == '\0')
1028 1030 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C");
1029 1031 else
1030 1032 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C%s:%ld:%ld",
1031 1033 denlstring(user, true, false), (long) uid,
1032 1034 (long) gid);
1033 1035 (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ":%s\n",
1034 1036 denlstring(a->q_paddr, true, false));
1035 1037 }
1036 1038
1037 1039 /*
1038 1040 ** RUNNERS_SIGTERM -- propagate a SIGTERM to queue runner process
1039 1041 **
1040 1042 ** This propagates the signal to the child processes that are queue
1041 1043 ** runners. This is for a queue runner "cleanup". After all of the
1042 1044 ** child queue runner processes are signaled (it should be SIGTERM
1043 1045 ** being the sig) then the old signal handler (Oldsh) is called
1044 1046 ** to handle any cleanup set for this process (provided it is not
1045 1047 ** SIG_DFL or SIG_IGN). The signal may not be handled immediately
1046 1048 ** if the BlockOldsh flag is set. If the current process doesn't
1047 1049 ** have a parent then handle the signal immediately, regardless of
1048 1050 ** BlockOldsh.
1049 1051 **
1050 1052 ** Parameters:
1051 1053 ** sig -- the signal number being sent
1052 1054 **
1053 1055 ** Returns:
1054 1056 ** none.
1055 1057 **
1056 1058 ** Side Effects:
1057 1059 ** Sets the NoMoreRunners boolean to true to stop more runners
1058 1060 ** from being started in runqueue().
1059 1061 **
1060 1062 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
1061 1063 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
1062 1064 ** DOING.
1063 1065 */
1064 1066
1065 1067 static bool volatile NoMoreRunners = false;
1066 1068 static sigfunc_t Oldsh_term = SIG_DFL;
1067 1069 static sigfunc_t Oldsh_hup = SIG_DFL;
1068 1070 static sigfunc_t volatile Oldsh = SIG_DFL;
1069 1071 static bool BlockOldsh = false;
1070 1072 static int volatile Oldsig = 0;
1071 1073 static SIGFUNC_DECL runners_sigterm __P((int));
1072 1074 static SIGFUNC_DECL runners_sighup __P((int));
1073 1075
1074 1076 static SIGFUNC_DECL
1075 1077 runners_sigterm(sig)
1076 1078 int sig;
1077 1079 {
1078 1080 int save_errno = errno;
1079 1081
1080 1082 FIX_SYSV_SIGNAL(sig, runners_sigterm);
1081 1083 errno = save_errno;
1082 1084 CHECK_CRITICAL(sig);
1083 1085 NoMoreRunners = true;
1084 1086 Oldsh = Oldsh_term;
1085 1087 Oldsig = sig;
1086 1088 proc_list_signal(PROC_QUEUE, sig);
1087 1089
1088 1090 if (!BlockOldsh || getppid() <= 1)
1089 1091 {
1090 1092 /* Check that a valid 'old signal handler' is callable */
1091 1093 if (Oldsh_term != SIG_DFL && Oldsh_term != SIG_IGN &&
1092 1094 Oldsh_term != runners_sigterm)
1093 1095 (*Oldsh_term)(sig);
1094 1096 }
1095 1097 errno = save_errno;
1096 1098 return SIGFUNC_RETURN;
1097 1099 }
1098 1100 /*
1099 1101 ** RUNNERS_SIGHUP -- propagate a SIGHUP to queue runner process
1100 1102 **
1101 1103 ** This propagates the signal to the child processes that are queue
1102 1104 ** runners. This is for a queue runner "cleanup". After all of the
1103 1105 ** child queue runner processes are signaled (it should be SIGHUP
1104 1106 ** being the sig) then the old signal handler (Oldsh) is called to
1105 1107 ** handle any cleanup set for this process (provided it is not SIG_DFL
1106 1108 ** or SIG_IGN). The signal may not be handled immediately if the
1107 1109 ** BlockOldsh flag is set. If the current process doesn't have
1108 1110 ** a parent then handle the signal immediately, regardless of
1109 1111 ** BlockOldsh.
1110 1112 **
1111 1113 ** Parameters:
1112 1114 ** sig -- the signal number being sent
1113 1115 **
1114 1116 ** Returns:
1115 1117 ** none.
1116 1118 **
1117 1119 ** Side Effects:
1118 1120 ** Sets the NoMoreRunners boolean to true to stop more runners
1119 1121 ** from being started in runqueue().
1120 1122 **
1121 1123 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
1122 1124 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
1123 1125 ** DOING.
1124 1126 */
1125 1127
1126 1128 static SIGFUNC_DECL
1127 1129 runners_sighup(sig)
1128 1130 int sig;
1129 1131 {
1130 1132 int save_errno = errno;
1131 1133
1132 1134 FIX_SYSV_SIGNAL(sig, runners_sighup);
1133 1135 errno = save_errno;
1134 1136 CHECK_CRITICAL(sig);
1135 1137 NoMoreRunners = true;
1136 1138 Oldsh = Oldsh_hup;
1137 1139 Oldsig = sig;
1138 1140 proc_list_signal(PROC_QUEUE, sig);
1139 1141
1140 1142 if (!BlockOldsh || getppid() <= 1)
1141 1143 {
1142 1144 /* Check that a valid 'old signal handler' is callable */
1143 1145 if (Oldsh_hup != SIG_DFL && Oldsh_hup != SIG_IGN &&
1144 1146 Oldsh_hup != runners_sighup)
1145 1147 (*Oldsh_hup)(sig);
1146 1148 }
1147 1149 errno = save_errno;
1148 1150 return SIGFUNC_RETURN;
1149 1151 }
1150 1152 /*
1151 1153 ** MARK_WORK_GROUP_RESTART -- mark a work group as needing a restart
1152 1154 **
1153 1155 ** Sets a workgroup for restarting.
1154 1156 **
1155 1157 ** Parameters:
1156 1158 ** wgrp -- the work group id to restart.
1157 1159 ** reason -- why (signal?), -1 to turn off restart
1158 1160 **
1159 1161 ** Returns:
1160 1162 ** none.
1161 1163 **
1162 1164 ** Side effects:
1163 1165 ** May set global RestartWorkGroup to true.
1164 1166 **
1165 1167 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
1166 1168 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
1167 1169 ** DOING.
1168 1170 */
1169 1171
1170 1172 void
1171 1173 mark_work_group_restart(wgrp, reason)
1172 1174 int wgrp;
1173 1175 int reason;
1174 1176 {
1175 1177 if (wgrp < 0 || wgrp > NumWorkGroups)
1176 1178 return;
1177 1179
1178 1180 WorkGrp[wgrp].wg_restart = reason;
1179 1181 if (reason >= 0)
1180 1182 RestartWorkGroup = true;
1181 1183 }
1182 1184 /*
1183 1185 ** RESTART_MARKED_WORK_GROUPS -- restart work groups marked as needing restart
1184 1186 **
1185 1187 ** Restart any workgroup marked as needing a restart provided more
1186 1188 ** runners are allowed.
1187 1189 **
1188 1190 ** Parameters:
1189 1191 ** none.
1190 1192 **
1191 1193 ** Returns:
1192 1194 ** none.
1193 1195 **
1194 1196 ** Side effects:
1195 1197 ** Sets global RestartWorkGroup to false.
1196 1198 */
1197 1199
1198 1200 void
1199 1201 restart_marked_work_groups()
1200 1202 {
1201 1203 int i;
1202 1204 int wasblocked;
1203 1205
1204 1206 if (NoMoreRunners)
1205 1207 return;
1206 1208
1207 1209 /* Block SIGCHLD so reapchild() doesn't mess with us */
1208 1210 wasblocked = sm_blocksignal(SIGCHLD);
1209 1211
1210 1212 for (i = 0; i < NumWorkGroups; i++)
1211 1213 {
1212 1214 if (WorkGrp[i].wg_restart >= 0)
1213 1215 {
1214 1216 if (LogLevel > 8)
1215 1217 sm_syslog(LOG_ERR, NOQID,
1216 1218 "restart queue runner=%d due to signal 0x%x",
1217 1219 i, WorkGrp[i].wg_restart);
1218 1220 restart_work_group(i);
1219 1221 }
1220 1222 }
1221 1223 RestartWorkGroup = false;
1222 1224
1223 1225 if (wasblocked == 0)
1224 1226 (void) sm_releasesignal(SIGCHLD);
1225 1227 }
1226 1228 /*
1227 1229 ** RESTART_WORK_GROUP -- restart a specific work group
1228 1230 **
1229 1231 ** Restart a specific workgroup provided more runners are allowed.
1230 1232 ** If the requested work group has been restarted too many times log
1231 1233 ** this and refuse to restart.
1232 1234 **
1233 1235 ** Parameters:
1234 1236 ** wgrp -- the work group id to restart
1235 1237 **
1236 1238 ** Returns:
1237 1239 ** none.
1238 1240 **
1239 1241 ** Side Effects:
1240 1242 ** starts another process doing the work of wgrp
1241 1243 */
1242 1244
1243 1245 #define MAX_PERSIST_RESTART 10 /* max allowed number of restarts */
1244 1246
1245 1247 static void
1246 1248 restart_work_group(wgrp)
1247 1249 int wgrp;
1248 1250 {
1249 1251 if (NoMoreRunners ||
1250 1252 wgrp < 0 || wgrp > NumWorkGroups)
1251 1253 return;
1252 1254
1253 1255 WorkGrp[wgrp].wg_restart = -1;
1254 1256 if (WorkGrp[wgrp].wg_restartcnt < MAX_PERSIST_RESTART)
1255 1257 {
1256 1258 /* avoid overflow; increment here */
1257 1259 WorkGrp[wgrp].wg_restartcnt++;
1258 1260 (void) run_work_group(wgrp, RWG_FORK|RWG_PERSISTENT|RWG_RUNALL);
1259 1261 }
1260 1262 else
1261 1263 {
1262 1264 sm_syslog(LOG_ERR, NOQID,
1263 1265 "ERROR: persistent queue runner=%d restarted too many times, queue runner lost",
1264 1266 wgrp);
1265 1267 }
1266 1268 }
1267 1269 /*
1268 1270 ** SCHEDULE_QUEUE_RUNS -- schedule the next queue run for a work group.
1269 1271 **
1270 1272 ** Parameters:
1271 1273 ** runall -- schedule even if individual bit is not set.
1272 1274 ** wgrp -- the work group id to schedule.
1273 1275 ** didit -- the queue run was performed for this work group.
1274 1276 **
1275 1277 ** Returns:
1276 1278 ** nothing
1277 1279 */
1278 1280
1279 1281 #define INCR_MOD(v, m) if (++v >= m) \
1280 1282 v = 0; \
1281 1283 else
1282 1284
1283 1285 static void
1284 1286 schedule_queue_runs(runall, wgrp, didit)
1285 1287 bool runall;
1286 1288 int wgrp;
1287 1289 bool didit;
1288 1290 {
1289 1291 int qgrp, cgrp, endgrp;
1290 1292 #if _FFR_QUEUE_SCHED_DBG
1291 1293 time_t lastsched;
1292 1294 bool sched;
1293 1295 #endif /* _FFR_QUEUE_SCHED_DBG */
1294 1296 time_t now;
1295 1297 time_t minqintvl;
1296 1298
1297 1299 /*
1298 1300 ** This is a bit ugly since we have to duplicate the
1299 1301 ** code that "walks" through a work queue group.
1300 1302 */
1301 1303
1302 1304 now = curtime();
1303 1305 minqintvl = 0;
1304 1306 cgrp = endgrp = WorkGrp[wgrp].wg_curqgrp;
1305 1307 do
1306 1308 {
1307 1309 time_t qintvl;
1308 1310
1309 1311 #if _FFR_QUEUE_SCHED_DBG
1310 1312 lastsched = 0;
1311 1313 sched = false;
1312 1314 #endif /* _FFR_QUEUE_SCHED_DBG */
1313 1315 qgrp = WorkGrp[wgrp].wg_qgs[cgrp]->qg_index;
1314 1316 if (Queue[qgrp]->qg_queueintvl > 0)
1315 1317 qintvl = Queue[qgrp]->qg_queueintvl;
1316 1318 else if (QueueIntvl > 0)
1317 1319 qintvl = QueueIntvl;
1318 1320 else
1319 1321 qintvl = (time_t) 0;
1320 1322 #if _FFR_QUEUE_SCHED_DBG
1321 1323 lastsched = Queue[qgrp]->qg_nextrun;
1322 1324 #endif /* _FFR_QUEUE_SCHED_DBG */
1323 1325 if ((runall || Queue[qgrp]->qg_nextrun <= now) && qintvl > 0)
1324 1326 {
1325 1327 #if _FFR_QUEUE_SCHED_DBG
1326 1328 sched = true;
1327 1329 #endif /* _FFR_QUEUE_SCHED_DBG */
1328 1330 if (minqintvl == 0 || qintvl < minqintvl)
1329 1331 minqintvl = qintvl;
1330 1332
1331 1333 /*
1332 1334 ** Only set a new time if a queue run was performed
1333 1335 ** for this queue group. If the queue was not run,
1334 1336 ** we could starve it by setting a new time on each
1335 1337 ** call.
1336 1338 */
1337 1339
1338 1340 if (didit)
1339 1341 Queue[qgrp]->qg_nextrun += qintvl;
1340 1342 }
1341 1343 #if _FFR_QUEUE_SCHED_DBG
1342 1344 if (tTd(69, 10))
1343 1345 sm_syslog(LOG_INFO, NOQID,
1344 1346 "sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, lastrun=%ld, nextrun=%ld, sched=%d",
1345 1347 wgrp, cgrp, qgrp, Queue[qgrp]->qg_queueintvl,
1346 1348 QueueIntvl, runall, lastsched,
1347 1349 Queue[qgrp]->qg_nextrun, sched);
1348 1350 #endif /* _FFR_QUEUE_SCHED_DBG */
1349 1351 INCR_MOD(cgrp, WorkGrp[wgrp].wg_numqgrp);
1350 1352 } while (endgrp != cgrp);
1351 1353 if (minqintvl > 0)
1352 1354 (void) sm_setevent(minqintvl, runqueueevent, 0);
1353 1355 }
1354 1356
1355 1357 #if _FFR_QUEUE_RUN_PARANOIA
1356 1358 /*
1357 1359 ** CHECKQUEUERUNNER -- check whether a queue group hasn't been run.
1358 1360 **
1359 1361 ** Use this if events may get lost and hence queue runners may not
1360 1362 ** be started and mail will pile up in a queue.
1361 1363 **
1362 1364 ** Parameters:
1363 1365 ** none.
1364 1366 **
1365 1367 ** Returns:
1366 1368 ** true if a queue run is necessary.
1367 1369 **
1368 1370 ** Side Effects:
1369 1371 ** may schedule a queue run.
1370 1372 */
1371 1373
1372 1374 bool
1373 1375 checkqueuerunner()
1374 1376 {
1375 1377 int qgrp;
1376 1378 time_t now, minqintvl;
1377 1379
1378 1380 now = curtime();
1379 1381 minqintvl = 0;
1380 1382 for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
1381 1383 {
1382 1384 time_t qintvl;
1383 1385
1384 1386 if (Queue[qgrp]->qg_queueintvl > 0)
1385 1387 qintvl = Queue[qgrp]->qg_queueintvl;
1386 1388 else if (QueueIntvl > 0)
1387 1389 qintvl = QueueIntvl;
1388 1390 else
1389 1391 qintvl = (time_t) 0;
1390 1392 if (Queue[qgrp]->qg_nextrun <= now - qintvl)
1391 1393 {
1392 1394 if (minqintvl == 0 || qintvl < minqintvl)
1393 1395 minqintvl = qintvl;
1394 1396 if (LogLevel > 1)
1395 1397 sm_syslog(LOG_WARNING, NOQID,
1396 1398 "checkqueuerunner: queue %d should have been run at %s, queue interval %ld",
1397 1399 qgrp,
1398 1400 arpadate(ctime(&Queue[qgrp]->qg_nextrun)),
1399 1401 qintvl);
1400 1402 }
1401 1403 }
1402 1404 if (minqintvl > 0)
1403 1405 {
1404 1406 (void) sm_setevent(minqintvl, runqueueevent, 0);
1405 1407 return true;
1406 1408 }
1407 1409 return false;
1408 1410 }
1409 1411 #endif /* _FFR_QUEUE_RUN_PARANOIA */
1410 1412
1411 1413 /*
1412 1414 ** RUNQUEUE -- run the jobs in the queue.
1413 1415 **
1414 1416 ** Gets the stuff out of the queue in some presumably logical
1415 1417 ** order and processes them.
1416 1418 **
1417 1419 ** Parameters:
1418 1420 ** forkflag -- true if the queue scanning should be done in
1419 1421 ** a child process. We double-fork so it is not our
1420 1422 ** child and we don't have to clean up after it.
1421 1423 ** false can be ignored if we have multiple queues.
1422 1424 ** verbose -- if true, print out status information.
1423 1425 ** persistent -- persistent queue runner?
1424 1426 ** runall -- run all groups or only a subset (DoQueueRun)?
1425 1427 **
1426 1428 ** Returns:
1427 1429 ** true if the queue run successfully began.
1428 1430 **
1429 1431 ** Side Effects:
1430 1432 ** runs things in the mail queue using run_work_group().
1431 1433 ** maybe schedules next queue run.
1432 1434 */
1433 1435
1434 1436 static ENVELOPE QueueEnvelope; /* the queue run envelope */
1435 1437 static time_t LastQueueTime = 0; /* last time a queue ID assigned */
1436 1438 static pid_t LastQueuePid = -1; /* last PID which had a queue ID */
1437 1439
1438 1440 /* values for qp_supdirs */
1439 1441 #define QP_NOSUB 0x0000 /* No subdirectories */
1440 1442 #define QP_SUBDF 0x0001 /* "df" subdirectory */
1441 1443 #define QP_SUBQF 0x0002 /* "qf" subdirectory */
1442 1444 #define QP_SUBXF 0x0004 /* "xf" subdirectory */
1443 1445
1444 1446 bool
1445 1447 runqueue(forkflag, verbose, persistent, runall)
1446 1448 bool forkflag;
1447 1449 bool verbose;
1448 1450 bool persistent;
1449 1451 bool runall;
1450 1452 {
1451 1453 int i;
1452 1454 bool ret = true;
1453 1455 static int curnum = 0;
1454 1456 sigfunc_t cursh;
1455 1457 #if SM_HEAP_CHECK
1456 1458 SM_NONVOLATILE int oldgroup = 0;
1457 1459
1458 1460 if (sm_debug_active(&DebugLeakQ, 1))
1459 1461 {
1460 1462 oldgroup = sm_heap_group();
1461 1463 sm_heap_newgroup();
1462 1464 sm_dprintf("runqueue() heap group #%d\n", sm_heap_group());
1463 1465 }
1464 1466 #endif /* SM_HEAP_CHECK */
1465 1467
1466 1468 /* queue run has been started, don't do any more this time */
1467 1469 DoQueueRun = false;
1468 1470
1469 1471 /* more than one queue or more than one directory per queue */
1470 1472 if (!forkflag && !verbose &&
1471 1473 (WorkGrp[0].wg_qgs[0]->qg_numqueues > 1 || NumWorkGroups > 1 ||
1472 1474 WorkGrp[0].wg_numqgrp > 1))
1473 1475 forkflag = true;
1474 1476
1475 1477 /*
1476 1478 ** For controlling queue runners via signals sent to this process.
1477 1479 ** Oldsh* will get called too by runners_sig* (if it is not SIG_IGN
1478 1480 ** or SIG_DFL) to preserve cleanup behavior. Now that this process
1479 1481 ** will have children (and perhaps grandchildren) this handler will
1480 1482 ** be left in place. This is because this process, once it has
1481 1483 ** finished spinning off queue runners, may go back to doing something
1482 1484 ** else (like being a daemon). And we still want on a SIG{TERM,HUP} to
1483 1485 ** clean up the child queue runners. Only install 'runners_sig*' once
1484 1486 ** else we'll get stuck looping forever.
1485 1487 */
1486 1488
1487 1489 cursh = sm_signal(SIGTERM, runners_sigterm);
1488 1490 if (cursh != runners_sigterm)
1489 1491 Oldsh_term = cursh;
1490 1492 cursh = sm_signal(SIGHUP, runners_sighup);
1491 1493 if (cursh != runners_sighup)
1492 1494 Oldsh_hup = cursh;
1493 1495
1494 1496 for (i = 0; i < NumWorkGroups && !NoMoreRunners; i++)
1495 1497 {
1496 1498 int rwgflags = RWG_NONE;
1497 1499
1498 1500 /*
1499 1501 ** If MaxQueueChildren active then test whether the start
1500 1502 ** of the next queue group's additional queue runners (maximum)
1501 1503 ** will result in MaxQueueChildren being exceeded.
1502 1504 **
1503 1505 ** Note: do not use continue; even though another workgroup
1504 1506 ** may have fewer queue runners, this would be "unfair",
1505 1507 ** i.e., this work group might "starve" then.
1506 1508 */
1507 1509
1508 1510 #if _FFR_QUEUE_SCHED_DBG
1509 1511 if (tTd(69, 10))
1510 1512 sm_syslog(LOG_INFO, NOQID,
1511 1513 "rq: curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d",
1512 1514 curnum, MaxQueueChildren, CurRunners,
1513 1515 WorkGrp[curnum].wg_maxact);
1514 1516 #endif /* _FFR_QUEUE_SCHED_DBG */
1515 1517 if (MaxQueueChildren > 0 &&
1516 1518 CurRunners + WorkGrp[curnum].wg_maxact > MaxQueueChildren)
1517 1519 break;
1518 1520
1519 1521 /*
1520 1522 ** Pick up where we left off (curnum), in case we
1521 1523 ** used up all the children last time without finishing.
1522 1524 ** This give a round-robin fairness to queue runs.
1523 1525 **
1524 1526 ** Increment CurRunners before calling run_work_group()
1525 1527 ** to avoid a "race condition" with proc_list_drop() which
1526 1528 ** decrements CurRunners if the queue runners terminate.
1527 1529 ** Notice: CurRunners is an upper limit, in some cases
1528 1530 ** (too few jobs in the queue) this value is larger than
1529 1531 ** the actual number of queue runners. The discrepancy can
1530 1532 ** increase if some queue runners "hang" for a long time.
1531 1533 */
1532 1534
1533 1535 CurRunners += WorkGrp[curnum].wg_maxact;
1534 1536 if (forkflag)
1535 1537 rwgflags |= RWG_FORK;
1536 1538 if (verbose)
1537 1539 rwgflags |= RWG_VERBOSE;
1538 1540 if (persistent)
1539 1541 rwgflags |= RWG_PERSISTENT;
1540 1542 if (runall)
1541 1543 rwgflags |= RWG_RUNALL;
1542 1544 ret = run_work_group(curnum, rwgflags);
1543 1545
1544 1546 /*
1545 1547 ** Failure means a message was printed for ETRN
1546 1548 ** and subsequent queues are likely to fail as well.
1547 1549 ** Decrement CurRunners in that case because
1548 1550 ** none have been started.
1549 1551 */
1550 1552
1551 1553 if (!ret)
1552 1554 {
1553 1555 CurRunners -= WorkGrp[curnum].wg_maxact;
1554 1556 break;
1555 1557 }
1556 1558
1557 1559 if (!persistent)
1558 1560 schedule_queue_runs(runall, curnum, true);
1559 1561 INCR_MOD(curnum, NumWorkGroups);
1560 1562 }
1561 1563
1562 1564 /* schedule left over queue runs */
1563 1565 if (i < NumWorkGroups && !NoMoreRunners && !persistent)
1564 1566 {
1565 1567 int h;
1566 1568
1567 1569 for (h = curnum; i < NumWorkGroups; i++)
1568 1570 {
1569 1571 schedule_queue_runs(runall, h, false);
1570 1572 INCR_MOD(h, NumWorkGroups);
1571 1573 }
1572 1574 }
1573 1575
1574 1576
1575 1577 #if SM_HEAP_CHECK
1576 1578 if (sm_debug_active(&DebugLeakQ, 1))
1577 1579 sm_heap_setgroup(oldgroup);
1578 1580 #endif /* SM_HEAP_CHECK */
1579 1581 return ret;
1580 1582 }
1581 1583
1582 1584 #if _FFR_SKIP_DOMAINS
1583 1585 /*
1584 1586 ** SKIP_DOMAINS -- Skip 'skip' number of domains in the WorkQ.
1585 1587 **
1586 1588 ** Added by Stephen Frost <sfrost@snowman.net> to support
1587 1589 ** having each runner process every N'th domain instead of
1588 1590 ** every N'th message.
1589 1591 **
1590 1592 ** Parameters:
1591 1593 ** skip -- number of domains in WorkQ to skip.
1592 1594 **
1593 1595 ** Returns:
1594 1596 ** total number of messages skipped.
1595 1597 **
1596 1598 ** Side Effects:
1597 1599 ** may change WorkQ
1598 1600 */
1599 1601
1600 1602 static int
1601 1603 skip_domains(skip)
1602 1604 int skip;
1603 1605 {
1604 1606 int n, seqjump;
1605 1607
1606 1608 for (n = 0, seqjump = 0; n < skip && WorkQ != NULL; seqjump++)
1607 1609 {
1608 1610 if (WorkQ->w_next != NULL)
1609 1611 {
1610 1612 if (WorkQ->w_host != NULL &&
1611 1613 WorkQ->w_next->w_host != NULL)
1612 1614 {
1613 1615 if (sm_strcasecmp(WorkQ->w_host,
1614 1616 WorkQ->w_next->w_host) != 0)
1615 1617 n++;
1616 1618 }
1617 1619 else
1618 1620 {
1619 1621 if ((WorkQ->w_host != NULL &&
1620 1622 WorkQ->w_next->w_host == NULL) ||
1621 1623 (WorkQ->w_host == NULL &&
1622 1624 WorkQ->w_next->w_host != NULL))
1623 1625 n++;
1624 1626 }
1625 1627 }
1626 1628 WorkQ = WorkQ->w_next;
1627 1629 }
1628 1630 return seqjump;
1629 1631 }
1630 1632 #endif /* _FFR_SKIP_DOMAINS */
1631 1633
1632 1634 /*
1633 1635 ** RUNNER_WORK -- have a queue runner do its work
1634 1636 **
1635 1637 ** Have a queue runner do its work a list of entries.
1636 1638 ** When work isn't directly being done then this process can take a signal
1637 1639 ** and terminate immediately (in a clean fashion of course).
1638 1640 ** When work is directly being done, it's not to be interrupted
1639 1641 ** immediately: the work should be allowed to finish at a clean point
1640 1642 ** before termination (in a clean fashion of course).
1641 1643 **
1642 1644 ** Parameters:
1643 1645 ** e -- envelope.
1644 1646 ** sequenceno -- 'th process to run WorkQ.
1645 1647 ** didfork -- did the calling process fork()?
1646 1648 ** skip -- process only each skip'th item.
1647 1649 ** njobs -- number of jobs in WorkQ.
1648 1650 **
1649 1651 ** Returns:
1650 1652 ** none.
1651 1653 **
1652 1654 ** Side Effects:
1653 1655 ** runs things in the mail queue.
1654 1656 */
1655 1657
1656 1658 static void
1657 1659 runner_work(e, sequenceno, didfork, skip, njobs)
1658 1660 register ENVELOPE *e;
1659 1661 int sequenceno;
1660 1662 bool didfork;
1661 1663 int skip;
1662 1664 int njobs;
1663 1665 {
1664 1666 int n, seqjump;
1665 1667 WORK *w;
1666 1668 time_t now;
1667 1669
1668 1670 SM_GET_LA(now);
1669 1671
1670 1672 /*
1671 1673 ** Here we temporarily block the second calling of the handlers.
1672 1674 ** This allows us to handle the signal without terminating in the
1673 1675 ** middle of direct work. If a signal does come, the test for
1674 1676 ** NoMoreRunners will find it.
1675 1677 */
1676 1678
1677 1679 BlockOldsh = true;
1678 1680 seqjump = skip;
1679 1681
1680 1682 /* process them once at a time */
1681 1683 while (WorkQ != NULL)
1682 1684 {
1683 1685 #if SM_HEAP_CHECK
1684 1686 SM_NONVOLATILE int oldgroup = 0;
1685 1687
1686 1688 if (sm_debug_active(&DebugLeakQ, 1))
1687 1689 {
1688 1690 oldgroup = sm_heap_group();
1689 1691 sm_heap_newgroup();
1690 1692 sm_dprintf("run_queue_group() heap group #%d\n",
1691 1693 sm_heap_group());
1692 1694 }
1693 1695 #endif /* SM_HEAP_CHECK */
1694 1696
1695 1697 /* do no more work */
1696 1698 if (NoMoreRunners)
1697 1699 {
1698 1700 /* Check that a valid signal handler is callable */
1699 1701 if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
1700 1702 Oldsh != runners_sighup &&
1701 1703 Oldsh != runners_sigterm)
1702 1704 (*Oldsh)(Oldsig);
1703 1705 break;
1704 1706 }
1705 1707
1706 1708 w = WorkQ; /* assign current work item */
1707 1709
1708 1710 /*
1709 1711 ** Set the head of the WorkQ to the next work item.
1710 1712 ** It is set 'skip' ahead (the number of parallel queue
1711 1713 ** runners working on WorkQ together) since each runner
1712 1714 ** works on every 'skip'th (N-th) item.
1713 1715 #if _FFR_SKIP_DOMAINS
1714 1716 ** In the case of the BYHOST Queue Sort Order, the 'item'
1715 1717 ** is a domain, so we work on every 'skip'th (N-th) domain.
1716 1718 #endif * _FFR_SKIP_DOMAINS *
1717 1719 */
1718 1720
1719 1721 #if _FFR_SKIP_DOMAINS
1720 1722 if (QueueSortOrder == QSO_BYHOST)
1721 1723 {
1722 1724 seqjump = 1;
1723 1725 if (WorkQ->w_next != NULL)
1724 1726 {
1725 1727 if (WorkQ->w_host != NULL &&
1726 1728 WorkQ->w_next->w_host != NULL)
1727 1729 {
1728 1730 if (sm_strcasecmp(WorkQ->w_host,
1729 1731 WorkQ->w_next->w_host)
1730 1732 != 0)
1731 1733 seqjump = skip_domains(skip);
1732 1734 else
1733 1735 WorkQ = WorkQ->w_next;
1734 1736 }
1735 1737 else
1736 1738 {
1737 1739 if ((WorkQ->w_host != NULL &&
1738 1740 WorkQ->w_next->w_host == NULL) ||
1739 1741 (WorkQ->w_host == NULL &&
1740 1742 WorkQ->w_next->w_host != NULL))
1741 1743 seqjump = skip_domains(skip);
1742 1744 else
1743 1745 WorkQ = WorkQ->w_next;
1744 1746 }
1745 1747 }
1746 1748 else
1747 1749 WorkQ = WorkQ->w_next;
1748 1750 }
1749 1751 else
1750 1752 #endif /* _FFR_SKIP_DOMAINS */
1751 1753 {
1752 1754 for (n = 0; n < skip && WorkQ != NULL; n++)
1753 1755 WorkQ = WorkQ->w_next;
1754 1756 }
1755 1757
1756 1758 e->e_to = NULL;
1757 1759
1758 1760 /*
1759 1761 ** Ignore jobs that are too expensive for the moment.
1760 1762 **
1761 1763 ** Get new load average every GET_NEW_LA_TIME seconds.
1762 1764 */
1763 1765
1764 1766 SM_GET_LA(now);
1765 1767 if (shouldqueue(WkRecipFact, Current_LA_time))
1766 1768 {
1767 1769 char *msg = "Aborting queue run: load average too high";
1768 1770
1769 1771 if (Verbose)
1770 1772 message("%s", msg);
1771 1773 if (LogLevel > 8)
1772 1774 sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
1773 1775 break;
1774 1776 }
1775 1777 if (shouldqueue(w->w_pri, w->w_ctime))
1776 1778 {
1777 1779 if (Verbose)
1778 1780 message(EmptyString);
1779 1781 if (QueueSortOrder == QSO_BYPRIORITY)
1780 1782 {
1781 1783 if (Verbose)
1782 1784 message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue",
1783 1785 qid_printqueue(w->w_qgrp,
1784 1786 w->w_qdir),
1785 1787 w->w_name + 2, sequenceno,
1786 1788 njobs);
1787 1789 if (LogLevel > 8)
1788 1790 sm_syslog(LOG_INFO, NOQID,
1789 1791 "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)",
1790 1792 qid_printqueue(w->w_qgrp,
1791 1793 w->w_qdir),
1792 1794 w->w_name + 2, w->w_pri,
1793 1795 CurrentLA, sequenceno,
1794 1796 njobs);
1795 1797 break;
1796 1798 }
1797 1799 else if (Verbose)
1798 1800 message("Skipping %s/%s (sequence %d of %d)",
1799 1801 qid_printqueue(w->w_qgrp, w->w_qdir),
1800 1802 w->w_name + 2, sequenceno, njobs);
1801 1803 }
1802 1804 else
1803 1805 {
1804 1806 if (Verbose)
1805 1807 {
1806 1808 message(EmptyString);
1807 1809 message("Running %s/%s (sequence %d of %d)",
1808 1810 qid_printqueue(w->w_qgrp, w->w_qdir),
1809 1811 w->w_name + 2, sequenceno, njobs);
1810 1812 }
1811 1813 if (didfork && MaxQueueChildren > 0)
1812 1814 {
1813 1815 sm_blocksignal(SIGCHLD);
1814 1816 (void) sm_signal(SIGCHLD, reapchild);
1815 1817 }
1816 1818 if (tTd(63, 100))
1817 1819 sm_syslog(LOG_DEBUG, NOQID,
1818 1820 "runqueue %s dowork(%s)",
1819 1821 qid_printqueue(w->w_qgrp, w->w_qdir),
1820 1822 w->w_name + 2);
1821 1823
1822 1824 (void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2,
1823 1825 ForkQueueRuns, false, e);
1824 1826 errno = 0;
1825 1827 }
1826 1828 sm_free(w->w_name); /* XXX */
1827 1829 if (w->w_host != NULL)
1828 1830 sm_free(w->w_host); /* XXX */
1829 1831 sm_free((char *) w); /* XXX */
1830 1832 sequenceno += seqjump; /* next sequence number */
1831 1833 #if SM_HEAP_CHECK
1832 1834 if (sm_debug_active(&DebugLeakQ, 1))
1833 1835 sm_heap_setgroup(oldgroup);
1834 1836 #endif /* SM_HEAP_CHECK */
1835 1837 }
1836 1838
1837 1839 BlockOldsh = false;
1838 1840
1839 1841 /* check the signals didn't happen during the revert */
1840 1842 if (NoMoreRunners)
1841 1843 {
1842 1844 /* Check that a valid signal handler is callable */
1843 1845 if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
1844 1846 Oldsh != runners_sighup && Oldsh != runners_sigterm)
1845 1847 (*Oldsh)(Oldsig);
1846 1848 }
1847 1849
1848 1850 Oldsh = SIG_DFL; /* after the NoMoreRunners check */
1849 1851 }
1850 1852 /*
1851 1853 ** RUN_WORK_GROUP -- run the jobs in a queue group from a work group.
1852 1854 **
1853 1855 ** Gets the stuff out of the queue in some presumably logical
1854 1856 ** order and processes them.
1855 1857 **
1856 1858 ** Parameters:
1857 1859 ** wgrp -- work group to process.
1858 1860 ** flags -- RWG_* flags
1859 1861 **
1860 1862 ** Returns:
1861 1863 ** true if the queue run successfully began.
1862 1864 **
1863 1865 ** Side Effects:
1864 1866 ** runs things in the mail queue.
1865 1867 */
1866 1868
1867 1869 /* Minimum sleep time for persistent queue runners */
1868 1870 #define MIN_SLEEP_TIME 5
1869 1871
1870 1872 bool
1871 1873 run_work_group(wgrp, flags)
1872 1874 int wgrp;
1873 1875 int flags;
1874 1876 {
1875 1877 register ENVELOPE *e;
1876 1878 int njobs, qdir;
1877 1879 int sequenceno = 1;
1878 1880 int qgrp, endgrp, h, i;
1879 1881 time_t now;
1880 1882 bool full, more;
1881 1883 SM_RPOOL_T *rpool;
1882 1884 extern ENVELOPE BlankEnvelope;
1883 1885 extern SIGFUNC_DECL reapchild __P((int));
1884 1886
1885 1887 if (wgrp < 0)
1886 1888 return false;
1887 1889
1888 1890 /*
1889 1891 ** If no work will ever be selected, don't even bother reading
1890 1892 ** the queue.
1891 1893 */
1892 1894
1893 1895 SM_GET_LA(now);
1894 1896
1895 1897 if (!bitset(RWG_PERSISTENT, flags) &&
1896 1898 shouldqueue(WkRecipFact, Current_LA_time))
1897 1899 {
1898 1900 char *msg = "Skipping queue run -- load average too high";
1899 1901
1900 1902 if (bitset(RWG_VERBOSE, flags))
1901 1903 message("458 %s\n", msg);
1902 1904 if (LogLevel > 8)
1903 1905 sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
1904 1906 return false;
1905 1907 }
1906 1908
1907 1909 /*
1908 1910 ** See if we already have too many children.
1909 1911 */
1910 1912
1911 1913 if (bitset(RWG_FORK, flags) &&
1912 1914 WorkGrp[wgrp].wg_lowqintvl > 0 &&
1913 1915 !bitset(RWG_PERSISTENT, flags) &&
1914 1916 MaxChildren > 0 && CurChildren >= MaxChildren)
1915 1917 {
1916 1918 char *msg = "Skipping queue run -- too many children";
1917 1919
1918 1920 if (bitset(RWG_VERBOSE, flags))
1919 1921 message("458 %s (%d)\n", msg, CurChildren);
1920 1922 if (LogLevel > 8)
1921 1923 sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)",
1922 1924 msg, CurChildren);
1923 1925 return false;
1924 1926 }
1925 1927
1926 1928 /*
1927 1929 ** See if we want to go off and do other useful work.
1928 1930 */
1929 1931
1930 1932 if (bitset(RWG_FORK, flags))
1931 1933 {
1932 1934 pid_t pid;
1933 1935
1934 1936 (void) sm_blocksignal(SIGCHLD);
1935 1937 (void) sm_signal(SIGCHLD, reapchild);
1936 1938
1937 1939 pid = dofork();
1938 1940 if (pid == -1)
1939 1941 {
1940 1942 const char *msg = "Skipping queue run -- fork() failed";
1941 1943 const char *err = sm_errstring(errno);
1942 1944
1943 1945 if (bitset(RWG_VERBOSE, flags))
1944 1946 message("458 %s: %s\n", msg, err);
1945 1947 if (LogLevel > 8)
1946 1948 sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s",
1947 1949 msg, err);
1948 1950 (void) sm_releasesignal(SIGCHLD);
1949 1951 return false;
1950 1952 }
1951 1953 if (pid != 0)
1952 1954 {
1953 1955 /* parent -- pick up intermediate zombie */
1954 1956 (void) sm_blocksignal(SIGALRM);
1955 1957
1956 1958 /* wgrp only used when queue runners are persistent */
1957 1959 proc_list_add(pid, "Queue runner", PROC_QUEUE,
1958 1960 WorkGrp[wgrp].wg_maxact,
1959 1961 bitset(RWG_PERSISTENT, flags) ? wgrp : -1,
1960 1962 NULL);
1961 1963 (void) sm_releasesignal(SIGALRM);
1962 1964 (void) sm_releasesignal(SIGCHLD);
1963 1965 return true;
1964 1966 }
1965 1967
1966 1968 /* child -- clean up signals */
1967 1969
1968 1970 /* Reset global flags */
1969 1971 RestartRequest = NULL;
1970 1972 RestartWorkGroup = false;
1971 1973 ShutdownRequest = NULL;
1972 1974 PendingSignal = 0;
1973 1975 CurrentPid = getpid();
1974 1976 close_sendmail_pid();
1975 1977
1976 1978 /*
1977 1979 ** Initialize exception stack and default exception
1978 1980 ** handler for child process.
1979 1981 */
1980 1982
1981 1983 sm_exc_newthread(fatal_error);
1982 1984 clrcontrol();
1983 1985 proc_list_clear();
1984 1986
1985 1987 /* Add parent process as first child item */
1986 1988 proc_list_add(CurrentPid, "Queue runner child process",
1987 1989 PROC_QUEUE_CHILD, 0, -1, NULL);
1988 1990 (void) sm_releasesignal(SIGCHLD);
1989 1991 (void) sm_signal(SIGCHLD, SIG_DFL);
1990 1992 (void) sm_signal(SIGHUP, SIG_DFL);
1991 1993 (void) sm_signal(SIGTERM, intsig);
1992 1994 }
1993 1995
1994 1996 /*
1995 1997 ** Release any resources used by the daemon code.
1996 1998 */
1997 1999
1998 2000 clrdaemon();
1999 2001
2000 2002 /* force it to run expensive jobs */
2001 2003 NoConnect = false;
2002 2004
2003 2005 /* drop privileges */
2004 2006 if (geteuid() == (uid_t) 0)
2005 2007 (void) drop_privileges(false);
2006 2008
2007 2009 /*
2008 2010 ** Create ourselves an envelope
2009 2011 */
2010 2012
2011 2013 CurEnv = &QueueEnvelope;
2012 2014 rpool = sm_rpool_new_x(NULL);
2013 2015 e = newenvelope(&QueueEnvelope, CurEnv, rpool);
2014 2016 e->e_flags = BlankEnvelope.e_flags;
2015 2017 e->e_parent = NULL;
2016 2018
2017 2019 /* make sure we have disconnected from parent */
2018 2020 if (bitset(RWG_FORK, flags))
2019 2021 {
2020 2022 disconnect(1, e);
2021 2023 QuickAbort = false;
2022 2024 }
2023 2025
2024 2026 /*
2025 2027 ** If we are running part of the queue, always ignore stored
2026 2028 ** host status.
2027 2029 */
2028 2030
2029 2031 if (QueueLimitId != NULL || QueueLimitSender != NULL ||
2030 2032 QueueLimitQuarantine != NULL ||
2031 2033 QueueLimitRecipient != NULL)
2032 2034 {
2033 2035 IgnoreHostStatus = true;
2034 2036 MinQueueAge = 0;
2035 2037 }
2036 2038
2037 2039 /*
2038 2040 ** Here is where we choose the queue group from the work group.
2039 2041 ** The caller of the "domorework" label must setup a new envelope.
2040 2042 */
2041 2043
2042 2044 endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */
2043 2045
2044 2046 domorework:
2045 2047
2046 2048 /*
2047 2049 ** Run a queue group if:
2048 2050 ** RWG_RUNALL bit is set or the bit for this group is set.
2049 2051 */
2050 2052
2051 2053 now = curtime();
2052 2054 for (;;)
2053 2055 {
2054 2056 /*
2055 2057 ** Find the next queue group within the work group that
2056 2058 ** has been marked as needing a run.
2057 2059 */
2058 2060
2059 2061 qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index;
2060 2062 WorkGrp[wgrp].wg_curqgrp++; /* advance */
2061 2063 WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */
2062 2064 if (bitset(RWG_RUNALL, flags) ||
2063 2065 (Queue[qgrp]->qg_nextrun <= now &&
2064 2066 Queue[qgrp]->qg_nextrun != (time_t) -1))
2065 2067 break;
2066 2068 if (endgrp == WorkGrp[wgrp].wg_curqgrp)
2067 2069 {
2068 2070 e->e_id = NULL;
2069 2071 if (bitset(RWG_FORK, flags))
2070 2072 finis(true, true, ExitStat);
2071 2073 return true; /* we're done */
2072 2074 }
2073 2075 }
2074 2076
2075 2077 qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */
2076 2078 #if _FFR_QUEUE_SCHED_DBG
2077 2079 if (tTd(69, 12))
2078 2080 sm_syslog(LOG_INFO, NOQID,
2079 2081 "rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d",
2080 2082 wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir),
2081 2083 WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp);
2082 2084 #endif /* _FFR_QUEUE_SCHED_DBG */
2083 2085
2084 2086 #if HASNICE
2085 2087 /* tweak niceness of queue runs */
2086 2088 if (Queue[qgrp]->qg_nice > 0)
2087 2089 (void) nice(Queue[qgrp]->qg_nice);
2088 2090 #endif /* HASNICE */
2089 2091
2090 2092 /* XXX running queue group... */
2091 2093 sm_setproctitle(true, CurEnv, "running queue: %s",
2092 2094 qid_printqueue(qgrp, qdir));
2093 2095
2094 2096 if (LogLevel > 69 || tTd(63, 99))
2095 2097 sm_syslog(LOG_DEBUG, NOQID,
2096 2098 "runqueue %s, pid=%d, forkflag=%d",
2097 2099 qid_printqueue(qgrp, qdir), (int) CurrentPid,
2098 2100 bitset(RWG_FORK, flags));
2099 2101
2100 2102 /*
2101 2103 ** Start making passes through the queue.
2102 2104 ** First, read and sort the entire queue.
2103 2105 ** Then, process the work in that order.
2104 2106 ** But if you take too long, start over.
2105 2107 */
2106 2108
2107 2109 for (i = 0; i < Queue[qgrp]->qg_numqueues; i++)
2108 2110 {
2109 2111 (void) gatherq(qgrp, qdir, false, &full, &more, &h);
2110 2112 #if SM_CONF_SHM
2111 2113 if (ShmId != SM_SHM_NO_ID)
2112 2114 QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h;
2113 2115 #endif /* SM_CONF_SHM */
2114 2116 /* If there are no more items in this queue advance */
2115 2117 if (!more)
2116 2118 {
2117 2119 /* A round-robin advance */
2118 2120 qdir++;
2119 2121 qdir %= Queue[qgrp]->qg_numqueues;
2120 2122 }
2121 2123
2122 2124 /* Has the WorkList reached the limit? */
2123 2125 if (full)
2124 2126 break; /* don't try to gather more */
2125 2127 }
2126 2128
2127 2129 /* order the existing work requests */
2128 2130 njobs = sortq(Queue[qgrp]->qg_maxlist);
2129 2131 Queue[qgrp]->qg_curnum = qdir; /* update */
2130 2132
2131 2133
2132 2134 if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags))
2133 2135 {
2134 2136 int loop, maxrunners;
2135 2137 pid_t pid;
2136 2138
2137 2139 /*
2138 2140 ** For this WorkQ we want to fork off N children (maxrunners)
2139 2141 ** at this point. Each child has a copy of WorkQ. Each child
2140 2142 ** will process every N-th item. The parent will wait for all
2141 2143 ** of the children to finish before moving on to the next
2142 2144 ** queue group within the work group. This saves us forking
2143 2145 ** a new runner-child for each work item.
2144 2146 ** It's valid for qg_maxqrun == 0 since this may be an
2145 2147 ** explicit "don't run this queue" setting.
2146 2148 */
2147 2149
2148 2150 maxrunners = Queue[qgrp]->qg_maxqrun;
2149 2151
2150 2152 /*
2151 2153 ** If no runners are configured for this group but
2152 2154 ** the queue is "forced" then lets use 1 runner.
2153 2155 */
2154 2156
2155 2157 if (maxrunners == 0 && bitset(RWG_FORCE, flags))
2156 2158 maxrunners = 1;
2157 2159
2158 2160 /* No need to have more runners then there are jobs */
2159 2161 if (maxrunners > njobs)
2160 2162 maxrunners = njobs;
2161 2163 for (loop = 0; loop < maxrunners; loop++)
2162 2164 {
2163 2165 /*
2164 2166 ** Since the delivery may happen in a child and the
2165 2167 ** parent does not wait, the parent may close the
2166 2168 ** maps thereby removing any shared memory used by
2167 2169 ** the map. Therefore, close the maps now so the
2168 2170 ** child will dynamically open them if necessary.
2169 2171 */
2170 2172
2171 2173 closemaps(false);
2172 2174
2173 2175 pid = fork();
2174 2176 if (pid < 0)
2175 2177 {
2176 2178 syserr("run_work_group: cannot fork");
2177 2179 return false;
2178 2180 }
2179 2181 else if (pid > 0)
2180 2182 {
2181 2183 /* parent -- clean out connection cache */
2182 2184 mci_flush(false, NULL);
2183 2185 #if _FFR_SKIP_DOMAINS
2184 2186 if (QueueSortOrder == QSO_BYHOST)
2185 2187 {
2186 2188 sequenceno += skip_domains(1);
2187 2189 }
2188 2190 else
2189 2191 #endif /* _FFR_SKIP_DOMAINS */
2190 2192 {
2191 2193 /* for the skip */
2192 2194 WorkQ = WorkQ->w_next;
2193 2195 sequenceno++;
2194 2196 }
2195 2197 proc_list_add(pid, "Queue child runner process",
2196 2198 PROC_QUEUE_CHILD, 0, -1, NULL);
2197 2199
2198 2200 /* No additional work, no additional runners */
2199 2201 if (WorkQ == NULL)
2200 2202 break;
2201 2203 }
2202 2204 else
2203 2205 {
2204 2206 /* child -- Reset global flags */
2205 2207 RestartRequest = NULL;
2206 2208 RestartWorkGroup = false;
2207 2209 ShutdownRequest = NULL;
2208 2210 PendingSignal = 0;
2209 2211 CurrentPid = getpid();
2210 2212 close_sendmail_pid();
2211 2213
2212 2214 /*
2213 2215 ** Initialize exception stack and default
2214 2216 ** exception handler for child process.
2215 2217 ** When fork()'d the child now has a private
2216 2218 ** copy of WorkQ at its current position.
2217 2219 */
2218 2220
2219 2221 sm_exc_newthread(fatal_error);
2220 2222
2221 2223 /*
2222 2224 ** SMTP processes (whether -bd or -bs) set
2223 2225 ** SIGCHLD to reapchild to collect
2224 2226 ** children status. However, at delivery
2225 2227 ** time, that status must be collected
2226 2228 ** by sm_wait() to be dealt with properly
2227 2229 ** (check success of delivery based
2228 2230 ** on status code, etc). Therefore, if we
2229 2231 ** are an SMTP process, reset SIGCHLD
2230 2232 ** back to the default so reapchild
2231 2233 ** doesn't collect status before
2232 2234 ** sm_wait().
2233 2235 */
2234 2236
2235 2237 if (OpMode == MD_SMTP ||
2236 2238 OpMode == MD_DAEMON ||
2237 2239 MaxQueueChildren > 0)
2238 2240 {
2239 2241 proc_list_clear();
2240 2242 sm_releasesignal(SIGCHLD);
2241 2243 (void) sm_signal(SIGCHLD, SIG_DFL);
2242 2244 }
2243 2245
2244 2246 /* child -- error messages to the transcript */
2245 2247 QuickAbort = OnlyOneError = false;
2246 2248 runner_work(e, sequenceno, true,
2247 2249 maxrunners, njobs);
2248 2250
2249 2251 /* This child is done */
2250 2252 finis(true, true, ExitStat);
2251 2253 /* NOTREACHED */
2252 2254 }
2253 2255 }
2254 2256
2255 2257 sm_releasesignal(SIGCHLD);
2256 2258
2257 2259 /*
2258 2260 ** Wait until all of the runners have completed before
2259 2261 ** seeing if there is another queue group in the
2260 2262 ** work group to process.
2261 2263 ** XXX Future enhancement: don't wait() for all children
2262 2264 ** here, just go ahead and make sure that overall the number
2263 2265 ** of children is not exceeded.
2264 2266 */
2265 2267
2266 2268 while (CurChildren > 0)
2267 2269 {
2268 2270 int status;
2269 2271 pid_t ret;
2270 2272
2271 2273 while ((ret = sm_wait(&status)) <= 0)
2272 2274 continue;
2273 2275 proc_list_drop(ret, status, NULL);
2274 2276 }
2275 2277 }
2276 2278 else if (Queue[qgrp]->qg_maxqrun > 0 || bitset(RWG_FORCE, flags))
2277 2279 {
2278 2280 /*
2279 2281 ** When current process will not fork children to do the work,
2280 2282 ** it will do the work itself. The 'skip' will be 1 since
2281 2283 ** there are no child runners to divide the work across.
2282 2284 */
2283 2285
2284 2286 runner_work(e, sequenceno, false, 1, njobs);
2285 2287 }
2286 2288
2287 2289 /* free memory allocated by newenvelope() above */
2288 2290 sm_rpool_free(rpool);
2289 2291 QueueEnvelope.e_rpool = NULL;
2290 2292
2291 2293 /* Are there still more queues in the work group to process? */
2292 2294 if (endgrp != WorkGrp[wgrp].wg_curqgrp)
2293 2295 {
2294 2296 rpool = sm_rpool_new_x(NULL);
2295 2297 e = newenvelope(&QueueEnvelope, CurEnv, rpool);
2296 2298 e->e_flags = BlankEnvelope.e_flags;
2297 2299 goto domorework;
2298 2300 }
2299 2301
2300 2302 /* No more queues in work group to process. Now check persistent. */
2301 2303 if (bitset(RWG_PERSISTENT, flags))
2302 2304 {
2303 2305 sequenceno = 1;
2304 2306 sm_setproctitle(true, CurEnv, "running queue: %s",
2305 2307 qid_printqueue(qgrp, qdir));
2306 2308
2307 2309 /*
2308 2310 ** close bogus maps, i.e., maps which caused a tempfail,
2309 2311 ** so we get fresh map connections on the next lookup.
2310 2312 ** closemaps() is also called when children are started.
2311 2313 */
2312 2314
2313 2315 closemaps(true);
2314 2316
2315 2317 /* Close any cached connections. */
2316 2318 mci_flush(true, NULL);
2317 2319
2318 2320 /* Clean out expired related entries. */
2319 2321 rmexpstab();
2320 2322
2321 2323 #if NAMED_BIND
2322 2324 /* Update MX records for FallbackMX. */
2323 2325 if (FallbackMX != NULL)
2324 2326 (void) getfallbackmxrr(FallbackMX);
2325 2327 #endif /* NAMED_BIND */
2326 2328
2327 2329 #if USERDB
2328 2330 /* close UserDatabase */
2329 2331 _udbx_close();
2330 2332 #endif /* USERDB */
2331 2333
2332 2334 #if SM_HEAP_CHECK
2333 2335 if (sm_debug_active(&SmHeapCheck, 2)
2334 2336 && access("memdump", F_OK) == 0
2335 2337 )
2336 2338 {
2337 2339 SM_FILE_T *out;
2338 2340
2339 2341 remove("memdump");
2340 2342 out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
2341 2343 "memdump.out", SM_IO_APPEND, NULL);
2342 2344 if (out != NULL)
2343 2345 {
2344 2346 (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n");
2345 2347 sm_heap_report(out,
2346 2348 sm_debug_level(&SmHeapCheck) - 1);
2347 2349 (void) sm_io_close(out, SM_TIME_DEFAULT);
2348 2350 }
2349 2351 }
2350 2352 #endif /* SM_HEAP_CHECK */
2351 2353
2352 2354 /* let me rest for a second to catch my breath */
2353 2355 if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME)
2354 2356 sleep(MIN_SLEEP_TIME);
2355 2357 else if (WorkGrp[wgrp].wg_lowqintvl <= 0)
2356 2358 sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME);
2357 2359 else
2358 2360 sleep(WorkGrp[wgrp].wg_lowqintvl);
2359 2361
2360 2362 /*
2361 2363 ** Get the LA outside the WorkQ loop if necessary.
2362 2364 ** In a persistent queue runner the code is repeated over
2363 2365 ** and over but gatherq() may ignore entries due to
2364 2366 ** shouldqueue() (do we really have to do this twice?).
2365 2367 ** Hence the queue runners would just idle around when once
2366 2368 ** CurrentLA caused all entries in a queue to be ignored.
2367 2369 */
2368 2370
2369 2371 if (njobs == 0)
2370 2372 SM_GET_LA(now);
2371 2373 rpool = sm_rpool_new_x(NULL);
2372 2374 e = newenvelope(&QueueEnvelope, CurEnv, rpool);
2373 2375 e->e_flags = BlankEnvelope.e_flags;
2374 2376 goto domorework;
2375 2377 }
2376 2378
2377 2379 /* exit without the usual cleanup */
2378 2380 e->e_id = NULL;
2379 2381 if (bitset(RWG_FORK, flags))
2380 2382 finis(true, true, ExitStat);
2381 2383 /* NOTREACHED */
2382 2384 return true;
2383 2385 }
2384 2386
2385 2387 /*
2386 2388 ** DOQUEUERUN -- do a queue run?
2387 2389 */
2388 2390
2389 2391 bool
2390 2392 doqueuerun()
2391 2393 {
2392 2394 return DoQueueRun;
2393 2395 }
2394 2396
2395 2397 /*
2396 2398 ** RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done.
2397 2399 **
2398 2400 ** Parameters:
2399 2401 ** none.
2400 2402 **
2401 2403 ** Returns:
2402 2404 ** none.
2403 2405 **
2404 2406 ** Side Effects:
2405 2407 ** The invocation of this function via an alarm may interrupt
2406 2408 ** a set of actions. Thus errno may be set in that context.
2407 2409 ** We need to restore errno at the end of this function to ensure
2408 2410 ** that any work done here that sets errno doesn't return a
2409 2411 ** misleading/false errno value. Errno may be EINTR upon entry to
2410 2412 ** this function because of non-restartable/continuable system
2411 2413 ** API was active. Iff this is true we will override errno as
2412 2414 ** a timeout (as a more accurate error message).
2413 2415 **
2414 2416 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
2415 2417 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2416 2418 ** DOING.
2417 2419 */
2418 2420
2419 2421 void
2420 2422 runqueueevent(ignore)
2421 2423 int ignore;
2422 2424 {
2423 2425 int save_errno = errno;
2424 2426
2425 2427 /*
2426 2428 ** Set the general bit that we want a queue run,
2427 2429 ** tested in doqueuerun()
2428 2430 */
2429 2431
2430 2432 DoQueueRun = true;
2431 2433 #if _FFR_QUEUE_SCHED_DBG
2432 2434 if (tTd(69, 10))
2433 2435 sm_syslog(LOG_INFO, NOQID, "rqe: done");
2434 2436 #endif /* _FFR_QUEUE_SCHED_DBG */
2435 2437
2436 2438 errno = save_errno;
2437 2439 if (errno == EINTR)
2438 2440 errno = ETIMEDOUT;
2439 2441 }
2440 2442 /*
2441 2443 ** GATHERQ -- gather messages from the message queue(s) the work queue.
2442 2444 **
2443 2445 ** Parameters:
2444 2446 ** qgrp -- the index of the queue group.
2445 2447 ** qdir -- the index of the queue directory.
2446 2448 ** doall -- if set, include everything in the queue (even
2447 2449 ** the jobs that cannot be run because the load
2448 2450 ** average is too high, or MaxQueueRun is reached).
2449 2451 ** Otherwise, exclude those jobs.
2450 2452 ** full -- (optional) to be set 'true' if WorkList is full
2451 2453 ** more -- (optional) to be set 'true' if there are still more
2452 2454 ** messages in this queue not added to WorkList
2453 2455 ** pnentries -- (optional) total nuber of entries in queue
2454 2456 **
2455 2457 ** Returns:
2456 2458 ** The number of request in the queue (not necessarily
2457 2459 ** the number of requests in WorkList however).
2458 2460 **
2459 2461 ** Side Effects:
2460 2462 ** prepares available work into WorkList
2461 2463 */
2462 2464
2463 2465 #define NEED_P 0001 /* 'P': priority */
2464 2466 #define NEED_T 0002 /* 'T': time */
2465 2467 #define NEED_R 0004 /* 'R': recipient */
2466 2468 #define NEED_S 0010 /* 'S': sender */
2467 2469 #define NEED_H 0020 /* host */
2468 2470 #define HAS_QUARANTINE 0040 /* has an unexpected 'q' line */
2469 2471 #define NEED_QUARANTINE 0100 /* 'q': reason */
2470 2472
2471 2473 static WORK *WorkList = NULL; /* list of unsort work */
2472 2474 static int WorkListSize = 0; /* current max size of WorkList */
2473 2475 static int WorkListCount = 0; /* # of work items in WorkList */
2474 2476
2475 2477 static int
2476 2478 gatherq(qgrp, qdir, doall, full, more, pnentries)
2477 2479 int qgrp;
2478 2480 int qdir;
2479 2481 bool doall;
2480 2482 bool *full;
2481 2483 bool *more;
2482 2484 int *pnentries;
2483 2485 {
2484 2486 register struct dirent *d;
2485 2487 register WORK *w;
2486 2488 register char *p;
2487 2489 DIR *f;
2488 2490 int i, num_ent, wn, nentries;
2489 2491 QUEUE_CHAR *check;
2490 2492 char qd[MAXPATHLEN];
2491 2493 char qf[MAXPATHLEN];
2492 2494
2493 2495 wn = WorkListCount - 1;
2494 2496 num_ent = 0;
2495 2497 nentries = 0;
2496 2498 if (qdir == NOQDIR)
2497 2499 (void) sm_strlcpy(qd, ".", sizeof(qd));
2498 2500 else
2499 2501 (void) sm_strlcpyn(qd, sizeof(qd), 2,
2500 2502 Queue[qgrp]->qg_qpaths[qdir].qp_name,
2501 2503 (bitset(QP_SUBQF,
2502 2504 Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
2503 2505 ? "/qf" : ""));
2504 2506
2505 2507 if (tTd(41, 1))
2506 2508 {
2507 2509 sm_dprintf("gatherq:\n");
2508 2510
2509 2511 check = QueueLimitId;
2510 2512 while (check != NULL)
2511 2513 {
2512 2514 sm_dprintf("\tQueueLimitId = %s%s\n",
2513 2515 check->queue_negate ? "!" : "",
2514 2516 check->queue_match);
2515 2517 check = check->queue_next;
2516 2518 }
2517 2519
2518 2520 check = QueueLimitSender;
2519 2521 while (check != NULL)
2520 2522 {
2521 2523 sm_dprintf("\tQueueLimitSender = %s%s\n",
2522 2524 check->queue_negate ? "!" : "",
2523 2525 check->queue_match);
2524 2526 check = check->queue_next;
2525 2527 }
2526 2528
2527 2529 check = QueueLimitRecipient;
2528 2530 while (check != NULL)
2529 2531 {
2530 2532 sm_dprintf("\tQueueLimitRecipient = %s%s\n",
2531 2533 check->queue_negate ? "!" : "",
2532 2534 check->queue_match);
2533 2535 check = check->queue_next;
2534 2536 }
2535 2537
2536 2538 if (QueueMode == QM_QUARANTINE)
2537 2539 {
2538 2540 check = QueueLimitQuarantine;
2539 2541 while (check != NULL)
2540 2542 {
2541 2543 sm_dprintf("\tQueueLimitQuarantine = %s%s\n",
2542 2544 check->queue_negate ? "!" : "",
2543 2545 check->queue_match);
2544 2546 check = check->queue_next;
2545 2547 }
2546 2548 }
2547 2549 }
2548 2550
2549 2551 /* open the queue directory */
2550 2552 f = opendir(qd);
2551 2553 if (f == NULL)
2552 2554 {
2553 2555 syserr("gatherq: cannot open \"%s\"",
2554 2556 qid_printqueue(qgrp, qdir));
2555 2557 if (full != NULL)
2556 2558 *full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0;
2557 2559 if (more != NULL)
2558 2560 *more = false;
2559 2561 return 0;
2560 2562 }
2561 2563
2562 2564 /*
2563 2565 ** Read the work directory.
2564 2566 */
2565 2567
2566 2568 while ((d = readdir(f)) != NULL)
2567 2569 {
2568 2570 SM_FILE_T *cf;
2569 2571 int qfver = 0;
2570 2572 char lbuf[MAXNAME + 1];
2571 2573 struct stat sbuf;
2572 2574
2573 2575 if (tTd(41, 50))
2574 2576 sm_dprintf("gatherq: checking %s..", d->d_name);
2575 2577
2576 2578 /* is this an interesting entry? */
2577 2579 if (!(((QueueMode == QM_NORMAL &&
2578 2580 d->d_name[0] == NORMQF_LETTER) ||
2579 2581 (QueueMode == QM_QUARANTINE &&
2580 2582 d->d_name[0] == QUARQF_LETTER) ||
2581 2583 (QueueMode == QM_LOST &&
2582 2584 d->d_name[0] == LOSEQF_LETTER)) &&
2583 2585 d->d_name[1] == 'f'))
2584 2586 {
2585 2587 if (tTd(41, 50))
2586 2588 sm_dprintf(" skipping\n");
2587 2589 continue;
2588 2590 }
2589 2591 if (tTd(41, 50))
2590 2592 sm_dprintf("\n");
2591 2593
2592 2594 if (strlen(d->d_name) >= MAXQFNAME)
2593 2595 {
2594 2596 if (Verbose)
2595 2597 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2596 2598 "gatherq: %s too long, %d max characters\n",
2597 2599 d->d_name, MAXQFNAME);
2598 2600 if (LogLevel > 0)
2599 2601 sm_syslog(LOG_ALERT, NOQID,
2600 2602 "gatherq: %s too long, %d max characters",
2601 2603 d->d_name, MAXQFNAME);
2602 2604 continue;
2603 2605 }
2604 2606
2605 2607 ++nentries;
2606 2608 check = QueueLimitId;
2607 2609 while (check != NULL)
2608 2610 {
2609 2611 if (strcontainedin(false, check->queue_match,
2610 2612 d->d_name) != check->queue_negate)
2611 2613 break;
2612 2614 else
2613 2615 check = check->queue_next;
2614 2616 }
2615 2617 if (QueueLimitId != NULL && check == NULL)
2616 2618 continue;
2617 2619
2618 2620 /* grow work list if necessary */
2619 2621 if (++wn >= MaxQueueRun && MaxQueueRun > 0)
2620 2622 {
2621 2623 if (wn == MaxQueueRun && LogLevel > 0)
2622 2624 sm_syslog(LOG_WARNING, NOQID,
2623 2625 "WorkList for %s maxed out at %d",
2624 2626 qid_printqueue(qgrp, qdir),
2625 2627 MaxQueueRun);
2626 2628 if (doall)
2627 2629 continue; /* just count entries */
2628 2630 break;
2629 2631 }
2630 2632 if (wn >= WorkListSize)
2631 2633 {
2632 2634 grow_wlist(qgrp, qdir);
2633 2635 if (wn >= WorkListSize)
2634 2636 continue;
2635 2637 }
2636 2638 SM_ASSERT(wn >= 0);
2637 2639 w = &WorkList[wn];
2638 2640
2639 2641 (void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", d->d_name);
2640 2642 if (stat(qf, &sbuf) < 0)
2641 2643 {
2642 2644 if (errno != ENOENT)
2643 2645 sm_syslog(LOG_INFO, NOQID,
2644 2646 "gatherq: can't stat %s/%s",
2645 2647 qid_printqueue(qgrp, qdir),
2646 2648 d->d_name);
2647 2649 wn--;
2648 2650 continue;
2649 2651 }
2650 2652 if (!bitset(S_IFREG, sbuf.st_mode))
2651 2653 {
2652 2654 /* Yikes! Skip it or we will hang on open! */
2653 2655 if (!((d->d_name[0] == DATAFL_LETTER ||
2654 2656 d->d_name[0] == NORMQF_LETTER ||
2655 2657 d->d_name[0] == QUARQF_LETTER ||
2656 2658 d->d_name[0] == LOSEQF_LETTER ||
2657 2659 d->d_name[0] == XSCRPT_LETTER) &&
2658 2660 d->d_name[1] == 'f' && d->d_name[2] == '\0'))
2659 2661 syserr("gatherq: %s/%s is not a regular file",
2660 2662 qid_printqueue(qgrp, qdir), d->d_name);
2661 2663 wn--;
2662 2664 continue;
2663 2665 }
2664 2666
2665 2667 /* avoid work if possible */
2666 2668 if ((QueueSortOrder == QSO_BYFILENAME ||
2667 2669 QueueSortOrder == QSO_BYMODTIME ||
2668 2670 QueueSortOrder == QSO_NONE ||
2669 2671 QueueSortOrder == QSO_RANDOM) &&
2670 2672 QueueLimitQuarantine == NULL &&
2671 2673 QueueLimitSender == NULL &&
2672 2674 QueueLimitRecipient == NULL)
2673 2675 {
2674 2676 w->w_qgrp = qgrp;
2675 2677 w->w_qdir = qdir;
2676 2678 w->w_name = newstr(d->d_name);
2677 2679 w->w_host = NULL;
2678 2680 w->w_lock = w->w_tooyoung = false;
2679 2681 w->w_pri = 0;
2680 2682 w->w_ctime = 0;
2681 2683 w->w_mtime = sbuf.st_mtime;
2682 2684 ++num_ent;
2683 2685 continue;
2684 2686 }
2685 2687
2686 2688 /* open control file */
2687 2689 cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B,
2688 2690 NULL);
2689 2691 if (cf == NULL && OpMode != MD_PRINT)
2690 2692 {
2691 2693 /* this may be some random person sending hir msgs */
2692 2694 if (tTd(41, 2))
2693 2695 sm_dprintf("gatherq: cannot open %s: %s\n",
2694 2696 d->d_name, sm_errstring(errno));
2695 2697 errno = 0;
2696 2698 wn--;
2697 2699 continue;
2698 2700 }
2699 2701 w->w_qgrp = qgrp;
2700 2702 w->w_qdir = qdir;
2701 2703 w->w_name = newstr(d->d_name);
2702 2704 w->w_host = NULL;
2703 2705 if (cf != NULL)
2704 2706 {
2705 2707 w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD,
2706 2708 NULL),
2707 2709 w->w_name, NULL,
2708 2710 LOCK_SH|LOCK_NB);
2709 2711 }
2710 2712 w->w_tooyoung = false;
2711 2713
2712 2714 /* make sure jobs in creation don't clog queue */
2713 2715 w->w_pri = 0x7fffffff;
2714 2716 w->w_ctime = 0;
2715 2717 w->w_mtime = sbuf.st_mtime;
2716 2718
2717 2719 /* extract useful information */
2718 2720 i = NEED_P|NEED_T;
2719 2721 if (QueueSortOrder == QSO_BYHOST
2720 2722 #if _FFR_RHS
2721 2723 || QueueSortOrder == QSO_BYSHUFFLE
2722 2724 #endif /* _FFR_RHS */
2723 2725 )
2724 2726 {
2725 2727 /* need w_host set for host sort order */
2726 2728 i |= NEED_H;
2727 2729 }
2728 2730 if (QueueLimitSender != NULL)
2729 2731 i |= NEED_S;
2730 2732 if (QueueLimitRecipient != NULL)
2731 2733 i |= NEED_R;
2732 2734 if (QueueLimitQuarantine != NULL)
2733 2735 i |= NEED_QUARANTINE;
2734 2736 while (cf != NULL && i != 0 &&
2735 2737 sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf,
2736 2738 sizeof(lbuf)) != NULL)
2737 2739 {
2738 2740 int c;
2739 2741 time_t age;
2740 2742
2741 2743 p = strchr(lbuf, '\n');
2742 2744 if (p != NULL)
2743 2745 *p = '\0';
2744 2746 else
2745 2747 {
2746 2748 /* flush rest of overly long line */
2747 2749 while ((c = sm_io_getc(cf, SM_TIME_DEFAULT))
2748 2750 != SM_IO_EOF && c != '\n')
2749 2751 continue;
2750 2752 }
2751 2753
2752 2754 switch (lbuf[0])
2753 2755 {
2754 2756 case 'V':
2755 2757 qfver = atoi(&lbuf[1]);
2756 2758 break;
2757 2759
2758 2760 case 'P':
2759 2761 w->w_pri = atol(&lbuf[1]);
2760 2762 i &= ~NEED_P;
2761 2763 break;
2762 2764
2763 2765 case 'T':
2764 2766 w->w_ctime = atol(&lbuf[1]);
2765 2767 i &= ~NEED_T;
2766 2768 break;
2767 2769
2768 2770 case 'q':
2769 2771 if (QueueMode != QM_QUARANTINE &&
2770 2772 QueueMode != QM_LOST)
2771 2773 {
2772 2774 if (tTd(41, 49))
2773 2775 sm_dprintf("%s not marked as quarantined but has a 'q' line\n",
2774 2776 w->w_name);
2775 2777 i |= HAS_QUARANTINE;
2776 2778 }
2777 2779 else if (QueueMode == QM_QUARANTINE)
2778 2780 {
2779 2781 if (QueueLimitQuarantine == NULL)
2780 2782 {
2781 2783 i &= ~NEED_QUARANTINE;
2782 2784 break;
2783 2785 }
2784 2786 p = &lbuf[1];
2785 2787 check = QueueLimitQuarantine;
2786 2788 while (check != NULL)
2787 2789 {
2788 2790 if (strcontainedin(false,
2789 2791 check->queue_match,
2790 2792 p) !=
2791 2793 check->queue_negate)
2792 2794 break;
2793 2795 else
2794 2796 check = check->queue_next;
2795 2797 }
2796 2798 if (check != NULL)
2797 2799 i &= ~NEED_QUARANTINE;
2798 2800 }
2799 2801 break;
2800 2802
2801 2803 case 'R':
2802 2804 if (w->w_host == NULL &&
2803 2805 (p = strrchr(&lbuf[1], '@')) != NULL)
2804 2806 {
2805 2807 #if _FFR_RHS
2806 2808 if (QueueSortOrder == QSO_BYSHUFFLE)
2807 2809 w->w_host = newstr(&p[1]);
2808 2810 else
2809 2811 #endif /* _FFR_RHS */
2810 2812 w->w_host = strrev(&p[1]);
2811 2813 makelower(w->w_host);
2812 2814 i &= ~NEED_H;
2813 2815 }
2814 2816 if (QueueLimitRecipient == NULL)
2815 2817 {
2816 2818 i &= ~NEED_R;
2817 2819 break;
2818 2820 }
2819 2821 if (qfver > 0)
2820 2822 {
2821 2823 p = strchr(&lbuf[1], ':');
2822 2824 if (p == NULL)
2823 2825 p = &lbuf[1];
2824 2826 else
2825 2827 ++p; /* skip over ':' */
2826 2828 }
2827 2829 else
2828 2830 p = &lbuf[1];
2829 2831 check = QueueLimitRecipient;
2830 2832 while (check != NULL)
2831 2833 {
2832 2834 if (strcontainedin(true,
2833 2835 check->queue_match,
2834 2836 p) !=
2835 2837 check->queue_negate)
2836 2838 break;
2837 2839 else
2838 2840 check = check->queue_next;
2839 2841 }
2840 2842 if (check != NULL)
2841 2843 i &= ~NEED_R;
2842 2844 break;
2843 2845
2844 2846 case 'S':
2845 2847 check = QueueLimitSender;
2846 2848 while (check != NULL)
2847 2849 {
2848 2850 if (strcontainedin(true,
2849 2851 check->queue_match,
2850 2852 &lbuf[1]) !=
2851 2853 check->queue_negate)
2852 2854 break;
2853 2855 else
2854 2856 check = check->queue_next;
2855 2857 }
2856 2858 if (check != NULL)
2857 2859 i &= ~NEED_S;
2858 2860 break;
2859 2861
2860 2862 case 'K':
2861 2863 #if _FFR_EXPDELAY
2862 2864 if (MaxQueueAge > 0)
2863 2865 {
2864 2866 time_t lasttry, delay;
2865 2867
2866 2868 lasttry = (time_t) atol(&lbuf[1]);
2867 2869 delay = MIN(lasttry - w->w_ctime,
2868 2870 MaxQueueAge);
2869 2871 age = curtime() - lasttry;
2870 2872 if (age < delay)
2871 2873 w->w_tooyoung = true;
2872 2874 break;
2873 2875 }
2874 2876 #endif /* _FFR_EXPDELAY */
2875 2877
2876 2878 age = curtime() - (time_t) atol(&lbuf[1]);
2877 2879 if (age >= 0 && MinQueueAge > 0 &&
2878 2880 age < MinQueueAge)
2879 2881 w->w_tooyoung = true;
2880 2882 break;
2881 2883
2882 2884 case 'N':
2883 2885 if (atol(&lbuf[1]) == 0)
2884 2886 w->w_tooyoung = false;
2885 2887 break;
2886 2888 }
2887 2889 }
2888 2890 if (cf != NULL)
2889 2891 (void) sm_io_close(cf, SM_TIME_DEFAULT);
2890 2892
2891 2893 if ((!doall && (shouldqueue(w->w_pri, w->w_ctime) ||
2892 2894 w->w_tooyoung)) ||
2893 2895 bitset(HAS_QUARANTINE, i) ||
2894 2896 bitset(NEED_QUARANTINE, i) ||
2895 2897 bitset(NEED_R|NEED_S, i))
2896 2898 {
2897 2899 /* don't even bother sorting this job in */
2898 2900 if (tTd(41, 49))
2899 2901 sm_dprintf("skipping %s (%x)\n", w->w_name, i);
2900 2902 sm_free(w->w_name); /* XXX */
2901 2903 if (w->w_host != NULL)
2902 2904 sm_free(w->w_host); /* XXX */
2903 2905 wn--;
2904 2906 }
2905 2907 else
2906 2908 ++num_ent;
2907 2909 }
2908 2910 (void) closedir(f);
2909 2911 wn++;
2910 2912
2911 2913 i = wn - WorkListCount;
2912 2914 WorkListCount += SM_MIN(num_ent, WorkListSize);
2913 2915
2914 2916 if (more != NULL)
2915 2917 *more = WorkListCount < wn;
2916 2918
2917 2919 if (full != NULL)
2918 2920 *full = (wn >= MaxQueueRun && MaxQueueRun > 0) ||
2919 2921 (WorkList == NULL && wn > 0);
2920 2922
2921 2923 if (pnentries != NULL)
2922 2924 *pnentries = nentries;
2923 2925 return i;
2924 2926 }
2925 2927 /*
2926 2928 ** SORTQ -- sort the work list
2927 2929 **
2928 2930 ** First the old WorkQ is cleared away. Then the WorkList is sorted
2929 2931 ** for all items so that important (higher sorting value) items are not
2930 2932 ** trunctated off. Then the most important items are moved from
2931 2933 ** WorkList to WorkQ. The lower count of 'max' or MaxListCount items
2932 2934 ** are moved.
2933 2935 **
2934 2936 ** Parameters:
2935 2937 ** max -- maximum number of items to be placed in WorkQ
2936 2938 **
2937 2939 ** Returns:
2938 2940 ** the number of items in WorkQ
2939 2941 **
2940 2942 ** Side Effects:
2941 2943 ** WorkQ gets released and filled with new work. WorkList
2942 2944 ** gets released. Work items get sorted in order.
2943 2945 */
2944 2946
2945 2947 static int
2946 2948 sortq(max)
2947 2949 int max;
2948 2950 {
2949 2951 register int i; /* local counter */
2950 2952 register WORK *w; /* tmp item pointer */
2951 2953 int wc = WorkListCount; /* trim size for WorkQ */
2952 2954
2953 2955 if (WorkQ != NULL)
2954 2956 {
2955 2957 WORK *nw;
2956 2958
2957 2959 /* Clear out old WorkQ. */
2958 2960 for (w = WorkQ; w != NULL; w = nw)
2959 2961 {
2960 2962 nw = w->w_next;
2961 2963 sm_free(w->w_name); /* XXX */
2962 2964 if (w->w_host != NULL)
2963 2965 sm_free(w->w_host); /* XXX */
2964 2966 sm_free((char *) w); /* XXX */
2965 2967 }
2966 2968 WorkQ = NULL;
2967 2969 }
2968 2970
2969 2971 if (WorkList == NULL || wc <= 0)
2970 2972 return 0;
2971 2973
2972 2974 /*
2973 2975 ** The sort now takes place using all of the items in WorkList.
2974 2976 ** The list gets trimmed to the most important items after the sort.
2975 2977 ** If the trim were to happen before the sort then one or more
2976 2978 ** important items might get truncated off -- not what we want.
2977 2979 */
2978 2980
2979 2981 if (QueueSortOrder == QSO_BYHOST)
2980 2982 {
2981 2983 /*
2982 2984 ** Sort the work directory for the first time,
2983 2985 ** based on host name, lock status, and priority.
2984 2986 */
2985 2987
2986 2988 qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf1);
2987 2989
2988 2990 /*
2989 2991 ** If one message to host is locked, "lock" all messages
2990 2992 ** to that host.
2991 2993 */
2992 2994
2993 2995 i = 0;
2994 2996 while (i < wc)
2995 2997 {
2996 2998 if (!WorkList[i].w_lock)
2997 2999 {
2998 3000 i++;
2999 3001 continue;
3000 3002 }
3001 3003 w = &WorkList[i];
3002 3004 while (++i < wc)
3003 3005 {
3004 3006 if (WorkList[i].w_host == NULL &&
3005 3007 w->w_host == NULL)
3006 3008 WorkList[i].w_lock = true;
3007 3009 else if (WorkList[i].w_host != NULL &&
3008 3010 w->w_host != NULL &&
3009 3011 sm_strcasecmp(WorkList[i].w_host,
3010 3012 w->w_host) == 0)
3011 3013 WorkList[i].w_lock = true;
3012 3014 else
3013 3015 break;
3014 3016 }
3015 3017 }
3016 3018
3017 3019 /*
3018 3020 ** Sort the work directory for the second time,
3019 3021 ** based on lock status, host name, and priority.
3020 3022 */
3021 3023
3022 3024 qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf2);
3023 3025 }
3024 3026 else if (QueueSortOrder == QSO_BYTIME)
3025 3027 {
3026 3028 /*
3027 3029 ** Simple sort based on submission time only.
3028 3030 */
3029 3031
3030 3032 qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf3);
3031 3033 }
3032 3034 else if (QueueSortOrder == QSO_BYFILENAME)
3033 3035 {
3034 3036 /*
3035 3037 ** Sort based on queue filename.
3036 3038 */
3037 3039
3038 3040 qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf4);
3039 3041 }
3040 3042 else if (QueueSortOrder == QSO_RANDOM)
3041 3043 {
3042 3044 /*
3043 3045 ** Sort randomly. To avoid problems with an instable sort,
3044 3046 ** use a random index into the queue file name to start
3045 3047 ** comparison.
3046 3048 */
3047 3049
3048 3050 randi = get_rand_mod(MAXQFNAME);
3049 3051 if (randi < 2)
3050 3052 randi = 3;
3051 3053 qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf5);
3052 3054 }
3053 3055 else if (QueueSortOrder == QSO_BYMODTIME)
3054 3056 {
3055 3057 /*
3056 3058 ** Simple sort based on modification time of queue file.
3057 3059 ** This puts the oldest items first.
3058 3060 */
3059 3061
3060 3062 qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf6);
3061 3063 }
3062 3064 #if _FFR_RHS
3063 3065 else if (QueueSortOrder == QSO_BYSHUFFLE)
3064 3066 {
3065 3067 /*
3066 3068 ** Simple sort based on shuffled host name.
3067 3069 */
3068 3070
3069 3071 init_shuffle_alphabet();
3070 3072 qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf7);
3071 3073 }
3072 3074 #endif /* _FFR_RHS */
3073 3075 else if (QueueSortOrder == QSO_BYPRIORITY)
3074 3076 {
3075 3077 /*
3076 3078 ** Simple sort based on queue priority only.
3077 3079 */
3078 3080
3079 3081 qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf0);
3080 3082 }
3081 3083 /* else don't sort at all */
3082 3084
3083 3085 /* Check if the per queue group item limit will be exceeded */
3084 3086 if (wc > max && max > 0)
3085 3087 wc = max;
3086 3088
3087 3089 /*
3088 3090 ** Convert the work list into canonical form.
3089 3091 ** Should be turning it into a list of envelopes here perhaps.
3090 3092 ** Only take the most important items up to the per queue group
3091 3093 ** maximum.
3092 3094 */
3093 3095
3094 3096 for (i = wc; --i >= 0; )
3095 3097 {
3096 3098 w = (WORK *) xalloc(sizeof(*w));
3097 3099 w->w_qgrp = WorkList[i].w_qgrp;
3098 3100 w->w_qdir = WorkList[i].w_qdir;
3099 3101 w->w_name = WorkList[i].w_name;
3100 3102 w->w_host = WorkList[i].w_host;
3101 3103 w->w_lock = WorkList[i].w_lock;
3102 3104 w->w_tooyoung = WorkList[i].w_tooyoung;
3103 3105 w->w_pri = WorkList[i].w_pri;
3104 3106 w->w_ctime = WorkList[i].w_ctime;
3105 3107 w->w_mtime = WorkList[i].w_mtime;
3106 3108 w->w_next = WorkQ;
3107 3109 WorkQ = w;
3108 3110 }
3109 3111
3110 3112 /* free the rest of the list */
3111 3113 for (i = WorkListCount; --i >= wc; )
3112 3114 {
3113 3115 sm_free(WorkList[i].w_name);
3114 3116 if (WorkList[i].w_host != NULL)
3115 3117 sm_free(WorkList[i].w_host);
3116 3118 }
3117 3119
3118 3120 if (WorkList != NULL)
3119 3121 sm_free(WorkList); /* XXX */
3120 3122 WorkList = NULL;
3121 3123 WorkListSize = 0;
3122 3124 WorkListCount = 0;
3123 3125
3124 3126 if (tTd(40, 1))
3125 3127 {
3126 3128 for (w = WorkQ; w != NULL; w = w->w_next)
3127 3129 {
3128 3130 if (w->w_host != NULL)
3129 3131 sm_dprintf("%22s: pri=%ld %s\n",
3130 3132 w->w_name, w->w_pri, w->w_host);
3131 3133 else
3132 3134 sm_dprintf("%32s: pri=%ld\n",
3133 3135 w->w_name, w->w_pri);
3134 3136 }
3135 3137 }
3136 3138
3137 3139 return wc; /* return number of WorkQ items */
3138 3140 }
3139 3141 /*
3140 3142 ** GROW_WLIST -- make the work list larger
3141 3143 **
3142 3144 ** Parameters:
3143 3145 ** qgrp -- the index for the queue group.
3144 3146 ** qdir -- the index for the queue directory.
3145 3147 **
3146 3148 ** Returns:
3147 3149 ** none.
3148 3150 **
3149 3151 ** Side Effects:
3150 3152 ** Adds another QUEUESEGSIZE entries to WorkList if possible.
3151 3153 ** It can fail if there isn't enough memory, so WorkListSize
3152 3154 ** should be checked again upon return.
3153 3155 */
3154 3156
3155 3157 static void
3156 3158 grow_wlist(qgrp, qdir)
3157 3159 int qgrp;
3158 3160 int qdir;
3159 3161 {
3160 3162 if (tTd(41, 1))
3161 3163 sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize);
3162 3164 if (WorkList == NULL)
3163 3165 {
3164 3166 WorkList = (WORK *) xalloc((sizeof(*WorkList)) *
3165 3167 (QUEUESEGSIZE + 1));
3166 3168 WorkListSize = QUEUESEGSIZE;
3167 3169 }
3168 3170 else
3169 3171 {
3170 3172 int newsize = WorkListSize + QUEUESEGSIZE;
3171 3173 WORK *newlist = (WORK *) sm_realloc((char *) WorkList,
3172 3174 (unsigned) sizeof(WORK) * (newsize + 1));
3173 3175
3174 3176 if (newlist != NULL)
3175 3177 {
3176 3178 WorkListSize = newsize;
3177 3179 WorkList = newlist;
3178 3180 if (LogLevel > 1)
3179 3181 {
3180 3182 sm_syslog(LOG_INFO, NOQID,
3181 3183 "grew WorkList for %s to %d",
3182 3184 qid_printqueue(qgrp, qdir),
3183 3185 WorkListSize);
3184 3186 }
3185 3187 }
3186 3188 else if (LogLevel > 0)
3187 3189 {
3188 3190 sm_syslog(LOG_ALERT, NOQID,
3189 3191 "FAILED to grow WorkList for %s to %d",
3190 3192 qid_printqueue(qgrp, qdir), newsize);
3191 3193 }
3192 3194 }
3193 3195 if (tTd(41, 1))
3194 3196 sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize);
3195 3197 }
3196 3198 /*
3197 3199 ** WORKCMPF0 -- simple priority-only compare function.
3198 3200 **
3199 3201 ** Parameters:
3200 3202 ** a -- the first argument.
3201 3203 ** b -- the second argument.
3202 3204 **
3203 3205 ** Returns:
3204 3206 ** -1 if a < b
3205 3207 ** 0 if a == b
3206 3208 ** +1 if a > b
3207 3209 **
3208 3210 */
3209 3211
3210 3212 static int
3211 3213 workcmpf0(a, b)
3212 3214 register WORK *a;
3213 3215 register WORK *b;
3214 3216 {
3215 3217 long pa = a->w_pri;
3216 3218 long pb = b->w_pri;
3217 3219
3218 3220 if (pa == pb)
3219 3221 return 0;
3220 3222 else if (pa > pb)
3221 3223 return 1;
3222 3224 else
3223 3225 return -1;
3224 3226 }
3225 3227 /*
3226 3228 ** WORKCMPF1 -- first compare function for ordering work based on host name.
3227 3229 **
3228 3230 ** Sorts on host name, lock status, and priority in that order.
3229 3231 **
3230 3232 ** Parameters:
3231 3233 ** a -- the first argument.
3232 3234 ** b -- the second argument.
3233 3235 **
3234 3236 ** Returns:
3235 3237 ** <0 if a < b
3236 3238 ** 0 if a == b
3237 3239 ** >0 if a > b
3238 3240 **
3239 3241 */
3240 3242
3241 3243 static int
3242 3244 workcmpf1(a, b)
3243 3245 register WORK *a;
3244 3246 register WORK *b;
3245 3247 {
3246 3248 int i;
3247 3249
3248 3250 /* host name */
3249 3251 if (a->w_host != NULL && b->w_host == NULL)
3250 3252 return 1;
3251 3253 else if (a->w_host == NULL && b->w_host != NULL)
3252 3254 return -1;
3253 3255 if (a->w_host != NULL && b->w_host != NULL &&
3254 3256 (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
3255 3257 return i;
3256 3258
3257 3259 /* lock status */
3258 3260 if (a->w_lock != b->w_lock)
3259 3261 return b->w_lock - a->w_lock;
3260 3262
3261 3263 /* job priority */
3262 3264 return workcmpf0(a, b);
3263 3265 }
3264 3266 /*
3265 3267 ** WORKCMPF2 -- second compare function for ordering work based on host name.
3266 3268 **
3267 3269 ** Sorts on lock status, host name, and priority in that order.
3268 3270 **
3269 3271 ** Parameters:
3270 3272 ** a -- the first argument.
3271 3273 ** b -- the second argument.
3272 3274 **
3273 3275 ** Returns:
3274 3276 ** <0 if a < b
3275 3277 ** 0 if a == b
3276 3278 ** >0 if a > b
3277 3279 **
3278 3280 */
3279 3281
3280 3282 static int
3281 3283 workcmpf2(a, b)
3282 3284 register WORK *a;
3283 3285 register WORK *b;
3284 3286 {
3285 3287 int i;
3286 3288
3287 3289 /* lock status */
3288 3290 if (a->w_lock != b->w_lock)
3289 3291 return a->w_lock - b->w_lock;
3290 3292
3291 3293 /* host name */
3292 3294 if (a->w_host != NULL && b->w_host == NULL)
3293 3295 return 1;
3294 3296 else if (a->w_host == NULL && b->w_host != NULL)
3295 3297 return -1;
3296 3298 if (a->w_host != NULL && b->w_host != NULL &&
3297 3299 (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
3298 3300 return i;
3299 3301
3300 3302 /* job priority */
3301 3303 return workcmpf0(a, b);
3302 3304 }
3303 3305 /*
3304 3306 ** WORKCMPF3 -- simple submission-time-only compare function.
3305 3307 **
3306 3308 ** Parameters:
3307 3309 ** a -- the first argument.
3308 3310 ** b -- the second argument.
3309 3311 **
3310 3312 ** Returns:
3311 3313 ** -1 if a < b
3312 3314 ** 0 if a == b
3313 3315 ** +1 if a > b
3314 3316 **
3315 3317 */
3316 3318
3317 3319 static int
3318 3320 workcmpf3(a, b)
3319 3321 register WORK *a;
3320 3322 register WORK *b;
3321 3323 {
3322 3324 if (a->w_ctime > b->w_ctime)
3323 3325 return 1;
3324 3326 else if (a->w_ctime < b->w_ctime)
3325 3327 return -1;
3326 3328 else
3327 3329 return 0;
3328 3330 }
3329 3331 /*
3330 3332 ** WORKCMPF4 -- compare based on file name
3331 3333 **
3332 3334 ** Parameters:
3333 3335 ** a -- the first argument.
3334 3336 ** b -- the second argument.
3335 3337 **
3336 3338 ** Returns:
3337 3339 ** -1 if a < b
3338 3340 ** 0 if a == b
3339 3341 ** +1 if a > b
3340 3342 **
3341 3343 */
3342 3344
3343 3345 static int
3344 3346 workcmpf4(a, b)
3345 3347 register WORK *a;
3346 3348 register WORK *b;
3347 3349 {
3348 3350 return strcmp(a->w_name, b->w_name);
3349 3351 }
3350 3352 /*
3351 3353 ** WORKCMPF5 -- compare based on assigned random number
3352 3354 **
3353 3355 ** Parameters:
3354 3356 ** a -- the first argument.
3355 3357 ** b -- the second argument.
3356 3358 **
3357 3359 ** Returns:
3358 3360 ** randomly 1/-1
3359 3361 */
3360 3362
3361 3363 /* ARGSUSED0 */
3362 3364 static int
3363 3365 workcmpf5(a, b)
3364 3366 register WORK *a;
3365 3367 register WORK *b;
3366 3368 {
3367 3369 if (strlen(a->w_name) < randi || strlen(b->w_name) < randi)
3368 3370 return -1;
3369 3371 return a->w_name[randi] - b->w_name[randi];
3370 3372 }
3371 3373 /*
3372 3374 ** WORKCMPF6 -- simple modification-time-only compare function.
3373 3375 **
3374 3376 ** Parameters:
3375 3377 ** a -- the first argument.
3376 3378 ** b -- the second argument.
3377 3379 **
3378 3380 ** Returns:
3379 3381 ** -1 if a < b
3380 3382 ** 0 if a == b
3381 3383 ** +1 if a > b
3382 3384 **
3383 3385 */
3384 3386
3385 3387 static int
3386 3388 workcmpf6(a, b)
3387 3389 register WORK *a;
3388 3390 register WORK *b;
3389 3391 {
3390 3392 if (a->w_mtime > b->w_mtime)
3391 3393 return 1;
3392 3394 else if (a->w_mtime < b->w_mtime)
3393 3395 return -1;
3394 3396 else
3395 3397 return 0;
3396 3398 }
3397 3399 #if _FFR_RHS
3398 3400 /*
3399 3401 ** WORKCMPF7 -- compare function for ordering work based on shuffled host name.
3400 3402 **
3401 3403 ** Sorts on lock status, host name, and priority in that order.
3402 3404 **
3403 3405 ** Parameters:
3404 3406 ** a -- the first argument.
3405 3407 ** b -- the second argument.
3406 3408 **
3407 3409 ** Returns:
3408 3410 ** <0 if a < b
3409 3411 ** 0 if a == b
3410 3412 ** >0 if a > b
3411 3413 **
3412 3414 */
3413 3415
3414 3416 static int
3415 3417 workcmpf7(a, b)
3416 3418 register WORK *a;
3417 3419 register WORK *b;
3418 3420 {
3419 3421 int i;
3420 3422
3421 3423 /* lock status */
3422 3424 if (a->w_lock != b->w_lock)
3423 3425 return a->w_lock - b->w_lock;
3424 3426
3425 3427 /* host name */
3426 3428 if (a->w_host != NULL && b->w_host == NULL)
3427 3429 return 1;
3428 3430 else if (a->w_host == NULL && b->w_host != NULL)
3429 3431 return -1;
3430 3432 if (a->w_host != NULL && b->w_host != NULL &&
3431 3433 (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0)
3432 3434 return i;
3433 3435
3434 3436 /* job priority */
3435 3437 return workcmpf0(a, b);
3436 3438 }
3437 3439 #endif /* _FFR_RHS */
3438 3440 /*
3439 3441 ** STRREV -- reverse string
3440 3442 **
3441 3443 ** Returns a pointer to a new string that is the reverse of
3442 3444 ** the string pointed to by fwd. The space for the new
3443 3445 ** string is obtained using xalloc().
3444 3446 **
3445 3447 ** Parameters:
3446 3448 ** fwd -- the string to reverse.
3447 3449 **
3448 3450 ** Returns:
3449 3451 ** the reversed string.
3450 3452 */
3451 3453
3452 3454 static char *
3453 3455 strrev(fwd)
3454 3456 char *fwd;
3455 3457 {
3456 3458 char *rev = NULL;
3457 3459 int len, cnt;
3458 3460
3459 3461 len = strlen(fwd);
3460 3462 rev = xalloc(len + 1);
3461 3463 for (cnt = 0; cnt < len; ++cnt)
3462 3464 rev[cnt] = fwd[len - cnt - 1];
3463 3465 rev[len] = '\0';
3464 3466 return rev;
3465 3467 }
3466 3468
3467 3469 #if _FFR_RHS
3468 3470
3469 3471 # define NASCII 128
3470 3472 # define NCHAR 256
3471 3473
3472 3474 static unsigned char ShuffledAlphabet[NCHAR];
3473 3475
3474 3476 void
3475 3477 init_shuffle_alphabet()
3476 3478 {
3477 3479 static bool init = false;
3478 3480 int i;
3479 3481
3480 3482 if (init)
3481 3483 return;
3482 3484
3483 3485 /* fill the ShuffledAlphabet */
3484 3486 for (i = 0; i < NASCII; i++)
3485 3487 ShuffledAlphabet[i] = i;
3486 3488
3487 3489 /* mix it */
3488 3490 for (i = 1; i < NASCII; i++)
3489 3491 {
3490 3492 register int j = get_random() % NASCII;
3491 3493 register int tmp;
3492 3494
3493 3495 tmp = ShuffledAlphabet[j];
3494 3496 ShuffledAlphabet[j] = ShuffledAlphabet[i];
3495 3497 ShuffledAlphabet[i] = tmp;
3496 3498 }
3497 3499
3498 3500 /* make it case insensitive */
3499 3501 for (i = 'A'; i <= 'Z'; i++)
3500 3502 ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A'];
3501 3503
3502 3504 /* fill the upper part */
3503 3505 for (i = 0; i < NASCII; i++)
3504 3506 ShuffledAlphabet[i + NASCII] = ShuffledAlphabet[i];
3505 3507 init = true;
3506 3508 }
3507 3509
3508 3510 static int
3509 3511 sm_strshufflecmp(a, b)
3510 3512 char *a;
3511 3513 char *b;
3512 3514 {
3513 3515 const unsigned char *us1 = (const unsigned char *) a;
3514 3516 const unsigned char *us2 = (const unsigned char *) b;
3515 3517
3516 3518 while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++])
3517 3519 {
3518 3520 if (*us1++ == '\0')
3519 3521 return 0;
3520 3522 }
3521 3523 return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]);
3522 3524 }
3523 3525 #endif /* _FFR_RHS */
3524 3526
3525 3527 /*
3526 3528 ** DOWORK -- do a work request.
3527 3529 **
3528 3530 ** Parameters:
3529 3531 ** qgrp -- the index of the queue group for the job.
3530 3532 ** qdir -- the index of the queue directory for the job.
3531 3533 ** id -- the ID of the job to run.
3532 3534 ** forkflag -- if set, run this in background.
3533 3535 ** requeueflag -- if set, reinstantiate the queue quickly.
3534 3536 ** This is used when expanding aliases in the queue.
3535 3537 ** If forkflag is also set, it doesn't wait for the
3536 3538 ** child.
3537 3539 ** e - the envelope in which to run it.
3538 3540 **
3539 3541 ** Returns:
3540 3542 ** process id of process that is running the queue job.
3541 3543 **
3542 3544 ** Side Effects:
3543 3545 ** The work request is satisfied if possible.
3544 3546 */
3545 3547
3546 3548 pid_t
3547 3549 dowork(qgrp, qdir, id, forkflag, requeueflag, e)
3548 3550 int qgrp;
3549 3551 int qdir;
3550 3552 char *id;
3551 3553 bool forkflag;
3552 3554 bool requeueflag;
3553 3555 register ENVELOPE *e;
3554 3556 {
3555 3557 register pid_t pid;
3556 3558 SM_RPOOL_T *rpool;
3557 3559
3558 3560 if (tTd(40, 1))
3559 3561 sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id);
3560 3562
3561 3563 /*
3562 3564 ** Fork for work.
3563 3565 */
3564 3566
3565 3567 if (forkflag)
3566 3568 {
3567 3569 /*
3568 3570 ** Since the delivery may happen in a child and the
3569 3571 ** parent does not wait, the parent may close the
3570 3572 ** maps thereby removing any shared memory used by
3571 3573 ** the map. Therefore, close the maps now so the
3572 3574 ** child will dynamically open them if necessary.
3573 3575 */
3574 3576
3575 3577 closemaps(false);
3576 3578
3577 3579 pid = fork();
3578 3580 if (pid < 0)
3579 3581 {
3580 3582 syserr("dowork: cannot fork");
3581 3583 return 0;
3582 3584 }
3583 3585 else if (pid > 0)
3584 3586 {
3585 3587 /* parent -- clean out connection cache */
3586 3588 mci_flush(false, NULL);
3587 3589 }
3588 3590 else
3589 3591 {
3590 3592 /*
3591 3593 ** Initialize exception stack and default exception
3592 3594 ** handler for child process.
3593 3595 */
3594 3596
3595 3597 /* Reset global flags */
3596 3598 RestartRequest = NULL;
3597 3599 RestartWorkGroup = false;
3598 3600 ShutdownRequest = NULL;
3599 3601 PendingSignal = 0;
3600 3602 CurrentPid = getpid();
3601 3603 sm_exc_newthread(fatal_error);
3602 3604
3603 3605 /*
3604 3606 ** See note above about SMTP processes and SIGCHLD.
3605 3607 */
3606 3608
3607 3609 if (OpMode == MD_SMTP ||
3608 3610 OpMode == MD_DAEMON ||
3609 3611 MaxQueueChildren > 0)
3610 3612 {
3611 3613 proc_list_clear();
3612 3614 sm_releasesignal(SIGCHLD);
3613 3615 (void) sm_signal(SIGCHLD, SIG_DFL);
3614 3616 }
3615 3617
3616 3618 /* child -- error messages to the transcript */
3617 3619 QuickAbort = OnlyOneError = false;
3618 3620 }
3619 3621 }
3620 3622 else
3621 3623 {
3622 3624 pid = 0;
3623 3625 }
3624 3626
3625 3627 if (pid == 0)
3626 3628 {
3627 3629 /*
3628 3630 ** CHILD
3629 3631 ** Lock the control file to avoid duplicate deliveries.
3630 3632 ** Then run the file as though we had just read it.
3631 3633 ** We save an idea of the temporary name so we
3632 3634 ** can recover on interrupt.
3633 3635 */
3634 3636
3635 3637 if (forkflag)
3636 3638 {
3637 3639 /* Reset global flags */
3638 3640 RestartRequest = NULL;
3639 3641 RestartWorkGroup = false;
3640 3642 ShutdownRequest = NULL;
3641 3643 PendingSignal = 0;
3642 3644 }
3643 3645
3644 3646 /* set basic modes, etc. */
3645 3647 sm_clear_events();
3646 3648 clearstats();
3647 3649 rpool = sm_rpool_new_x(NULL);
3648 3650 clearenvelope(e, false, rpool);
3649 3651 e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
3650 3652 set_delivery_mode(SM_DELIVER, e);
3651 3653 e->e_errormode = EM_MAIL;
3652 3654 e->e_id = id;
3653 3655 e->e_qgrp = qgrp;
3654 3656 e->e_qdir = qdir;
3655 3657 GrabTo = UseErrorsTo = false;
3656 3658 ExitStat = EX_OK;
3657 3659 if (forkflag)
3658 3660 {
3659 3661 disconnect(1, e);
3660 3662 set_op_mode(MD_QUEUERUN);
3661 3663 }
3662 3664 sm_setproctitle(true, e, "%s from queue", qid_printname(e));
3663 3665 if (LogLevel > 76)
3664 3666 sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d",
3665 3667 (int) CurrentPid);
3666 3668
3667 3669 /* don't use the headers from sendmail.cf... */
3668 3670 e->e_header = NULL;
3669 3671
3670 3672 /* read the queue control file -- return if locked */
3671 3673 if (!readqf(e, false))
3672 3674 {
3673 3675 if (tTd(40, 4) && e->e_id != NULL)
3674 3676 sm_dprintf("readqf(%s) failed\n",
3675 3677 qid_printname(e));
3676 3678 e->e_id = NULL;
3677 3679 if (forkflag)
3678 3680 finis(false, true, EX_OK);
3679 3681 else
3680 3682 {
3681 3683 /* adding this frees 8 bytes */
3682 3684 clearenvelope(e, false, rpool);
3683 3685
3684 3686 /* adding this frees 12 bytes */
3685 3687 sm_rpool_free(rpool);
3686 3688 e->e_rpool = NULL;
3687 3689 return 0;
3688 3690 }
3689 3691 }
3690 3692
3691 3693 e->e_flags |= EF_INQUEUE;
3692 3694 eatheader(e, requeueflag, true);
3693 3695
3694 3696 if (requeueflag)
3695 3697 queueup(e, false, false);
3696 3698
3697 3699 /* do the delivery */
3698 3700 sendall(e, SM_DELIVER);
3699 3701
3700 3702 /* finish up and exit */
3701 3703 if (forkflag)
3702 3704 finis(true, true, ExitStat);
3703 3705 else
3704 3706 {
3705 3707 (void) dropenvelope(e, true, false);
3706 3708 sm_rpool_free(rpool);
3707 3709 e->e_rpool = NULL;
3708 3710 }
3709 3711 }
3710 3712 e->e_id = NULL;
3711 3713 return pid;
3712 3714 }
3713 3715
3714 3716 /*
3715 3717 ** DOWORKLIST -- process a list of envelopes as work requests
3716 3718 **
3717 3719 ** Similar to dowork(), except that after forking, it processes an
3718 3720 ** envelope and its siblings, treating each envelope as a work request.
3719 3721 **
3720 3722 ** Parameters:
3721 3723 ** el -- envelope to be processed including its siblings.
3722 3724 ** forkflag -- if set, run this in background.
3723 3725 ** requeueflag -- if set, reinstantiate the queue quickly.
3724 3726 ** This is used when expanding aliases in the queue.
3725 3727 ** If forkflag is also set, it doesn't wait for the
3726 3728 ** child.
3727 3729 **
3728 3730 ** Returns:
3729 3731 ** process id of process that is running the queue job.
3730 3732 **
3731 3733 ** Side Effects:
3732 3734 ** The work request is satisfied if possible.
3733 3735 */
3734 3736
3735 3737 pid_t
3736 3738 doworklist(el, forkflag, requeueflag)
3737 3739 ENVELOPE *el;
3738 3740 bool forkflag;
3739 3741 bool requeueflag;
3740 3742 {
3741 3743 register pid_t pid;
3742 3744 ENVELOPE *ei;
3743 3745
3744 3746 if (tTd(40, 1))
3745 3747 sm_dprintf("doworklist()\n");
3746 3748
3747 3749 /*
3748 3750 ** Fork for work.
3749 3751 */
3750 3752
3751 3753 if (forkflag)
3752 3754 {
3753 3755 /*
3754 3756 ** Since the delivery may happen in a child and the
3755 3757 ** parent does not wait, the parent may close the
3756 3758 ** maps thereby removing any shared memory used by
3757 3759 ** the map. Therefore, close the maps now so the
3758 3760 ** child will dynamically open them if necessary.
3759 3761 */
3760 3762
3761 3763 closemaps(false);
3762 3764
3763 3765 pid = fork();
3764 3766 if (pid < 0)
3765 3767 {
3766 3768 syserr("doworklist: cannot fork");
3767 3769 return 0;
3768 3770 }
3769 3771 else if (pid > 0)
3770 3772 {
3771 3773 /* parent -- clean out connection cache */
3772 3774 mci_flush(false, NULL);
3773 3775 }
3774 3776 else
3775 3777 {
3776 3778 /*
3777 3779 ** Initialize exception stack and default exception
3778 3780 ** handler for child process.
3779 3781 */
3780 3782
3781 3783 /* Reset global flags */
3782 3784 RestartRequest = NULL;
3783 3785 RestartWorkGroup = false;
3784 3786 ShutdownRequest = NULL;
3785 3787 PendingSignal = 0;
3786 3788 CurrentPid = getpid();
3787 3789 sm_exc_newthread(fatal_error);
3788 3790
3789 3791 /*
3790 3792 ** See note above about SMTP processes and SIGCHLD.
3791 3793 */
3792 3794
3793 3795 if (OpMode == MD_SMTP ||
3794 3796 OpMode == MD_DAEMON ||
3795 3797 MaxQueueChildren > 0)
3796 3798 {
3797 3799 proc_list_clear();
3798 3800 sm_releasesignal(SIGCHLD);
3799 3801 (void) sm_signal(SIGCHLD, SIG_DFL);
3800 3802 }
3801 3803
3802 3804 /* child -- error messages to the transcript */
3803 3805 QuickAbort = OnlyOneError = false;
3804 3806 }
3805 3807 }
3806 3808 else
3807 3809 {
3808 3810 pid = 0;
3809 3811 }
3810 3812
3811 3813 if (pid != 0)
3812 3814 return pid;
3813 3815
3814 3816 /*
3815 3817 ** IN CHILD
3816 3818 ** Lock the control file to avoid duplicate deliveries.
3817 3819 ** Then run the file as though we had just read it.
3818 3820 ** We save an idea of the temporary name so we
3819 3821 ** can recover on interrupt.
3820 3822 */
3821 3823
3822 3824 if (forkflag)
3823 3825 {
3824 3826 /* Reset global flags */
3825 3827 RestartRequest = NULL;
3826 3828 RestartWorkGroup = false;
3827 3829 ShutdownRequest = NULL;
3828 3830 PendingSignal = 0;
3829 3831 }
3830 3832
3831 3833 /* set basic modes, etc. */
3832 3834 sm_clear_events();
3833 3835 clearstats();
3834 3836 GrabTo = UseErrorsTo = false;
3835 3837 ExitStat = EX_OK;
3836 3838 if (forkflag)
3837 3839 {
3838 3840 disconnect(1, el);
3839 3841 set_op_mode(MD_QUEUERUN);
3840 3842 }
3841 3843 if (LogLevel > 76)
3842 3844 sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d",
3843 3845 (int) CurrentPid);
3844 3846
3845 3847 for (ei = el; ei != NULL; ei = ei->e_sibling)
3846 3848 {
3847 3849 ENVELOPE e;
3848 3850 SM_RPOOL_T *rpool;
3849 3851
3850 3852 if (WILL_BE_QUEUED(ei->e_sendmode))
3851 3853 continue;
3852 3854 else if (QueueMode != QM_QUARANTINE &&
3853 3855 ei->e_quarmsg != NULL)
3854 3856 continue;
3855 3857
3856 3858 rpool = sm_rpool_new_x(NULL);
3857 3859 clearenvelope(&e, true, rpool);
3858 3860 e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
3859 3861 set_delivery_mode(SM_DELIVER, &e);
3860 3862 e.e_errormode = EM_MAIL;
3861 3863 e.e_id = ei->e_id;
3862 3864 e.e_qgrp = ei->e_qgrp;
3863 3865 e.e_qdir = ei->e_qdir;
3864 3866 openxscript(&e);
3865 3867 sm_setproctitle(true, &e, "%s from queue", qid_printname(&e));
3866 3868
3867 3869 /* don't use the headers from sendmail.cf... */
3868 3870 e.e_header = NULL;
3869 3871 CurEnv = &e;
3870 3872
3871 3873 /* read the queue control file -- return if locked */
3872 3874 if (readqf(&e, false))
3873 3875 {
3874 3876 e.e_flags |= EF_INQUEUE;
3875 3877 eatheader(&e, requeueflag, true);
3876 3878
3877 3879 if (requeueflag)
3878 3880 queueup(&e, false, false);
3879 3881
3880 3882 /* do the delivery */
3881 3883 sendall(&e, SM_DELIVER);
3882 3884 (void) dropenvelope(&e, true, false);
3883 3885 }
3884 3886 else
3885 3887 {
3886 3888 if (tTd(40, 4) && e.e_id != NULL)
3887 3889 sm_dprintf("readqf(%s) failed\n",
3888 3890 qid_printname(&e));
3889 3891 }
3890 3892 sm_rpool_free(rpool);
3891 3893 ei->e_id = NULL;
3892 3894 }
3893 3895
3894 3896 /* restore CurEnv */
3895 3897 CurEnv = el;
3896 3898
3897 3899 /* finish up and exit */
3898 3900 if (forkflag)
3899 3901 finis(true, true, ExitStat);
3900 3902 return 0;
3901 3903 }
3902 3904 /*
3903 3905 ** READQF -- read queue file and set up environment.
3904 3906 **
3905 3907 ** Parameters:
3906 3908 ** e -- the envelope of the job to run.
3907 3909 ** openonly -- only open the qf (returned as e_lockfp)
3908 3910 **
3909 3911 ** Returns:
3910 3912 ** true if it successfully read the queue file.
3911 3913 ** false otherwise.
3912 3914 **
3913 3915 ** Side Effects:
3914 3916 ** The queue file is returned locked.
3915 3917 */
3916 3918
3917 3919 static bool
3918 3920 readqf(e, openonly)
3919 3921 register ENVELOPE *e;
3920 3922 bool openonly;
3921 3923 {
3922 3924 register SM_FILE_T *qfp;
3923 3925 ADDRESS *ctladdr;
3924 3926 struct stat st, stf;
3925 3927 char *bp;
3926 3928 int qfver = 0;
3927 3929 long hdrsize = 0;
3928 3930 register char *p;
3929 3931 char *frcpt = NULL;
3930 3932 char *orcpt = NULL;
3931 3933 bool nomore = false;
3932 3934 bool bogus = false;
3933 3935 MODE_T qsafe;
3934 3936 char *err;
3935 3937 char qf[MAXPATHLEN];
3936 3938 char buf[MAXLINE];
3937 3939 int bufsize;
3938 3940
3939 3941 /*
3940 3942 ** Read and process the file.
3941 3943 */
3942 3944
3943 3945 SM_REQUIRE(e != NULL);
3944 3946 bp = NULL;
3945 3947 (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof(qf));
3946 3948 qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR_B, NULL);
3947 3949 if (qfp == NULL)
3948 3950 {
3949 3951 int save_errno = errno;
3950 3952
3951 3953 if (tTd(40, 8))
3952 3954 sm_dprintf("readqf(%s): sm_io_open failure (%s)\n",
3953 3955 qf, sm_errstring(errno));
3954 3956 errno = save_errno;
3955 3957 if (errno != ENOENT
3956 3958 )
3957 3959 syserr("readqf: no control file %s", qf);
3958 3960 RELEASE_QUEUE;
3959 3961 return false;
3960 3962 }
3961 3963
3962 3964 if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL,
3963 3965 LOCK_EX|LOCK_NB))
3964 3966 {
3965 3967 /* being processed by another queuer */
3966 3968 if (Verbose)
3967 3969 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3968 3970 "%s: locked\n", e->e_id);
3969 3971 if (tTd(40, 8))
3970 3972 sm_dprintf("%s: locked\n", e->e_id);
3971 3973 if (LogLevel > 19)
3972 3974 sm_syslog(LOG_DEBUG, e->e_id, "locked");
3973 3975 (void) sm_io_close(qfp, SM_TIME_DEFAULT);
3974 3976 RELEASE_QUEUE;
3975 3977 return false;
3976 3978 }
3977 3979
3978 3980 RELEASE_QUEUE;
3979 3981
3980 3982 /*
3981 3983 ** Prevent locking race condition.
3982 3984 **
3983 3985 ** Process A: readqf(): qfp = fopen(qffile)
3984 3986 ** Process B: queueup(): rename(tf, qf)
3985 3987 ** Process B: unlocks(tf)
3986 3988 ** Process A: lockfile(qf);
3987 3989 **
3988 3990 ** Process A (us) has the old qf file (before the rename deleted
3989 3991 ** the directory entry) and will be delivering based on old data.
3990 3992 ** This can lead to multiple deliveries of the same recipients.
3991 3993 **
3992 3994 ** Catch this by checking if the underlying qf file has changed
3993 3995 ** *after* acquiring our lock and if so, act as though the file
3994 3996 ** was still locked (i.e., just return like the lockfile() case
3995 3997 ** above.
3996 3998 */
3997 3999
3998 4000 if (stat(qf, &stf) < 0 ||
3999 4001 fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0)
4000 4002 {
4001 4003 /* must have been being processed by someone else */
4002 4004 if (tTd(40, 8))
4003 4005 sm_dprintf("readqf(%s): [f]stat failure (%s)\n",
4004 4006 qf, sm_errstring(errno));
4005 4007 (void) sm_io_close(qfp, SM_TIME_DEFAULT);
4006 4008 return false;
4007 4009 }
4008 4010
4009 4011 if (st.st_nlink != stf.st_nlink ||
4010 4012 st.st_dev != stf.st_dev ||
4011 4013 ST_INODE(st) != ST_INODE(stf) ||
4012 4014 #if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */
4013 4015 st.st_gen != stf.st_gen ||
4014 4016 #endif /* HAS_ST_GEN && 0 */
4015 4017 st.st_uid != stf.st_uid ||
4016 4018 st.st_gid != stf.st_gid ||
4017 4019 st.st_size != stf.st_size)
4018 4020 {
4019 4021 /* changed after opened */
4020 4022 if (Verbose)
4021 4023 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4022 4024 "%s: changed\n", e->e_id);
4023 4025 if (tTd(40, 8))
4024 4026 sm_dprintf("%s: changed\n", e->e_id);
4025 4027 if (LogLevel > 19)
4026 4028 sm_syslog(LOG_DEBUG, e->e_id, "changed");
4027 4029 (void) sm_io_close(qfp, SM_TIME_DEFAULT);
4028 4030 return false;
4029 4031 }
4030 4032
4031 4033 /*
4032 4034 ** Check the queue file for plausibility to avoid attacks.
4033 4035 */
4034 4036
4035 4037 qsafe = S_IWOTH|S_IWGRP;
4036 4038 if (bitset(S_IWGRP, QueueFileMode))
4037 4039 qsafe &= ~S_IWGRP;
4038 4040
4039 4041 bogus = st.st_uid != geteuid() &&
4040 4042 st.st_uid != TrustedUid &&
4041 4043 geteuid() != RealUid;
4042 4044
4043 4045 /*
4044 4046 ** If this qf file results from a set-group-ID binary, then
4045 4047 ** we check whether the directory is group-writable,
4046 4048 ** the queue file mode contains the group-writable bit, and
4047 4049 ** the groups are the same.
4048 4050 ** Notice: this requires that the set-group-ID binary is used to
4049 4051 ** run the queue!
4050 4052 */
4051 4053
4052 4054 if (bogus && st.st_gid == getegid() && UseMSP)
4053 4055 {
4054 4056 char delim;
4055 4057 struct stat dst;
4056 4058
4057 4059 bp = SM_LAST_DIR_DELIM(qf);
4058 4060 if (bp == NULL)
4059 4061 delim = '\0';
4060 4062 else
4061 4063 {
4062 4064 delim = *bp;
4063 4065 *bp = '\0';
4064 4066 }
4065 4067 if (stat(delim == '\0' ? "." : qf, &dst) < 0)
4066 4068 syserr("readqf: cannot stat directory %s",
4067 4069 delim == '\0' ? "." : qf);
4068 4070 else
4069 4071 {
4070 4072 bogus = !(bitset(S_IWGRP, QueueFileMode) &&
4071 4073 bitset(S_IWGRP, dst.st_mode) &&
4072 4074 dst.st_gid == st.st_gid);
4073 4075 }
4074 4076 if (delim != '\0')
4075 4077 *bp = delim;
4076 4078 bp = NULL;
4077 4079 }
4078 4080 if (!bogus)
4079 4081 bogus = bitset(qsafe, st.st_mode);
4080 4082 if (bogus)
4081 4083 {
4082 4084 if (LogLevel > 0)
4083 4085 {
4084 4086 sm_syslog(LOG_ALERT, e->e_id,
4085 4087 "bogus queue file, uid=%d, gid=%d, mode=%o",
4086 4088 st.st_uid, st.st_gid, st.st_mode);
4087 4089 }
4088 4090 if (tTd(40, 8))
4089 4091 sm_dprintf("readqf(%s): bogus file\n", qf);
4090 4092 e->e_flags |= EF_INQUEUE;
4091 4093 if (!openonly)
4092 4094 loseqfile(e, "bogus file uid/gid in mqueue");
4093 4095 (void) sm_io_close(qfp, SM_TIME_DEFAULT);
4094 4096 return false;
4095 4097 }
4096 4098
4097 4099 if (st.st_size == 0)
4098 4100 {
4099 4101 /* must be a bogus file -- if also old, just remove it */
4100 4102 if (!openonly && st.st_ctime + 10 * 60 < curtime())
4101 4103 {
4102 4104 (void) xunlink(queuename(e, DATAFL_LETTER));
4103 4105 (void) xunlink(queuename(e, ANYQFL_LETTER));
4104 4106 }
4105 4107 (void) sm_io_close(qfp, SM_TIME_DEFAULT);
4106 4108 return false;
4107 4109 }
4108 4110
4109 4111 if (st.st_nlink == 0)
4110 4112 {
4111 4113 /*
4112 4114 ** Race condition -- we got a file just as it was being
4113 4115 ** unlinked. Just assume it is zero length.
4114 4116 */
4115 4117
4116 4118 (void) sm_io_close(qfp, SM_TIME_DEFAULT);
4117 4119 return false;
4118 4120 }
4119 4121
4120 4122 #if _FFR_TRUSTED_QF
4121 4123 /*
4122 4124 ** If we don't own the file mark it as unsafe.
4123 4125 ** However, allow TrustedUser to own it as well
4124 4126 ** in case TrustedUser manipulates the queue.
4125 4127 */
4126 4128
4127 4129 if (st.st_uid != geteuid() && st.st_uid != TrustedUid)
4128 4130 e->e_flags |= EF_UNSAFE;
4129 4131 #else /* _FFR_TRUSTED_QF */
4130 4132 /* If we don't own the file mark it as unsafe */
4131 4133 if (st.st_uid != geteuid())
4132 4134 e->e_flags |= EF_UNSAFE;
4133 4135 #endif /* _FFR_TRUSTED_QF */
4134 4136
4135 4137 /* good file -- save this lock */
4136 4138 e->e_lockfp = qfp;
4137 4139
4138 4140 /* Just wanted the open file */
4139 4141 if (openonly)
4140 4142 return true;
4141 4143
4142 4144 /* do basic system initialization */
4143 4145 initsys(e);
4144 4146 macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
4145 4147
4146 4148 LineNumber = 0;
4147 4149 e->e_flags |= EF_GLOBALERRS;
4148 4150 set_op_mode(MD_QUEUERUN);
4149 4151 ctladdr = NULL;
4150 4152 e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
4151 4153 e->e_dfqgrp = e->e_qgrp;
4152 4154 e->e_dfqdir = e->e_qdir;
4153 4155 #if _FFR_QUEUE_MACRO
4154 4156 macdefine(&e->e_macro, A_TEMP, macid("{queue}"),
4155 4157 qid_printqueue(e->e_qgrp, e->e_qdir));
4156 4158 #endif /* _FFR_QUEUE_MACRO */
4157 4159 e->e_dfino = -1;
4158 4160 e->e_msgsize = -1;
4159 4161 while (bufsize = sizeof(buf),
4160 4162 (bp = fgetfolded(buf, &bufsize, qfp)) != NULL)
4161 4163 {
4162 4164 unsigned long qflags;
4163 4165 ADDRESS *q;
4164 4166 int r;
4165 4167 time_t now;
4166 4168 auto char *ep;
4167 4169
4168 4170 if (tTd(40, 4))
4169 4171 sm_dprintf("+++++ %s\n", bp);
4170 4172 if (nomore)
4171 4173 {
4172 4174 /* hack attack */
4173 4175 hackattack:
4174 4176 syserr("SECURITY ALERT: extra or bogus data in queue file: %s",
4175 4177 bp);
4176 4178 err = "bogus queue line";
4177 4179 goto fail;
4178 4180 }
4179 4181 switch (bp[0])
4180 4182 {
4181 4183 case 'A': /* AUTH= parameter */
4182 4184 if (!xtextok(&bp[1]))
4183 4185 goto hackattack;
4184 4186 e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
4185 4187 break;
4186 4188
4187 4189 case 'B': /* body type */
4188 4190 r = check_bodytype(&bp[1]);
4189 4191 if (!BODYTYPE_VALID(r))
4190 4192 goto hackattack;
4191 4193 e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
4192 4194 break;
4193 4195
4194 4196 case 'C': /* specify controlling user */
4195 4197 ctladdr = setctluser(&bp[1], qfver, e);
4196 4198 break;
4197 4199
4198 4200 case 'D': /* data file name */
4199 4201 /* obsolete -- ignore */
4200 4202 break;
4201 4203
4202 4204 case 'd': /* data file directory name */
4203 4205 {
4204 4206 int qgrp, qdir;
4205 4207
4206 4208 #if _FFR_MSP_PARANOIA
4207 4209 /* forbid queue groups in MSP? */
4208 4210 if (UseMSP)
4209 4211 goto hackattack;
4210 4212 #endif /* _FFR_MSP_PARANOIA */
4211 4213 for (qgrp = 0;
4212 4214 qgrp < NumQueue && Queue[qgrp] != NULL;
4213 4215 ++qgrp)
4214 4216 {
4215 4217 for (qdir = 0;
4216 4218 qdir < Queue[qgrp]->qg_numqueues;
4217 4219 ++qdir)
4218 4220 {
4219 4221 if (strcmp(&bp[1],
4220 4222 Queue[qgrp]->qg_qpaths[qdir].qp_name)
4221 4223 == 0)
4222 4224 {
4223 4225 e->e_dfqgrp = qgrp;
4224 4226 e->e_dfqdir = qdir;
4225 4227 goto done;
4226 4228 }
4227 4229 }
4228 4230 }
4229 4231 err = "bogus queue file directory";
4230 4232 goto fail;
4231 4233 done:
4232 4234 break;
4233 4235 }
4234 4236
4235 4237 case 'E': /* specify error recipient */
4236 4238 /* no longer used */
4237 4239 break;
4238 4240
4239 4241 case 'F': /* flag bits */
4240 4242 if (strncmp(bp, "From ", 5) == 0)
4241 4243 {
4242 4244 /* we are being spoofed! */
4243 4245 syserr("SECURITY ALERT: bogus qf line %s", bp);
4244 4246 err = "bogus queue line";
4245 4247 goto fail;
4246 4248 }
4247 4249 for (p = &bp[1]; *p != '\0'; p++)
4248 4250 {
4249 4251 switch (*p)
4250 4252 {
4251 4253 case '8': /* has 8 bit data */
4252 4254 e->e_flags |= EF_HAS8BIT;
4253 4255 break;
4254 4256
4255 4257 case 'b': /* delete Bcc: header */
4256 4258 e->e_flags |= EF_DELETE_BCC;
4257 4259 break;
4258 4260
4259 4261 case 'd': /* envelope has DSN RET= */
4260 4262 e->e_flags |= EF_RET_PARAM;
4261 4263 break;
4262 4264
4263 4265 case 'n': /* don't return body */
4264 4266 e->e_flags |= EF_NO_BODY_RETN;
4265 4267 break;
4266 4268
4267 4269 case 'r': /* response */
4268 4270 e->e_flags |= EF_RESPONSE;
4269 4271 break;
4270 4272
4271 4273 case 's': /* split */
4272 4274 e->e_flags |= EF_SPLIT;
4273 4275 break;
4274 4276
4275 4277 case 'w': /* warning sent */
4276 4278 e->e_flags |= EF_WARNING;
4277 4279 break;
4278 4280 }
4279 4281 }
4280 4282 break;
4281 4283
4282 4284 case 'q': /* quarantine reason */
4283 4285 e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
4284 4286 macdefine(&e->e_macro, A_PERM,
4285 4287 macid("{quarantine}"), e->e_quarmsg);
4286 4288 break;
4287 4289
4288 4290 case 'H': /* header */
4289 4291
4290 4292 /*
4291 4293 ** count size before chompheader() destroys the line.
4292 4294 ** this isn't accurate due to macro expansion, but
4293 4295 ** better than before. "-3" to skip H?? at least.
4294 4296 */
4295 4297
4296 4298 hdrsize += strlen(bp) - 3;
4297 4299 (void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e);
4298 4300 break;
4299 4301
4300 4302 case 'I': /* data file's inode number */
4301 4303 /* regenerated below */
4302 4304 break;
4303 4305
4304 4306 case 'K': /* time of last delivery attempt */
4305 4307 e->e_dtime = atol(&buf[1]);
4306 4308 break;
4307 4309
4308 4310 case 'L': /* Solaris Content-Length: */
4309 4311 case 'M': /* message */
4310 4312 /* ignore this; we want a new message next time */
4311 4313 break;
4312 4314
4313 4315 case 'N': /* number of delivery attempts */
4314 4316 e->e_ntries = atoi(&buf[1]);
4315 4317
4316 4318 /* if this has been tried recently, let it be */
4317 4319 now = curtime();
4318 4320 if (e->e_ntries > 0 && e->e_dtime <= now &&
4319 4321 now < e->e_dtime + MinQueueAge)
4320 4322 {
4321 4323 char *howlong;
4322 4324
4323 4325 howlong = pintvl(now - e->e_dtime, true);
4324 4326 if (Verbose)
4325 4327 (void) sm_io_fprintf(smioout,
4326 4328 SM_TIME_DEFAULT,
4327 4329 "%s: too young (%s)\n",
4328 4330 e->e_id, howlong);
4329 4331 if (tTd(40, 8))
4330 4332 sm_dprintf("%s: too young (%s)\n",
4331 4333 e->e_id, howlong);
4332 4334 if (LogLevel > 19)
4333 4335 sm_syslog(LOG_DEBUG, e->e_id,
4334 4336 "too young (%s)",
4335 4337 howlong);
4336 4338 e->e_id = NULL;
4337 4339 unlockqueue(e);
4338 4340 if (bp != buf)
4339 4341 sm_free(bp);
4340 4342 return false;
4341 4343 }
4342 4344 macdefine(&e->e_macro, A_TEMP,
4343 4345 macid("{ntries}"), &buf[1]);
4344 4346
4345 4347 #if NAMED_BIND
4346 4348 /* adjust BIND parameters immediately */
4347 4349 if (e->e_ntries == 0)
4348 4350 {
4349 4351 _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
4350 4352 _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
4351 4353 }
4352 4354 else
4353 4355 {
4354 4356 _res.retry = TimeOuts.res_retry[RES_TO_NORMAL];
4355 4357 _res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL];
4356 4358 }
4357 4359 #endif /* NAMED_BIND */
4358 4360 break;
4359 4361
4360 4362 case 'P': /* message priority */
4361 4363 e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
4362 4364 break;
4363 4365
4364 4366 case 'Q': /* original recipient */
4365 4367 orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
4366 4368 break;
4367 4369
4368 4370 case 'r': /* final recipient */
4369 4371 frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
4370 4372 break;
4371 4373
4372 4374 case 'R': /* specify recipient */
4373 4375 p = bp;
4374 4376 qflags = 0;
4375 4377 if (qfver >= 1)
4376 4378 {
4377 4379 /* get flag bits */
4378 4380 while (*++p != '\0' && *p != ':')
4379 4381 {
4380 4382 switch (*p)
4381 4383 {
4382 4384 case 'N':
4383 4385 qflags |= QHASNOTIFY;
4384 4386 break;
4385 4387
4386 4388 case 'S':
4387 4389 qflags |= QPINGONSUCCESS;
4388 4390 break;
4389 4391
4390 4392 case 'F':
4391 4393 qflags |= QPINGONFAILURE;
4392 4394 break;
4393 4395
4394 4396 case 'D':
4395 4397 qflags |= QPINGONDELAY;
4396 4398 break;
4397 4399
4398 4400 case 'P':
4399 4401 qflags |= QPRIMARY;
4400 4402 break;
4401 4403
4402 4404 case 'A':
4403 4405 if (ctladdr != NULL)
4404 4406 ctladdr->q_flags |= QALIAS;
4405 4407 break;
4406 4408
4407 4409 default: /* ignore or complain? */
4408 4410 break;
4409 4411 }
4410 4412 }
4411 4413 }
4412 4414 else
4413 4415 qflags |= QPRIMARY;
4414 4416 macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
4415 4417 "e r");
4416 4418 if (*p != '\0')
4417 4419 q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0',
4418 4420 NULL, e, true);
4419 4421 else
4420 4422 q = NULL;
4421 4423 if (q != NULL)
4422 4424 {
4423 4425 /* make sure we keep the current qgrp */
4424 4426 if (ISVALIDQGRP(e->e_qgrp))
4425 4427 q->q_qgrp = e->e_qgrp;
4426 4428 q->q_alias = ctladdr;
4427 4429 if (qfver >= 1)
4428 4430 q->q_flags &= ~Q_PINGFLAGS;
4429 4431 q->q_flags |= qflags;
4430 4432 q->q_finalrcpt = frcpt;
4431 4433 q->q_orcpt = orcpt;
4432 4434 (void) recipient(q, &e->e_sendqueue, 0, e);
4433 4435 }
4434 4436 frcpt = NULL;
4435 4437 orcpt = NULL;
4436 4438 macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
4437 4439 NULL);
4438 4440 break;
4439 4441
4440 4442 case 'S': /* sender */
4441 4443 setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]),
4442 4444 e, NULL, '\0', true);
4443 4445 break;
4444 4446
4445 4447 case 'T': /* init time */
4446 4448 e->e_ctime = atol(&bp[1]);
4447 4449 break;
4448 4450
4449 4451 case 'V': /* queue file version number */
4450 4452 qfver = atoi(&bp[1]);
4451 4453 if (qfver <= QF_VERSION)
4452 4454 break;
4453 4455 syserr("Version number in queue file (%d) greater than max (%d)",
4454 4456 qfver, QF_VERSION);
4455 4457 err = "unsupported queue file version";
4456 4458 goto fail;
4457 4459 /* NOTREACHED */
4458 4460 break;
4459 4461
4460 4462 case 'Z': /* original envelope id from ESMTP */
4461 4463 e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
4462 4464 macdefine(&e->e_macro, A_PERM,
4463 4465 macid("{dsn_envid}"), e->e_envid);
4464 4466 break;
4465 4467
4466 4468 case '!': /* deliver by */
4467 4469
4468 4470 /* format: flag (1 char) space long-integer */
4469 4471 e->e_dlvr_flag = buf[1];
4470 4472 e->e_deliver_by = strtol(&buf[3], NULL, 10);
4471 4473
4472 4474 case '$': /* define macro */
4473 4475 {
4474 4476 char *p;
4475 4477
4476 4478 /* XXX elimate p? */
4477 4479 r = macid_parse(&bp[1], &ep);
4478 4480 if (r == 0)
4479 4481 break;
4480 4482 p = sm_rpool_strdup_x(e->e_rpool, ep);
4481 4483 macdefine(&e->e_macro, A_PERM, r, p);
4482 4484 }
4483 4485 break;
4484 4486
4485 4487 case '.': /* terminate file */
4486 4488 nomore = true;
4487 4489 break;
4488 4490
4489 4491 #if _FFR_QUEUEDELAY
4490 4492 case 'G':
4491 4493 case 'Y':
4492 4494
4493 4495 /*
4494 4496 ** Maintain backward compatibility for
4495 4497 ** users who defined _FFR_QUEUEDELAY in
4496 4498 ** previous releases. Remove this
4497 4499 ** code in 8.14 or 8.15.
4498 4500 */
4499 4501
4500 4502 if (qfver == 5 || qfver == 7)
4501 4503 break;
4502 4504
4503 4505 /* If not qfver 5 or 7, then 'G' or 'Y' is invalid */
4504 4506 /* FALLTHROUGH */
4505 4507 #endif /* _FFR_QUEUEDELAY */
4506 4508
4507 4509 default:
4508 4510 syserr("readqf: %s: line %d: bad line \"%s\"",
4509 4511 qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
4510 4512 err = "unrecognized line";
4511 4513 goto fail;
4512 4514 }
4513 4515
4514 4516 if (bp != buf)
4515 4517 SM_FREE(bp);
4516 4518 }
4517 4519
4518 4520 /*
4519 4521 ** If we haven't read any lines, this queue file is empty.
4520 4522 ** Arrange to remove it without referencing any null pointers.
4521 4523 */
4522 4524
4523 4525 if (LineNumber == 0)
4524 4526 {
4525 4527 errno = 0;
4526 4528 e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE;
4527 4529 return true;
4528 4530 }
4529 4531
4530 4532 /* Check to make sure we have a complete queue file read */
4531 4533 if (!nomore)
4532 4534 {
4533 4535 syserr("readqf: %s: incomplete queue file read", qf);
4534 4536 (void) sm_io_close(qfp, SM_TIME_DEFAULT);
4535 4537 return false;
4536 4538 }
4537 4539
4538 4540 #if _FFR_QF_PARANOIA
4539 4541 /* Check to make sure key fields were read */
4540 4542 if (e->e_from.q_mailer == NULL)
4541 4543 {
4542 4544 syserr("readqf: %s: sender not specified in queue file", qf);
4543 4545 (void) sm_io_close(qfp, SM_TIME_DEFAULT);
4544 4546 return false;
4545 4547 }
4546 4548 /* other checks? */
4547 4549 #endif /* _FFR_QF_PARANOIA */
4548 4550
4549 4551 /* possibly set ${dsn_ret} macro */
4550 4552 if (bitset(EF_RET_PARAM, e->e_flags))
4551 4553 {
4552 4554 if (bitset(EF_NO_BODY_RETN, e->e_flags))
4553 4555 macdefine(&e->e_macro, A_PERM,
4554 4556 macid("{dsn_ret}"), "hdrs");
4555 4557 else
4556 4558 macdefine(&e->e_macro, A_PERM,
4557 4559 macid("{dsn_ret}"), "full");
4558 4560 }
4559 4561
4560 4562 /*
4561 4563 ** Arrange to read the data file.
4562 4564 */
4563 4565
4564 4566 p = queuename(e, DATAFL_LETTER);
4565 4567 e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY_B,
4566 4568 NULL);
4567 4569 if (e->e_dfp == NULL)
4568 4570 {
4569 4571 syserr("readqf: cannot open %s", p);
4570 4572 }
4571 4573 else
4572 4574 {
4573 4575 e->e_flags |= EF_HAS_DF;
4574 4576 if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st)
4575 4577 >= 0)
4576 4578 {
4577 4579 e->e_msgsize = st.st_size + hdrsize;
4578 4580 e->e_dfdev = st.st_dev;
4579 4581 e->e_dfino = ST_INODE(st);
4580 4582 (void) sm_snprintf(buf, sizeof(buf), "%ld",
4581 4583 e->e_msgsize);
4582 4584 macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"),
4583 4585 buf);
4584 4586 }
4585 4587 }
4586 4588
4587 4589 return true;
4588 4590
4589 4591 fail:
4590 4592 /*
4591 4593 ** There was some error reading the qf file (reason is in err var.)
4592 4594 ** Cleanup:
4593 4595 ** close file; clear e_lockfp since it is the same as qfp,
4594 4596 ** hence it is invalid (as file) after qfp is closed;
4595 4597 ** the qf file is on disk, so set the flag to avoid calling
4596 4598 ** queueup() with bogus data.
4597 4599 */
4598 4600
4599 4601 if (bp != buf)
4600 4602 SM_FREE(bp);
4601 4603 if (qfp != NULL)
4602 4604 (void) sm_io_close(qfp, SM_TIME_DEFAULT);
4603 4605 e->e_lockfp = NULL;
4604 4606 e->e_flags |= EF_INQUEUE;
4605 4607 loseqfile(e, err);
4606 4608 return false;
4607 4609 }
4608 4610 /*
4609 4611 ** PRTSTR -- print a string, "unprintable" characters are shown as \oct
4610 4612 **
4611 4613 ** Parameters:
4612 4614 ** s -- string to print
4613 4615 ** ml -- maximum length of output
4614 4616 **
4615 4617 ** Returns:
4616 4618 ** number of entries
4617 4619 **
4618 4620 ** Side Effects:
4619 4621 ** Prints a string on stdout.
4620 4622 */
4621 4623
4622 4624 static void prtstr __P((char *, int));
4623 4625
4624 4626 static void
4625 4627 prtstr(s, ml)
4626 4628 char *s;
4627 4629 int ml;
4628 4630 {
4629 4631 int c;
4630 4632
4631 4633 if (s == NULL)
4632 4634 return;
4633 4635 while (ml-- > 0 && ((c = *s++) != '\0'))
4634 4636 {
4635 4637 if (c == '\\')
4636 4638 {
4637 4639 if (ml-- > 0)
4638 4640 {
4639 4641 (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
4640 4642 (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
4641 4643 }
4642 4644 }
4643 4645 else if (isascii(c) && isprint(c))
4644 4646 (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
4645 4647 else
4646 4648 {
4647 4649 if ((ml -= 3) > 0)
4648 4650 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4649 4651 "\\%03o", c & 0xFF);
4650 4652 }
4651 4653 }
4652 4654 }
4653 4655 /*
4654 4656 ** PRINTNQE -- print out number of entries in the mail queue
4655 4657 **
4656 4658 ** Parameters:
4657 4659 ** out -- output file pointer.
4658 4660 ** prefix -- string to output in front of each line.
4659 4661 **
4660 4662 ** Returns:
4661 4663 ** none.
4662 4664 */
4663 4665
4664 4666 void
4665 4667 printnqe(out, prefix)
4666 4668 SM_FILE_T *out;
4667 4669 char *prefix;
4668 4670 {
4669 4671 #if SM_CONF_SHM
4670 4672 int i, k = 0, nrequests = 0;
4671 4673 bool unknown = false;
4672 4674
4673 4675 if (ShmId == SM_SHM_NO_ID)
4674 4676 {
4675 4677 if (prefix == NULL)
4676 4678 (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
4677 4679 "Data unavailable: shared memory not updated\n");
4678 4680 else
4679 4681 (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
4680 4682 "%sNOTCONFIGURED:-1\r\n", prefix);
4681 4683 return;
4682 4684 }
4683 4685 for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
4684 4686 {
4685 4687 int j;
4686 4688
4687 4689 k++;
4688 4690 for (j = 0; j < Queue[i]->qg_numqueues; j++)
4689 4691 {
4690 4692 int n;
4691 4693
4692 4694 if (StopRequest)
4693 4695 stop_sendmail();
4694 4696
4695 4697 n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx);
4696 4698 if (prefix != NULL)
4697 4699 (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
4698 4700 "%s%s:%d\r\n",
4699 4701 prefix, qid_printqueue(i, j), n);
4700 4702 else if (n < 0)
4701 4703 {
4702 4704 (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
4703 4705 "%s: unknown number of entries\n",
4704 4706 qid_printqueue(i, j));
4705 4707 unknown = true;
4706 4708 }
4707 4709 else if (n == 0)
4708 4710 {
4709 4711 (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
4710 4712 "%s is empty\n",
4711 4713 qid_printqueue(i, j));
4712 4714 }
4713 4715 else if (n > 0)
4714 4716 {
4715 4717 (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
4716 4718 "%s: entries=%d\n",
4717 4719 qid_printqueue(i, j), n);
4718 4720 nrequests += n;
4719 4721 k++;
4720 4722 }
4721 4723 }
4722 4724 }
4723 4725 if (prefix == NULL && k > 1)
4724 4726 (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
4725 4727 "\t\tTotal requests: %d%s\n",
4726 4728 nrequests, unknown ? " (about)" : "");
4727 4729 #else /* SM_CONF_SHM */
4728 4730 if (prefix == NULL)
4729 4731 (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
4730 4732 "Data unavailable without shared memory support\n");
4731 4733 else
4732 4734 (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
4733 4735 "%sNOTAVAILABLE:-1\r\n", prefix);
4734 4736 #endif /* SM_CONF_SHM */
4735 4737 }
4736 4738 /*
4737 4739 ** PRINTQUEUE -- print out a representation of the mail queue
4738 4740 **
4739 4741 ** Parameters:
4740 4742 ** none.
4741 4743 **
4742 4744 ** Returns:
4743 4745 ** none.
4744 4746 **
4745 4747 ** Side Effects:
4746 4748 ** Prints a listing of the mail queue on the standard output.
4747 4749 */
4748 4750
4749 4751 void
4750 4752 printqueue()
4751 4753 {
4752 4754 int i, k = 0, nrequests = 0;
4753 4755
4754 4756 for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
4755 4757 {
4756 4758 int j;
4757 4759
4758 4760 k++;
4759 4761 for (j = 0; j < Queue[i]->qg_numqueues; j++)
4760 4762 {
4761 4763 if (StopRequest)
4762 4764 stop_sendmail();
4763 4765 nrequests += print_single_queue(i, j);
4764 4766 k++;
4765 4767 }
4766 4768 }
4767 4769 if (k > 1)
4768 4770 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4769 4771 "\t\tTotal requests: %d\n",
4770 4772 nrequests);
4771 4773 }
4772 4774 /*
4773 4775 ** PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue
4774 4776 **
4775 4777 ** Parameters:
4776 4778 ** qgrp -- the index of the queue group.
4777 4779 ** qdir -- the queue directory.
4778 4780 **
4779 4781 ** Returns:
4780 4782 ** number of requests in mail queue.
4781 4783 **
4782 4784 ** Side Effects:
4783 4785 ** Prints a listing of the mail queue on the standard output.
4784 4786 */
4785 4787
4786 4788 int
4787 4789 print_single_queue(qgrp, qdir)
4788 4790 int qgrp;
4789 4791 int qdir;
4790 4792 {
4791 4793 register WORK *w;
4792 4794 SM_FILE_T *f;
4793 4795 int nrequests;
4794 4796 char qd[MAXPATHLEN];
4795 4797 char qddf[MAXPATHLEN];
4796 4798 char buf[MAXLINE];
4797 4799
4798 4800 if (qdir == NOQDIR)
4799 4801 {
4800 4802 (void) sm_strlcpy(qd, ".", sizeof(qd));
4801 4803 (void) sm_strlcpy(qddf, ".", sizeof(qddf));
4802 4804 }
4803 4805 else
4804 4806 {
4805 4807 (void) sm_strlcpyn(qd, sizeof(qd), 2,
4806 4808 Queue[qgrp]->qg_qpaths[qdir].qp_name,
4807 4809 (bitset(QP_SUBQF,
4808 4810 Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
4809 4811 ? "/qf" : ""));
4810 4812 (void) sm_strlcpyn(qddf, sizeof(qddf), 2,
4811 4813 Queue[qgrp]->qg_qpaths[qdir].qp_name,
4812 4814 (bitset(QP_SUBDF,
4813 4815 Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
4814 4816 ? "/df" : ""));
4815 4817 }
4816 4818
4817 4819 /*
4818 4820 ** Check for permission to print the queue
4819 4821 */
4820 4822
4821 4823 if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
4822 4824 {
4823 4825 struct stat st;
4824 4826 #ifdef NGROUPS_MAX
4825 4827 int n;
4826 4828 extern GIDSET_T InitialGidSet[NGROUPS_MAX];
4827 4829 #endif /* NGROUPS_MAX */
4828 4830
4829 4831 if (stat(qd, &st) < 0)
4830 4832 {
4831 4833 syserr("Cannot stat %s",
4832 4834 qid_printqueue(qgrp, qdir));
4833 4835 return 0;
4834 4836 }
4835 4837 #ifdef NGROUPS_MAX
4836 4838 n = NGROUPS_MAX;
4837 4839 while (--n >= 0)
4838 4840 {
4839 4841 if (InitialGidSet[n] == st.st_gid)
4840 4842 break;
4841 4843 }
4842 4844 if (n < 0 && RealGid != st.st_gid)
4843 4845 #else /* NGROUPS_MAX */
4844 4846 if (RealGid != st.st_gid)
4845 4847 #endif /* NGROUPS_MAX */
4846 4848 {
4847 4849 usrerr("510 You are not permitted to see the queue");
4848 4850 setstat(EX_NOPERM);
4849 4851 return 0;
4850 4852 }
4851 4853 }
4852 4854
4853 4855 /*
4854 4856 ** Read and order the queue.
4855 4857 */
4856 4858
4857 4859 nrequests = gatherq(qgrp, qdir, true, NULL, NULL, NULL);
4858 4860 (void) sortq(Queue[qgrp]->qg_maxlist);
4859 4861
4860 4862 /*
4861 4863 ** Print the work list that we have read.
4862 4864 */
4863 4865
4864 4866 /* first see if there is anything */
4865 4867 if (nrequests <= 0)
4866 4868 {
4867 4869 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n",
4868 4870 qid_printqueue(qgrp, qdir));
4869 4871 return 0;
4870 4872 }
4871 4873
4872 4874 sm_getla(); /* get load average */
4873 4875
4874 4876 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s",
4875 4877 qid_printqueue(qgrp, qdir),
4876 4878 nrequests, nrequests == 1 ? "" : "s");
4877 4879 if (MaxQueueRun > 0 && nrequests > MaxQueueRun)
4878 4880 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4879 4881 ", only %d printed", MaxQueueRun);
4880 4882 if (Verbose)
4881 4883 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4882 4884 ")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n");
4883 4885 else
4884 4886 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4885 4887 ")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n");
4886 4888 for (w = WorkQ; w != NULL; w = w->w_next)
4887 4889 {
4888 4890 struct stat st;
4889 4891 auto time_t submittime = 0;
4890 4892 long dfsize;
4891 4893 int flags = 0;
4892 4894 int qfver;
4893 4895 char quarmsg[MAXLINE];
4894 4896 char statmsg[MAXLINE];
4895 4897 char bodytype[MAXNAME + 1];
4896 4898 char qf[MAXPATHLEN];
4897 4899
4898 4900 if (StopRequest)
4899 4901 stop_sendmail();
4900 4902
4901 4903 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s",
4902 4904 w->w_name + 2);
4903 4905 (void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", w->w_name);
4904 4906 f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B,
4905 4907 NULL);
4906 4908 if (f == NULL)
4907 4909 {
4908 4910 if (errno == EPERM)
4909 4911 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4910 4912 " (permission denied)\n");
4911 4913 else if (errno == ENOENT)
4912 4914 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4913 4915 " (job completed)\n");
4914 4916 else
4915 4917 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4916 4918 " (%s)\n",
4917 4919 sm_errstring(errno));
4918 4920 errno = 0;
4919 4921 continue;
4920 4922 }
4921 4923 w->w_name[0] = DATAFL_LETTER;
4922 4924 (void) sm_strlcpyn(qf, sizeof(qf), 3, qddf, "/", w->w_name);
4923 4925 if (stat(qf, &st) >= 0)
4924 4926 dfsize = st.st_size;
4925 4927 else
4926 4928 {
4927 4929 ENVELOPE e;
4928 4930
4929 4931 /*
4930 4932 ** Maybe the df file can't be statted because
4931 4933 ** it is in a different directory than the qf file.
4932 4934 ** In order to find out, we must read the qf file.
4933 4935 */
4934 4936
4935 4937 newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL));
4936 4938 e.e_id = w->w_name + 2;
4937 4939 e.e_qgrp = qgrp;
4938 4940 e.e_qdir = qdir;
4939 4941 dfsize = -1;
4940 4942 if (readqf(&e, false))
4941 4943 {
4942 4944 char *df = queuename(&e, DATAFL_LETTER);
4943 4945 if (stat(df, &st) >= 0)
4944 4946 dfsize = st.st_size;
4945 4947 }
4946 4948 if (e.e_lockfp != NULL)
4947 4949 {
4948 4950 (void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT);
4949 4951 e.e_lockfp = NULL;
4950 4952 }
4951 4953 clearenvelope(&e, false, e.e_rpool);
4952 4954 sm_rpool_free(e.e_rpool);
4953 4955 }
4954 4956 if (w->w_lock)
4955 4957 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*");
4956 4958 else if (QueueMode == QM_LOST)
4957 4959 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?");
4958 4960 else if (w->w_tooyoung)
4959 4961 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-");
4960 4962 else if (shouldqueue(w->w_pri, w->w_ctime))
4961 4963 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X");
4962 4964 else
4963 4965 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " ");
4964 4966
4965 4967 errno = 0;
4966 4968
4967 4969 quarmsg[0] = '\0';
4968 4970 statmsg[0] = bodytype[0] = '\0';
4969 4971 qfver = 0;
4970 4972 while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
4971 4973 {
4972 4974 register int i;
4973 4975 register char *p;
4974 4976
4975 4977 if (StopRequest)
4976 4978 stop_sendmail();
4977 4979
4978 4980 fixcrlf(buf, true);
4979 4981 switch (buf[0])
4980 4982 {
4981 4983 case 'V': /* queue file version */
4982 4984 qfver = atoi(&buf[1]);
4983 4985 break;
4984 4986
4985 4987 case 'M': /* error message */
4986 4988 if ((i = strlen(&buf[1])) >= sizeof(statmsg))
4987 4989 i = sizeof(statmsg) - 1;
4988 4990 memmove(statmsg, &buf[1], i);
4989 4991 statmsg[i] = '\0';
4990 4992 break;
4991 4993
4992 4994 case 'q': /* quarantine reason */
4993 4995 if ((i = strlen(&buf[1])) >= sizeof(quarmsg))
4994 4996 i = sizeof(quarmsg) - 1;
4995 4997 memmove(quarmsg, &buf[1], i);
4996 4998 quarmsg[i] = '\0';
4997 4999 break;
4998 5000
4999 5001 case 'B': /* body type */
5000 5002 if ((i = strlen(&buf[1])) >= sizeof(bodytype))
5001 5003 i = sizeof(bodytype) - 1;
5002 5004 memmove(bodytype, &buf[1], i);
5003 5005 bodytype[i] = '\0';
5004 5006 break;
5005 5007
5006 5008 case 'S': /* sender name */
5007 5009 if (Verbose)
5008 5010 {
5009 5011 (void) sm_io_fprintf(smioout,
5010 5012 SM_TIME_DEFAULT,
5011 5013 "%8ld %10ld%c%.12s ",
5012 5014 dfsize,
5013 5015 w->w_pri,
5014 5016 bitset(EF_WARNING, flags)
5015 5017 ? '+' : ' ',
5016 5018 ctime(&submittime) + 4);
5017 5019 prtstr(&buf[1], 78);
5018 5020 }
5019 5021 else
5020 5022 {
5021 5023 (void) sm_io_fprintf(smioout,
5022 5024 SM_TIME_DEFAULT,
5023 5025 "%8ld %.16s ",
5024 5026 dfsize,
5025 5027 ctime(&submittime));
5026 5028 prtstr(&buf[1], 39);
5027 5029 }
5028 5030
5029 5031 if (quarmsg[0] != '\0')
5030 5032 {
5031 5033 (void) sm_io_fprintf(smioout,
5032 5034 SM_TIME_DEFAULT,
5033 5035 "\n QUARANTINE: %.*s",
5034 5036 Verbose ? 100 : 60,
5035 5037 quarmsg);
5036 5038 quarmsg[0] = '\0';
5037 5039 }
5038 5040
5039 5041 if (statmsg[0] != '\0' || bodytype[0] != '\0')
5040 5042 {
5041 5043 (void) sm_io_fprintf(smioout,
5042 5044 SM_TIME_DEFAULT,
5043 5045 "\n %10.10s",
5044 5046 bodytype);
5045 5047 if (statmsg[0] != '\0')
5046 5048 (void) sm_io_fprintf(smioout,
5047 5049 SM_TIME_DEFAULT,
5048 5050 " (%.*s)",
5049 5051 Verbose ? 100 : 60,
5050 5052 statmsg);
5051 5053 statmsg[0] = '\0';
5052 5054 }
5053 5055 break;
5054 5056
5055 5057 case 'C': /* controlling user */
5056 5058 if (Verbose)
5057 5059 (void) sm_io_fprintf(smioout,
5058 5060 SM_TIME_DEFAULT,
5059 5061 "\n\t\t\t\t\t\t(---%.64s---)",
5060 5062 &buf[1]);
5061 5063 break;
5062 5064
5063 5065 case 'R': /* recipient name */
5064 5066 p = &buf[1];
5065 5067 if (qfver >= 1)
5066 5068 {
5067 5069 p = strchr(p, ':');
5068 5070 if (p == NULL)
5069 5071 break;
5070 5072 p++;
5071 5073 }
5072 5074 if (Verbose)
5073 5075 {
5074 5076 (void) sm_io_fprintf(smioout,
5075 5077 SM_TIME_DEFAULT,
5076 5078 "\n\t\t\t\t\t\t");
5077 5079 prtstr(p, 71);
5078 5080 }
5079 5081 else
5080 5082 {
5081 5083 (void) sm_io_fprintf(smioout,
5082 5084 SM_TIME_DEFAULT,
5083 5085 "\n\t\t\t\t\t ");
5084 5086 prtstr(p, 38);
5085 5087 }
5086 5088 if (Verbose && statmsg[0] != '\0')
5087 5089 {
5088 5090 (void) sm_io_fprintf(smioout,
5089 5091 SM_TIME_DEFAULT,
5090 5092 "\n\t\t (%.100s)",
5091 5093 statmsg);
5092 5094 statmsg[0] = '\0';
5093 5095 }
5094 5096 break;
5095 5097
5096 5098 case 'T': /* creation time */
5097 5099 submittime = atol(&buf[1]);
5098 5100 break;
5099 5101
5100 5102 case 'F': /* flag bits */
5101 5103 for (p = &buf[1]; *p != '\0'; p++)
5102 5104 {
5103 5105 switch (*p)
5104 5106 {
5105 5107 case 'w':
5106 5108 flags |= EF_WARNING;
5107 5109 break;
5108 5110 }
5109 5111 }
5110 5112 }
5111 5113 }
5112 5114 if (submittime == (time_t) 0)
5113 5115 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
5114 5116 " (no control file)");
5115 5117 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n");
5116 5118 (void) sm_io_close(f, SM_TIME_DEFAULT);
5117 5119 }
5118 5120 return nrequests;
5119 5121 }
5120 5122
5121 5123 /*
5122 5124 ** QUEUE_LETTER -- get the proper queue letter for the current QueueMode.
5123 5125 **
5124 5126 ** Parameters:
5125 5127 ** e -- envelope to build it in/from.
5126 5128 ** type -- the file type, used as the first character
5127 5129 ** of the file name.
5128 5130 **
5129 5131 ** Returns:
5130 5132 ** the letter to use
5131 5133 */
5132 5134
5133 5135 static char
5134 5136 queue_letter(e, type)
5135 5137 ENVELOPE *e;
5136 5138 int type;
5137 5139 {
5138 5140 /* Change type according to QueueMode */
5139 5141 if (type == ANYQFL_LETTER)
5140 5142 {
5141 5143 if (e->e_quarmsg != NULL)
5142 5144 type = QUARQF_LETTER;
5143 5145 else
5144 5146 {
5145 5147 switch (QueueMode)
5146 5148 {
5147 5149 case QM_NORMAL:
5148 5150 type = NORMQF_LETTER;
5149 5151 break;
5150 5152
5151 5153 case QM_QUARANTINE:
5152 5154 type = QUARQF_LETTER;
5153 5155 break;
5154 5156
5155 5157 case QM_LOST:
5156 5158 type = LOSEQF_LETTER;
5157 5159 break;
5158 5160
5159 5161 default:
5160 5162 /* should never happen */
5161 5163 abort();
5162 5164 /* NOTREACHED */
5163 5165 }
5164 5166 }
5165 5167 }
5166 5168 return type;
5167 5169 }
5168 5170
5169 5171 /*
5170 5172 ** QUEUENAME -- build a file name in the queue directory for this envelope.
5171 5173 **
5172 5174 ** Parameters:
5173 5175 ** e -- envelope to build it in/from.
5174 5176 ** type -- the file type, used as the first character
5175 5177 ** of the file name.
5176 5178 **
5177 5179 ** Returns:
5178 5180 ** a pointer to the queue name (in a static buffer).
5179 5181 **
5180 5182 ** Side Effects:
5181 5183 ** If no id code is already assigned, queuename() will
5182 5184 ** assign an id code with assign_queueid(). If no queue
5183 5185 ** directory is assigned, one will be set with setnewqueue().
5184 5186 */
5185 5187
5186 5188 char *
5187 5189 queuename(e, type)
5188 5190 register ENVELOPE *e;
5189 5191 int type;
5190 5192 {
5191 5193 int qd, qg;
5192 5194 char *sub = "/";
5193 5195 char pref[3];
5194 5196 static char buf[MAXPATHLEN];
5195 5197
5196 5198 /* Assign an ID if needed */
5197 5199 if (e->e_id == NULL)
5198 5200 assign_queueid(e);
5199 5201 type = queue_letter(e, type);
5200 5202
5201 5203 /* begin of filename */
5202 5204 pref[0] = (char) type;
5203 5205 pref[1] = 'f';
5204 5206 pref[2] = '\0';
5205 5207
5206 5208 /* Assign a queue group/directory if needed */
5207 5209 if (type == XSCRPT_LETTER)
5208 5210 {
5209 5211 /*
5210 5212 ** We don't want to call setnewqueue() if we are fetching
5211 5213 ** the pathname of the transcript file, because setnewqueue
5212 5214 ** chooses a queue, and sometimes we need to write to the
5213 5215 ** transcript file before we have gathered enough information
5214 5216 ** to choose a queue.
5215 5217 */
5216 5218
5217 5219 if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
5218 5220 {
5219 5221 if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR)
5220 5222 {
5221 5223 e->e_xfqgrp = e->e_qgrp;
5222 5224 e->e_xfqdir = e->e_qdir;
5223 5225 }
5224 5226 else
5225 5227 {
5226 5228 e->e_xfqgrp = 0;
5227 5229 if (Queue[e->e_xfqgrp]->qg_numqueues <= 1)
5228 5230 e->e_xfqdir = 0;
5229 5231 else
5230 5232 {
5231 5233 e->e_xfqdir = get_rand_mod(
5232 5234 Queue[e->e_xfqgrp]->qg_numqueues);
5233 5235 }
5234 5236 }
5235 5237 }
5236 5238 qd = e->e_xfqdir;
5237 5239 qg = e->e_xfqgrp;
5238 5240 }
5239 5241 else
5240 5242 {
5241 5243 if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
5242 5244 (void) setnewqueue(e);
5243 5245 if (type == DATAFL_LETTER)
5244 5246 {
5245 5247 qd = e->e_dfqdir;
5246 5248 qg = e->e_dfqgrp;
5247 5249 }
5248 5250 else
5249 5251 {
5250 5252 qd = e->e_qdir;
5251 5253 qg = e->e_qgrp;
5252 5254 }
5253 5255 }
5254 5256
5255 5257 /* xf files always have a valid qd and qg picked above */
5256 5258 if ((qd == NOQDIR || qg == NOQGRP) && type != XSCRPT_LETTER)
5257 5259 (void) sm_strlcpyn(buf, sizeof(buf), 2, pref, e->e_id);
5258 5260 else
5259 5261 {
5260 5262 switch (type)
5261 5263 {
5262 5264 case DATAFL_LETTER:
5263 5265 if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
5264 5266 sub = "/df/";
5265 5267 break;
5266 5268
5267 5269 case QUARQF_LETTER:
5268 5270 case TEMPQF_LETTER:
5269 5271 case NEWQFL_LETTER:
5270 5272 case LOSEQF_LETTER:
5271 5273 case NORMQF_LETTER:
5272 5274 if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
5273 5275 sub = "/qf/";
5274 5276 break;
5275 5277
5276 5278 case XSCRPT_LETTER:
5277 5279 if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
5278 5280 sub = "/xf/";
5279 5281 break;
5280 5282
5281 5283 default:
5282 5284 sm_abort("queuename: bad queue file type %d", type);
5283 5285 }
5284 5286
5285 5287 (void) sm_strlcpyn(buf, sizeof(buf), 4,
5286 5288 Queue[qg]->qg_qpaths[qd].qp_name,
5287 5289 sub, pref, e->e_id);
5288 5290 }
5289 5291
5290 5292 if (tTd(7, 2))
5291 5293 sm_dprintf("queuename: %s\n", buf);
5292 5294 return buf;
5293 5295 }
5294 5296
5295 5297 /*
5296 5298 ** INIT_QID_ALG -- Initialize the (static) parameters that are used to
5297 5299 ** generate a queue ID.
5298 5300 **
5299 5301 ** This function is called by the daemon to reset
5300 5302 ** LastQueueTime and LastQueuePid which are used by assign_queueid().
5301 5303 ** Otherwise the algorithm may cause problems because
5302 5304 ** LastQueueTime and LastQueuePid are set indirectly by main()
5303 5305 ** before the daemon process is started, hence LastQueuePid is not
5304 5306 ** the pid of the daemon and therefore a child of the daemon can
5305 5307 ** actually have the same pid as LastQueuePid which means the section
5306 5308 ** in assign_queueid():
5307 5309 ** * see if we need to get a new base time/pid *
5308 5310 ** is NOT triggered which will cause the same queue id to be generated.
5309 5311 **
5310 5312 ** Parameters:
5311 5313 ** none
5312 5314 **
5313 5315 ** Returns:
5314 5316 ** none.
5315 5317 */
5316 5318
5317 5319 void
5318 5320 init_qid_alg()
5319 5321 {
5320 5322 LastQueueTime = 0;
5321 5323 LastQueuePid = -1;
5322 5324 }
5323 5325
5324 5326 /*
5325 5327 ** ASSIGN_QUEUEID -- assign a queue ID for this envelope.
5326 5328 **
5327 5329 ** Assigns an id code if one does not already exist.
5328 5330 ** This code assumes that nothing will remain in the queue for
5329 5331 ** longer than 60 years. It is critical that files with the given
5330 5332 ** name do not already exist in the queue.
5331 5333 ** [No longer initializes e_qdir to NOQDIR.]
5332 5334 **
5333 5335 ** Parameters:
5334 5336 ** e -- envelope to set it in.
5335 5337 **
5336 5338 ** Returns:
5337 5339 ** none.
5338 5340 */
5339 5341
5340 5342 static const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
5341 5343 # define QIC_LEN 60
5342 5344 # define QIC_LEN_R 62
5343 5345
5344 5346 /*
5345 5347 ** Note: the length is "officially" 60 because minutes and seconds are
5346 5348 ** usually only 0-59. However (Linux):
5347 5349 ** tm_sec The number of seconds after the minute, normally in
5348 5350 ** the range 0 to 59, but can be up to 61 to allow for
5349 5351 ** leap seconds.
5350 5352 ** Hence the real length of the string is 62 to take this into account.
5351 5353 ** Alternatively % QIC_LEN can (should) be used for access everywhere.
5352 5354 */
5353 5355
5354 5356 # define queuenextid() CurrentPid
5355 5357 #define QIC_LEN_SQR (QIC_LEN * QIC_LEN)
5356 5358
5357 5359 void
5358 5360 assign_queueid(e)
5359 5361 register ENVELOPE *e;
5360 5362 {
5361 5363 pid_t pid = queuenextid();
5362 5364 static unsigned int cX = 0;
5363 5365 static unsigned int random_offset;
5364 5366 struct tm *tm;
5365 5367 char idbuf[MAXQFNAME - 2];
5366 5368 unsigned int seq;
5367 5369
5368 5370 if (e->e_id != NULL)
5369 5371 return;
5370 5372
5371 5373 /* see if we need to get a new base time/pid */
5372 5374 if (cX >= QIC_LEN_SQR || LastQueueTime == 0 || LastQueuePid != pid)
5373 5375 {
5374 5376 time_t then = LastQueueTime;
5375 5377
5376 5378 /* if the first time through, pick a random offset */
5377 5379 if (LastQueueTime == 0)
5378 5380 random_offset = ((unsigned int)get_random())
5379 5381 % QIC_LEN_SQR;
5380 5382
5381 5383 while ((LastQueueTime = curtime()) == then &&
5382 5384 LastQueuePid == pid)
5383 5385 {
5384 5386 (void) sleep(1);
5385 5387 }
5386 5388 LastQueuePid = queuenextid();
5387 5389 cX = 0;
5388 5390 }
5389 5391
5390 5392 /*
5391 5393 ** Generate a new sequence number between 0 and QIC_LEN_SQR-1.
5392 5394 ** This lets us generate up to QIC_LEN_SQR unique queue ids
5393 5395 ** per second, per process. With envelope splitting,
5394 5396 ** a single message can consume many queue ids.
5395 5397 */
5396 5398
5397 5399 seq = (cX + random_offset) % QIC_LEN_SQR;
5398 5400 ++cX;
5399 5401 if (tTd(7, 50))
5400 5402 sm_dprintf("assign_queueid: random_offset=%u (%u)\n",
5401 5403 random_offset, seq);
5402 5404
5403 5405 tm = gmtime(&LastQueueTime);
5404 5406 idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN];
5405 5407 idbuf[1] = QueueIdChars[tm->tm_mon];
5406 5408 idbuf[2] = QueueIdChars[tm->tm_mday];
5407 5409 idbuf[3] = QueueIdChars[tm->tm_hour];
5408 5410 idbuf[4] = QueueIdChars[tm->tm_min % QIC_LEN_R];
5409 5411 idbuf[5] = QueueIdChars[tm->tm_sec % QIC_LEN_R];
5410 5412 idbuf[6] = QueueIdChars[seq / QIC_LEN];
5411 5413 idbuf[7] = QueueIdChars[seq % QIC_LEN];
5412 5414 (void) sm_snprintf(&idbuf[8], sizeof(idbuf) - 8, "%06d",
5413 5415 (int) LastQueuePid);
5414 5416 e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf);
5415 5417 macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
5416 5418 #if 0
5417 5419 /* XXX: inherited from MainEnvelope */
5418 5420 e->e_qgrp = NOQGRP; /* too early to do anything else */
5419 5421 e->e_qdir = NOQDIR;
5420 5422 e->e_xfqgrp = NOQGRP;
5421 5423 #endif /* 0 */
5422 5424
5423 5425 /* New ID means it's not on disk yet */
5424 5426 e->e_qfletter = '\0';
5425 5427
5426 5428 if (tTd(7, 1))
5427 5429 sm_dprintf("assign_queueid: assigned id %s, e=%p\n",
5428 5430 e->e_id, e);
5429 5431 if (LogLevel > 93)
5430 5432 sm_syslog(LOG_DEBUG, e->e_id, "assigned id");
5431 5433 }
5432 5434 /*
5433 5435 ** SYNC_QUEUE_TIME -- Assure exclusive PID in any given second
5434 5436 **
5435 5437 ** Make sure one PID can't be used by two processes in any one second.
5436 5438 **
5437 5439 ** If the system rotates PIDs fast enough, may get the
5438 5440 ** same pid in the same second for two distinct processes.
5439 5441 ** This will interfere with the queue file naming system.
5440 5442 **
5441 5443 ** Parameters:
5442 5444 ** none
5443 5445 **
5444 5446 ** Returns:
5445 5447 ** none
5446 5448 */
5447 5449
5448 5450 void
5449 5451 sync_queue_time()
5450 5452 {
5451 5453 #if FAST_PID_RECYCLE
5452 5454 if (OpMode != MD_TEST &&
5453 5455 OpMode != MD_CHECKCONFIG &&
5454 5456 OpMode != MD_VERIFY &&
5455 5457 LastQueueTime > 0 &&
5456 5458 LastQueuePid == CurrentPid &&
5457 5459 curtime() == LastQueueTime)
5458 5460 (void) sleep(1);
5459 5461 #endif /* FAST_PID_RECYCLE */
5460 5462 }
5461 5463 /*
5462 5464 ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope
5463 5465 **
5464 5466 ** Parameters:
5465 5467 ** e -- the envelope to unlock.
5466 5468 **
5467 5469 ** Returns:
5468 5470 ** none
5469 5471 **
5470 5472 ** Side Effects:
5471 5473 ** unlocks the queue for `e'.
5472 5474 */
5473 5475
5474 5476 void
5475 5477 unlockqueue(e)
5476 5478 ENVELOPE *e;
5477 5479 {
5478 5480 if (tTd(51, 4))
5479 5481 sm_dprintf("unlockqueue(%s)\n",
5480 5482 e->e_id == NULL ? "NOQUEUE" : e->e_id);
5481 5483
5482 5484
5483 5485 /* if there is a lock file in the envelope, close it */
5484 5486 if (e->e_lockfp != NULL)
5485 5487 (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
5486 5488 e->e_lockfp = NULL;
5487 5489
5488 5490 /* don't create a queue id if we don't already have one */
5489 5491 if (e->e_id == NULL)
5490 5492 return;
5491 5493
5492 5494 /* remove the transcript */
5493 5495 if (LogLevel > 87)
5494 5496 sm_syslog(LOG_DEBUG, e->e_id, "unlock");
5495 5497 if (!tTd(51, 104))
5496 5498 (void) xunlink(queuename(e, XSCRPT_LETTER));
5497 5499 }
5498 5500 /*
5499 5501 ** SETCTLUSER -- create a controlling address
5500 5502 **
5501 5503 ** Create a fake "address" given only a local login name; this is
5502 5504 ** used as a "controlling user" for future recipient addresses.
5503 5505 **
5504 5506 ** Parameters:
5505 5507 ** user -- the user name of the controlling user.
5506 5508 ** qfver -- the version stamp of this queue file.
5507 5509 ** e -- envelope
5508 5510 **
5509 5511 ** Returns:
5510 5512 ** An address descriptor for the controlling user,
5511 5513 ** using storage allocated from e->e_rpool.
5512 5514 **
5513 5515 */
5514 5516
5515 5517 static ADDRESS *
5516 5518 setctluser(user, qfver, e)
5517 5519 char *user;
5518 5520 int qfver;
5519 5521 ENVELOPE *e;
5520 5522 {
5521 5523 register ADDRESS *a;
5522 5524 struct passwd *pw;
5523 5525 char *p;
5524 5526
5525 5527 /*
5526 5528 ** See if this clears our concept of controlling user.
5527 5529 */
5528 5530
5529 5531 if (user == NULL || *user == '\0')
5530 5532 return NULL;
5531 5533
5532 5534 /*
5533 5535 ** Set up addr fields for controlling user.
5534 5536 */
5535 5537
5536 5538 a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof(*a));
5537 5539 memset((char *) a, '\0', sizeof(*a));
5538 5540
5539 5541 if (*user == ':')
5540 5542 {
5541 5543 p = &user[1];
5542 5544 a->q_user = sm_rpool_strdup_x(e->e_rpool, p);
5543 5545 }
5544 5546 else
5545 5547 {
5546 5548 p = strtok(user, ":");
5547 5549 a->q_user = sm_rpool_strdup_x(e->e_rpool, user);
5548 5550 if (qfver >= 2)
5549 5551 {
5550 5552 if ((p = strtok(NULL, ":")) != NULL)
5551 5553 a->q_uid = atoi(p);
5552 5554 if ((p = strtok(NULL, ":")) != NULL)
5553 5555 a->q_gid = atoi(p);
5554 5556 if ((p = strtok(NULL, ":")) != NULL)
5555 5557 {
5556 5558 char *o;
5557 5559
5558 5560 a->q_flags |= QGOODUID;
5559 5561
5560 5562 /* if there is another ':': restore it */
5561 5563 if ((o = strtok(NULL, ":")) != NULL && o > p)
5562 5564 o[-1] = ':';
5563 5565 }
5564 5566 }
5565 5567 else if ((pw = sm_getpwnam(user)) != NULL)
5566 5568 {
5567 5569 if (*pw->pw_dir == '\0')
5568 5570 a->q_home = NULL;
5569 5571 else if (strcmp(pw->pw_dir, "/") == 0)
5570 5572 a->q_home = "";
5571 5573 else
5572 5574 a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir);
5573 5575 a->q_uid = pw->pw_uid;
5574 5576 a->q_gid = pw->pw_gid;
5575 5577 a->q_flags |= QGOODUID;
5576 5578 }
5577 5579 }
5578 5580
5579 5581 a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */
5580 5582 a->q_mailer = LocalMailer;
5581 5583 if (p == NULL)
5582 5584 a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
5583 5585 else
5584 5586 a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p);
5585 5587 return a;
5586 5588 }
5587 5589 /*
5588 5590 ** LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know
5589 5591 **
5590 5592 ** Parameters:
5591 5593 ** e -- the envelope (e->e_id will be used).
5592 5594 ** why -- reported to whomever can hear.
5593 5595 **
5594 5596 ** Returns:
5595 5597 ** none.
5596 5598 */
5597 5599
5598 5600 void
5599 5601 loseqfile(e, why)
5600 5602 register ENVELOPE *e;
5601 5603 char *why;
5602 5604 {
5603 5605 bool loseit = true;
5604 5606 char *p;
5605 5607 char buf[MAXPATHLEN];
5606 5608
5607 5609 if (e == NULL || e->e_id == NULL)
5608 5610 return;
5609 5611 p = queuename(e, ANYQFL_LETTER);
5610 5612 if (sm_strlcpy(buf, p, sizeof(buf)) >= sizeof(buf))
5611 5613 return;
5612 5614 if (!bitset(EF_INQUEUE, e->e_flags))
5613 5615 queueup(e, false, true);
5614 5616 else if (QueueMode == QM_LOST)
5615 5617 loseit = false;
5616 5618
5617 5619 /* if already lost, no need to re-lose */
5618 5620 if (loseit)
5619 5621 {
5620 5622 p = queuename(e, LOSEQF_LETTER);
5621 5623 if (rename(buf, p) < 0)
5622 5624 syserr("cannot rename(%s, %s), uid=%d",
5623 5625 buf, p, (int) geteuid());
5624 5626 else if (LogLevel > 0)
5625 5627 sm_syslog(LOG_ALERT, e->e_id,
5626 5628 "Losing %s: %s", buf, why);
5627 5629 }
5628 5630 if (e->e_dfp != NULL)
5629 5631 {
5630 5632 (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
5631 5633 e->e_dfp = NULL;
5632 5634 }
5633 5635 e->e_flags &= ~EF_HAS_DF;
5634 5636 }
5635 5637 /*
5636 5638 ** NAME2QID -- translate a queue group name to a queue group id
5637 5639 **
5638 5640 ** Parameters:
5639 5641 ** queuename -- name of queue group.
5640 5642 **
5641 5643 ** Returns:
5642 5644 ** queue group id if found.
5643 5645 ** NOQGRP otherwise.
5644 5646 */
5645 5647
5646 5648 int
5647 5649 name2qid(queuename)
5648 5650 char *queuename;
5649 5651 {
5650 5652 register STAB *s;
5651 5653
5652 5654 s = stab(queuename, ST_QUEUE, ST_FIND);
5653 5655 if (s == NULL)
5654 5656 return NOQGRP;
5655 5657 return s->s_quegrp->qg_index;
5656 5658 }
5657 5659 /*
5658 5660 ** QID_PRINTNAME -- create externally printable version of queue id
5659 5661 **
5660 5662 ** Parameters:
5661 5663 ** e -- the envelope.
5662 5664 **
5663 5665 ** Returns:
5664 5666 ** a printable version
5665 5667 */
5666 5668
5667 5669 char *
5668 5670 qid_printname(e)
5669 5671 ENVELOPE *e;
5670 5672 {
5671 5673 char *id;
5672 5674 static char idbuf[MAXQFNAME + 34];
5673 5675
5674 5676 if (e == NULL)
5675 5677 return "";
5676 5678
5677 5679 if (e->e_id == NULL)
5678 5680 id = "";
5679 5681 else
5680 5682 id = e->e_id;
5681 5683
5682 5684 if (e->e_qdir == NOQDIR)
5683 5685 return id;
5684 5686
5685 5687 (void) sm_snprintf(idbuf, sizeof(idbuf), "%.32s/%s",
5686 5688 Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name,
5687 5689 id);
5688 5690 return idbuf;
5689 5691 }
5690 5692 /*
5691 5693 ** QID_PRINTQUEUE -- create full version of queue directory for data files
5692 5694 **
5693 5695 ** Parameters:
5694 5696 ** qgrp -- index in queue group.
5695 5697 ** qdir -- the short version of the queue directory
5696 5698 **
5697 5699 ** Returns:
5698 5700 ** the full pathname to the queue (might point to a static var)
5699 5701 */
5700 5702
5701 5703 char *
5702 5704 qid_printqueue(qgrp, qdir)
5703 5705 int qgrp;
5704 5706 int qdir;
5705 5707 {
5706 5708 char *subdir;
5707 5709 static char dir[MAXPATHLEN];
5708 5710
5709 5711 if (qdir == NOQDIR)
5710 5712 return Queue[qgrp]->qg_qdir;
5711 5713
5712 5714 if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0)
5713 5715 subdir = NULL;
5714 5716 else
5715 5717 subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name;
5716 5718
5717 5719 (void) sm_strlcpyn(dir, sizeof(dir), 4,
5718 5720 Queue[qgrp]->qg_qdir,
5719 5721 subdir == NULL ? "" : "/",
5720 5722 subdir == NULL ? "" : subdir,
5721 5723 (bitset(QP_SUBDF,
5722 5724 Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
5723 5725 ? "/df" : ""));
5724 5726 return dir;
5725 5727 }
5726 5728
5727 5729 /*
5728 5730 ** PICKQDIR -- Pick a queue directory from a queue group
5729 5731 **
5730 5732 ** Parameters:
5731 5733 ** qg -- queue group
5732 5734 ** fsize -- file size in bytes
5733 5735 ** e -- envelope, or NULL
5734 5736 **
5735 5737 ** Result:
5736 5738 ** NOQDIR if no queue directory in qg has enough free space to
5737 5739 ** hold a file of size 'fsize', otherwise the index of
5738 5740 ** a randomly selected queue directory which resides on a
5739 5741 ** file system with enough disk space.
5740 5742 ** XXX This could be extended to select a queuedir with
5741 5743 ** a few (the fewest?) number of entries. That data
5742 5744 ** is available if shared memory is used.
5743 5745 **
5744 5746 ** Side Effects:
5745 5747 ** If the request fails and e != NULL then sm_syslog is called.
5746 5748 */
5747 5749
5748 5750 int
5749 5751 pickqdir(qg, fsize, e)
5750 5752 QUEUEGRP *qg;
5751 5753 long fsize;
5752 5754 ENVELOPE *e;
5753 5755 {
5754 5756 int qdir;
5755 5757 int i;
5756 5758 long avail = 0;
5757 5759
5758 5760 /* Pick a random directory, as a starting point. */
5759 5761 if (qg->qg_numqueues <= 1)
5760 5762 qdir = 0;
5761 5763 else
5762 5764 qdir = get_rand_mod(qg->qg_numqueues);
5763 5765
5764 5766 #if _FFR_TESTS
5765 5767 if (tTd(4, 101))
5766 5768 return NOQDIR;
5767 5769 #endif /* _FFR_TESTS */
5768 5770 if (MinBlocksFree <= 0 && fsize <= 0)
5769 5771 return qdir;
5770 5772
5771 5773 /*
5772 5774 ** Now iterate over the queue directories,
5773 5775 ** looking for a directory with enough space for this message.
5774 5776 */
5775 5777
5776 5778 i = qdir;
5777 5779 do
5778 5780 {
5779 5781 QPATHS *qp = &qg->qg_qpaths[i];
5780 5782 long needed = 0;
5781 5783 long fsavail = 0;
5782 5784
5783 5785 if (fsize > 0)
5784 5786 needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx)
5785 5787 + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx)
5786 5788 > 0) ? 1 : 0);
5787 5789 if (MinBlocksFree > 0)
5788 5790 needed += MinBlocksFree;
5789 5791 fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx);
5790 5792 #if SM_CONF_SHM
5791 5793 if (fsavail <= 0)
5792 5794 {
5793 5795 long blksize;
5794 5796
5795 5797 /*
5796 5798 ** might be not correctly updated,
5797 5799 ** let's try to get the info directly.
5798 5800 */
5799 5801
5800 5802 fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx),
5801 5803 &blksize);
5802 5804 if (fsavail < 0)
5803 5805 fsavail = 0;
5804 5806 }
5805 5807 #endif /* SM_CONF_SHM */
5806 5808 if (needed <= fsavail)
5807 5809 return i;
5808 5810 if (avail < fsavail)
5809 5811 avail = fsavail;
5810 5812
5811 5813 if (qg->qg_numqueues > 0)
5812 5814 i = (i + 1) % qg->qg_numqueues;
5813 5815 } while (i != qdir);
5814 5816
5815 5817 if (e != NULL && LogLevel > 0)
5816 5818 sm_syslog(LOG_ALERT, e->e_id,
5817 5819 "low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld",
5818 5820 CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
5819 5821 fsize, MinBlocksFree,
5820 5822 qg->qg_qdir, avail);
5821 5823 return NOQDIR;
5822 5824 }
5823 5825 /*
5824 5826 ** SETNEWQUEUE -- Sets a new queue group and directory
5825 5827 **
5826 5828 ** Assign a queue group and directory to an envelope and store the
5827 5829 ** directory in e->e_qdir.
5828 5830 **
5829 5831 ** Parameters:
5830 5832 ** e -- envelope to assign a queue for.
5831 5833 **
5832 5834 ** Returns:
5833 5835 ** true if successful
5834 5836 ** false otherwise
5835 5837 **
5836 5838 ** Side Effects:
5837 5839 ** On success, e->e_qgrp and e->e_qdir are non-negative.
5838 5840 ** On failure (not enough disk space),
5839 5841 ** e->qgrp = NOQGRP, e->e_qdir = NOQDIR
5840 5842 ** and usrerr() is invoked (which could raise an exception).
5841 5843 */
5842 5844
5843 5845 bool
5844 5846 setnewqueue(e)
5845 5847 ENVELOPE *e;
5846 5848 {
5847 5849 if (tTd(41, 20))
5848 5850 sm_dprintf("setnewqueue: called\n");
5849 5851
5850 5852 /* not set somewhere else */
5851 5853 if (e->e_qgrp == NOQGRP)
5852 5854 {
5853 5855 ADDRESS *q;
5854 5856
5855 5857 /*
5856 5858 ** Use the queue group of the "first" recipient, as set by
5857 5859 ** the "queuegroup" rule set. If that is not defined, then
5858 5860 ** use the queue group of the mailer of the first recipient.
5859 5861 ** If that is not defined either, then use the default
5860 5862 ** queue group.
5861 5863 ** Notice: "first" depends on the sorting of sendqueue
5862 5864 ** in recipient().
5863 5865 ** To avoid problems with "bad" recipients look
5864 5866 ** for a valid address first.
5865 5867 */
5866 5868
5867 5869 q = e->e_sendqueue;
5868 5870 while (q != NULL &&
5869 5871 (QS_IS_BADADDR(q->q_state) || QS_IS_DEAD(q->q_state)))
5870 5872 {
5871 5873 q = q->q_next;
5872 5874 }
5873 5875 if (q == NULL)
5874 5876 e->e_qgrp = 0;
5875 5877 else if (q->q_qgrp >= 0)
5876 5878 e->e_qgrp = q->q_qgrp;
5877 5879 else if (q->q_mailer != NULL &&
5878 5880 ISVALIDQGRP(q->q_mailer->m_qgrp))
5879 5881 e->e_qgrp = q->q_mailer->m_qgrp;
5880 5882 else
5881 5883 e->e_qgrp = 0;
5882 5884 e->e_dfqgrp = e->e_qgrp;
5883 5885 }
5884 5886
5885 5887 if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir))
5886 5888 {
5887 5889 if (tTd(41, 20))
5888 5890 sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n",
5889 5891 qid_printqueue(e->e_qgrp, e->e_qdir));
5890 5892 return true;
5891 5893 }
5892 5894
5893 5895 filesys_update();
5894 5896 e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e);
5895 5897 if (e->e_qdir == NOQDIR)
5896 5898 {
5897 5899 e->e_qgrp = NOQGRP;
5898 5900 if (!bitset(EF_FATALERRS, e->e_flags))
5899 5901 usrerr("452 4.4.5 Insufficient disk space; try again later");
5900 5902 e->e_flags |= EF_FATALERRS;
5901 5903 return false;
5902 5904 }
5903 5905
5904 5906 if (tTd(41, 3))
5905 5907 sm_dprintf("setnewqueue: Assigned queue directory %s\n",
5906 5908 qid_printqueue(e->e_qgrp, e->e_qdir));
5907 5909
5908 5910 if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
5909 5911 {
5910 5912 e->e_xfqgrp = e->e_qgrp;
5911 5913 e->e_xfqdir = e->e_qdir;
5912 5914 }
5913 5915 e->e_dfqdir = e->e_qdir;
5914 5916 return true;
5915 5917 }
5916 5918 /*
5917 5919 ** CHKQDIR -- check a queue directory
5918 5920 **
5919 5921 ** Parameters:
5920 5922 ** name -- name of queue directory
5921 5923 ** sff -- flags for safefile()
5922 5924 **
5923 5925 ** Returns:
5924 5926 ** is it a queue directory?
5925 5927 */
5926 5928
5927 5929 static bool chkqdir __P((char *, long));
5928 5930
5929 5931 static bool
5930 5932 chkqdir(name, sff)
5931 5933 char *name;
5932 5934 long sff;
5933 5935 {
5934 5936 struct stat statb;
5935 5937 int i;
5936 5938
5937 5939 /* skip over . and .. directories */
5938 5940 if (name[0] == '.' &&
5939 5941 (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
5940 5942 return false;
5941 5943 #if HASLSTAT
5942 5944 if (lstat(name, &statb) < 0)
5943 5945 #else /* HASLSTAT */
5944 5946 if (stat(name, &statb) < 0)
5945 5947 #endif /* HASLSTAT */
5946 5948 {
5947 5949 if (tTd(41, 2))
5948 5950 sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
5949 5951 name, sm_errstring(errno));
5950 5952 return false;
5951 5953 }
5952 5954 #if HASLSTAT
5953 5955 if (S_ISLNK(statb.st_mode))
5954 5956 {
5955 5957 /*
5956 5958 ** For a symlink we need to make sure the
5957 5959 ** target is a directory
5958 5960 */
5959 5961
5960 5962 if (stat(name, &statb) < 0)
5961 5963 {
5962 5964 if (tTd(41, 2))
5963 5965 sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
5964 5966 name, sm_errstring(errno));
5965 5967 return false;
5966 5968 }
5967 5969 }
5968 5970 #endif /* HASLSTAT */
5969 5971
5970 5972 if (!S_ISDIR(statb.st_mode))
5971 5973 {
5972 5974 if (tTd(41, 2))
5973 5975 sm_dprintf("chkqdir: \"%s\": Not a directory\n",
5974 5976 name);
5975 5977 return false;
5976 5978 }
5977 5979
5978 5980 /* Print a warning if unsafe (but still use it) */
5979 5981 /* XXX do this only if we want the warning? */
5980 5982 i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0);
5981 5983 if (i != 0)
5982 5984 {
5983 5985 if (tTd(41, 2))
5984 5986 sm_dprintf("chkqdir: \"%s\": Not safe: %s\n",
5985 5987 name, sm_errstring(i));
5986 5988 #if _FFR_CHK_QUEUE
5987 5989 if (LogLevel > 8)
5988 5990 sm_syslog(LOG_WARNING, NOQID,
5989 5991 "queue directory \"%s\": Not safe: %s",
5990 5992 name, sm_errstring(i));
5991 5993 #endif /* _FFR_CHK_QUEUE */
5992 5994 }
5993 5995 return true;
5994 5996 }
5995 5997 /*
5996 5998 ** MULTIQUEUE_CACHE -- cache a list of paths to queues.
5997 5999 **
5998 6000 ** Each potential queue is checked as the cache is built.
5999 6001 ** Thereafter, each is blindly trusted.
6000 6002 ** Note that we can be called again after a timeout to rebuild
6001 6003 ** (although code for that is not ready yet).
6002 6004 **
6003 6005 ** Parameters:
6004 6006 ** basedir -- base of all queue directories.
6005 6007 ** blen -- strlen(basedir).
6006 6008 ** qg -- queue group.
6007 6009 ** qn -- number of queue directories already cached.
6008 6010 ** phash -- pointer to hash value over queue dirs.
6009 6011 #if SM_CONF_SHM
6010 6012 ** only used if shared memory is active.
6011 6013 #endif * SM_CONF_SHM *
6012 6014 **
6013 6015 ** Returns:
6014 6016 ** new number of queue directories.
6015 6017 */
6016 6018
6017 6019 #define INITIAL_SLOTS 20
6018 6020 #define ADD_SLOTS 10
6019 6021
6020 6022 static int
6021 6023 multiqueue_cache(basedir, blen, qg, qn, phash)
6022 6024 char *basedir;
6023 6025 int blen;
6024 6026 QUEUEGRP *qg;
6025 6027 int qn;
6026 6028 unsigned int *phash;
6027 6029 {
6028 6030 char *cp;
6029 6031 int i, len;
6030 6032 int slotsleft = 0;
6031 6033 long sff = SFF_ANYFILE;
6032 6034 char qpath[MAXPATHLEN];
6033 6035 char subdir[MAXPATHLEN];
6034 6036 char prefix[MAXPATHLEN]; /* dir relative to basedir */
6035 6037
6036 6038 if (tTd(41, 20))
6037 6039 sm_dprintf("multiqueue_cache: called\n");
6038 6040
6039 6041 /* Initialize to current directory */
6040 6042 prefix[0] = '.';
6041 6043 prefix[1] = '\0';
6042 6044 if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL)
6043 6045 {
6044 6046 for (i = 0; i < qg->qg_numqueues; i++)
6045 6047 {
6046 6048 if (qg->qg_qpaths[i].qp_name != NULL)
6047 6049 (void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */
6048 6050 }
6049 6051 (void) sm_free((char *) qg->qg_qpaths); /* XXX */
6050 6052 qg->qg_qpaths = NULL;
6051 6053 qg->qg_numqueues = 0;
6052 6054 }
6053 6055
6054 6056 /* If running as root, allow safedirpath() checks to use privs */
6055 6057 if (RunAsUid == 0)
6056 6058 sff |= SFF_ROOTOK;
6057 6059 #if _FFR_CHK_QUEUE
6058 6060 sff |= SFF_SAFEDIRPATH|SFF_NOWWFILES;
6059 6061 if (!UseMSP)
6060 6062 sff |= SFF_NOGWFILES;
6061 6063 #endif /* _FFR_CHK_QUEUE */
6062 6064
6063 6065 if (!SM_IS_DIR_START(qg->qg_qdir))
6064 6066 {
6065 6067 /*
6066 6068 ** XXX we could add basedir, but then we have to realloc()
6067 6069 ** the string... Maybe another time.
6068 6070 */
6069 6071
6070 6072 syserr("QueuePath %s not absolute", qg->qg_qdir);
6071 6073 ExitStat = EX_CONFIG;
6072 6074 return qn;
6073 6075 }
6074 6076
6075 6077 /* qpath: directory of current workgroup */
6076 6078 len = sm_strlcpy(qpath, qg->qg_qdir, sizeof(qpath));
6077 6079 if (len >= sizeof(qpath))
6078 6080 {
6079 6081 syserr("QueuePath %.256s too long (%d max)",
6080 6082 qg->qg_qdir, (int) sizeof(qpath));
6081 6083 ExitStat = EX_CONFIG;
6082 6084 return qn;
6083 6085 }
6084 6086
6085 6087 /* begin of qpath must be same as basedir */
6086 6088 if (strncmp(basedir, qpath, blen) != 0 &&
6087 6089 (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1))
6088 6090 {
6089 6091 syserr("QueuePath %s not subpath of QueueDirectory %s",
6090 6092 qpath, basedir);
6091 6093 ExitStat = EX_CONFIG;
6092 6094 return qn;
6093 6095 }
6094 6096
6095 6097 /* Do we have a nested subdirectory? */
6096 6098 if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL)
6097 6099 {
6098 6100
6099 6101 /* Copy subdirectory into prefix for later use */
6100 6102 if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof(prefix)) >=
6101 6103 sizeof(prefix))
6102 6104 {
6103 6105 syserr("QueuePath %.256s too long (%d max)",
6104 6106 qg->qg_qdir, (int) sizeof(qpath));
6105 6107 ExitStat = EX_CONFIG;
6106 6108 return qn;
6107 6109 }
6108 6110 cp = SM_LAST_DIR_DELIM(prefix);
6109 6111 SM_ASSERT(cp != NULL);
6110 6112 *cp = '\0'; /* cut off trailing / */
6111 6113 }
6112 6114
6113 6115 /* This is guaranteed by the basedir check above */
6114 6116 SM_ASSERT(len >= blen - 1);
6115 6117 cp = &qpath[len - 1];
6116 6118 if (*cp == '*')
6117 6119 {
6118 6120 register DIR *dp;
6119 6121 register struct dirent *d;
6120 6122 int off;
6121 6123 char *delim;
6122 6124 char relpath[MAXPATHLEN];
6123 6125
6124 6126 *cp = '\0'; /* Overwrite wildcard */
6125 6127 if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL)
6126 6128 {
6127 6129 syserr("QueueDirectory: can not wildcard relative path");
6128 6130 if (tTd(41, 2))
6129 6131 sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n",
6130 6132 qpath);
6131 6133 ExitStat = EX_CONFIG;
6132 6134 return qn;
6133 6135 }
6134 6136 if (cp == qpath)
6135 6137 {
6136 6138 /*
6137 6139 ** Special case of top level wildcard, like /foo*
6138 6140 ** Change to //foo*
6139 6141 */
6140 6142
6141 6143 (void) sm_strlcpy(qpath + 1, qpath, sizeof(qpath) - 1);
6142 6144 ++cp;
6143 6145 }
6144 6146 delim = cp;
6145 6147 *(cp++) = '\0'; /* Replace / with \0 */
6146 6148 len = strlen(cp); /* Last component of queue directory */
6147 6149
6148 6150 /*
6149 6151 ** Path relative to basedir, with trailing /
6150 6152 ** It will be modified below to specify the subdirectories
6151 6153 ** so they can be opened without chdir().
6152 6154 */
6153 6155
6154 6156 off = sm_strlcpyn(relpath, sizeof(relpath), 2, prefix, "/");
6155 6157 SM_ASSERT(off < sizeof(relpath));
6156 6158
6157 6159 if (tTd(41, 2))
6158 6160 sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n",
6159 6161 relpath, cp);
6160 6162
6161 6163 /* It is always basedir: we don't need to store it per group */
6162 6164 /* XXX: optimize this! -> one more global? */
6163 6165 qg->qg_qdir = newstr(basedir);
6164 6166 qg->qg_qdir[blen - 1] = '\0'; /* cut off trailing / */
6165 6167
6166 6168 /*
6167 6169 ** XXX Should probably wrap this whole loop in a timeout
6168 6170 ** in case some wag decides to NFS mount the queues.
6169 6171 */
6170 6172
6171 6173 /* Test path to get warning messages. */
6172 6174 if (qn == 0)
6173 6175 {
6174 6176 /* XXX qg_runasuid and qg_runasgid for specials? */
6175 6177 i = safedirpath(basedir, RunAsUid, RunAsGid, NULL,
6176 6178 sff, 0, 0);
6177 6179 if (i != 0 && tTd(41, 2))
6178 6180 sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n",
6179 6181 basedir, sm_errstring(i));
6180 6182 }
6181 6183
6182 6184 if ((dp = opendir(prefix)) == NULL)
6183 6185 {
6184 6186 syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix);
6185 6187 if (tTd(41, 2))
6186 6188 sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n",
6187 6189 qg->qg_qdir, prefix,
6188 6190 sm_errstring(errno));
6189 6191 ExitStat = EX_CONFIG;
6190 6192 return qn;
6191 6193 }
6192 6194 while ((d = readdir(dp)) != NULL)
6193 6195 {
6194 6196 /* Skip . and .. directories */
6195 6197 if (strcmp(d->d_name, ".") == 0 ||
6196 6198 strcmp(d->d_name, "..") == 0)
6197 6199 continue;
6198 6200
6199 6201 i = strlen(d->d_name);
6200 6202 if (i < len || strncmp(d->d_name, cp, len) != 0)
6201 6203 {
6202 6204 if (tTd(41, 5))
6203 6205 sm_dprintf("multiqueue_cache: \"%s\", skipped\n",
6204 6206 d->d_name);
6205 6207 continue;
6206 6208 }
6207 6209
6208 6210 /* Create relative pathname: prefix + local directory */
6209 6211 i = sizeof(relpath) - off;
6210 6212 if (sm_strlcpy(relpath + off, d->d_name, i) >= i)
6211 6213 continue; /* way too long */
6212 6214
6213 6215 if (!chkqdir(relpath, sff))
6214 6216 continue;
6215 6217
6216 6218 if (qg->qg_qpaths == NULL)
6217 6219 {
6218 6220 slotsleft = INITIAL_SLOTS;
6219 6221 qg->qg_qpaths = (QPATHS *)xalloc((sizeof(*qg->qg_qpaths)) *
6220 6222 slotsleft);
6221 6223 qg->qg_numqueues = 0;
6222 6224 }
6223 6225 else if (slotsleft < 1)
6224 6226 {
6225 6227 qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths,
6226 6228 (sizeof(*qg->qg_qpaths)) *
6227 6229 (qg->qg_numqueues +
6228 6230 ADD_SLOTS));
6229 6231 if (qg->qg_qpaths == NULL)
6230 6232 {
6231 6233 (void) closedir(dp);
6232 6234 return qn;
6233 6235 }
6234 6236 slotsleft += ADD_SLOTS;
6235 6237 }
6236 6238
6237 6239 /* check subdirs */
6238 6240 qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB;
6239 6241
6240 6242 #define CHKRSUBDIR(name, flag) \
6241 6243 (void) sm_strlcpyn(subdir, sizeof(subdir), 3, relpath, "/", name); \
6242 6244 if (chkqdir(subdir, sff)) \
6243 6245 qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag; \
6244 6246 else
6245 6247
6246 6248
6247 6249 CHKRSUBDIR("qf", QP_SUBQF);
6248 6250 CHKRSUBDIR("df", QP_SUBDF);
6249 6251 CHKRSUBDIR("xf", QP_SUBXF);
6250 6252
6251 6253 /* assert(strlen(d->d_name) < MAXPATHLEN - 14) */
6252 6254 /* maybe even - 17 (subdirs) */
6253 6255
6254 6256 if (prefix[0] != '.')
6255 6257 qg->qg_qpaths[qg->qg_numqueues].qp_name =
6256 6258 newstr(relpath);
6257 6259 else
6258 6260 qg->qg_qpaths[qg->qg_numqueues].qp_name =
6259 6261 newstr(d->d_name);
6260 6262
6261 6263 if (tTd(41, 2))
6262 6264 sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n",
6263 6265 qg->qg_numqueues, relpath,
6264 6266 qg->qg_qpaths[qg->qg_numqueues].qp_subdirs);
6265 6267 #if SM_CONF_SHM
6266 6268 qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn;
6267 6269 *phash = hash_q(relpath, *phash);
6268 6270 #endif /* SM_CONF_SHM */
6269 6271 qg->qg_numqueues++;
6270 6272 ++qn;
6271 6273 slotsleft--;
6272 6274 }
6273 6275 (void) closedir(dp);
6274 6276
6275 6277 /* undo damage */
6276 6278 *delim = '/';
6277 6279 }
6278 6280 if (qg->qg_numqueues == 0)
6279 6281 {
6280 6282 qg->qg_qpaths = (QPATHS *) xalloc(sizeof(*qg->qg_qpaths));
6281 6283
6282 6284 /* test path to get warning messages */
6283 6285 i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0);
6284 6286 if (i == ENOENT)
6285 6287 {
6286 6288 syserr("can not opendir(%s)", qpath);
6287 6289 if (tTd(41, 2))
6288 6290 sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n",
6289 6291 qpath, sm_errstring(i));
6290 6292 ExitStat = EX_CONFIG;
6291 6293 return qn;
6292 6294 }
6293 6295
6294 6296 qg->qg_qpaths[0].qp_subdirs = QP_NOSUB;
6295 6297 qg->qg_numqueues = 1;
6296 6298
6297 6299 /* check subdirs */
6298 6300 #define CHKSUBDIR(name, flag) \
6299 6301 (void) sm_strlcpyn(subdir, sizeof(subdir), 3, qg->qg_qdir, "/", name); \
6300 6302 if (chkqdir(subdir, sff)) \
6301 6303 qg->qg_qpaths[0].qp_subdirs |= flag; \
6302 6304 else
6303 6305
6304 6306 CHKSUBDIR("qf", QP_SUBQF);
6305 6307 CHKSUBDIR("df", QP_SUBDF);
6306 6308 CHKSUBDIR("xf", QP_SUBXF);
6307 6309
6308 6310 if (qg->qg_qdir[blen - 1] != '\0' &&
6309 6311 qg->qg_qdir[blen] != '\0')
6310 6312 {
6311 6313 /*
6312 6314 ** Copy the last component into qpaths and
6313 6315 ** cut off qdir
6314 6316 */
6315 6317
6316 6318 qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen);
6317 6319 qg->qg_qdir[blen - 1] = '\0';
6318 6320 }
6319 6321 else
6320 6322 qg->qg_qpaths[0].qp_name = newstr(".");
6321 6323
6322 6324 #if SM_CONF_SHM
6323 6325 qg->qg_qpaths[0].qp_idx = qn;
6324 6326 *phash = hash_q(qg->qg_qpaths[0].qp_name, *phash);
6325 6327 #endif /* SM_CONF_SHM */
6326 6328 ++qn;
6327 6329 }
6328 6330 return qn;
6329 6331 }
6330 6332
6331 6333 /*
6332 6334 ** FILESYS_FIND -- find entry in FileSys table, or add new one
6333 6335 **
6334 6336 ** Given the pathname of a directory, determine the file system
6335 6337 ** in which that directory resides, and return a pointer to the
6336 6338 ** entry in the FileSys table that describes the file system.
6337 6339 ** A new entry is added if necessary (and requested).
6338 6340 ** If the directory does not exist, -1 is returned.
6339 6341 **
6340 6342 ** Parameters:
6341 6343 ** name -- name of directory (must be persistent!)
6342 6344 ** path -- pathname of directory (name plus maybe "/df")
6343 6345 ** add -- add to structure if not found.
6344 6346 **
6345 6347 ** Returns:
6346 6348 ** >=0: found: index in file system table
6347 6349 ** <0: some error, i.e.,
6348 6350 ** FSF_TOO_MANY: too many filesystems (-> syserr())
6349 6351 ** FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
6350 6352 ** FSF_NOT_FOUND: not in list
6351 6353 */
6352 6354
6353 6355 static short filesys_find __P((const char *, const char *, bool));
6354 6356
6355 6357 #define FSF_NOT_FOUND (-1)
6356 6358 #define FSF_STAT_FAIL (-2)
6357 6359 #define FSF_TOO_MANY (-3)
6358 6360
6359 6361 static short
6360 6362 filesys_find(name, path, add)
6361 6363 const char *name;
6362 6364 const char *path;
6363 6365 bool add;
6364 6366 {
6365 6367 struct stat st;
6366 6368 short i;
6367 6369
6368 6370 if (stat(path, &st) < 0)
6369 6371 {
6370 6372 syserr("cannot stat queue directory %s", path);
6371 6373 return FSF_STAT_FAIL;
6372 6374 }
6373 6375 for (i = 0; i < NumFileSys; ++i)
6374 6376 {
6375 6377 if (FILE_SYS_DEV(i) == st.st_dev)
6376 6378 {
6377 6379 /*
6378 6380 ** Make sure the file system (FS) name is set:
6379 6381 ** even though the source code indicates that
6380 6382 ** FILE_SYS_DEV() is only set below, it could be
6381 6383 ** set via shared memory, hence we need to perform
6382 6384 ** this check/assignment here.
6383 6385 */
6384 6386
6385 6387 if (NULL == FILE_SYS_NAME(i))
6386 6388 FILE_SYS_NAME(i) = name;
6387 6389 return i;
6388 6390 }
6389 6391 }
6390 6392 if (i >= MAXFILESYS)
6391 6393 {
6392 6394 syserr("too many queue file systems (%d max)", MAXFILESYS);
6393 6395 return FSF_TOO_MANY;
6394 6396 }
6395 6397 if (!add)
6396 6398 return FSF_NOT_FOUND;
6397 6399
6398 6400 ++NumFileSys;
6399 6401 FILE_SYS_NAME(i) = name;
6400 6402 FILE_SYS_DEV(i) = st.st_dev;
6401 6403 FILE_SYS_AVAIL(i) = 0;
6402 6404 FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */
6403 6405 return i;
6404 6406 }
6405 6407
6406 6408 /*
6407 6409 ** FILESYS_SETUP -- set up mapping from queue directories to file systems
6408 6410 **
6409 6411 ** This data structure is used to efficiently check the amount of
6410 6412 ** free space available in a set of queue directories.
6411 6413 **
6412 6414 ** Parameters:
6413 6415 ** add -- initialize structure if necessary.
6414 6416 **
6415 6417 ** Returns:
6416 6418 ** 0: success
6417 6419 ** <0: some error, i.e.,
6418 6420 ** FSF_NOT_FOUND: not in list
6419 6421 ** FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
6420 6422 ** FSF_TOO_MANY: too many filesystems (-> syserr())
6421 6423 */
6422 6424
6423 6425 static int filesys_setup __P((bool));
6424 6426
6425 6427 static int
6426 6428 filesys_setup(add)
6427 6429 bool add;
6428 6430 {
6429 6431 int i, j;
6430 6432 short fs;
6431 6433 int ret;
6432 6434
6433 6435 ret = 0;
6434 6436 for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
6435 6437 {
6436 6438 for (j = 0; j < Queue[i]->qg_numqueues; ++j)
6437 6439 {
6438 6440 QPATHS *qp = &Queue[i]->qg_qpaths[j];
6439 6441 char qddf[MAXPATHLEN];
6440 6442
6441 6443 (void) sm_strlcpyn(qddf, sizeof(qddf), 2, qp->qp_name,
6442 6444 (bitset(QP_SUBDF, qp->qp_subdirs)
6443 6445 ? "/df" : ""));
6444 6446 fs = filesys_find(qp->qp_name, qddf, add);
6445 6447 if (fs >= 0)
6446 6448 qp->qp_fsysidx = fs;
6447 6449 else
6448 6450 qp->qp_fsysidx = 0;
6449 6451 if (fs < ret)
6450 6452 ret = fs;
6451 6453 }
6452 6454 }
6453 6455 return ret;
6454 6456 }
6455 6457
6456 6458 /*
6457 6459 ** FILESYS_UPDATE -- update amount of free space on all file systems
6458 6460 **
6459 6461 ** The FileSys table is used to cache the amount of free space
6460 6462 ** available on all queue directory file systems.
6461 6463 ** This function updates the cached information if it has expired.
6462 6464 **
6463 6465 ** Parameters:
6464 6466 ** none.
6465 6467 **
6466 6468 ** Returns:
6467 6469 ** none.
6468 6470 **
6469 6471 ** Side Effects:
6470 6472 ** Updates FileSys table.
6471 6473 */
6472 6474
6473 6475 void
6474 6476 filesys_update()
6475 6477 {
6476 6478 int i;
6477 6479 long avail, blksize;
6478 6480 time_t now;
6479 6481 static time_t nextupdate = 0;
6480 6482
6481 6483 #if SM_CONF_SHM
6482 6484 /*
6483 6485 ** Only the daemon updates the shared memory, i.e.,
6484 6486 ** if shared memory is available but the pid is not the
6485 6487 ** one of the daemon, then don't do anything.
6486 6488 */
6487 6489
6488 6490 if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid)
6489 6491 return;
6490 6492 #endif /* SM_CONF_SHM */
6491 6493 now = curtime();
6492 6494 if (now < nextupdate)
6493 6495 return;
6494 6496 nextupdate = now + FILESYS_UPDATE_INTERVAL;
6495 6497 for (i = 0; i < NumFileSys; ++i)
6496 6498 {
6497 6499 FILESYS *fs = &FILE_SYS(i);
6498 6500
6499 6501 avail = freediskspace(FILE_SYS_NAME(i), &blksize);
6500 6502 if (avail < 0 || blksize <= 0)
6501 6503 {
6502 6504 if (LogLevel > 5)
6503 6505 sm_syslog(LOG_ERR, NOQID,
6504 6506 "filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld",
6505 6507 sm_errstring(errno),
6506 6508 FILE_SYS_NAME(i), avail, blksize);
6507 6509 fs->fs_avail = 0;
6508 6510 fs->fs_blksize = 1024; /* avoid divide by zero */
6509 6511 nextupdate = now + 2; /* let's do this soon again */
6510 6512 }
6511 6513 else
6512 6514 {
6513 6515 fs->fs_avail = avail;
6514 6516 fs->fs_blksize = blksize;
6515 6517 }
6516 6518 }
6517 6519 }
6518 6520
6519 6521 #if _FFR_ANY_FREE_FS
6520 6522 /*
6521 6523 ** FILESYS_FREE -- check whether there is at least one fs with enough space.
6522 6524 **
6523 6525 ** Parameters:
6524 6526 ** fsize -- file size in bytes
6525 6527 **
6526 6528 ** Returns:
6527 6529 ** true iff there is one fs with more than fsize bytes free.
6528 6530 */
6529 6531
6530 6532 bool
6531 6533 filesys_free(fsize)
6532 6534 long fsize;
6533 6535 {
6534 6536 int i;
6535 6537
6536 6538 if (fsize <= 0)
6537 6539 return true;
6538 6540 for (i = 0; i < NumFileSys; ++i)
6539 6541 {
6540 6542 long needed = 0;
6541 6543
6542 6544 if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0)
6543 6545 continue;
6544 6546 needed += fsize / FILE_SYS_BLKSIZE(i)
6545 6547 + ((fsize % FILE_SYS_BLKSIZE(i)
6546 6548 > 0) ? 1 : 0)
6547 6549 + MinBlocksFree;
6548 6550 if (needed <= FILE_SYS_AVAIL(i))
6549 6551 return true;
6550 6552 }
6551 6553 return false;
6552 6554 }
6553 6555 #endif /* _FFR_ANY_FREE_FS */
6554 6556
6555 6557 /*
6556 6558 ** DISK_STATUS -- show amount of free space in queue directories
6557 6559 **
6558 6560 ** Parameters:
6559 6561 ** out -- output file pointer.
6560 6562 ** prefix -- string to output in front of each line.
6561 6563 **
6562 6564 ** Returns:
6563 6565 ** none.
6564 6566 */
6565 6567
6566 6568 void
6567 6569 disk_status(out, prefix)
6568 6570 SM_FILE_T *out;
6569 6571 char *prefix;
6570 6572 {
6571 6573 int i;
6572 6574 long avail, blksize;
6573 6575 long free;
6574 6576
6575 6577 for (i = 0; i < NumFileSys; ++i)
6576 6578 {
6577 6579 avail = freediskspace(FILE_SYS_NAME(i), &blksize);
6578 6580 if (avail >= 0 && blksize > 0)
6579 6581 {
6580 6582 free = (long)((double) avail *
6581 6583 ((double) blksize / 1024));
6582 6584 }
6583 6585 else
6584 6586 free = -1;
6585 6587 (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
6586 6588 "%s%d/%s/%ld\r\n",
6587 6589 prefix, i,
6588 6590 FILE_SYS_NAME(i),
6589 6591 free);
6590 6592 }
6591 6593 }
6592 6594
6593 6595 #if SM_CONF_SHM
6594 6596
6595 6597 /*
6596 6598 ** INIT_SEM -- initialize semaphore system
6597 6599 **
6598 6600 ** Parameters:
6599 6601 ** owner -- is this the owner of semaphores?
6600 6602 **
6601 6603 ** Returns:
6602 6604 ** none.
6603 6605 */
6604 6606
6605 6607 #if _FFR_USE_SEM_LOCKING
6606 6608 #if SM_CONF_SEM
6607 6609 static int SemId = -1; /* Semaphore Id */
6608 6610 int SemKey = SM_SEM_KEY;
6609 6611 #endif /* SM_CONF_SEM */
6610 6612 #endif /* _FFR_USE_SEM_LOCKING */
6611 6613
6612 6614 static void init_sem __P((bool));
6613 6615
6614 6616 static void
6615 6617 init_sem(owner)
6616 6618 bool owner;
6617 6619 {
6618 6620 #if _FFR_USE_SEM_LOCKING
6619 6621 #if SM_CONF_SEM
6620 6622 SemId = sm_sem_start(SemKey, 1, 0, owner);
6621 6623 if (SemId < 0)
6622 6624 {
6623 6625 sm_syslog(LOG_ERR, NOQID,
6624 6626 "func=init_sem, sem_key=%ld, sm_sem_start=%d, error=%s",
6625 6627 (long) SemKey, SemId, sm_errstring(-SemId));
6626 6628 return;
6627 6629 }
6628 6630 if (owner && RunAsUid != 0)
6629 6631 {
6630 6632 int r;
6631 6633
6632 6634 r = sm_semsetowner(SemId, RunAsUid, RunAsGid, 0660);
6633 6635 if (r != 0)
6634 6636 sm_syslog(LOG_ERR, NOQID,
6635 6637 "key=%ld, sm_semsetowner=%d, RunAsUid=%d, RunAsGid=%d",
6636 6638 (long) SemKey, r, RunAsUid, RunAsGid);
6637 6639 }
6638 6640 #endif /* SM_CONF_SEM */
6639 6641 #endif /* _FFR_USE_SEM_LOCKING */
6640 6642 return;
6641 6643 }
6642 6644
6643 6645 /*
6644 6646 ** STOP_SEM -- stop semaphore system
6645 6647 **
6646 6648 ** Parameters:
6647 6649 ** owner -- is this the owner of semaphores?
6648 6650 **
6649 6651 ** Returns:
6650 6652 ** none.
6651 6653 */
6652 6654
6653 6655 static void stop_sem __P((bool));
6654 6656
6655 6657 static void
6656 6658 stop_sem(owner)
6657 6659 bool owner;
6658 6660 {
6659 6661 #if _FFR_USE_SEM_LOCKING
6660 6662 #if SM_CONF_SEM
6661 6663 if (owner && SemId >= 0)
6662 6664 sm_sem_stop(SemId);
6663 6665 #endif /* SM_CONF_SEM */
6664 6666 #endif /* _FFR_USE_SEM_LOCKING */
6665 6667 return;
6666 6668 }
6667 6669
6668 6670 /*
6669 6671 ** UPD_QS -- update information about queue when adding/deleting an entry
6670 6672 **
6671 6673 ** Parameters:
6672 6674 ** e -- envelope.
6673 6675 ** count -- add/remove entry (+1/0/-1: add/no change/remove)
6674 6676 ** space -- update the space available as well.
6675 6677 ** (>0/0/<0: add/no change/remove)
6676 6678 ** where -- caller (for logging)
6677 6679 **
6678 6680 ** Returns:
6679 6681 ** none.
6680 6682 **
6681 6683 ** Side Effects:
6682 6684 ** Modifies available space in filesystem.
6683 6685 ** Changes number of entries in queue directory.
6684 6686 */
6685 6687
6686 6688 void
6687 6689 upd_qs(e, count, space, where)
6688 6690 ENVELOPE *e;
6689 6691 int count;
6690 6692 int space;
6691 6693 char *where;
6692 6694 {
6693 6695 short fidx;
6694 6696 int idx;
6695 6697 # if _FFR_USE_SEM_LOCKING
6696 6698 int r;
6697 6699 # endif /* _FFR_USE_SEM_LOCKING */
6698 6700 long s;
6699 6701
6700 6702 if (ShmId == SM_SHM_NO_ID || e == NULL)
6701 6703 return;
6702 6704 if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
6703 6705 return;
6704 6706 idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx;
6705 6707 if (tTd(73,2))
6706 6708 sm_dprintf("func=upd_qs, count=%d, space=%d, where=%s, idx=%d, entries=%d\n",
6707 6709 count, space, where, idx, QSHM_ENTRIES(idx));
6708 6710
6709 6711 /* XXX in theory this needs to be protected with a mutex */
6710 6712 if (QSHM_ENTRIES(idx) >= 0 && count != 0)
6711 6713 {
6712 6714 # if _FFR_USE_SEM_LOCKING
6713 6715 r = sm_sem_acq(SemId, 0, 1);
6714 6716 # endif /* _FFR_USE_SEM_LOCKING */
6715 6717 QSHM_ENTRIES(idx) += count;
6716 6718 # if _FFR_USE_SEM_LOCKING
6717 6719 if (r >= 0)
6718 6720 r = sm_sem_rel(SemId, 0, 1);
6719 6721 # endif /* _FFR_USE_SEM_LOCKING */
6720 6722 }
6721 6723
6722 6724 fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx;
6723 6725 if (fidx < 0)
6724 6726 return;
6725 6727
6726 6728 /* update available space also? (might be loseqfile) */
6727 6729 if (space == 0)
6728 6730 return;
6729 6731
6730 6732 /* convert size to blocks; this causes rounding errors */
6731 6733 s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx);
6732 6734 if (s == 0)
6733 6735 return;
6734 6736
6735 6737 /* XXX in theory this needs to be protected with a mutex */
6736 6738 if (space > 0)
6737 6739 FILE_SYS_AVAIL(fidx) += s;
6738 6740 else
6739 6741 FILE_SYS_AVAIL(fidx) -= s;
6740 6742
6741 6743 }
6742 6744
6743 6745 static bool write_key_file __P((char *, long));
6744 6746 static long read_key_file __P((char *, long));
6745 6747
6746 6748 /*
6747 6749 ** WRITE_KEY_FILE -- record some key into a file.
6748 6750 **
6749 6751 ** Parameters:
6750 6752 ** keypath -- file name.
6751 6753 ** key -- key to write.
6752 6754 **
6753 6755 ** Returns:
6754 6756 ** true iff file could be written.
6755 6757 **
6756 6758 ** Side Effects:
6757 6759 ** writes file.
6758 6760 */
6759 6761
6760 6762 static bool
6761 6763 write_key_file(keypath, key)
6762 6764 char *keypath;
6763 6765 long key;
6764 6766 {
6765 6767 bool ok;
6766 6768 long sff;
6767 6769 SM_FILE_T *keyf;
6768 6770
6769 6771 ok = false;
6770 6772 if (keypath == NULL || *keypath == '\0')
6771 6773 return ok;
6772 6774 sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
6773 6775 if (TrustedUid != 0 && RealUid == TrustedUid)
6774 6776 sff |= SFF_OPENASROOT;
6775 6777 keyf = safefopen(keypath, O_WRONLY|O_TRUNC, FileMode, sff);
6776 6778 if (keyf == NULL)
6777 6779 {
6778 6780 sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
6779 6781 keypath, sm_errstring(errno));
6780 6782 }
6781 6783 else
6782 6784 {
6783 6785 if (geteuid() == 0 && RunAsUid != 0)
6784 6786 {
6785 6787 # if HASFCHOWN
6786 6788 int fd;
6787 6789
6788 6790 fd = keyf->f_file;
6789 6791 if (fd >= 0 && fchown(fd, RunAsUid, -1) < 0)
6790 6792 {
6791 6793 int err = errno;
6792 6794
6793 6795 sm_syslog(LOG_ALERT, NOQID,
6794 6796 "ownership change on %s to %d failed: %s",
6795 6797 keypath, RunAsUid, sm_errstring(err));
6796 6798 }
6797 6799 # endif /* HASFCHOWN */
6798 6800 }
6799 6801 ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) !=
6800 6802 SM_IO_EOF;
6801 6803 ok = (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF) && ok;
6802 6804 }
6803 6805 return ok;
6804 6806 }
6805 6807
6806 6808 /*
6807 6809 ** READ_KEY_FILE -- read a key from a file.
6808 6810 **
6809 6811 ** Parameters:
6810 6812 ** keypath -- file name.
6811 6813 ** key -- default key.
6812 6814 **
6813 6815 ** Returns:
6814 6816 ** key.
6815 6817 */
6816 6818
6817 6819 static long
6818 6820 read_key_file(keypath, key)
6819 6821 char *keypath;
6820 6822 long key;
6821 6823 {
6822 6824 int r;
6823 6825 long sff, n;
6824 6826 SM_FILE_T *keyf;
6825 6827
6826 6828 if (keypath == NULL || *keypath == '\0')
6827 6829 return key;
6828 6830 sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY;
6829 6831 if (RealUid == 0 || (TrustedUid != 0 && RealUid == TrustedUid))
6830 6832 sff |= SFF_OPENASROOT;
6831 6833 keyf = safefopen(keypath, O_RDONLY, FileMode, sff);
6832 6834 if (keyf == NULL)
6833 6835 {
6834 6836 sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s",
6835 6837 keypath, sm_errstring(errno));
6836 6838 }
6837 6839 else
6838 6840 {
6839 6841 r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n);
6840 6842 if (r == 1)
6841 6843 key = n;
6842 6844 (void) sm_io_close(keyf, SM_TIME_DEFAULT);
6843 6845 }
6844 6846 return key;
6845 6847 }
6846 6848
6847 6849 /*
6848 6850 ** INIT_SHM -- initialize shared memory structure
6849 6851 **
6850 6852 ** Initialize or attach to shared memory segment.
6851 6853 ** Currently it is not a fatal error if this doesn't work.
6852 6854 ** However, it causes us to have a "fallback" storage location
6853 6855 ** for everything that is supposed to be in the shared memory,
6854 6856 ** which makes the code slightly ugly.
6855 6857 **
6856 6858 ** Parameters:
6857 6859 ** qn -- number of queue directories.
6858 6860 ** owner -- owner of shared memory.
6859 6861 ** hash -- identifies data that is stored in shared memory.
6860 6862 **
6861 6863 ** Returns:
6862 6864 ** none.
6863 6865 */
6864 6866
6865 6867 static void init_shm __P((int, bool, unsigned int));
6866 6868
6867 6869 static void
6868 6870 init_shm(qn, owner, hash)
6869 6871 int qn;
6870 6872 bool owner;
6871 6873 unsigned int hash;
6872 6874 {
6873 6875 int i;
6874 6876 int count;
6875 6877 int save_errno;
6876 6878 bool keyselect;
6877 6879
6878 6880 PtrFileSys = &FileSys[0];
6879 6881 PNumFileSys = &Numfilesys;
6880 6882 /* if this "key" is specified: select one yourself */
6881 6883 #define SEL_SHM_KEY ((key_t) -1)
6882 6884 #define FIRST_SHM_KEY 25
6883 6885
6884 6886 /* This allows us to disable shared memory at runtime. */
6885 6887 if (ShmKey == 0)
6886 6888 return;
6887 6889
6888 6890 count = 0;
6889 6891 shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T);
6890 6892 keyselect = ShmKey == SEL_SHM_KEY;
6891 6893 if (keyselect)
6892 6894 {
6893 6895 if (owner)
6894 6896 ShmKey = FIRST_SHM_KEY;
6895 6897 else
6896 6898 {
6897 6899 errno = 0;
6898 6900 ShmKey = read_key_file(ShmKeyFile, ShmKey);
6899 6901 keyselect = false;
6900 6902 if (ShmKey == SEL_SHM_KEY)
6901 6903 {
6902 6904 save_errno = (errno != 0) ? errno : EINVAL;
6903 6905 goto error;
6904 6906 }
6905 6907 }
6906 6908 }
6907 6909 for (;;)
6908 6910 {
6909 6911 /* allow read/write access for group? */
6910 6912 Pshm = sm_shmstart(ShmKey, shms,
6911 6913 SHM_R|SHM_W|(SHM_R>>3)|(SHM_W>>3),
6912 6914 &ShmId, owner);
6913 6915 save_errno = errno;
6914 6916 if (Pshm != NULL || !sm_file_exists(save_errno))
6915 6917 break;
6916 6918 if (++count >= 3)
6917 6919 {
6918 6920 if (keyselect)
6919 6921 {
6920 6922 ++ShmKey;
6921 6923
6922 6924 /* back where we started? */
6923 6925 if (ShmKey == SEL_SHM_KEY)
6924 6926 break;
6925 6927 continue;
6926 6928 }
6927 6929 break;
6928 6930 }
6929 6931
6930 6932 /* only sleep if we are at the first key */
6931 6933 if (!keyselect || ShmKey == SEL_SHM_KEY)
6932 6934 sleep(count);
6933 6935 }
6934 6936 if (Pshm != NULL)
6935 6937 {
6936 6938 int *p;
6937 6939
6938 6940 if (keyselect)
6939 6941 (void) write_key_file(ShmKeyFile, (long) ShmKey);
6940 6942 if (owner && RunAsUid != 0)
6941 6943 {
6942 6944 i = sm_shmsetowner(ShmId, RunAsUid, RunAsGid, 0660);
6943 6945 if (i != 0)
6944 6946 sm_syslog(LOG_ERR, NOQID,
6945 6947 "key=%ld, sm_shmsetowner=%d, RunAsUid=%d, RunAsGid=%d",
6946 6948 (long) ShmKey, i, RunAsUid, RunAsGid);
6947 6949 }
6948 6950 p = (int *) Pshm;
6949 6951 if (owner)
6950 6952 {
6951 6953 *p = (int) shms;
6952 6954 *((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid;
6953 6955 p = (int *) SHM_OFF_TAG(Pshm);
6954 6956 *p = hash;
6955 6957 }
6956 6958 else
6957 6959 {
6958 6960 if (*p != (int) shms)
6959 6961 {
6960 6962 save_errno = EINVAL;
6961 6963 cleanup_shm(false);
6962 6964 goto error;
6963 6965 }
6964 6966 p = (int *) SHM_OFF_TAG(Pshm);
6965 6967 if (*p != (int) hash)
6966 6968 {
6967 6969 save_errno = EINVAL;
6968 6970 cleanup_shm(false);
6969 6971 goto error;
6970 6972 }
6971 6973
6972 6974 /*
6973 6975 ** XXX how to check the pid?
6974 6976 ** Read it from the pid-file? That does
6975 6977 ** not need to exist.
6976 6978 ** We could disable shm if we can't confirm
6977 6979 ** that it is the right one.
6978 6980 */
6979 6981 }
6980 6982
6981 6983 PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm);
6982 6984 PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm);
6983 6985 QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm);
6984 6986 PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm);
6985 6987 *PRSATmpCnt = 0;
6986 6988 if (owner)
6987 6989 {
6988 6990 /* initialize values in shared memory */
6989 6991 NumFileSys = 0;
6990 6992 for (i = 0; i < qn; i++)
6991 6993 QShm[i].qs_entries = -1;
6992 6994 }
6993 6995 init_sem(owner);
6994 6996 return;
6995 6997 }
6996 6998 error:
6997 6999 if (LogLevel > (owner ? 8 : 11))
6998 7000 {
6999 7001 sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID,
7000 7002 "can't %s shared memory, key=%ld: %s",
7001 7003 owner ? "initialize" : "attach to",
7002 7004 (long) ShmKey, sm_errstring(save_errno));
7003 7005 }
7004 7006 }
7005 7007 #endif /* SM_CONF_SHM */
7006 7008
7007 7009
7008 7010 /*
7009 7011 ** SETUP_QUEUES -- set up all queue groups
7010 7012 **
7011 7013 ** Parameters:
7012 7014 ** owner -- owner of shared memory?
7013 7015 **
7014 7016 ** Returns:
7015 7017 ** none.
7016 7018 **
7017 7019 #if SM_CONF_SHM
7018 7020 ** Side Effects:
7019 7021 ** attaches shared memory.
7020 7022 #endif * SM_CONF_SHM *
7021 7023 */
7022 7024
7023 7025 void
7024 7026 setup_queues(owner)
7025 7027 bool owner;
7026 7028 {
7027 7029 int i, qn, len;
7028 7030 unsigned int hashval;
7029 7031 time_t now;
7030 7032 char basedir[MAXPATHLEN];
7031 7033 struct stat st;
7032 7034
7033 7035 /*
7034 7036 ** Determine basedir for all queue directories.
7035 7037 ** All queue directories must be (first level) subdirectories
7036 7038 ** of the basedir. The basedir is the QueueDir
7037 7039 ** without wildcards, but with trailing /
7038 7040 */
7039 7041
7040 7042 hashval = 0;
7041 7043 errno = 0;
7042 7044 len = sm_strlcpy(basedir, QueueDir, sizeof(basedir));
7043 7045
7044 7046 /* Provide space for trailing '/' */
7045 7047 if (len >= sizeof(basedir) - 1)
7046 7048 {
7047 7049 syserr("QueueDirectory: path too long: %d, max %d",
7048 7050 len, (int) sizeof(basedir) - 1);
7049 7051 ExitStat = EX_CONFIG;
7050 7052 return;
7051 7053 }
7052 7054 SM_ASSERT(len > 0);
7053 7055 if (basedir[len - 1] == '*')
7054 7056 {
7055 7057 char *cp;
7056 7058
7057 7059 cp = SM_LAST_DIR_DELIM(basedir);
7058 7060 if (cp == NULL)
7059 7061 {
7060 7062 syserr("QueueDirectory: can not wildcard relative path \"%s\"",
7061 7063 QueueDir);
7062 7064 if (tTd(41, 2))
7063 7065 sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n",
7064 7066 QueueDir);
7065 7067 ExitStat = EX_CONFIG;
7066 7068 return;
7067 7069 }
7068 7070
7069 7071 /* cut off wildcard pattern */
7070 7072 *++cp = '\0';
7071 7073 len = cp - basedir;
7072 7074 }
7073 7075 else if (!SM_IS_DIR_DELIM(basedir[len - 1]))
7074 7076 {
7075 7077 /* append trailing slash since it is a directory */
7076 7078 basedir[len] = '/';
7077 7079 basedir[++len] = '\0';
7078 7080 }
7079 7081
7080 7082 /* len counts up to the last directory delimiter */
7081 7083 SM_ASSERT(basedir[len - 1] == '/');
7082 7084
7083 7085 if (chdir(basedir) < 0)
7084 7086 {
7085 7087 int save_errno = errno;
7086 7088
7087 7089 syserr("can not chdir(%s)", basedir);
7088 7090 if (save_errno == EACCES)
7089 7091 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
7090 7092 "Program mode requires special privileges, e.g., root or TrustedUser.\n");
7091 7093 if (tTd(41, 2))
7092 7094 sm_dprintf("setup_queues: \"%s\": %s\n",
7093 7095 basedir, sm_errstring(errno));
7094 7096 ExitStat = EX_CONFIG;
7095 7097 return;
7096 7098 }
7097 7099 #if SM_CONF_SHM
7098 7100 hashval = hash_q(basedir, hashval);
7099 7101 #endif /* SM_CONF_SHM */
7100 7102
7101 7103 /* initialize for queue runs */
7102 7104 DoQueueRun = false;
7103 7105 now = curtime();
7104 7106 for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
7105 7107 Queue[i]->qg_nextrun = now;
7106 7108
7107 7109
7108 7110 if (UseMSP && OpMode != MD_TEST)
7109 7111 {
7110 7112 long sff = SFF_CREAT;
7111 7113
7112 7114 if (stat(".", &st) < 0)
7113 7115 {
7114 7116 syserr("can not stat(%s)", basedir);
7115 7117 if (tTd(41, 2))
7116 7118 sm_dprintf("setup_queues: \"%s\": %s\n",
7117 7119 basedir, sm_errstring(errno));
7118 7120 ExitStat = EX_CONFIG;
7119 7121 return;
7120 7122 }
7121 7123 if (RunAsUid == 0)
7122 7124 sff |= SFF_ROOTOK;
7123 7125
7124 7126 /*
7125 7127 ** Check queue directory permissions.
7126 7128 ** Can we write to a group writable queue directory?
7127 7129 */
7128 7130
7129 7131 if (bitset(S_IWGRP, QueueFileMode) &&
7130 7132 bitset(S_IWGRP, st.st_mode) &&
7131 7133 safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff,
7132 7134 QueueFileMode, NULL) != 0)
7133 7135 {
7134 7136 syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)",
7135 7137 basedir, (int) RunAsGid, (int) st.st_gid);
7136 7138 }
7137 7139 if (bitset(S_IWOTH|S_IXOTH, st.st_mode))
7138 7140 {
7139 7141 #if _FFR_MSP_PARANOIA
7140 7142 syserr("dangerous permissions=%o on queue directory %s",
7141 7143 (int) st.st_mode, basedir);
7142 7144 #else /* _FFR_MSP_PARANOIA */
7143 7145 if (LogLevel > 0)
7144 7146 sm_syslog(LOG_ERR, NOQID,
7145 7147 "dangerous permissions=%o on queue directory %s",
7146 7148 (int) st.st_mode, basedir);
7147 7149 #endif /* _FFR_MSP_PARANOIA */
7148 7150 }
7149 7151 #if _FFR_MSP_PARANOIA
7150 7152 if (NumQueue > 1)
7151 7153 syserr("can not use multiple queues for MSP");
7152 7154 #endif /* _FFR_MSP_PARANOIA */
7153 7155 }
7154 7156
7155 7157 /* initial number of queue directories */
7156 7158 qn = 0;
7157 7159 for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
7158 7160 qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval);
7159 7161
7160 7162 #if SM_CONF_SHM
7161 7163 init_shm(qn, owner, hashval);
7162 7164 i = filesys_setup(owner || ShmId == SM_SHM_NO_ID);
7163 7165 if (i == FSF_NOT_FOUND)
7164 7166 {
7165 7167 /*
7166 7168 ** We didn't get the right filesystem data
7167 7169 ** This may happen if we don't have the right shared memory.
7168 7170 ** So let's do this without shared memory.
7169 7171 */
7170 7172
7171 7173 SM_ASSERT(!owner);
7172 7174 cleanup_shm(false); /* release shared memory */
7173 7175 i = filesys_setup(false);
7174 7176 if (i < 0)
7175 7177 syserr("filesys_setup failed twice, result=%d", i);
7176 7178 else if (LogLevel > 8)
7177 7179 sm_syslog(LOG_WARNING, NOQID,
7178 7180 "shared memory does not contain expected data, ignored");
7179 7181 }
7180 7182 #else /* SM_CONF_SHM */
7181 7183 i = filesys_setup(true);
7182 7184 #endif /* SM_CONF_SHM */
7183 7185 if (i < 0)
7184 7186 ExitStat = EX_CONFIG;
7185 7187 }
7186 7188
7187 7189 #if SM_CONF_SHM
7188 7190 /*
7189 7191 ** CLEANUP_SHM -- do some cleanup work for shared memory etc
7190 7192 **
7191 7193 ** Parameters:
7192 7194 ** owner -- owner of shared memory?
7193 7195 **
7194 7196 ** Returns:
7195 7197 ** none.
7196 7198 **
7197 7199 ** Side Effects:
7198 7200 ** detaches shared memory.
7199 7201 */
7200 7202
7201 7203 void
7202 7204 cleanup_shm(owner)
7203 7205 bool owner;
7204 7206 {
7205 7207 if (ShmId != SM_SHM_NO_ID)
7206 7208 {
7207 7209 if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8)
7208 7210 sm_syslog(LOG_INFO, NOQID, "sm_shmstop failed=%s",
7209 7211 sm_errstring(errno));
7210 7212 Pshm = NULL;
7211 7213 ShmId = SM_SHM_NO_ID;
7212 7214 }
7213 7215 stop_sem(owner);
7214 7216 }
7215 7217 #endif /* SM_CONF_SHM */
7216 7218
7217 7219 /*
7218 7220 ** CLEANUP_QUEUES -- do some cleanup work for queues
7219 7221 **
7220 7222 ** Parameters:
7221 7223 ** none.
7222 7224 **
7223 7225 ** Returns:
7224 7226 ** none.
7225 7227 **
7226 7228 */
7227 7229
7228 7230 void
7229 7231 cleanup_queues()
7230 7232 {
7231 7233 sync_queue_time();
7232 7234 }
7233 7235 /*
7234 7236 ** SET_DEF_QUEUEVAL -- set default values for a queue group.
7235 7237 **
7236 7238 ** Parameters:
7237 7239 ** qg -- queue group
7238 7240 ** all -- set all values (true for default group)?
7239 7241 **
7240 7242 ** Returns:
7241 7243 ** none.
7242 7244 **
7243 7245 ** Side Effects:
7244 7246 ** sets default values for the queue group.
7245 7247 */
7246 7248
7247 7249 void
7248 7250 set_def_queueval(qg, all)
7249 7251 QUEUEGRP *qg;
7250 7252 bool all;
7251 7253 {
7252 7254 if (bitnset(QD_DEFINED, qg->qg_flags))
7253 7255 return;
7254 7256 if (all)
7255 7257 qg->qg_qdir = QueueDir;
7256 7258 #if _FFR_QUEUE_GROUP_SORTORDER
7257 7259 qg->qg_sortorder = QueueSortOrder;
7258 7260 #endif /* _FFR_QUEUE_GROUP_SORTORDER */
7259 7261 qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1;
7260 7262 qg->qg_nice = NiceQueueRun;
7261 7263 }
7262 7264 /*
7263 7265 ** MAKEQUEUE -- define a new queue.
7264 7266 **
7265 7267 ** Parameters:
7266 7268 ** line -- description of queue. This is in labeled fields.
7267 7269 ** The fields are:
7268 7270 ** F -- the flags associated with the queue
7269 7271 ** I -- the interval between running the queue
7270 7272 ** J -- the maximum # of jobs in work list
7271 7273 ** [M -- the maximum # of jobs in a queue run]
7272 7274 ** N -- the niceness at which to run
7273 7275 ** P -- the path to the queue
7274 7276 ** S -- the queue sorting order
7275 7277 ** R -- number of parallel queue runners
7276 7278 ** r -- max recipients per envelope
7277 7279 ** The first word is the canonical name of the queue.
7278 7280 ** qdef -- this is a 'Q' definition from .cf
7279 7281 **
7280 7282 ** Returns:
7281 7283 ** none.
7282 7284 **
7283 7285 ** Side Effects:
7284 7286 ** enters the queue into the queue table.
7285 7287 */
7286 7288
7287 7289 void
7288 7290 makequeue(line, qdef)
7289 7291 char *line;
7290 7292 bool qdef;
7291 7293 {
7292 7294 register char *p;
7293 7295 register QUEUEGRP *qg;
7294 7296 register STAB *s;
7295 7297 int i;
7296 7298 char fcode;
7297 7299
7298 7300 /* allocate a queue and set up defaults */
7299 7301 qg = (QUEUEGRP *) xalloc(sizeof(*qg));
7300 7302 memset((char *) qg, '\0', sizeof(*qg));
7301 7303
7302 7304 if (line[0] == '\0')
7303 7305 {
7304 7306 syserr("name required for queue");
7305 7307 return;
7306 7308 }
7307 7309
7308 7310 /* collect the queue name */
7309 7311 for (p = line;
7310 7312 *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
7311 7313 p++)
7312 7314 continue;
7313 7315 if (*p != '\0')
7314 7316 *p++ = '\0';
7315 7317 qg->qg_name = newstr(line);
7316 7318
7317 7319 /* set default values, can be overridden below */
7318 7320 set_def_queueval(qg, false);
7319 7321
7320 7322 /* now scan through and assign info from the fields */
7321 7323 while (*p != '\0')
7322 7324 {
7323 7325 auto char *delimptr;
7324 7326
7325 7327 while (*p != '\0' &&
7326 7328 (*p == ',' || (isascii(*p) && isspace(*p))))
7327 7329 p++;
7328 7330
7329 7331 /* p now points to field code */
7330 7332 fcode = *p;
7331 7333 while (*p != '\0' && *p != '=' && *p != ',')
7332 7334 p++;
7333 7335 if (*p++ != '=')
7334 7336 {
7335 7337 syserr("queue %s: `=' expected", qg->qg_name);
7336 7338 return;
7337 7339 }
7338 7340 while (isascii(*p) && isspace(*p))
7339 7341 p++;
7340 7342
7341 7343 /* p now points to the field body */
7342 7344 p = munchstring(p, &delimptr, ',');
7343 7345
7344 7346 /* install the field into the queue struct */
7345 7347 switch (fcode)
7346 7348 {
7347 7349 case 'P': /* pathname */
7348 7350 if (*p == '\0')
7349 7351 syserr("queue %s: empty path name",
7350 7352 qg->qg_name);
7351 7353 else
7352 7354 qg->qg_qdir = newstr(p);
7353 7355 break;
7354 7356
7355 7357 case 'F': /* flags */
7356 7358 for (; *p != '\0'; p++)
7357 7359 if (!(isascii(*p) && isspace(*p)))
7358 7360 setbitn(*p, qg->qg_flags);
7359 7361 break;
7360 7362
7361 7363 /*
7362 7364 ** Do we need two intervals here:
7363 7365 ** One for persistent queue runners,
7364 7366 ** one for "normal" queue runs?
7365 7367 */
7366 7368
7367 7369 case 'I': /* interval between running the queue */
7368 7370 qg->qg_queueintvl = convtime(p, 'm');
7369 7371 break;
7370 7372
7371 7373 case 'N': /* run niceness */
7372 7374 qg->qg_nice = atoi(p);
7373 7375 break;
7374 7376
7375 7377 case 'R': /* maximum # of runners for the group */
7376 7378 i = atoi(p);
7377 7379
7378 7380 /* can't have more runners than allowed total */
7379 7381 if (MaxQueueChildren > 0 && i > MaxQueueChildren)
7380 7382 {
7381 7383 qg->qg_maxqrun = MaxQueueChildren;
7382 7384 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
7383 7385 "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n",
7384 7386 qg->qg_name, i,
7385 7387 MaxQueueChildren);
7386 7388 }
7387 7389 else
7388 7390 qg->qg_maxqrun = i;
7389 7391 break;
7390 7392
7391 7393 case 'J': /* maximum # of jobs in work list */
7392 7394 qg->qg_maxlist = atoi(p);
7393 7395 break;
7394 7396
7395 7397 case 'r': /* max recipients per envelope */
7396 7398 qg->qg_maxrcpt = atoi(p);
7397 7399 break;
7398 7400
7399 7401 #if _FFR_QUEUE_GROUP_SORTORDER
7400 7402 case 'S': /* queue sorting order */
7401 7403 switch (*p)
7402 7404 {
7403 7405 case 'h': /* Host first */
7404 7406 case 'H':
7405 7407 qg->qg_sortorder = QSO_BYHOST;
7406 7408 break;
7407 7409
7408 7410 case 'p': /* Priority order */
7409 7411 case 'P':
7410 7412 qg->qg_sortorder = QSO_BYPRIORITY;
7411 7413 break;
7412 7414
7413 7415 case 't': /* Submission time */
7414 7416 case 'T':
7415 7417 qg->qg_sortorder = QSO_BYTIME;
7416 7418 break;
7417 7419
7418 7420 case 'f': /* File name */
7419 7421 case 'F':
7420 7422 qg->qg_sortorder = QSO_BYFILENAME;
7421 7423 break;
7422 7424
7423 7425 case 'm': /* Modification time */
7424 7426 case 'M':
7425 7427 qg->qg_sortorder = QSO_BYMODTIME;
7426 7428 break;
7427 7429
7428 7430 case 'r': /* Random */
7429 7431 case 'R':
7430 7432 qg->qg_sortorder = QSO_RANDOM;
7431 7433 break;
7432 7434
7433 7435 # if _FFR_RHS
7434 7436 case 's': /* Shuffled host name */
7435 7437 case 'S':
7436 7438 qg->qg_sortorder = QSO_BYSHUFFLE;
7437 7439 break;
7438 7440 # endif /* _FFR_RHS */
7439 7441
7440 7442 case 'n': /* none */
7441 7443 case 'N':
7442 7444 qg->qg_sortorder = QSO_NONE;
7443 7445 break;
7444 7446
7445 7447 default:
7446 7448 syserr("Invalid queue sort order \"%s\"", p);
7447 7449 }
7448 7450 break;
7449 7451 #endif /* _FFR_QUEUE_GROUP_SORTORDER */
7450 7452
7451 7453 default:
7452 7454 syserr("Q%s: unknown queue equate %c=",
7453 7455 qg->qg_name, fcode);
7454 7456 break;
7455 7457 }
7456 7458
7457 7459 p = delimptr;
7458 7460 }
7459 7461
7460 7462 #if !HASNICE
7461 7463 if (qg->qg_nice != NiceQueueRun)
7462 7464 {
7463 7465 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
7464 7466 "Q%s: Warning: N= set on system that doesn't support nice()\n",
7465 7467 qg->qg_name);
7466 7468 }
7467 7469 #endif /* !HASNICE */
7468 7470
7469 7471 /* do some rationality checking */
7470 7472 if (NumQueue >= MAXQUEUEGROUPS)
7471 7473 {
7472 7474 syserr("too many queue groups defined (%d max)",
7473 7475 MAXQUEUEGROUPS);
7474 7476 return;
7475 7477 }
7476 7478
7477 7479 if (qg->qg_qdir == NULL)
7478 7480 {
7479 7481 if (QueueDir == NULL || *QueueDir == '\0')
7480 7482 {
7481 7483 syserr("QueueDir must be defined before queue groups");
7482 7484 return;
7483 7485 }
7484 7486 qg->qg_qdir = newstr(QueueDir);
7485 7487 }
7486 7488
7487 7489 if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags))
7488 7490 {
7489 7491 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
7490 7492 "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n",
7491 7493 qg->qg_name, qg->qg_maxqrun, QD_FORK);
7492 7494 }
7493 7495
7494 7496 /* enter the queue into the symbol table */
7495 7497 if (tTd(37, 8))
7496 7498 sm_syslog(LOG_INFO, NOQID,
7497 7499 "Adding %s to stab, path: %s", qg->qg_name,
7498 7500 qg->qg_qdir);
7499 7501 s = stab(qg->qg_name, ST_QUEUE, ST_ENTER);
7500 7502 if (s->s_quegrp != NULL)
7501 7503 {
7502 7504 i = s->s_quegrp->qg_index;
7503 7505
7504 7506 /* XXX what about the pointers inside this struct? */
7505 7507 sm_free(s->s_quegrp); /* XXX */
7506 7508 }
7507 7509 else
7508 7510 i = NumQueue++;
7509 7511 Queue[i] = s->s_quegrp = qg;
7510 7512 qg->qg_index = i;
7511 7513
7512 7514 /* set default value for max queue runners */
7513 7515 if (qg->qg_maxqrun < 0)
7514 7516 {
7515 7517 if (MaxRunnersPerQueue > 0)
7516 7518 qg->qg_maxqrun = MaxRunnersPerQueue;
7517 7519 else
7518 7520 qg->qg_maxqrun = 1;
7519 7521 }
7520 7522 if (qdef)
7521 7523 setbitn(QD_DEFINED, qg->qg_flags);
7522 7524 }
7523 7525 #if 0
7524 7526 /*
7525 7527 ** HASHFQN -- calculate a hash value for a fully qualified host name
7526 7528 **
7527 7529 ** Arguments:
7528 7530 ** fqn -- an all lower-case host.domain string
7529 7531 ** buckets -- the number of buckets (queue directories)
7530 7532 **
7531 7533 ** Returns:
7532 7534 ** a bucket number (signed integer)
7533 7535 ** -1 on error
7534 7536 **
7535 7537 ** Contributed by Exactis.com, Inc.
7536 7538 */
7537 7539
7538 7540 int
7539 7541 hashfqn(fqn, buckets)
7540 7542 register char *fqn;
7541 7543 int buckets;
7542 7544 {
7543 7545 register char *p;
7544 7546 register int h = 0, hash, cnt;
7545 7547
7546 7548 if (fqn == NULL)
7547 7549 return -1;
7548 7550
7549 7551 /*
7550 7552 ** A variation on the gdb hash
7551 7553 ** This is the best as of Feb 19, 1996 --bcx
7552 7554 */
7553 7555
7554 7556 p = fqn;
7555 7557 h = 0x238F13AF * strlen(p);
7556 7558 for (cnt = 0; *p != 0; ++p, cnt++)
7557 7559 {
7558 7560 h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF;
7559 7561 }
7560 7562 h = (1103515243 * h + 12345) & 0x7FFFFFFF;
7561 7563 if (buckets < 2)
7562 7564 hash = 0;
7563 7565 else
7564 7566 hash = (h % buckets);
7565 7567
7566 7568 return hash;
7567 7569 }
7568 7570 #endif /* 0 */
7569 7571
7570 7572 /*
7571 7573 ** A structure for sorting Queue according to maxqrun without
7572 7574 ** screwing up Queue itself.
7573 7575 */
7574 7576
7575 7577 struct sortqgrp
7576 7578 {
7577 7579 int sg_idx; /* original index */
7578 7580 int sg_maxqrun; /* max queue runners */
7579 7581 };
7580 7582 typedef struct sortqgrp SORTQGRP_T;
7581 7583 static int cmpidx __P((const void *, const void *));
7582 7584
7583 7585 static int
7584 7586 cmpidx(a, b)
7585 7587 const void *a;
7586 7588 const void *b;
7587 7589 {
7588 7590 /* The sort is highest to lowest, so the comparison is reversed */
7589 7591 if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun)
7590 7592 return 1;
7591 7593 else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun)
7592 7594 return -1;
7593 7595 else
7594 7596 return 0;
7595 7597 }
7596 7598
7597 7599 /*
7598 7600 ** MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren
7599 7601 **
7600 7602 ** Take the now defined queue groups and assign them to work groups.
7601 7603 ** This is done to balance out the number of concurrently active
7602 7604 ** queue runners such that MaxQueueChildren is not exceeded. This may
7603 7605 ** result in more than one queue group per work group. In such a case
7604 7606 ** the number of running queue groups in that work group will have no
7605 7607 ** more than the work group maximum number of runners (a "fair" portion
7606 7608 ** of MaxQueueRunners). All queue groups within a work group will get a
7607 7609 ** chance at running.
7608 7610 **
7609 7611 ** Parameters:
7610 7612 ** none.
7611 7613 **
7612 7614 ** Returns:
7613 7615 ** nothing.
7614 7616 **
7615 7617 ** Side Effects:
7616 7618 ** Sets up WorkGrp structure.
7617 7619 */
7618 7620
7619 7621 void
7620 7622 makeworkgroups()
7621 7623 {
7622 7624 int i, j, total_runners, dir, h;
7623 7625 SORTQGRP_T si[MAXQUEUEGROUPS + 1];
7624 7626
7625 7627 total_runners = 0;
7626 7628 if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0)
7627 7629 {
7628 7630 /*
7629 7631 ** There is only the "mqueue" queue group (a default)
7630 7632 ** containing all of the queues. We want to provide to
7631 7633 ** this queue group the maximum allowable queue runners.
7632 7634 ** To match older behavior (8.10/8.11) we'll try for
7633 7635 ** 1 runner per queue capping it at MaxQueueChildren.
7634 7636 ** So if there are N queues, then there will be N runners
7635 7637 ** for the "mqueue" queue group (where N is kept less than
7636 7638 ** MaxQueueChildren).
7637 7639 */
7638 7640
7639 7641 NumWorkGroups = 1;
7640 7642 WorkGrp[0].wg_numqgrp = 1;
7641 7643 WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *));
7642 7644 WorkGrp[0].wg_qgs[0] = Queue[0];
7643 7645 if (MaxQueueChildren > 0 &&
7644 7646 Queue[0]->qg_numqueues > MaxQueueChildren)
7645 7647 WorkGrp[0].wg_runners = MaxQueueChildren;
7646 7648 else
7647 7649 WorkGrp[0].wg_runners = Queue[0]->qg_numqueues;
7648 7650
7649 7651 Queue[0]->qg_wgrp = 0;
7650 7652
7651 7653 /* can't have more runners than allowed total */
7652 7654 if (MaxQueueChildren > 0 &&
7653 7655 Queue[0]->qg_maxqrun > MaxQueueChildren)
7654 7656 Queue[0]->qg_maxqrun = MaxQueueChildren;
7655 7657 WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun;
7656 7658 WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl;
7657 7659 return;
7658 7660 }
7659 7661
7660 7662 for (i = 0; i < NumQueue; i++)
7661 7663 {
7662 7664 si[i].sg_maxqrun = Queue[i]->qg_maxqrun;
7663 7665 si[i].sg_idx = i;
7664 7666 }
7665 7667 qsort(si, NumQueue, sizeof(si[0]), cmpidx);
7666 7668
7667 7669 NumWorkGroups = 0;
7668 7670 for (i = 0; i < NumQueue; i++)
7669 7671 {
7670 7672 total_runners += si[i].sg_maxqrun;
7671 7673 if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren)
7672 7674 NumWorkGroups++;
7673 7675 else
7674 7676 break;
7675 7677 }
7676 7678
7677 7679 if (NumWorkGroups < 1)
7678 7680 NumWorkGroups = 1; /* gotta have one at least */
7679 7681 else if (NumWorkGroups > MAXWORKGROUPS)
7680 7682 NumWorkGroups = MAXWORKGROUPS; /* the limit */
7681 7683
7682 7684 /*
7683 7685 ** We now know the number of work groups to pack the queue groups
7684 7686 ** into. The queue groups in 'Queue' are sorted from highest
7685 7687 ** to lowest for the number of runners per queue group.
7686 7688 ** We put the queue groups with the largest number of runners
7687 7689 ** into work groups first. Then the smaller ones are fitted in
7688 7690 ** where it looks best.
7689 7691 */
7690 7692
7691 7693 j = 0;
7692 7694 dir = 1;
7693 7695 for (i = 0; i < NumQueue; i++)
7694 7696 {
7695 7697 /* a to-and-fro packing scheme, continue from last position */
7696 7698 if (j >= NumWorkGroups)
7697 7699 {
7698 7700 dir = -1;
7699 7701 j = NumWorkGroups - 1;
7700 7702 }
7701 7703 else if (j < 0)
7702 7704 {
7703 7705 j = 0;
7704 7706 dir = 1;
7705 7707 }
7706 7708
7707 7709 if (WorkGrp[j].wg_qgs == NULL)
7708 7710 WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) *
7709 7711 (WorkGrp[j].wg_numqgrp + 1));
7710 7712 else
7711 7713 WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs,
7712 7714 sizeof(QUEUEGRP *) *
7713 7715 (WorkGrp[j].wg_numqgrp + 1));
7714 7716 if (WorkGrp[j].wg_qgs == NULL)
7715 7717 {
7716 7718 syserr("!cannot allocate memory for work queues, need %d bytes",
7717 7719 (int) (sizeof(QUEUEGRP *) *
7718 7720 (WorkGrp[j].wg_numqgrp + 1)));
7719 7721 }
7720 7722
7721 7723 h = si[i].sg_idx;
7722 7724 WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[h];
7723 7725 WorkGrp[j].wg_numqgrp++;
7724 7726 WorkGrp[j].wg_runners += Queue[h]->qg_maxqrun;
7725 7727 Queue[h]->qg_wgrp = j;
7726 7728
7727 7729 if (WorkGrp[j].wg_maxact == 0)
7728 7730 {
7729 7731 /* can't have more runners than allowed total */
7730 7732 if (MaxQueueChildren > 0 &&
7731 7733 Queue[h]->qg_maxqrun > MaxQueueChildren)
7732 7734 Queue[h]->qg_maxqrun = MaxQueueChildren;
7733 7735 WorkGrp[j].wg_maxact = Queue[h]->qg_maxqrun;
7734 7736 }
7735 7737
7736 7738 /*
7737 7739 ** XXX: must wg_lowqintvl be the GCD?
7738 7740 ** qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for
7739 7741 ** qg2 occur?
7740 7742 */
7741 7743
7742 7744 /* keep track of the lowest interval for a persistent runner */
7743 7745 if (Queue[h]->qg_queueintvl > 0 &&
7744 7746 WorkGrp[j].wg_lowqintvl < Queue[h]->qg_queueintvl)
7745 7747 WorkGrp[j].wg_lowqintvl = Queue[h]->qg_queueintvl;
7746 7748 j += dir;
7747 7749 }
7748 7750 if (tTd(41, 9))
7749 7751 {
7750 7752 for (i = 0; i < NumWorkGroups; i++)
7751 7753 {
7752 7754 sm_dprintf("Workgroup[%d]=", i);
7753 7755 for (j = 0; j < WorkGrp[i].wg_numqgrp; j++)
7754 7756 {
7755 7757 sm_dprintf("%s, ",
7756 7758 WorkGrp[i].wg_qgs[j]->qg_name);
7757 7759 }
7758 7760 sm_dprintf("\n");
7759 7761 }
7760 7762 }
7761 7763 }
7762 7764
7763 7765 /*
7764 7766 ** DUP_DF -- duplicate envelope data file
7765 7767 **
7766 7768 ** Copy the data file from the 'old' envelope to the 'new' envelope
7767 7769 ** in the most efficient way possible.
7768 7770 **
7769 7771 ** Create a hard link from the 'old' data file to the 'new' data file.
7770 7772 ** If the old and new queue directories are on different file systems,
7771 7773 ** then the new data file link is created in the old queue directory,
7772 7774 ** and the new queue file will contain a 'd' record pointing to the
7773 7775 ** directory containing the new data file.
7774 7776 **
7775 7777 ** Parameters:
7776 7778 ** old -- old envelope.
7777 7779 ** new -- new envelope.
7778 7780 **
7779 7781 ** Results:
7780 7782 ** Returns true on success, false on failure.
7781 7783 **
7782 7784 ** Side Effects:
7783 7785 ** On success, the new data file is created.
7784 7786 ** On fatal failure, EF_FATALERRS is set in old->e_flags.
7785 7787 */
7786 7788
7787 7789 static bool dup_df __P((ENVELOPE *, ENVELOPE *));
7788 7790
7789 7791 static bool
7790 7792 dup_df(old, new)
7791 7793 ENVELOPE *old;
7792 7794 ENVELOPE *new;
7793 7795 {
7794 7796 int ofs, nfs, r;
7795 7797 char opath[MAXPATHLEN];
7796 7798 char npath[MAXPATHLEN];
7797 7799
7798 7800 if (!bitset(EF_HAS_DF, old->e_flags))
7799 7801 {
7800 7802 /*
7801 7803 ** this can happen if: SuperSafe != True
7802 7804 ** and a bounce mail is sent that is split.
7803 7805 */
7804 7806
7805 7807 queueup(old, false, true);
7806 7808 }
7807 7809 SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir));
7808 7810 SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir));
7809 7811
7810 7812 (void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof(opath));
7811 7813 (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath));
7812 7814
7813 7815 if (old->e_dfp != NULL)
7814 7816 {
7815 7817 r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL);
7816 7818 if (r < 0 && errno != EINVAL)
7817 7819 {
7818 7820 syserr("@can't commit %s", opath);
7819 7821 old->e_flags |= EF_FATALERRS;
7820 7822 return false;
7821 7823 }
7822 7824 }
7823 7825
7824 7826 /*
7825 7827 ** Attempt to create a hard link, if we think both old and new
7826 7828 ** are on the same file system, otherwise copy the file.
7827 7829 **
7828 7830 ** Don't waste time attempting a hard link unless old and new
7829 7831 ** are on the same file system.
7830 7832 */
7831 7833
7832 7834 SM_REQUIRE(ISVALIDQGRP(old->e_dfqgrp) && ISVALIDQDIR(old->e_dfqdir));
7833 7835 SM_REQUIRE(ISVALIDQGRP(new->e_dfqgrp) && ISVALIDQDIR(new->e_dfqdir));
7834 7836
7835 7837 ofs = Queue[old->e_dfqgrp]->qg_qpaths[old->e_dfqdir].qp_fsysidx;
7836 7838 nfs = Queue[new->e_dfqgrp]->qg_qpaths[new->e_dfqdir].qp_fsysidx;
7837 7839 if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs))
7838 7840 {
7839 7841 if (link(opath, npath) == 0)
7840 7842 {
7841 7843 new->e_flags |= EF_HAS_DF;
7842 7844 SYNC_DIR(npath, true);
7843 7845 return true;
7844 7846 }
7845 7847 goto error;
7846 7848 }
7847 7849
7848 7850 /*
7849 7851 ** Can't link across queue directories, so try to create a hard
7850 7852 ** link in the same queue directory as the old df file.
7851 7853 ** The qf file will refer to the new df file using a 'd' record.
7852 7854 */
7853 7855
7854 7856 new->e_dfqgrp = old->e_dfqgrp;
7855 7857 new->e_dfqdir = old->e_dfqdir;
7856 7858 (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath));
7857 7859 if (link(opath, npath) == 0)
7858 7860 {
7859 7861 new->e_flags |= EF_HAS_DF;
7860 7862 SYNC_DIR(npath, true);
7861 7863 return true;
7862 7864 }
7863 7865
7864 7866 error:
7865 7867 if (LogLevel > 0)
7866 7868 sm_syslog(LOG_ERR, old->e_id,
7867 7869 "dup_df: can't link %s to %s, error=%s, envelope splitting failed",
7868 7870 opath, npath, sm_errstring(errno));
7869 7871 return false;
7870 7872 }
7871 7873
7872 7874 /*
7873 7875 ** SPLIT_ENV -- Allocate a new envelope based on a given envelope.
7874 7876 **
7875 7877 ** Parameters:
7876 7878 ** e -- envelope.
7877 7879 ** sendqueue -- sendqueue for new envelope.
7878 7880 ** qgrp -- index of queue group.
7879 7881 ** qdir -- queue directory.
7880 7882 **
7881 7883 ** Results:
7882 7884 ** new envelope.
7883 7885 **
7884 7886 */
7885 7887
7886 7888 static ENVELOPE *split_env __P((ENVELOPE *, ADDRESS *, int, int));
7887 7889
7888 7890 static ENVELOPE *
7889 7891 split_env(e, sendqueue, qgrp, qdir)
7890 7892 ENVELOPE *e;
7891 7893 ADDRESS *sendqueue;
7892 7894 int qgrp;
7893 7895 int qdir;
7894 7896 {
7895 7897 ENVELOPE *ee;
7896 7898
7897 7899 ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof(*ee));
7898 7900 STRUCTCOPY(*e, *ee);
7899 7901 ee->e_message = NULL; /* XXX use original message? */
7900 7902 ee->e_id = NULL;
7901 7903 assign_queueid(ee);
7902 7904 ee->e_sendqueue = sendqueue;
7903 7905 ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS
7904 7906 |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF);
7905 7907 ee->e_flags |= EF_NORECEIPT; /* XXX really? */
7906 7908 ee->e_from.q_state = QS_SENDER;
7907 7909 ee->e_dfp = NULL;
7908 7910 ee->e_lockfp = NULL;
7909 7911 if (e->e_xfp != NULL)
7910 7912 ee->e_xfp = sm_io_dup(e->e_xfp);
7911 7913
7912 7914 /* failed to dup e->e_xfp, start a new transcript */
7913 7915 if (ee->e_xfp == NULL)
7914 7916 openxscript(ee);
7915 7917
7916 7918 ee->e_qgrp = ee->e_dfqgrp = qgrp;
7917 7919 ee->e_qdir = ee->e_dfqdir = qdir;
7918 7920 ee->e_errormode = EM_MAIL;
7919 7921 ee->e_statmsg = NULL;
7920 7922 if (e->e_quarmsg != NULL)
7921 7923 ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
7922 7924 e->e_quarmsg);
7923 7925
7924 7926 /*
7925 7927 ** XXX Not sure if this copying is necessary.
7926 7928 ** sendall() does this copying, but I (dm) don't know if that is
7927 7929 ** because of the storage management discipline we were using
7928 7930 ** before rpools were introduced, or if it is because these lists
7929 7931 ** can be modified later.
7930 7932 */
7931 7933
7932 7934 ee->e_header = copyheader(e->e_header, ee->e_rpool);
7933 7935 ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool);
7934 7936
7935 7937 return ee;
7936 7938 }
7937 7939
7938 7940 /* return values from split functions, check also below! */
7939 7941 #define SM_SPLIT_FAIL (0)
7940 7942 #define SM_SPLIT_NONE (1)
7941 7943 #define SM_SPLIT_NEW(n) (1 + (n))
7942 7944
7943 7945 /*
7944 7946 ** SPLIT_ACROSS_QUEUE_GROUPS
7945 7947 **
7946 7948 ** This function splits an envelope across multiple queue groups
7947 7949 ** based on the queue group of each recipient.
7948 7950 **
7949 7951 ** Parameters:
7950 7952 ** e -- envelope.
7951 7953 **
7952 7954 ** Results:
7953 7955 ** SM_SPLIT_FAIL on failure
7954 7956 ** SM_SPLIT_NONE if no splitting occurred,
7955 7957 ** or 1 + the number of additional envelopes created.
7956 7958 **
7957 7959 ** Side Effects:
7958 7960 ** On success, e->e_sibling points to a list of zero or more
7959 7961 ** additional envelopes, and the associated data files exist
7960 7962 ** on disk. But the queue files are not created.
7961 7963 **
7962 7964 ** On failure, e->e_sibling is not changed.
7963 7965 ** The order of recipients in e->e_sendqueue is permuted.
7964 7966 ** Abandoned data files for additional envelopes that failed
7965 7967 ** to be created may exist on disk.
7966 7968 */
7967 7969
7968 7970 static int q_qgrp_compare __P((const void *, const void *));
7969 7971 static int e_filesys_compare __P((const void *, const void *));
7970 7972
7971 7973 static int
7972 7974 q_qgrp_compare(p1, p2)
7973 7975 const void *p1;
7974 7976 const void *p2;
7975 7977 {
7976 7978 ADDRESS **pq1 = (ADDRESS **) p1;
7977 7979 ADDRESS **pq2 = (ADDRESS **) p2;
7978 7980
7979 7981 return (*pq1)->q_qgrp - (*pq2)->q_qgrp;
7980 7982 }
7981 7983
7982 7984 static int
7983 7985 e_filesys_compare(p1, p2)
7984 7986 const void *p1;
7985 7987 const void *p2;
7986 7988 {
7987 7989 ENVELOPE **pe1 = (ENVELOPE **) p1;
7988 7990 ENVELOPE **pe2 = (ENVELOPE **) p2;
7989 7991 int fs1, fs2;
7990 7992
7991 7993 fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx;
7992 7994 fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx;
7993 7995 if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2))
7994 7996 return -1;
7995 7997 if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2))
7996 7998 return 1;
7997 7999 return 0;
7998 8000 }
7999 8001
8000 8002 static int split_across_queue_groups __P((ENVELOPE *));
8001 8003 static int
8002 8004 split_across_queue_groups(e)
8003 8005 ENVELOPE *e;
8004 8006 {
8005 8007 int naddrs, nsplits, i;
8006 8008 bool changed;
8007 8009 char **pvp;
8008 8010 ADDRESS *q, **addrs;
8009 8011 ENVELOPE *ee, *es;
8010 8012 ENVELOPE *splits[MAXQUEUEGROUPS];
8011 8013 char pvpbuf[PSBUFSIZE];
8012 8014
8013 8015 SM_REQUIRE(ISVALIDQGRP(e->e_qgrp));
8014 8016
8015 8017 /* Count addresses and assign queue groups. */
8016 8018 naddrs = 0;
8017 8019 changed = false;
8018 8020 for (q = e->e_sendqueue; q != NULL; q = q->q_next)
8019 8021 {
8020 8022 if (QS_IS_DEAD(q->q_state))
8021 8023 continue;
8022 8024 ++naddrs;
8023 8025
8024 8026 /* bad addresses and those already sent stay put */
8025 8027 if (QS_IS_BADADDR(q->q_state) ||
8026 8028 QS_IS_SENT(q->q_state))
8027 8029 q->q_qgrp = e->e_qgrp;
8028 8030 else if (!ISVALIDQGRP(q->q_qgrp))
8029 8031 {
8030 8032 /* call ruleset which should return a queue group */
8031 8033 i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp,
8032 8034 pvpbuf, sizeof(pvpbuf));
8033 8035 if (i == EX_OK &&
8034 8036 pvp != NULL && pvp[0] != NULL &&
8035 8037 (pvp[0][0] & 0377) == CANONNET &&
8036 8038 pvp[1] != NULL && pvp[1][0] != '\0')
8037 8039 {
8038 8040 i = name2qid(pvp[1]);
8039 8041 if (ISVALIDQGRP(i))
8040 8042 {
8041 8043 q->q_qgrp = i;
8042 8044 changed = true;
8043 8045 if (tTd(20, 4))
8044 8046 sm_syslog(LOG_INFO, NOQID,
8045 8047 "queue group name %s -> %d",
8046 8048 pvp[1], i);
8047 8049 continue;
8048 8050 }
8049 8051 else if (LogLevel > 10)
8050 8052 sm_syslog(LOG_INFO, NOQID,
8051 8053 "can't find queue group name %s, selection ignored",
8052 8054 pvp[1]);
8053 8055 }
8054 8056 if (q->q_mailer != NULL &&
8055 8057 ISVALIDQGRP(q->q_mailer->m_qgrp))
8056 8058 {
8057 8059 changed = true;
8058 8060 q->q_qgrp = q->q_mailer->m_qgrp;
8059 8061 }
8060 8062 else if (ISVALIDQGRP(e->e_qgrp))
8061 8063 q->q_qgrp = e->e_qgrp;
8062 8064 else
8063 8065 q->q_qgrp = 0;
8064 8066 }
8065 8067 }
8066 8068
8067 8069 /* only one address? nothing to split. */
8068 8070 if (naddrs <= 1 && !changed)
8069 8071 return SM_SPLIT_NONE;
8070 8072
8071 8073 /* sort the addresses by queue group */
8072 8074 addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *));
8073 8075 for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
8074 8076 {
8075 8077 if (QS_IS_DEAD(q->q_state))
8076 8078 continue;
8077 8079 addrs[i++] = q;
8078 8080 }
8079 8081 qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare);
8080 8082
8081 8083 /* split into multiple envelopes, by queue group */
8082 8084 nsplits = 0;
8083 8085 es = NULL;
8084 8086 e->e_sendqueue = NULL;
8085 8087 for (i = 0; i < naddrs; ++i)
8086 8088 {
8087 8089 if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp)
8088 8090 addrs[i]->q_next = NULL;
8089 8091 else
8090 8092 addrs[i]->q_next = addrs[i + 1];
8091 8093
8092 8094 /* same queue group as original envelope? */
8093 8095 if (addrs[i]->q_qgrp == e->e_qgrp)
8094 8096 {
8095 8097 if (e->e_sendqueue == NULL)
8096 8098 e->e_sendqueue = addrs[i];
8097 8099 continue;
8098 8100 }
8099 8101
8100 8102 /* different queue group than original envelope */
8101 8103 if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp)
8102 8104 {
8103 8105 ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR);
8104 8106 es = ee;
8105 8107 splits[nsplits++] = ee;
8106 8108 }
8107 8109 }
8108 8110
8109 8111 /* no splits? return right now. */
8110 8112 if (nsplits <= 0)
8111 8113 return SM_SPLIT_NONE;
8112 8114
8113 8115 /* assign a queue directory to each additional envelope */
8114 8116 for (i = 0; i < nsplits; ++i)
8115 8117 {
8116 8118 es = splits[i];
8117 8119 #if 0
8118 8120 es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es);
8119 8121 #endif /* 0 */
8120 8122 if (!setnewqueue(es))
8121 8123 goto failure;
8122 8124 }
8123 8125
8124 8126 /* sort the additional envelopes by queue file system */
8125 8127 qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare);
8126 8128
8127 8129 /* create data files for each additional envelope */
8128 8130 if (!dup_df(e, splits[0]))
8129 8131 {
8130 8132 i = 0;
8131 8133 goto failure;
8132 8134 }
8133 8135 for (i = 1; i < nsplits; ++i)
8134 8136 {
8135 8137 /* copy or link to the previous data file */
8136 8138 if (!dup_df(splits[i - 1], splits[i]))
8137 8139 goto failure;
8138 8140 }
8139 8141
8140 8142 /* success: prepend the new envelopes to the e->e_sibling list */
8141 8143 for (i = 0; i < nsplits; ++i)
8142 8144 {
8143 8145 es = splits[i];
8144 8146 es->e_sibling = e->e_sibling;
8145 8147 e->e_sibling = es;
8146 8148 }
8147 8149 return SM_SPLIT_NEW(nsplits);
8148 8150
8149 8151 /* failure: clean up */
8150 8152 failure:
8151 8153 if (i > 0)
8152 8154 {
8153 8155 int j;
8154 8156
8155 8157 for (j = 0; j < i; j++)
8156 8158 (void) unlink(queuename(splits[j], DATAFL_LETTER));
8157 8159 }
8158 8160 e->e_sendqueue = addrs[0];
8159 8161 for (i = 0; i < naddrs - 1; ++i)
8160 8162 addrs[i]->q_next = addrs[i + 1];
8161 8163 addrs[naddrs - 1]->q_next = NULL;
8162 8164 return SM_SPLIT_FAIL;
8163 8165 }
8164 8166
8165 8167 /*
8166 8168 ** SPLIT_WITHIN_QUEUE
8167 8169 **
8168 8170 ** Split an envelope with multiple recipients into several
8169 8171 ** envelopes within the same queue directory, if the number of
8170 8172 ** recipients exceeds the limit for the queue group.
8171 8173 **
8172 8174 ** Parameters:
8173 8175 ** e -- envelope.
8174 8176 **
8175 8177 ** Results:
8176 8178 ** SM_SPLIT_FAIL on failure
8177 8179 ** SM_SPLIT_NONE if no splitting occurred,
8178 8180 ** or 1 + the number of additional envelopes created.
8179 8181 */
8180 8182
8181 8183 #define SPLIT_LOG_LEVEL 8
8182 8184
8183 8185 static int split_within_queue __P((ENVELOPE *));
8184 8186
8185 8187 static int
8186 8188 split_within_queue(e)
8187 8189 ENVELOPE *e;
8188 8190 {
8189 8191 int maxrcpt, nrcpt, ndead, nsplit, i;
8190 8192 int j, l;
8191 8193 char *lsplits;
8192 8194 ADDRESS *q, **addrs;
8193 8195 ENVELOPE *ee, *firstsibling;
8194 8196
8195 8197 if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags))
8196 8198 return SM_SPLIT_NONE;
8197 8199
8198 8200 /* don't bother if there is no recipient limit */
8199 8201 maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt;
8200 8202 if (maxrcpt <= 0)
8201 8203 return SM_SPLIT_NONE;
8202 8204
8203 8205 /* count recipients */
8204 8206 nrcpt = 0;
8205 8207 for (q = e->e_sendqueue; q != NULL; q = q->q_next)
8206 8208 {
8207 8209 if (QS_IS_DEAD(q->q_state))
8208 8210 continue;
8209 8211 ++nrcpt;
8210 8212 }
8211 8213 if (nrcpt <= maxrcpt)
8212 8214 return SM_SPLIT_NONE;
8213 8215
8214 8216 /*
8215 8217 ** Preserve the recipient list
8216 8218 ** so that we can restore it in case of error.
8217 8219 ** (But we discard dead addresses.)
8218 8220 */
8219 8221
8220 8222 addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *));
8221 8223 for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
8222 8224 {
8223 8225 if (QS_IS_DEAD(q->q_state))
8224 8226 continue;
8225 8227 addrs[i++] = q;
8226 8228 }
8227 8229
8228 8230 /*
8229 8231 ** Partition the recipient list so that bad and sent addresses
8230 8232 ** come first. These will go with the original envelope, and
8231 8233 ** do not count towards the maxrcpt limit.
8232 8234 ** addrs[] does not contain QS_IS_DEAD() addresses.
8233 8235 */
8234 8236
8235 8237 ndead = 0;
8236 8238 for (i = 0; i < nrcpt; ++i)
8237 8239 {
8238 8240 if (QS_IS_BADADDR(addrs[i]->q_state) ||
8239 8241 QS_IS_SENT(addrs[i]->q_state) ||
8240 8242 QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */
8241 8243 {
8242 8244 if (i > ndead)
8243 8245 {
8244 8246 ADDRESS *tmp = addrs[i];
8245 8247
8246 8248 addrs[i] = addrs[ndead];
8247 8249 addrs[ndead] = tmp;
8248 8250 }
8249 8251 ++ndead;
8250 8252 }
8251 8253 }
8252 8254
8253 8255 /* Check if no splitting required. */
8254 8256 if (nrcpt - ndead <= maxrcpt)
8255 8257 return SM_SPLIT_NONE;
8256 8258
8257 8259 /* fix links */
8258 8260 for (i = 0; i < nrcpt - 1; ++i)
8259 8261 addrs[i]->q_next = addrs[i + 1];
8260 8262 addrs[nrcpt - 1]->q_next = NULL;
8261 8263 e->e_sendqueue = addrs[0];
8262 8264
8263 8265 /* prepare buffer for logging */
8264 8266 if (LogLevel > SPLIT_LOG_LEVEL)
8265 8267 {
8266 8268 l = MAXLINE;
8267 8269 lsplits = sm_malloc(l);
8268 8270 if (lsplits != NULL)
8269 8271 *lsplits = '\0';
8270 8272 j = 0;
8271 8273 }
8272 8274 else
8273 8275 {
8274 8276 /* get rid of stupid compiler warnings */
8275 8277 lsplits = NULL;
8276 8278 j = l = 0;
8277 8279 }
8278 8280
8279 8281 /* split the envelope */
8280 8282 firstsibling = e->e_sibling;
8281 8283 i = maxrcpt + ndead;
8282 8284 nsplit = 0;
8283 8285 for (;;)
8284 8286 {
8285 8287 addrs[i - 1]->q_next = NULL;
8286 8288 ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir);
8287 8289 if (!dup_df(e, ee))
8288 8290 {
8289 8291
8290 8292 ee = firstsibling;
8291 8293 while (ee != NULL)
8292 8294 {
8293 8295 (void) unlink(queuename(ee, DATAFL_LETTER));
8294 8296 ee = ee->e_sibling;
8295 8297 }
8296 8298
8297 8299 /* Error. Restore e's sibling & recipient lists. */
8298 8300 e->e_sibling = firstsibling;
8299 8301 for (i = 0; i < nrcpt - 1; ++i)
8300 8302 addrs[i]->q_next = addrs[i + 1];
8301 8303 if (lsplits != NULL)
8302 8304 sm_free(lsplits);
8303 8305 return SM_SPLIT_FAIL;
8304 8306 }
8305 8307
8306 8308 /* prepend the new envelope to e->e_sibling */
8307 8309 ee->e_sibling = e->e_sibling;
8308 8310 e->e_sibling = ee;
8309 8311 ++nsplit;
8310 8312 if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
8311 8313 {
8312 8314 if (j >= l - strlen(ee->e_id) - 3)
8313 8315 {
8314 8316 char *p;
8315 8317
8316 8318 l += MAXLINE;
8317 8319 p = sm_realloc(lsplits, l);
8318 8320 if (p == NULL)
8319 8321 {
8320 8322 /* let's try to get this done */
8321 8323 sm_free(lsplits);
8322 8324 lsplits = NULL;
8323 8325 }
8324 8326 else
8325 8327 lsplits = p;
8326 8328 }
8327 8329 if (lsplits != NULL)
8328 8330 {
8329 8331 if (j == 0)
8330 8332 j += sm_strlcat(lsplits + j,
8331 8333 ee->e_id,
8332 8334 l - j);
8333 8335 else
8334 8336 j += sm_strlcat2(lsplits + j,
8335 8337 "; ",
8336 8338 ee->e_id,
8337 8339 l - j);
8338 8340 SM_ASSERT(j < l);
8339 8341 }
8340 8342 }
8341 8343 if (nrcpt - i <= maxrcpt)
8342 8344 break;
8343 8345 i += maxrcpt;
8344 8346 }
8345 8347 if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
8346 8348 {
8347 8349 if (nsplit > 0)
8348 8350 {
8349 8351 sm_syslog(LOG_NOTICE, e->e_id,
8350 8352 "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s",
8351 8353 maxrcpt, nrcpt - ndead, nsplit,
8352 8354 nsplit > 1 ? "s" : "", lsplits);
8353 8355 }
8354 8356 sm_free(lsplits);
8355 8357 }
8356 8358 return SM_SPLIT_NEW(nsplit);
8357 8359 }
8358 8360 /*
8359 8361 ** SPLIT_BY_RECIPIENT
8360 8362 **
8361 8363 ** Split an envelope with multiple recipients into multiple
8362 8364 ** envelopes as required by the sendmail configuration.
8363 8365 **
8364 8366 ** Parameters:
8365 8367 ** e -- envelope.
8366 8368 **
8367 8369 ** Results:
8368 8370 ** Returns true on success, false on failure.
8369 8371 **
8370 8372 ** Side Effects:
8371 8373 ** see split_across_queue_groups(), split_within_queue(e)
8372 8374 */
8373 8375
8374 8376 bool
8375 8377 split_by_recipient(e)
8376 8378 ENVELOPE *e;
8377 8379 {
8378 8380 int split, n, i, j, l;
8379 8381 char *lsplits;
8380 8382 ENVELOPE *ee, *next, *firstsibling;
8381 8383
8382 8384 if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) ||
8383 8385 bitset(EF_SPLIT, e->e_flags))
8384 8386 return true;
8385 8387 n = split_across_queue_groups(e);
8386 8388 if (n == SM_SPLIT_FAIL)
8387 8389 return false;
8388 8390 firstsibling = ee = e->e_sibling;
8389 8391 if (n > 1 && LogLevel > SPLIT_LOG_LEVEL)
8390 8392 {
8391 8393 l = MAXLINE;
8392 8394 lsplits = sm_malloc(l);
8393 8395 if (lsplits != NULL)
8394 8396 *lsplits = '\0';
8395 8397 j = 0;
8396 8398 }
8397 8399 else
8398 8400 {
8399 8401 /* get rid of stupid compiler warnings */
8400 8402 lsplits = NULL;
8401 8403 j = l = 0;
8402 8404 }
8403 8405 for (i = 1; i < n; ++i)
8404 8406 {
8405 8407 next = ee->e_sibling;
8406 8408 if (split_within_queue(ee) == SM_SPLIT_FAIL)
8407 8409 {
8408 8410 e->e_sibling = firstsibling;
8409 8411 return false;
8410 8412 }
8411 8413 ee->e_flags |= EF_SPLIT;
8412 8414 if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
8413 8415 {
8414 8416 if (j >= l - strlen(ee->e_id) - 3)
8415 8417 {
8416 8418 char *p;
8417 8419
8418 8420 l += MAXLINE;
8419 8421 p = sm_realloc(lsplits, l);
8420 8422 if (p == NULL)
8421 8423 {
8422 8424 /* let's try to get this done */
8423 8425 sm_free(lsplits);
8424 8426 lsplits = NULL;
8425 8427 }
8426 8428 else
8427 8429 lsplits = p;
8428 8430 }
8429 8431 if (lsplits != NULL)
8430 8432 {
8431 8433 if (j == 0)
8432 8434 j += sm_strlcat(lsplits + j,
8433 8435 ee->e_id, l - j);
8434 8436 else
8435 8437 j += sm_strlcat2(lsplits + j, "; ",
8436 8438 ee->e_id, l - j);
8437 8439 SM_ASSERT(j < l);
8438 8440 }
8439 8441 }
8440 8442 ee = next;
8441 8443 }
8442 8444 if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1)
8443 8445 {
8444 8446 sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s",
8445 8447 n - 1, n > 2 ? "s" : "", lsplits);
8446 8448 sm_free(lsplits);
8447 8449 }
8448 8450 split = split_within_queue(e) != SM_SPLIT_FAIL;
8449 8451 if (split)
8450 8452 e->e_flags |= EF_SPLIT;
8451 8453 return split;
8452 8454 }
8453 8455
8454 8456 /*
8455 8457 ** QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope
8456 8458 **
8457 8459 ** Add/remove quarantine reason and requeue appropriately.
8458 8460 **
8459 8461 ** Parameters:
8460 8462 ** qgrp -- queue group for the item
8461 8463 ** qdir -- queue directory in the given queue group
8462 8464 ** e -- envelope information for the item
8463 8465 ** reason -- quarantine reason, NULL means unquarantine.
8464 8466 **
8465 8467 ** Results:
8466 8468 ** true if item changed, false otherwise
8467 8469 **
8468 8470 ** Side Effects:
8469 8471 ** Changes quarantine tag in queue file and renames it.
8470 8472 */
8471 8473
8472 8474 static bool
8473 8475 quarantine_queue_item(qgrp, qdir, e, reason)
8474 8476 int qgrp;
8475 8477 int qdir;
8476 8478 ENVELOPE *e;
8477 8479 char *reason;
8478 8480 {
8479 8481 bool dirty = false;
8480 8482 bool failing = false;
8481 8483 bool foundq = false;
8482 8484 bool finished = false;
8483 8485 int fd;
8484 8486 int flags;
8485 8487 int oldtype;
8486 8488 int newtype;
8487 8489 int save_errno;
8488 8490 MODE_T oldumask = 0;
8489 8491 SM_FILE_T *oldqfp, *tempqfp;
8490 8492 char *bp;
8491 8493 int bufsize;
8492 8494 char oldqf[MAXPATHLEN];
8493 8495 char tempqf[MAXPATHLEN];
8494 8496 char newqf[MAXPATHLEN];
8495 8497 char buf[MAXLINE];
8496 8498
8497 8499 oldtype = queue_letter(e, ANYQFL_LETTER);
8498 8500 (void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof(oldqf));
8499 8501 (void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof(tempqf));
8500 8502
8501 8503 /*
8502 8504 ** Instead of duplicating all the open
8503 8505 ** and lock code here, tell readqf() to
8504 8506 ** do that work and return the open
8505 8507 ** file pointer in e_lockfp. Note that
8506 8508 ** we must release the locks properly when
8507 8509 ** we are done.
8508 8510 */
8509 8511
8510 8512 if (!readqf(e, true))
8511 8513 {
8512 8514 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
8513 8515 "Skipping %s\n", qid_printname(e));
8514 8516 return false;
8515 8517 }
8516 8518 oldqfp = e->e_lockfp;
8517 8519
8518 8520 /* open the new queue file */
8519 8521 flags = O_CREAT|O_WRONLY|O_EXCL;
8520 8522 if (bitset(S_IWGRP, QueueFileMode))
8521 8523 oldumask = umask(002);
8522 8524 fd = open(tempqf, flags, QueueFileMode);
8523 8525 if (bitset(S_IWGRP, QueueFileMode))
8524 8526 (void) umask(oldumask);
8525 8527 RELEASE_QUEUE;
8526 8528
8527 8529 if (fd < 0)
8528 8530 {
8529 8531 save_errno = errno;
8530 8532 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
8531 8533 "Skipping %s: Could not open %s: %s\n",
8532 8534 qid_printname(e), tempqf,
8533 8535 sm_errstring(save_errno));
8534 8536 (void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
8535 8537 return false;
8536 8538 }
8537 8539 if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB))
8538 8540 {
8539 8541 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
8540 8542 "Skipping %s: Could not lock %s\n",
8541 8543 qid_printname(e), tempqf);
8542 8544 (void) close(fd);
8543 8545 (void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
8544 8546 return false;
8545 8547 }
8546 8548
8547 8549 tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd,
8548 8550 SM_IO_WRONLY_B, NULL);
8549 8551 if (tempqfp == NULL)
8550 8552 {
8551 8553 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
8552 8554 "Skipping %s: Could not lock %s\n",
8553 8555 qid_printname(e), tempqf);
8554 8556 (void) close(fd);
8555 8557 (void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
8556 8558 return false;
8557 8559 }
8558 8560
8559 8561 /* Copy the data over, changing the quarantine reason */
8560 8562 while (bufsize = sizeof(buf),
8561 8563 (bp = fgetfolded(buf, &bufsize, oldqfp)) != NULL)
8562 8564 {
8563 8565 if (tTd(40, 4))
8564 8566 sm_dprintf("+++++ %s\n", bp);
8565 8567 switch (bp[0])
8566 8568 {
8567 8569 case 'q': /* quarantine reason */
8568 8570 foundq = true;
8569 8571 if (reason == NULL)
8570 8572 {
8571 8573 if (Verbose)
8572 8574 {
8573 8575 (void) sm_io_fprintf(smioout,
8574 8576 SM_TIME_DEFAULT,
8575 8577 "%s: Removed quarantine of \"%s\"\n",
8576 8578 e->e_id, &bp[1]);
8577 8579 }
8578 8580 sm_syslog(LOG_INFO, e->e_id, "unquarantine");
8579 8581 dirty = true;
8580 8582 }
8581 8583 else if (strcmp(reason, &bp[1]) == 0)
8582 8584 {
8583 8585 if (Verbose)
8584 8586 {
8585 8587 (void) sm_io_fprintf(smioout,
8586 8588 SM_TIME_DEFAULT,
8587 8589 "%s: Already quarantined with \"%s\"\n",
8588 8590 e->e_id, reason);
8589 8591 }
8590 8592 (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
8591 8593 "q%s\n", reason);
8592 8594 }
8593 8595 else
8594 8596 {
8595 8597 if (Verbose)
8596 8598 {
8597 8599 (void) sm_io_fprintf(smioout,
8598 8600 SM_TIME_DEFAULT,
8599 8601 "%s: Quarantine changed from \"%s\" to \"%s\"\n",
8600 8602 e->e_id, &bp[1],
8601 8603 reason);
8602 8604 }
8603 8605 (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
8604 8606 "q%s\n", reason);
8605 8607 sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
8606 8608 reason);
8607 8609 dirty = true;
8608 8610 }
8609 8611 break;
8610 8612
8611 8613 case 'S':
8612 8614 /*
8613 8615 ** If we are quarantining an unquarantined item,
8614 8616 ** need to put in a new 'q' line before it's
8615 8617 ** too late.
8616 8618 */
8617 8619
8618 8620 if (!foundq && reason != NULL)
8619 8621 {
8620 8622 if (Verbose)
8621 8623 {
8622 8624 (void) sm_io_fprintf(smioout,
8623 8625 SM_TIME_DEFAULT,
8624 8626 "%s: Quarantined with \"%s\"\n",
8625 8627 e->e_id, reason);
8626 8628 }
8627 8629 (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
8628 8630 "q%s\n", reason);
8629 8631 sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
8630 8632 reason);
8631 8633 foundq = true;
8632 8634 dirty = true;
8633 8635 }
8634 8636
8635 8637 /* Copy the line to the new file */
8636 8638 (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
8637 8639 "%s\n", bp);
8638 8640 break;
8639 8641
8640 8642 case '.':
8641 8643 finished = true;
8642 8644 /* FALLTHROUGH */
8643 8645
8644 8646 default:
8645 8647 /* Copy the line to the new file */
8646 8648 (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
8647 8649 "%s\n", bp);
8648 8650 break;
8649 8651 }
8650 8652 if (bp != buf)
8651 8653 sm_free(bp);
8652 8654 }
8653 8655
8654 8656 /* Make sure we read the whole old file */
8655 8657 errno = sm_io_error(tempqfp);
8656 8658 if (errno != 0 && errno != SM_IO_EOF)
8657 8659 {
8658 8660 save_errno = errno;
8659 8661 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
8660 8662 "Skipping %s: Error reading %s: %s\n",
8661 8663 qid_printname(e), oldqf,
8662 8664 sm_errstring(save_errno));
8663 8665 failing = true;
8664 8666 }
8665 8667
8666 8668 if (!failing && !finished)
8667 8669 {
8668 8670 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
8669 8671 "Skipping %s: Incomplete file: %s\n",
8670 8672 qid_printname(e), oldqf);
8671 8673 failing = true;
8672 8674 }
8673 8675
8674 8676 /* Check if we actually changed anything or we can just bail now */
8675 8677 if (!dirty)
8676 8678 {
8677 8679 /* pretend we failed, even though we technically didn't */
8678 8680 failing = true;
8679 8681 }
8680 8682
8681 8683 /* Make sure we wrote things out safely */
8682 8684 if (!failing &&
8683 8685 (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 ||
8684 8686 ((SuperSafe == SAFE_REALLY ||
8685 8687 SuperSafe == SAFE_REALLY_POSTMILTER ||
8686 8688 SuperSafe == SAFE_INTERACTIVE) &&
8687 8689 fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) ||
8688 8690 ((errno = sm_io_error(tempqfp)) != 0)))
8689 8691 {
8690 8692 save_errno = errno;
8691 8693 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
8692 8694 "Skipping %s: Error writing %s: %s\n",
8693 8695 qid_printname(e), tempqf,
8694 8696 sm_errstring(save_errno));
8695 8697 failing = true;
8696 8698 }
8697 8699
8698 8700
8699 8701 /* Figure out the new filename */
8700 8702 newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER);
8701 8703 if (oldtype == newtype)
8702 8704 {
8703 8705 /* going to rename tempqf to oldqf */
8704 8706 (void) sm_strlcpy(newqf, oldqf, sizeof(newqf));
8705 8707 }
8706 8708 else
8707 8709 {
8708 8710 /* going to rename tempqf to new name based on newtype */
8709 8711 (void) sm_strlcpy(newqf, queuename(e, newtype), sizeof(newqf));
8710 8712 }
8711 8713
8712 8714 save_errno = 0;
8713 8715
8714 8716 /* rename tempqf to newqf */
8715 8717 if (!failing &&
8716 8718 rename(tempqf, newqf) < 0)
8717 8719 save_errno = (errno == 0) ? EINVAL : errno;
8718 8720
8719 8721 /* Check rename() success */
8720 8722 if (!failing && save_errno != 0)
8721 8723 {
8722 8724 sm_syslog(LOG_DEBUG, e->e_id,
8723 8725 "quarantine_queue_item: rename(%s, %s): %s",
8724 8726 tempqf, newqf, sm_errstring(save_errno));
8725 8727
8726 8728 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
8727 8729 "Error renaming %s to %s: %s\n",
8728 8730 tempqf, newqf,
8729 8731 sm_errstring(save_errno));
8730 8732 if (oldtype == newtype)
8731 8733 {
8732 8734 /*
8733 8735 ** Bail here since we don't know the state of
8734 8736 ** the filesystem and may need to keep tempqf
8735 8737 ** for the user to rescue us.
8736 8738 */
8737 8739
8738 8740 RELEASE_QUEUE;
8739 8741 errno = save_errno;
8740 8742 syserr("!452 Error renaming control file %s", tempqf);
8741 8743 /* NOTREACHED */
8742 8744 }
8743 8745 else
8744 8746 {
8745 8747 /* remove new file (if rename() half completed) */
8746 8748 if (xunlink(newqf) < 0)
8747 8749 {
8748 8750 save_errno = errno;
8749 8751 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
8750 8752 "Error removing %s: %s\n",
8751 8753 newqf,
8752 8754 sm_errstring(save_errno));
8753 8755 }
8754 8756
8755 8757 /* tempqf removed below */
8756 8758 failing = true;
8757 8759 }
8758 8760
8759 8761 }
8760 8762
8761 8763 /* If changing file types, need to remove old type */
8762 8764 if (!failing && oldtype != newtype)
8763 8765 {
8764 8766 if (xunlink(oldqf) < 0)
8765 8767 {
8766 8768 save_errno = errno;
8767 8769 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
8768 8770 "Error removing %s: %s\n",
8769 8771 oldqf, sm_errstring(save_errno));
8770 8772 }
8771 8773 }
8772 8774
8773 8775 /* see if anything above failed */
8774 8776 if (failing)
8775 8777 {
8776 8778 /* Something failed: remove new file, old file still there */
8777 8779 (void) xunlink(tempqf);
8778 8780 }
8779 8781
8780 8782 /*
8781 8783 ** fsync() after file operations to make sure metadata is
8782 8784 ** written to disk on filesystems in which renames are
8783 8785 ** not guaranteed. It's ok if they fail, mail won't be lost.
8784 8786 */
8785 8787
8786 8788 if (SuperSafe != SAFE_NO)
8787 8789 {
8788 8790 /* for soft-updates */
8789 8791 (void) fsync(sm_io_getinfo(tempqfp,
8790 8792 SM_IO_WHAT_FD, NULL));
8791 8793
8792 8794 if (!failing)
8793 8795 {
8794 8796 /* for soft-updates */
8795 8797 (void) fsync(sm_io_getinfo(oldqfp,
8796 8798 SM_IO_WHAT_FD, NULL));
8797 8799 }
8798 8800
8799 8801 /* for other odd filesystems */
8800 8802 SYNC_DIR(tempqf, false);
8801 8803 }
8802 8804
8803 8805 /* Close up shop */
8804 8806 RELEASE_QUEUE;
8805 8807 if (tempqfp != NULL)
8806 8808 (void) sm_io_close(tempqfp, SM_TIME_DEFAULT);
8807 8809 if (oldqfp != NULL)
8808 8810 (void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
8809 8811
8810 8812 /* All went well */
8811 8813 return !failing;
8812 8814 }
8813 8815
8814 8816 /*
8815 8817 ** QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue
8816 8818 **
8817 8819 ** Read all matching queue items, add/remove quarantine
8818 8820 ** reason, and requeue appropriately.
8819 8821 **
8820 8822 ** Parameters:
8821 8823 ** reason -- quarantine reason, "." means unquarantine.
8822 8824 ** qgrplimit -- limit to single queue group unless NOQGRP
8823 8825 **
8824 8826 ** Results:
8825 8827 ** none.
8826 8828 **
8827 8829 ** Side Effects:
8828 8830 ** Lots of changes to the queue.
8829 8831 */
8830 8832
8831 8833 void
8832 8834 quarantine_queue(reason, qgrplimit)
8833 8835 char *reason;
8834 8836 int qgrplimit;
8835 8837 {
8836 8838 int changed = 0;
8837 8839 int qgrp;
8838 8840
8839 8841 /* Convert internal representation of unquarantine */
8840 8842 if (reason != NULL && reason[0] == '.' && reason[1] == '\0')
8841 8843 reason = NULL;
8842 8844
8843 8845 if (reason != NULL)
8844 8846 {
8845 8847 /* clean it */
8846 8848 reason = newstr(denlstring(reason, true, true));
8847 8849 }
8848 8850
8849 8851 for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
8850 8852 {
8851 8853 int qdir;
8852 8854
8853 8855 if (qgrplimit != NOQGRP && qgrplimit != qgrp)
8854 8856 continue;
8855 8857
8856 8858 for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++)
8857 8859 {
8858 8860 int i;
8859 8861 int nrequests;
8860 8862
8861 8863 if (StopRequest)
8862 8864 stop_sendmail();
8863 8865
8864 8866 nrequests = gatherq(qgrp, qdir, true, NULL, NULL, NULL);
8865 8867
8866 8868 /* first see if there is anything */
8867 8869 if (nrequests <= 0)
8868 8870 {
8869 8871 if (Verbose)
8870 8872 {
8871 8873 (void) sm_io_fprintf(smioout,
8872 8874 SM_TIME_DEFAULT, "%s: no matches\n",
8873 8875 qid_printqueue(qgrp, qdir));
8874 8876 }
8875 8877 continue;
8876 8878 }
8877 8879
8878 8880 if (Verbose)
8879 8881 {
8880 8882 (void) sm_io_fprintf(smioout,
8881 8883 SM_TIME_DEFAULT, "Processing %s:\n",
8882 8884 qid_printqueue(qgrp, qdir));
8883 8885 }
8884 8886
8885 8887 for (i = 0; i < WorkListCount; i++)
8886 8888 {
8887 8889 ENVELOPE e;
8888 8890
8889 8891 if (StopRequest)
8890 8892 stop_sendmail();
8891 8893
8892 8894 /* setup envelope */
8893 8895 clearenvelope(&e, true, sm_rpool_new_x(NULL));
8894 8896 e.e_id = WorkList[i].w_name + 2;
8895 8897 e.e_qgrp = qgrp;
8896 8898 e.e_qdir = qdir;
8897 8899
8898 8900 if (tTd(70, 101))
8899 8901 {
8900 8902 sm_io_fprintf(smioout, SM_TIME_DEFAULT,
8901 8903 "Would do %s\n", e.e_id);
8902 8904 changed++;
8903 8905 }
8904 8906 else if (quarantine_queue_item(qgrp, qdir,
8905 8907 &e, reason))
8906 8908 changed++;
8907 8909
8908 8910 /* clean up */
8909 8911 sm_rpool_free(e.e_rpool);
8910 8912 e.e_rpool = NULL;
8911 8913 }
8912 8914 if (WorkList != NULL)
8913 8915 sm_free(WorkList); /* XXX */
8914 8916 WorkList = NULL;
8915 8917 WorkListSize = 0;
8916 8918 WorkListCount = 0;
8917 8919 }
8918 8920 }
8919 8921 if (Verbose)
8920 8922 {
8921 8923 if (changed == 0)
8922 8924 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
8923 8925 "No changes\n");
8924 8926 else
8925 8927 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
8926 8928 "%d change%s\n",
8927 8929 changed,
8928 8930 changed == 1 ? "" : "s");
8929 8931 }
8930 8932 }
↓ open down ↓ |
8902 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX