1*5b133f3fSguenther /* $OpenBSD: isakmpd.c,v 1.109 2023/03/08 04:43:06 guenther Exp $ */
2b7bb0eceSniklas /* $EOM: isakmpd.c,v 1.54 2000/10/05 09:28:22 niklas Exp $ */
32040585eSniklas
42040585eSniklas /*
542af7185Sniklas * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved.
6bdbf6df3Sniklas * Copyright (c) 1999, 2000 Angelos D. Keromytis. All rights reserved.
7867e2181Sho * Copyright (c) 1999, 2000, 2001 H�kan Olsson. All rights reserved.
82040585eSniklas *
92040585eSniklas * Redistribution and use in source and binary forms, with or without
102040585eSniklas * modification, are permitted provided that the following conditions
112040585eSniklas * are met:
122040585eSniklas * 1. Redistributions of source code must retain the above copyright
132040585eSniklas * notice, this list of conditions and the following disclaimer.
142040585eSniklas * 2. Redistributions in binary form must reproduce the above copyright
152040585eSniklas * notice, this list of conditions and the following disclaimer in the
162040585eSniklas * documentation and/or other materials provided with the distribution.
172040585eSniklas *
182040585eSniklas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
192040585eSniklas * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
202040585eSniklas * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
212040585eSniklas * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
222040585eSniklas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
232040585eSniklas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
242040585eSniklas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
252040585eSniklas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
262040585eSniklas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
272040585eSniklas * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
282040585eSniklas */
292040585eSniklas
302040585eSniklas /*
312040585eSniklas * This code was written under funding by Ericsson Radio Systems.
322040585eSniklas */
332040585eSniklas
34dc4b6c27Sniklas #include <errno.h>
35c3c2e2acSniklas #include <sys/types.h>
36c3c2e2acSniklas #include <sys/stat.h>
372040585eSniklas #include <signal.h>
382040585eSniklas #include <stdio.h>
392040585eSniklas #include <stdlib.h>
402040585eSniklas #include <string.h>
4115537254Shshoexer #include <time.h>
42a9c89582Sho #include <netdb.h>
432040585eSniklas #include <unistd.h>
44f41e756cSho #include <fcntl.h>
45426a6f8cSkrw #include <paths.h>
462040585eSniklas
472040585eSniklas #include "app.h"
482040585eSniklas #include "conf.h"
4922e84989Sniklas #include "connection.h"
502040585eSniklas #include "init.h"
515fc2ae46Sniklas #include "libcrypto.h"
522040585eSniklas #include "log.h"
53bfb67d4dScloder #include "message.h"
54da35d433Sho #include "monitor.h"
55bfb67d4dScloder #include "nat_traversal.h"
56c6e4dfa3Sho #include "sa.h"
572040585eSniklas #include "timer.h"
582040585eSniklas #include "transport.h"
592040585eSniklas #include "udp.h"
60287d4ce7Shshoexer #include "udp_encap.h"
612040585eSniklas #include "ui.h"
6276fb8883Sniklas #include "util.h"
634144914bSniklas #include "cert.h"
642040585eSniklas
6528ecb04cSniklas #include "policy.h"
6628ecb04cSniklas
674c8c122bSho static void usage(void);
684c8c122bSho
692040585eSniklas /*
702040585eSniklas * Set if -d is given, currently just for running in the foreground and log
712040585eSniklas * to stderr instead of syslog.
722040585eSniklas */
732040585eSniklas int debug = 0;
742040585eSniklas
759a07a765Shshoexer /* Set when no policy file shall be used. */
7639fc3963Shshoexer int acquire_only = 0;
7739fc3963Shshoexer
78428bd1b5Shshoexer /* Set when SAs shall be deleted on shutdown. */
7985ead044Shshoexer int delete_sas = 1;
80428bd1b5Shshoexer
812040585eSniklas /*
822040585eSniklas * If we receive a SIGHUP signal, this flag gets set to show we need to
832040585eSniklas * reconfigure ASAP.
842040585eSniklas */
853354b35fSho volatile sig_atomic_t sighupped = 0;
862040585eSniklas
872b81057dSniklas /*
882b81057dSniklas * If we receive a USR1 signal, this flag gets set to show we need to dump
892b81057dSniklas * a report over our internal state ASAP. The file to report to is settable
902b81057dSniklas * via the -R parameter.
912b81057dSniklas */
923354b35fSho volatile sig_atomic_t sigusr1ed = 0;
932b81057dSniklas static char *report_file = "/var/run/isakmpd.report";
942b81057dSniklas
95c6e4dfa3Sho /*
96e4d25771Stodd * If we receive a TERM signal, perform a "clean shutdown" of the daemon.
97c6e4dfa3Sho * This includes to send DELETE notifications for all our active SAs.
98a8478dd3Sho * Also on recv of an INT signal (Ctrl-C out of an '-d' session, typically).
99c6e4dfa3Sho */
1003354b35fSho volatile sig_atomic_t sigtermed = 0;
101c6e4dfa3Sho void daemon_shutdown_now(int);
102b8909177Smoritz void set_slave_signals(void);
103426a6f8cSkrw void sanitise_stdfd(void);
104c6e4dfa3Sho
1055fc2ae46Sniklas /* The default path of the PID file. */
106e8a27e95Smoritz char *pid_file = "/var/run/isakmpd.pid";
1075fc2ae46Sniklas
108af729d73Sho /* The path of the IKE packet capture log file. */
109af729d73Sho static char *pcap_file = 0;
110af729d73Sho
1112040585eSniklas static void
usage(void)1124c8c122bSho usage(void)
1132040585eSniklas {
11461336936Sderaadt extern char *__progname;
11561336936Sderaadt
1162040585eSniklas fprintf(stderr,
117428bd1b5Shshoexer "usage: %s [-46adKLnSTv] [-c config-file] [-D class=level] [-f fifo]\n"
118cadf506fSjmc " [-i pid-file] [-l packetlog-file] [-N udpencap-port]\n"
119cadf506fSjmc " [-p listen-port] [-R report-file]\n",
120520d5687Sderaadt __progname);
1212040585eSniklas exit(1);
1222040585eSniklas }
1232040585eSniklas
1242040585eSniklas static void
parse_args(int argc,char * argv[])1252040585eSniklas parse_args(int argc, char *argv[])
1262040585eSniklas {
12751ca15aeSniklas int ch;
12851ca15aeSniklas int cls, level;
129af729d73Sho int do_packetlog = 0;
1302040585eSniklas
1311ee656d4Sderaadt while ((ch = getopt(argc, argv, "46ac:dD:f:i:KnN:p:Ll:R:STv")) != -1) {
1322040585eSniklas switch (ch) {
133e2cc3c2eSho case '4':
134e2cc3c2eSho bind_family |= BIND_FAMILY_INET4;
135e2cc3c2eSho break;
136e2cc3c2eSho
137e2cc3c2eSho case '6':
138e2cc3c2eSho bind_family |= BIND_FAMILY_INET6;
139e2cc3c2eSho break;
140e2cc3c2eSho
14139fc3963Shshoexer case 'a':
1423579491dSderaadt acquire_only = 1;
14339fc3963Shshoexer break;
14439fc3963Shshoexer
1452040585eSniklas case 'c':
1462040585eSniklas conf_path = optarg;
1472040585eSniklas break;
14851ca15aeSniklas
1492040585eSniklas case 'd':
1502040585eSniklas debug++;
1512040585eSniklas break;
15251ca15aeSniklas
1532040585eSniklas case 'D':
154fb9475d6Sderaadt if (sscanf(optarg, "%d=%d", &cls, &level) != 2) {
155fb9475d6Sderaadt if (sscanf(optarg, "A=%d", &level) == 1) {
15650eea14cSho for (cls = 0; cls < LOG_ENDCLASS;
15750eea14cSho cls++)
158b225a241Sniklas log_debug_cmd(cls, level);
159fb9475d6Sderaadt } else
16012f43dabShshoexer log_print("parse_args: -D argument "
16112f43dabShshoexer "unparseable: %s", optarg);
162fb9475d6Sderaadt } else
1632040585eSniklas log_debug_cmd(cls, level);
1642040585eSniklas break;
16551ca15aeSniklas
1662040585eSniklas case 'f':
1672040585eSniklas ui_fifo = optarg;
1682040585eSniklas break;
16951ca15aeSniklas
1705fc2ae46Sniklas case 'i':
1715fc2ae46Sniklas pid_file = optarg;
1725fc2ae46Sniklas break;
1735fc2ae46Sniklas
174b6e0b5cbShshoexer case 'K':
175b6e0b5cbShshoexer ignore_policy++;
176b6e0b5cbShshoexer break;
177b6e0b5cbShshoexer
1782040585eSniklas case 'n':
1792040585eSniklas app_none++;
1802040585eSniklas break;
18151ca15aeSniklas
182287d4ce7Shshoexer case 'N':
183287d4ce7Shshoexer udp_encap_default_port = optarg;
184287d4ce7Shshoexer break;
185287d4ce7Shshoexer
1862040585eSniklas case 'p':
187a86dabf6Sho udp_default_port = optarg;
1882040585eSniklas break;
18951ca15aeSniklas
190af729d73Sho case 'l':
191af729d73Sho pcap_file = optarg;
19236d3c850Shshoexer /* FALLTHROUGH */
193af729d73Sho
194af729d73Sho case 'L':
195af729d73Sho do_packetlog++;
196af729d73Sho break;
197af729d73Sho
1982b81057dSniklas case 'R':
1992b81057dSniklas report_file = optarg;
2002b81057dSniklas break;
20151ca15aeSniklas
202428bd1b5Shshoexer case 'S':
20385ead044Shshoexer delete_sas = 0;
20414f1d06aSmpf ui_daemon_passive = 1;
205428bd1b5Shshoexer break;
206428bd1b5Shshoexer
207bfb67d4dScloder case 'T':
208bfb67d4dScloder disable_nat_t = 1;
209bfb67d4dScloder break;
210bfb67d4dScloder
211ce261536Shshoexer case 'v':
212ce261536Shshoexer verbose_logging = 1;
213ce261536Shshoexer break;
214ce261536Shshoexer
2152040585eSniklas default:
2162040585eSniklas usage();
2172040585eSniklas }
2182040585eSniklas }
2192040585eSniklas argc -= optind;
2202040585eSniklas argv += optind;
221af729d73Sho
222fde55bc4Spyr if (argc > 0)
223fde55bc4Spyr usage();
224fde55bc4Spyr
225af729d73Sho if (do_packetlog && !pcap_file)
226af729d73Sho pcap_file = PCAP_FILE_DEFAULT;
2272040585eSniklas }
2282040585eSniklas
2292040585eSniklas static void
sighup(int sig)2302040585eSniklas sighup(int sig)
2312040585eSniklas {
2322040585eSniklas sighupped = 1;
2332040585eSniklas }
2342040585eSniklas
2352b81057dSniklas /* Report internal state on SIGUSR1. */
2362b81057dSniklas static void
report(void)2372b81057dSniklas report(void)
2382b81057dSniklas {
239605cc57cSho FILE *rfp, *old;
240c3c2e2acSniklas mode_t old_umask;
241c3c2e2acSniklas
242c3c2e2acSniklas old_umask = umask(S_IRWXG | S_IRWXO);
243da35d433Sho rfp = monitor_fopen(report_file, "w");
244c3c2e2acSniklas umask(old_umask);
2452b81057dSniklas
246fb9475d6Sderaadt if (!rfp) {
247b4caba6dShshoexer log_error("report: fopen (\"%s\", \"w\") failed", report_file);
2482b81057dSniklas return;
2492b81057dSniklas }
2502b81057dSniklas /* Divert the log channel to the report file during the report. */
2512b81057dSniklas old = log_current();
252605cc57cSho log_to(rfp);
2532b81057dSniklas ui_report("r");
2542b81057dSniklas log_to(old);
255ea1948caSho fclose(rfp);
2562b81057dSniklas }
2572b81057dSniklas
2582b81057dSniklas static void
sigusr1(int sig)2592b81057dSniklas sigusr1(int sig)
2602b81057dSniklas {
2612b81057dSniklas sigusr1ed = 1;
2622b81057dSniklas }
2632b81057dSniklas
264c6e4dfa3Sho static int
phase2_sa_check(struct sa * sa,void * arg)265c6e4dfa3Sho phase2_sa_check(struct sa *sa, void *arg)
266c6e4dfa3Sho {
267c6e4dfa3Sho return sa->phase == 2;
268c6e4dfa3Sho }
269c6e4dfa3Sho
270723115aaShshoexer static int
phase1_sa_check(struct sa * sa,void * arg)271723115aaShshoexer phase1_sa_check(struct sa *sa, void *arg)
272723115aaShshoexer {
273723115aaShshoexer return sa->phase == 1;
274723115aaShshoexer }
275723115aaShshoexer
276b8909177Smoritz void
set_slave_signals(void)277b8909177Smoritz set_slave_signals(void)
278b8909177Smoritz {
279b8909177Smoritz int n;
280b8909177Smoritz
281b8909177Smoritz for (n = 1; n < _NSIG; n++)
282b8909177Smoritz signal(n, SIG_DFL);
283b8909177Smoritz
284b8909177Smoritz /*
285b8909177Smoritz * Do a clean daemon shutdown on TERM/INT. These signals must be
286b8909177Smoritz * initialized before monitor_init(). INT is only used with '-d'.
287b8909177Smoritz */
288b8909177Smoritz signal(SIGTERM, daemon_shutdown_now);
289b8909177Smoritz if (debug == 1) /* i.e '-dd' will skip this. */
290b8909177Smoritz signal(SIGINT, daemon_shutdown_now);
291b8909177Smoritz
292b8909177Smoritz /* Reinitialize on HUP reception. */
293b8909177Smoritz signal(SIGHUP, sighup);
294b8909177Smoritz
295b8909177Smoritz /* Report state on USR1 reception. */
296b8909177Smoritz signal(SIGUSR1, sigusr1);
297b8909177Smoritz }
298b8909177Smoritz
299c6e4dfa3Sho static void
daemon_shutdown(void)300c6e4dfa3Sho daemon_shutdown(void)
301c6e4dfa3Sho {
302c6e4dfa3Sho /* Perform a (protocol-wise) clean shutdown of the daemon. */
303c6e4dfa3Sho struct sa *sa;
304c6e4dfa3Sho
305fb9475d6Sderaadt if (sigtermed == 1) {
306c6e4dfa3Sho log_print("isakmpd: shutting down...");
307c6e4dfa3Sho
30885ead044Shshoexer if (delete_sas &&
30985ead044Shshoexer strncmp("no", conf_get_str("General", "Delete-SAs"), 2)) {
310723115aaShshoexer /*
311428bd1b5Shshoexer * Delete all active SAs. First IPsec SAs, then
312428bd1b5Shshoexer * ISAKMPD. Each DELETE is another (outgoing) message.
313723115aaShshoexer */
314723115aaShshoexer while ((sa = sa_find(phase2_sa_check, NULL)))
315c6e4dfa3Sho sa_delete(sa, 1);
316723115aaShshoexer
317723115aaShshoexer while ((sa = sa_find(phase1_sa_check, NULL)))
318723115aaShshoexer sa_delete(sa, 1);
319428bd1b5Shshoexer }
320feaa02feShshoexer
321feaa02feShshoexer /* We only want to do this once. */
322867e2181Sho sigtermed++;
323c6e4dfa3Sho }
324fb9475d6Sderaadt if (transport_prio_sendqs_empty()) {
325867e2181Sho /*
326867e2181Sho * When the prioritized transport sendq:s are empty, i.e all
327867e2181Sho * the DELETE notifications have been sent, we can shutdown.
328867e2181Sho */
329867e2181Sho
330c6e4dfa3Sho log_packet_stop();
331c6e4dfa3Sho log_print("isakmpd: exit");
332c6e4dfa3Sho exit(0);
333c6e4dfa3Sho }
334c6e4dfa3Sho }
335c6e4dfa3Sho
336a8478dd3Sho /* Called on SIGTERM, SIGINT or by ui_shutdown_daemon(). */
337c6e4dfa3Sho void
daemon_shutdown_now(int sig)338c6e4dfa3Sho daemon_shutdown_now(int sig)
339c6e4dfa3Sho {
340c6e4dfa3Sho sigtermed = 1;
341c6e4dfa3Sho }
342c6e4dfa3Sho
3435fc2ae46Sniklas /* Write pid file. */
3445fc2ae46Sniklas static void
write_pid_file(void)3455fc2ae46Sniklas write_pid_file(void)
3465fc2ae46Sniklas {
3475fc2ae46Sniklas FILE *fp;
3485fc2ae46Sniklas
3495fc2ae46Sniklas unlink(pid_file);
3505fc2ae46Sniklas
351e8a27e95Smoritz fp = fopen(pid_file, "w");
352fb9475d6Sderaadt if (fp != NULL) {
35370213997Sho if (fprintf(fp, "%ld\n", (long) getpid()) < 0)
35412f43dabShshoexer log_error("write_pid_file: failed to write PID to "
35512f43dabShshoexer "\"%.100s\"", pid_file);
356ea1948caSho fclose(fp);
357fb9475d6Sderaadt } else
35812f43dabShshoexer log_fatal("write_pid_file: fopen (\"%.100s\", \"w\") failed",
35912f43dabShshoexer pid_file);
3605fc2ae46Sniklas }
3615fc2ae46Sniklas
362426a6f8cSkrw void
sanitise_stdfd(void)363426a6f8cSkrw sanitise_stdfd(void)
364426a6f8cSkrw {
365426a6f8cSkrw int nullfd, dupfd;
366426a6f8cSkrw
367426a6f8cSkrw if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
368426a6f8cSkrw fprintf(stderr, "Couldn't open /dev/null: %s\n",
369426a6f8cSkrw strerror(errno));
370426a6f8cSkrw exit(1);
371426a6f8cSkrw }
372426a6f8cSkrw while (++dupfd <= STDERR_FILENO) {
373426a6f8cSkrw /* Only populate closed fds */
374426a6f8cSkrw if (fcntl(dupfd, F_GETFL) == -1 && errno == EBADF) {
375426a6f8cSkrw if (dup2(nullfd, dupfd) == -1) {
376426a6f8cSkrw fprintf(stderr, "dup2: %s\n", strerror(errno));
377426a6f8cSkrw exit(1);
378426a6f8cSkrw }
379426a6f8cSkrw }
380426a6f8cSkrw }
381426a6f8cSkrw if (nullfd > STDERR_FILENO)
382426a6f8cSkrw close(nullfd);
383426a6f8cSkrw }
384426a6f8cSkrw
3852040585eSniklas int
main(int argc,char * argv[])3862040585eSniklas main(int argc, char *argv[])
3872040585eSniklas {
388783260f4Sniklas fd_set *rfds, *wfds;
3892040585eSniklas int n, m;
390783260f4Sniklas size_t mask_size;
3916ee513e5Sjca struct timespec ts, *timeout;
3922040585eSniklas
39331516eecShshoexer closefrom(STDERR_FILENO + 1);
39431516eecShshoexer
395fb9475d6Sderaadt /*
396fb9475d6Sderaadt * Make sure init() won't alloc fd 0, 1 or 2, as daemon() will close
397fb9475d6Sderaadt * them.
398fb9475d6Sderaadt */
399426a6f8cSkrw sanitise_stdfd();
400f41e756cSho
401f41e756cSho /* Log cmd line parsing and initialization errors to stderr. */
40230d6f32eSho log_to(stderr);
4032040585eSniklas parse_args(argc, argv);
404a8478dd3Sho log_init(debug);
405ceac201fSlum log_print("isakmpd: starting");
406da35d433Sho
407a9c89582Sho /* Open protocols and services databases. */
408a9c89582Sho setprotoent(1);
409a9c89582Sho setservent(1);
410a9c89582Sho
411a0c57444Shshoexer /* Open command fifo */
412a0c57444Shshoexer ui_init();
413a0c57444Shshoexer
414b8909177Smoritz set_slave_signals();
41531516eecShshoexer /* Daemonize before forking unpriv'ed child */
41631516eecShshoexer if (!debug)
41731516eecShshoexer if (daemon(0, 0))
41831516eecShshoexer log_fatal("main: daemon (0, 0) failed");
41931516eecShshoexer
42015537254Shshoexer /* Set timezone before priv'separation */
42115537254Shshoexer tzset();
42215537254Shshoexer
423e8a27e95Smoritz write_pid_file();
424e8a27e95Smoritz
425db6e54dbSho if (monitor_init(debug)) {
42631516eecShshoexer /* The parent, with privileges enters infinite monitor loop. */
427da35d433Sho monitor_loop(debug);
428da35d433Sho exit(0); /* Never reached. */
429da35d433Sho }
430da35d433Sho /* Child process only from this point on, no privileges left. */
431da35d433Sho
4322040585eSniklas init();
433f41e756cSho
434af729d73Sho /* If we wanted IKE packet capture to file, initialize it now. */
435af729d73Sho if (pcap_file != 0)
436af729d73Sho log_packet_init(pcap_file);
437af729d73Sho
438783260f4Sniklas /* Allocate the file descriptor sets just big enough. */
439783260f4Sniklas n = getdtablesize();
440783260f4Sniklas mask_size = howmany(n, NFDBITS) * sizeof(fd_mask);
4415ae94ef8Sderaadt rfds = malloc(mask_size);
442783260f4Sniklas if (!rfds)
44312f43dabShshoexer log_fatal("main: malloc (%lu) failed",
44412f43dabShshoexer (unsigned long)mask_size);
4455ae94ef8Sderaadt wfds = malloc(mask_size);
446783260f4Sniklas if (!wfds)
44712f43dabShshoexer log_fatal("main: malloc (%lu) failed",
44812f43dabShshoexer (unsigned long)mask_size);
449783260f4Sniklas
45031516eecShshoexer monitor_init_done();
45131516eecShshoexer
452fb9475d6Sderaadt while (1) {
4532040585eSniklas /* If someone has sent SIGHUP to us, reconfigure. */
454fb9475d6Sderaadt if (sighupped) {
455dcd65249Shshoexer sighupped = 0;
456b7f5c844Sho log_print("SIGHUP received");
4572040585eSniklas reinit();
458b7f5c844Sho }
4592b81057dSniklas /* and if someone sent SIGUSR1, do a state report. */
460fb9475d6Sderaadt if (sigusr1ed) {
461dcd65249Shshoexer sigusr1ed = 0;
462b7f5c844Sho log_print("SIGUSR1 received");
4632b81057dSniklas report();
464b7f5c844Sho }
465c6e4dfa3Sho /*
46612f43dabShshoexer * and if someone set 'sigtermed' (SIGTERM, SIGINT or via the
46712f43dabShshoexer * UI), this indicates we should start a controlled shutdown
46812f43dabShshoexer * of the daemon.
469c6e4dfa3Sho *
47012f43dabShshoexer * Note: Since _one_ message is sent per iteration of this
47112f43dabShshoexer * enclosing while-loop, and we want to send a number of
47212f43dabShshoexer * DELETE notifications, we must loop atleast this number of
47312f43dabShshoexer * times. The daemon_shutdown() function starts by queueing
47412f43dabShshoexer * the DELETEs, all other calls just increments the
47512f43dabShshoexer * 'sigtermed' variable until it reaches a "safe" value, and
47612f43dabShshoexer * the daemon exits.
477c6e4dfa3Sho */
478c6e4dfa3Sho if (sigtermed)
479c6e4dfa3Sho daemon_shutdown();
480c6e4dfa3Sho
4812040585eSniklas /* Setup the descriptors to look for incoming messages at. */
4820dc10397Shshoexer bzero(rfds, mask_size);
483783260f4Sniklas n = transport_fd_set(rfds);
484783260f4Sniklas FD_SET(ui_socket, rfds);
4852040585eSniklas if (ui_socket + 1 > n)
4862040585eSniklas n = ui_socket + 1;
4872040585eSniklas
4882040585eSniklas /*
48912f43dabShshoexer * XXX Some day we might want to deal with an abstract
49012f43dabShshoexer * application class instead, with many instantiations
49112f43dabShshoexer * possible.
4922040585eSniklas */
493fb9475d6Sderaadt if (!app_none && app_socket >= 0) {
494783260f4Sniklas FD_SET(app_socket, rfds);
4952040585eSniklas if (app_socket + 1 > n)
4962040585eSniklas n = app_socket + 1;
4972040585eSniklas }
4982040585eSniklas /* Setup the descriptors that have pending messages to send. */
4990dc10397Shshoexer bzero(wfds, mask_size);
500783260f4Sniklas m = transport_pending_wfd_set(wfds);
5012040585eSniklas if (m > n)
5022040585eSniklas n = m;
5032040585eSniklas
5042040585eSniklas /* Find out when the next timed event is. */
5056ee513e5Sjca timeout = &ts;
5062040585eSniklas timer_next_event(&timeout);
5072040585eSniklas
5086ee513e5Sjca n = pselect(n, rfds, wfds, NULL, timeout, NULL);
509fb9475d6Sderaadt if (n == -1) {
510fb9475d6Sderaadt if (errno != EINTR) {
511b4caba6dShshoexer log_error("main: select");
512dc4b6c27Sniklas
5132040585eSniklas /*
514fb9475d6Sderaadt * In order to give the unexpected error
515fb9475d6Sderaadt * condition time to resolve without letting
516fb9475d6Sderaadt * this process eat up all available CPU
517dc4b6c27Sniklas * we sleep for a short while.
5182040585eSniklas */
5192040585eSniklas sleep(1);
5202040585eSniklas }
521fb9475d6Sderaadt } else if (n) {
522783260f4Sniklas transport_handle_messages(rfds);
523783260f4Sniklas transport_send_messages(wfds);
524783260f4Sniklas if (FD_ISSET(ui_socket, rfds))
5252040585eSniklas ui_handler();
52612f43dabShshoexer if (!app_none && app_socket >= 0 &&
52712f43dabShshoexer FD_ISSET(app_socket, rfds))
5282040585eSniklas app_handler();
5292040585eSniklas }
5302040585eSniklas timer_handle_expirations();
5312040585eSniklas }
5322040585eSniklas }
533