xref: /freebsd/contrib/sendmail/src/sched.c (revision 1719886f6d08408b834d270c59ffcfd821c8f63a)
1 /*
2  * Copyright (c) 2021 Proofpoint, Inc. and its suppliers.
3  *	All rights reserved.
4  *
5  * By using this file, you agree to the terms and conditions set
6  * forth in the LICENSE file which can be found at the top level of
7  * the sendmail distribution.
8  *
9  */
10 
11 #include <sendmail.h>
12 
13 #if _FFR_DMTRIGGER
14 #include <sm/sendmail.h>
15 #include <sm/notify.h>
16 
17 static ENVELOPE	QmEnvelope;
18 
19 /*
20 **  Macro for fork():
21 **  FORK_P1(): parent
22 **  FORK_C1(): child
23 **  Note: these are not "universal", e.g.,
24 **  proc_list_add() might be used in parent or child.
25 **  maybe check pname != NULL to invoke proc_list_add()?
26 */
27 
28 #define FORK_P1(emsg, pname, ptype)	\
29 	do {	\
30 		(void) sm_blocksignal(SIGCHLD);	\
31 		(void) sm_signal(SIGCHLD, reapchild);	\
32 		\
33 		pid = dofork();	\
34 		if (pid == -1)	\
35 		{	\
36 			const char *msg = emsg;	\
37 			const char *err = sm_errstring(errno);	\
38 		\
39 			if (LogLevel > 8)	\
40 				sm_syslog(LOG_INFO, NOQID, "%s: %s",	\
41 					  msg, err);	\
42 			(void) sm_releasesignal(SIGCHLD);	\
43 			return false;	\
44 		}	\
45 		if (pid != 0)	\
46 		{	\
47 			proc_list_add(pid, pname, ptype, 0, -1, NULL); \
48 			/* parent -- pick up intermediate zombie */	\
49 			(void) sm_releasesignal(SIGCHLD);	\
50 			return true;	\
51 		}	\
52 	} while (0)
53 
54 #define FORK_C1()	\
55 	do {	\
56 		/* child -- clean up signals */	\
57 		\
58 		/* Reset global flags */	\
59 		RestartRequest = NULL;	\
60 		RestartWorkGroup = false;	\
61 		ShutdownRequest = NULL;	\
62 		PendingSignal = 0;	\
63 		CurrentPid = getpid();	\
64 		close_sendmail_pid();	\
65 		\
66 		/*	\
67 		**  Initialize exception stack and default exception	\
68 		**  handler for child process.	\
69 		*/	\
70 		\
71 		sm_exc_newthread(fatal_error);	\
72 		clrcontrol();	\
73 		proc_list_clear();	\
74 		\
75 		(void) sm_releasesignal(SIGCHLD);	\
76 		(void) sm_signal(SIGCHLD, SIG_DFL);	\
77 		(void) sm_signal(SIGHUP, SIG_DFL);	\
78 		(void) sm_signal(SIGTERM, intsig);	\
79 		\
80 		/* drop privileges */	\
81 		if (geteuid() == (uid_t) 0)	\
82 			(void) drop_privileges(false);	\
83 		disconnect(1, NULL);	\
84 		QuickAbort = false;	\
85 		\
86 	} while (0)
87 
88 /*
89 **  QM -- queue "manager"
90 **
91 **	Parameters:
92 **		none.
93 **
94 **	Returns:
95 **		false on error
96 **
97 **	Side Effects:
98 **		fork()s and runs as process to deliver queue entries
99 */
100 
101 bool
102 qm()
103 {
104 	int r;
105 	pid_t pid;
106 	long tmo;
107 
108 	sm_syslog(LOG_DEBUG, NOQID, "queue manager: start");
109 
110 	FORK_P1("Queue manager -- fork() failed", "QM", PROC_QM);
111 	FORK_C1();
112 
113 	r = sm_notify_start(true, 0);
114 	if (r != 0)
115 		syserr("sm_notify_start() failed=%d", r);
116 
117 	/*
118 	**  Initially wait indefinitely, then only wait
119 	**  until something needs to get done (not yet implemented).
120 	*/
121 
122 	tmo = -1;
123 	while (true)
124 	{
125 		char buf[64];
126 		ENVELOPE *e;
127 		SM_RPOOL_T *rpool;
128 
129 /*
130 **  TODO: This should try to receive multiple ids:
131 **  after it got one, check for more with a very short timeout
132 **  and collect them in a list.
133 **  but them some other code should be used to run all of them.
134 */
135 
136 		sm_syslog(LOG_DEBUG, NOQID, "queue manager: rcv=start");
137 		r = sm_notify_rcv(buf, sizeof(buf), tmo);
138 		if (-ETIMEDOUT == r)
139 		{
140 			sm_syslog(LOG_DEBUG, NOQID, "queue manager: rcv=timed_out");
141 			continue;
142 		}
143 		if (r < 0)
144 		{
145 			sm_syslog(LOG_DEBUG, NOQID, "queue manager: rcv=%d", r);
146 			goto end;
147 		}
148 		if (r > 0 && r < sizeof(buf))
149 			buf[r] = '\0';
150 		buf[sizeof(buf) - 1] = '\0';
151 		sm_syslog(LOG_DEBUG, NOQID, "queue manager: got=%s", buf);
152 		CurEnv = &QmEnvelope;
153 		rpool = sm_rpool_new_x(NULL);
154 		e = newenvelope(&QmEnvelope, CurEnv, rpool);
155 		e->e_flags = BlankEnvelope.e_flags;
156 		e->e_parent = NULL;
157 		r = sm_io_sscanf(buf, "N:%d:%d:%s", &e->e_qgrp, &e->e_qdir, e->e_id);
158 		if (r != 3)
159 		{
160 			sm_syslog(LOG_DEBUG, NOQID, "queue manager: buf=%s, scan=%d", buf, r);
161 			goto end;
162 		}
163 		dowork(e->e_qgrp, e->e_qdir, e->e_id, true, false, e);
164 	}
165 
166   end:
167 	sm_syslog(LOG_DEBUG, NOQID, "queue manager: stop");
168 	finis(false, false, EX_OK);
169 	/* NOTREACHED */
170 	return false;
171 }
172 #endif /* _FFR_DMTRIGGER */
173