+daemon_inetd_wtimeout(time_t last_connection_time)
+{
+time_t resignal_interval = inetd_wait_timeout;
+
+if (last_connection_time == (time_t)0)
+ {
+ DEBUG(D_any)
+ debug_printf("inetd wait timeout expired, but still not seen first message, ignoring\n");
+ }
+else
+ {
+ time_t now = time(NULL);
+ if (now == (time_t)-1)
+ {
+ DEBUG(D_any) debug_printf("failed to get time: %s\n", strerror(errno));
+ }
+ else if ((now - last_connection_time) >= inetd_wait_timeout)
+ {
+ DEBUG(D_any)
+ debug_printf("inetd wait timeout %d expired, ending daemon\n",
+ inetd_wait_timeout);
+ log_write(0, LOG_MAIN, "exim %s daemon terminating, inetd wait timeout reached.\n",
+ version_string);
+ daemon_die(); /* Does not return */
+ }
+ else
+ resignal_interval -= (now - last_connection_time);
+ }
+
+sigalrm_seen = FALSE;
+ALARM(resignal_interval);
+}
+
+
+/* Re-sort the qrunners list, and return the shortest interval.
+That could be negatime.
+The next-tick times should have been updated by any runs initiated,
+though will not be when the global limit on runners was reached.
+
+Unlikely to have many queues, so insertion-sort.
+*/
+
+static int
+next_qrunner_interval(void)
+{
+qrunner * sorted = NULL;
+for (qrunner * q = qrunners, * next; q; q = next)
+ {
+ next = q->next;
+ q->next = NULL;
+ if (sorted)
+ {
+ qrunner ** p = &sorted;
+ for (qrunner * qq; qq = *p; p = &qq->next)
+ if ( q->next_tick < qq->next_tick
+ || q->next_tick == qq->next_tick && q->interval < qq->interval
+ )
+ {
+ *p = q;
+ q->next = qq;
+ goto INSERTED;
+ }
+ *p = q;
+ INSERTED: ;
+ }
+ else
+ sorted = q;
+ }
+qrunners = sorted;
+return qrunners ? qrunners->next_tick - time(NULL) : 0;
+}
+
+/* See if we can do a queue run. If policy limit permit, kick one off.
+If both notification and timer events are present, handle the former
+and leave the timer outstanding.
+
+Return the number of seconds until the next due runner.
+*/
+
+static int
+daemon_qrun(int local_queue_run_max, struct pollfd * fd_polls, int listen_socket_count)
+{
+DEBUG(D_any) debug_printf("%s received\n",
+#ifndef DISABLE_QUEUE_RAMP
+ *queuerun_msgid ? "qrun notification" :
+#endif
+ "SIGALRM");
+
+/* Do a full queue run in a child process, if required, unless we already have
+enough queue runners on the go. If we are not running as root, a re-exec is
+required. In the calling process, restart the alamr timer for the next run. */
+
+if (is_multiple_qrun()) /* we are managing periodic runs */
+ if (local_queue_run_max <= 0 || queue_run_count < local_queue_run_max)
+ {
+ qrunner * q = NULL;
+
+#ifndef DISABLE_QUEUE_RAMP
+ /* If this is a triggered run for a specific message, see if we can start
+ another runner for this queue. */
+
+ if (*queuerun_msgid)
+ {
+ for (qrunner * qq = qrunners; qq; qq = qq->next)
+ if (qq->name == queuerun_msg_qname)
+ {
+ q = qq->run_count < qq->run_max ? qq : NULL;
+ break;
+ }
+ }
+ else
+#endif
+ /* Normal periodic run: in order of run priority, find the first queue
+ for which we can start a runner */
+
+ for (q = qrunners; q; q = q->next)
+ if (q->run_count < q->run_max) break;
+
+ if (q) /* found a queue to run */
+ {
+ pid_t pid;
+
+ /* Bump this queue's next-tick by it's interval */
+
+ if (q->interval)
+ {
+ time_t now = time(NULL);
+ do ; while ((q->next_tick += q->interval) <= now);
+ }
+
+ if ((pid = exim_fork(US"queue-runner")) == 0)
+ {
+ /* Disable debugging if it's required only for the daemon process. We
+ leave the above message, because it ties up with the "child ended"
+ debugging messages. */
+
+ if (f.debug_daemon) debug_selector = 0;
+
+ /* Close any open listening sockets in the child */
+
+ close_daemon_sockets(daemon_notifier_fd,
+ fd_polls, listen_socket_count);
+
+ /* Reset SIGHUP and SIGCHLD in the child in both cases. */
+
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+
+ /* Re-exec if privilege has been given up, unless deliver_drop_
+ privilege is set. Reset SIGALRM before exec(). */
+
+ if (geteuid() != root_uid && !deliver_drop_privilege)
+ {
+ uschar opt[8];
+ uschar *p = opt;
+ uschar *extra[7];
+ int extracount = 1;
+
+ signal(SIGALRM, SIG_DFL);
+ queue_name = US"";
+
+ *p++ = '-';
+ *p++ = 'q';
+ if ( q->queue_2stage
+#ifndef DISABLE_QUEUE_RAMP
+ && !*queuerun_msgid
+#endif
+ ) *p++ = 'q';
+ if (q->queue_run_first_delivery) *p++ = 'i';
+ if (q->queue_run_force) *p++ = 'f';
+ if (q->deliver_force_thaw) *p++ = 'f';
+ if (q->queue_run_local) *p++ = 'l';
+ *p = 0;
+
+ extra[0] = q->name
+ ? string_sprintf("%sG%s", opt, q->name) : opt;
+
+#ifndef DISABLE_QUEUE_RAMP
+ if (*queuerun_msgid)
+ {
+ log_write(0, LOG_MAIN, "notify triggered queue run");
+ extra[extracount++] = queuerun_msgid; /* Trigger only the */
+ extra[extracount++] = queuerun_msgid; /* one message */
+ }
+#endif
+
+ /* If -R or -S were on the original command line, ensure they get
+ passed on. */
+
+ if (deliver_selectstring)
+ {
+ extra[extracount++] = f.deliver_selectstring_regex ? US"-Rr" : US"-R";
+ extra[extracount++] = deliver_selectstring;
+ }
+
+ if (deliver_selectstring_sender)
+ {
+ extra[extracount++] = f.deliver_selectstring_sender_regex
+ ? US"-Sr" : US"-S";
+ extra[extracount++] = deliver_selectstring_sender;
+ }
+
+ /* Overlay this process with a new execution. */
+
+ (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, FALSE, extracount,
+ extra[0], extra[1], extra[2], extra[3], extra[4], extra[5], extra[6]);
+
+ /* Control never returns here. */
+ }
+
+ /* No need to re-exec; SIGALRM remains set to the default handler */
+
+#ifndef DISABLE_QUEUE_RAMP
+ if (*queuerun_msgid)
+ {
+ log_write(0, LOG_MAIN, "notify triggered queue run");
+ f.queue_2stage = FALSE;
+ queue_run(q, queuerun_msgid, queuerun_msgid, FALSE);
+ }
+ else
+#endif
+ queue_run(q, NULL, NULL, FALSE);
+ exim_underbar_exit(EXIT_SUCCESS);
+ }
+
+ if (pid < 0)
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "daemon: fork of queue-runner "
+ "process failed: %s", strerror(errno));
+ log_close_all();
+ }
+ else
+ {
+ for (int i = 0; i < local_queue_run_max; ++i)
+ if (queue_runner_slots[i].pid <= 0)
+ {
+ queue_runner_slots[i].pid = pid;
+ queue_runner_slots[i].queue_name = q->name;
+ q->run_count++;
+ queue_run_count++;
+ break;
+ }
+ DEBUG(D_any) debug_printf("%d queue-runner process%s running\n",
+ queue_run_count, queue_run_count == 1 ? "" : "es");
+ }
+ }
+ }
+
+/* The queue run has been initiated (unless we were already running enough) */
+
+#ifndef DISABLE_QUEUE_RAMP
+if (*queuerun_msgid) /* it was a fast-ramp kick; dealt with */
+ *queuerun_msgid = 0;
+else /* periodic or one-time queue run */
+#endif
+ /* Set up next timer callback. Impose a minimum 1s tick,
+ even when a run was outstanding */
+ {
+ int interval = next_qrunner_interval();
+ if (interval <= 0) interval = 1;
+
+ sigalrm_seen = FALSE;
+ if (qrunners) /* there are still periodic qrunners */
+ {
+ ALARM(interval); /* set up next qrun tick */
+ return interval;
+ }
+ }
+return 0;
+}
+
+
+
+
+static const uschar *
+describe_queue_runners(void)