plugins: Wire up rust plugin support for NBD_INFO_INIT_STATE
[nbdkit/ericb.git] / server / main.c
blob86f5352cc7c94758692473ae14accad974445ea8
1 /* nbdkit
2 * Copyright (C) 2013-2019 Red Hat Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
33 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdbool.h>
38 #include <string.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <getopt.h>
42 #include <limits.h>
43 #include <errno.h>
44 #include <assert.h>
45 #include <syslog.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <sys/socket.h>
50 #ifdef HAVE_SYS_MMAN_H
51 #include <sys/mman.h>
52 #endif
54 #ifdef HAVE_LINUX_VM_SOCKETS_H
55 #include <linux/vm_sockets.h>
56 #endif
58 #include <pthread.h>
60 #include <dlfcn.h>
62 #include "internal.h"
63 #include "nbd-protocol.h"
64 #include "options.h"
65 #include "exit-with-parent.h"
67 #ifdef ENABLE_LIBFUZZER
68 #define main fuzzer_main
69 #endif
71 static char *make_random_fifo (void);
72 static struct backend *open_plugin_so (size_t i, const char *filename, int short_name);
73 static struct backend *open_filter_so (struct backend *next, size_t i, const char *filename, int short_name);
74 static void start_serving (void);
75 static void write_pidfile (void);
76 static bool is_config_key (const char *key, size_t len);
78 struct debug_flag *debug_flags; /* -D */
79 bool exit_with_parent; /* --exit-with-parent */
80 const char *exportname; /* -e */
81 bool foreground; /* -f */
82 const char *ipaddr; /* -i */
83 enum log_to log_to = LOG_TO_DEFAULT; /* --log */
84 unsigned mask_handshake = ~0U; /* --mask-handshake */
85 bool newstyle = true; /* false = -o, true = -n */
86 bool no_sr; /* --no-sr */
87 char *pidfile; /* -P */
88 const char *port; /* -p */
89 bool read_only; /* -r */
90 const char *run; /* --run */
91 bool listen_stdin; /* -s */
92 const char *selinux_label; /* --selinux-label */
93 bool swap; /* --swap */
94 unsigned threads; /* -t */
95 int tls; /* --tls : 0=off 1=on 2=require */
96 const char *tls_certificates_dir; /* --tls-certificates */
97 const char *tls_psk; /* --tls-psk */
98 bool tls_verify_peer; /* --tls-verify-peer */
99 char *unixsocket; /* -U */
100 const char *user, *group; /* -u & -g */
101 bool verbose; /* -v */
102 bool vsock; /* --vsock */
103 unsigned int socket_activation /* $LISTEN_FDS and $LISTEN_PID set */;
105 /* The currently loaded plugin. */
106 struct backend *backend;
108 static char *random_fifo_dir = NULL;
109 static char *random_fifo = NULL;
111 static void
112 usage (void)
114 /* --{short,long}-options remain undocumented */
115 printf (
116 #include "synopsis.c"
118 printf ("\n"
119 "Please read the nbdkit(1) manual page for full usage.\n");
122 static void
123 display_version (void)
125 printf ("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
128 static void
129 dump_config (void)
131 printf ("%s=%s\n", "bindir", bindir);
132 printf ("%s=%s\n", "filterdir", filterdir);
133 printf ("%s=%s\n", "libdir", libdir);
134 printf ("%s=%s\n", "mandir", mandir);
135 printf ("%s=%s\n", "name", PACKAGE_NAME);
136 printf ("%s=%s\n", "plugindir", plugindir);
137 printf ("%s=%s\n", "root_tls_certificates_dir", root_tls_certificates_dir);
138 printf ("%s=%s\n", "sbindir", sbindir);
139 #ifdef HAVE_LIBSELINUX
140 printf ("selinux=yes\n");
141 #else
142 printf ("selinux=no\n");
143 #endif
144 printf ("%s=%s\n", "sysconfdir", sysconfdir);
145 #ifdef HAVE_GNUTLS
146 printf ("tls=yes\n");
147 #else
148 printf ("tls=no\n");
149 #endif
150 printf ("%s=%s\n", "version", PACKAGE_VERSION);
154 main (int argc, char *argv[])
156 int c;
157 bool help = false, version = false, dump_plugin = false;
158 int tls_set_on_cli = false;
159 bool short_name;
160 const char *filename;
161 char *p;
162 static struct filter_filename {
163 struct filter_filename *next;
164 const char *filename;
165 } *filter_filenames = NULL;
166 size_t i;
167 const char *magic_config_key;
169 /* Refuse to run if stdin/out/err are closed, whether or not -s is used. */
170 if (fcntl (STDERR_FILENO, F_GETFL) == -1) {
171 /* Nowhere we can report the error. Oh well. */
172 exit (EXIT_FAILURE);
174 if (fcntl (STDIN_FILENO, F_GETFL) == -1 ||
175 fcntl (STDOUT_FILENO, F_GETFL) == -1) {
176 perror ("expecting stdin/stdout to be opened");
177 exit (EXIT_FAILURE);
180 #if !ENABLE_LIBFUZZER
181 threadlocal_init ();
182 #else
183 static bool main_called = false;
184 if (!main_called) {
185 threadlocal_init ();
186 main_called = true;
188 #endif
190 /* The default setting for TLS depends on whether we were
191 * compiled with GnuTLS.
193 #ifdef HAVE_GNUTLS
194 tls = 1;
195 #else
196 tls = 0;
197 #endif
199 /* Returns 0 if no socket activation, or the number of FDs. */
200 socket_activation = get_socket_activation ();
202 for (;;) {
203 c = getopt_long (argc, argv, short_options, long_options, NULL);
204 if (c == -1)
205 break;
207 switch (c) {
208 case DUMP_CONFIG_OPTION:
209 dump_config ();
210 exit (EXIT_SUCCESS);
212 case DUMP_PLUGIN_OPTION:
213 dump_plugin = true;
214 break;
216 case EXIT_WITH_PARENT_OPTION:
217 #ifdef HAVE_EXIT_WITH_PARENT
218 exit_with_parent = true;
219 foreground = true;
220 break;
221 #else
222 fprintf (stderr,
223 "%s: --exit-with-parent is not implemented "
224 "for this operating system\n",
225 program_name);
226 exit (EXIT_FAILURE);
227 #endif
229 case FILTER_OPTION:
231 struct filter_filename *t;
233 t = malloc (sizeof *t);
234 if (t == NULL) {
235 perror ("malloc");
236 exit (EXIT_FAILURE);
238 t->next = filter_filenames;
239 t->filename = optarg;
240 filter_filenames = t;
242 break;
244 case LOG_OPTION:
245 if (strcmp (optarg, "stderr") == 0)
246 log_to = LOG_TO_STDERR;
247 else if (strcmp (optarg, "syslog") == 0)
248 log_to = LOG_TO_SYSLOG;
249 else if (strcmp (optarg, "null") == 0)
250 log_to = LOG_TO_NULL;
251 else {
252 fprintf (stderr, "%s: "
253 "--log must be \"stderr\", \"syslog\" or \"null\"\n",
254 program_name);
255 exit (EXIT_FAILURE);
257 break;
259 case LONG_OPTIONS_OPTION:
260 for (i = 0; long_options[i].name != NULL; ++i) {
261 if (strcmp (long_options[i].name, "long-options") != 0 &&
262 strcmp (long_options[i].name, "short-options") != 0)
263 printf ("--%s\n", long_options[i].name);
265 exit (EXIT_SUCCESS);
267 case RUN_OPTION:
268 if (socket_activation) {
269 fprintf (stderr, "%s: cannot use socket activation with --run flag\n",
270 program_name);
271 exit (EXIT_FAILURE);
273 run = optarg;
274 foreground = true;
275 break;
277 case SELINUX_LABEL_OPTION:
278 selinux_label = optarg;
279 break;
281 case SHORT_OPTIONS_OPTION:
282 for (i = 0; short_options[i]; ++i) {
283 if (short_options[i] != ':')
284 printf ("-%c\n", short_options[i]);
286 exit (EXIT_SUCCESS);
288 case SWAP_OPTION:
289 swap = 1;
290 break;
292 case TLS_OPTION:
293 tls_set_on_cli = true;
294 if (strcasecmp (optarg, "require") == 0 ||
295 strcasecmp (optarg, "required") == 0 ||
296 strcasecmp (optarg, "force") == 0)
297 tls = 2;
298 else {
299 tls = nbdkit_parse_bool (optarg);
300 if (tls == -1)
301 exit (EXIT_FAILURE);
303 break;
305 case TLS_CERTIFICATES_OPTION:
306 tls_certificates_dir = optarg;
307 break;
309 case TLS_PSK_OPTION:
310 tls_psk = optarg;
311 break;
313 case TLS_VERIFY_PEER_OPTION:
314 tls_verify_peer = true;
315 break;
317 case VSOCK_OPTION:
318 #ifdef AF_VSOCK
319 vsock = true;
320 break;
321 #else
322 fprintf (stderr, "%s: AF_VSOCK is not supported on this platform\n",
323 program_name);
324 exit (EXIT_FAILURE);
325 #endif
327 case 'D':
328 add_debug_flag (optarg);
329 break;
331 case 'e':
332 exportname = optarg;
333 if (strnlen (exportname, NBD_MAX_STRING + 1) > NBD_MAX_STRING) {
334 nbdkit_error ("export name too long");
335 exit (EXIT_FAILURE);
337 /* TODO: Check that name is valid UTF-8? */
338 newstyle = true;
339 break;
341 case 'f':
342 foreground = true;
343 break;
345 case 'g':
346 group = optarg;
347 break;
349 case 'i':
350 if (socket_activation) {
351 fprintf (stderr, "%s: cannot use socket activation with -i flag\n",
352 program_name);
353 exit (EXIT_FAILURE);
355 ipaddr = optarg;
356 break;
358 case MASK_HANDSHAKE_OPTION:
359 if (nbdkit_parse_unsigned ("mask-handshake",
360 optarg, &mask_handshake) == -1)
361 exit (EXIT_FAILURE);
362 break;
364 case 'n':
365 newstyle = true;
366 break;
368 case NO_SR_OPTION:
369 no_sr = true;
370 break;
372 case 'o':
373 newstyle = false;
374 break;
376 case 'P':
377 pidfile = nbdkit_absolute_path (optarg);
378 if (pidfile == NULL)
379 exit (EXIT_FAILURE);
380 break;
382 case 'p':
383 if (socket_activation) {
384 fprintf (stderr, "%s: cannot use socket activation with -p flag\n",
385 program_name);
386 exit (EXIT_FAILURE);
388 port = optarg;
389 break;
391 case 'r':
392 read_only = true;
393 break;
395 case 's':
396 if (socket_activation) {
397 fprintf (stderr, "%s: cannot use socket activation with -s flag\n",
398 program_name);
399 exit (EXIT_FAILURE);
401 listen_stdin = true;
402 break;
404 case 't':
405 if (nbdkit_parse_unsigned ("threads", optarg, &threads) == -1)
406 exit (EXIT_FAILURE);
407 /* XXX Worth a maximimum limit on threads? */
408 break;
410 case 'U':
411 if (socket_activation) {
412 fprintf (stderr, "%s: cannot use socket activation with -U flag\n",
413 program_name);
414 exit (EXIT_FAILURE);
416 if (strcmp (optarg, "-") == 0)
417 unixsocket = make_random_fifo ();
418 else
419 unixsocket = nbdkit_absolute_path (optarg);
420 if (unixsocket == NULL)
421 exit (EXIT_FAILURE);
422 break;
424 case 'u':
425 user = optarg;
426 break;
428 case 'v':
429 verbose = true;
430 break;
432 case 'V':
433 version = true;
434 break;
436 case HELP_OPTION:
437 help = true;
438 break;
440 default:
441 usage ();
442 exit (EXIT_FAILURE);
446 /* No extra parameters. */
447 if (optind >= argc) {
448 if (help) {
449 usage ();
450 exit (EXIT_SUCCESS);
452 if (version) {
453 display_version ();
454 exit (EXIT_SUCCESS);
456 if (dump_plugin) {
457 /* Incorrect use of --dump-plugin. */
458 fprintf (stderr,
459 "%s: use 'nbdkit plugin --dump-plugin' or\n"
460 "'nbdkit /path/to/plugin.so --dump-plugin'\n",
461 program_name);
462 exit (EXIT_FAILURE);
465 /* Otherwise this is an error. */
466 fprintf (stderr,
467 "%s: no plugins given on the command line.\n"
468 "Use '%s --help' or "
469 "read the nbdkit(1) manual page for documentation.\n",
470 program_name, program_name);
471 exit (EXIT_FAILURE);
474 /* Oldstyle protocol + exportname not allowed. */
475 if (!newstyle && exportname != NULL) {
476 fprintf (stderr,
477 "%s: cannot use oldstyle protocol (-o) and exportname (-e)\n",
478 program_name);
479 exit (EXIT_FAILURE);
482 /* If exportname was not set on the command line, use "". */
483 if (exportname == NULL)
484 exportname = "";
486 /* --tls=require and oldstyle won't work. */
487 if (tls == 2 && !newstyle) {
488 fprintf (stderr,
489 "%s: cannot use oldstyle protocol (-o) and require TLS\n",
490 program_name);
491 exit (EXIT_FAILURE);
494 /* Set the umask to a known value. This makes the behaviour of
495 * plugins when creating files more predictable, and also removes an
496 * implicit dependency on umask when calling mkstemp(3).
498 umask (0022);
500 /* If we will or might use syslog. */
501 if (log_to == LOG_TO_SYSLOG || log_to == LOG_TO_DEFAULT)
502 openlog (program_name, LOG_PID, 0);
504 /* Initialize TLS. */
505 crypto_init (tls_set_on_cli);
506 assert (tls != -1);
508 /* Implement --exit-with-parent early in case plugin initialization
509 * takes a long time and the parent exits during that time.
511 #ifdef HAVE_EXIT_WITH_PARENT
512 if (exit_with_parent) {
513 if (set_exit_with_parent () == -1) {
514 perror ("nbdkit: --exit-with-parent");
515 exit (EXIT_FAILURE);
518 #endif
520 /* The remaining command line arguments are the plugin name and
521 * parameters. If --help, --version or --dump-plugin were specified
522 * then we open the plugin so that we can display the per-plugin
523 * help/version/plugin information.
525 filename = argv[optind++];
526 short_name = is_short_name (filename);
528 /* Is there an executable script located in the plugindir?
529 * If so we simply execute it with the current command line.
531 if (short_name) {
532 struct stat statbuf;
533 CLEANUP_FREE char *script;
535 if (asprintf (&script,
536 "%s/nbdkit-%s-plugin", plugindir, filename) == -1) {
537 perror ("asprintf");
538 exit (EXIT_FAILURE);
541 if (stat (script, &statbuf) == 0 &&
542 (statbuf.st_mode & S_IXUSR) != 0) {
543 /* We're going to execute the plugin directly.
544 * Replace argv[0] with argv[optind-1] and move further arguments
545 * down the list.
547 argv[0] = argv[optind-1];
548 for (i = optind; i <= argc; i++)
549 argv[i-1] = argv[i];
550 execv (script, argv);
551 perror (script);
552 exit (EXIT_FAILURE);
556 /* Open the plugin (first) and then wrap the plugin with the
557 * filters. The filters are wrapped in reverse order that they
558 * appear on the command line so that in the end ‘backend’ points to
559 * the first filter on the command line.
561 backend = open_plugin_so (0, filename, short_name);
562 i = 1;
563 while (filter_filenames) {
564 struct filter_filename *t = filter_filenames;
566 filename = t->filename;
567 short_name = is_short_name (filename);
569 backend = open_filter_so (backend, i++, filename, short_name);
571 filter_filenames = t->next;
572 free (t);
575 /* Apply nbdkit.* flags for the server. */
576 apply_debug_flags (NULL, "nbdkit");
578 /* Check all debug flags were used, and free them. */
579 free_debug_flags ();
581 if (help) {
582 struct backend *b;
584 usage ();
585 for_each_backend (b) {
586 printf ("\n");
587 b->usage (b);
589 backend->free (backend);
590 exit (EXIT_SUCCESS);
593 if (version) {
594 const char *v;
595 struct backend *b;
597 display_version ();
598 for_each_backend (b) {
599 printf ("%s", b->name);
600 if ((v = b->version (b)) != NULL)
601 printf (" %s", v);
602 printf ("\n");
604 backend->free (backend);
605 exit (EXIT_SUCCESS);
608 /* Call config and config_complete to parse the parameters.
610 * If the plugin provides magic_config_key then any "bare" values
611 * (ones not containing "=") are prefixed with this key.
613 * For backwards compatibility with old plugins, and to support
614 * scripting languages, if magic_config_key == NULL then if the
615 * first parameter is bare it is prefixed with the key "script", and
616 * any other bare parameters are errors.
618 magic_config_key = backend->magic_config_key (backend);
619 for (i = 0; optind < argc; ++i, ++optind) {
620 p = strchr (argv[optind], '=');
621 if (p && is_config_key (argv[optind], p - argv[optind])) { /* key=value */
622 *p = '\0';
623 backend->config (backend, argv[optind], p+1);
625 else if (magic_config_key == NULL) {
626 if (i == 0) /* magic script parameter */
627 backend->config (backend, "script", argv[optind]);
628 else {
629 fprintf (stderr,
630 "%s: expecting key=value on the command line but got: %s\n",
631 program_name, argv[optind]);
632 exit (EXIT_FAILURE);
635 else { /* magic config key */
636 backend->config (backend, magic_config_key, argv[optind]);
640 /* This must run after parsing the parameters so that the script can
641 * be loaded for scripting languages. But it must be called before
642 * config_complete so that the plugin doesn't check for missing
643 * parameters.
645 if (dump_plugin) {
646 backend->dump_fields (backend);
647 backend->free (backend);
648 exit (EXIT_SUCCESS);
651 backend->config_complete (backend);
653 /* Select the correct thread model based on config. */
654 lock_init_thread_model ();
656 set_up_quit_pipe ();
657 #if !ENABLE_LIBFUZZER
658 set_up_signals ();
659 #endif
661 start_serving ();
663 backend->free (backend);
664 backend = NULL;
666 free (unixsocket);
667 free (pidfile);
669 if (random_fifo) {
670 unlink (random_fifo);
671 free (random_fifo);
674 if (random_fifo_dir) {
675 rmdir (random_fifo_dir);
676 free (random_fifo_dir);
679 crypto_free ();
680 close_quit_pipe ();
682 /* Note: Don't exit here, otherwise this won't work when compiled
683 * for libFuzzer.
685 return EXIT_SUCCESS;
688 /* Implementation of '-U -' */
689 static char *
690 make_random_fifo (void)
692 char template[] = "/tmp/nbdkitXXXXXX";
693 char *sock;
695 if (mkdtemp (template) == NULL) {
696 perror ("mkdtemp");
697 return NULL;
700 random_fifo_dir = strdup (template);
701 if (random_fifo_dir == NULL) {
702 perror ("strdup");
703 return NULL;
706 if (asprintf (&random_fifo, "%s/socket", template) == -1) {
707 perror ("asprintf");
708 return NULL;
711 sock = strdup (random_fifo);
712 if (sock == NULL) {
713 perror ("strdup");
714 return NULL;
717 return sock;
720 static struct backend *
721 open_plugin_so (size_t i, const char *name, int short_name)
723 struct backend *ret;
724 char *filename = (char *) name;
725 bool free_filename = false;
726 void *dl;
727 struct nbdkit_plugin *(*plugin_init) (void);
728 char *error;
730 if (short_name) {
731 /* Short names are rewritten relative to the plugindir. */
732 if (asprintf (&filename,
733 "%s/nbdkit-%s-plugin.so", plugindir, name) == -1) {
734 perror ("asprintf");
735 exit (EXIT_FAILURE);
737 free_filename = true;
740 dl = dlopen (filename, RTLD_NOW|RTLD_GLOBAL);
741 if (dl == NULL) {
742 fprintf (stderr,
743 "%s: error: cannot open plugin '%s': %s\n"
744 "Use '%s --help' or "
745 "read the nbdkit(1) manual page for documentation.\n",
746 program_name, name, dlerror (),
747 program_name);
748 exit (EXIT_FAILURE);
751 /* Initialize the plugin. See dlopen(3) to understand C weirdness. */
752 dlerror ();
753 *(void **) (&plugin_init) = dlsym (dl, "plugin_init");
754 if ((error = dlerror ()) != NULL) {
755 fprintf (stderr, "%s: %s: %s\n", program_name, name, error);
756 exit (EXIT_FAILURE);
758 if (!plugin_init) {
759 fprintf (stderr, "%s: %s: invalid plugin_init\n", program_name, name);
760 exit (EXIT_FAILURE);
763 /* Register the plugin. */
764 ret = plugin_register (i, filename, dl, plugin_init);
766 if (free_filename)
767 free (filename);
769 return ret;
772 static struct backend *
773 open_filter_so (struct backend *next, size_t i,
774 const char *name, int short_name)
776 struct backend *ret;
777 char *filename = (char *) name;
778 bool free_filename = false;
779 void *dl;
780 struct nbdkit_filter *(*filter_init) (void);
781 char *error;
783 if (short_name) {
784 /* Short names are rewritten relative to the filterdir. */
785 if (asprintf (&filename,
786 "%s/nbdkit-%s-filter.so", filterdir, name) == -1) {
787 perror ("asprintf");
788 exit (EXIT_FAILURE);
790 free_filename = true;
793 dl = dlopen (filename, RTLD_NOW|RTLD_GLOBAL);
794 if (dl == NULL) {
795 fprintf (stderr, "%s: error: cannot open filter '%s': %s\n",
796 program_name, name, dlerror ());
797 exit (EXIT_FAILURE);
800 /* Initialize the filter. See dlopen(3) to understand C weirdness. */
801 dlerror ();
802 *(void **) (&filter_init) = dlsym (dl, "filter_init");
803 if ((error = dlerror ()) != NULL) {
804 fprintf (stderr, "%s: %s: %s\n", program_name, name, error);
805 exit (EXIT_FAILURE);
807 if (!filter_init) {
808 fprintf (stderr, "%s: %s: invalid filter_init\n", program_name, name);
809 exit (EXIT_FAILURE);
812 /* Register the filter. */
813 ret = filter_register (next, i, filename, dl, filter_init);
815 if (free_filename)
816 free (filename);
818 return ret;
821 static void
822 start_serving (void)
824 int *socks;
825 size_t nr_socks;
826 size_t i;
828 /* If the user has mixed up -p/--run/-s/-U/--vsock options, then
829 * give an error.
831 * XXX Actually the server could easily be extended to handle both
832 * TCP/IP and Unix sockets, or even multiple TCP/IP ports.
834 if ((port && unixsocket) ||
835 (port && listen_stdin) ||
836 (unixsocket && listen_stdin) ||
837 (listen_stdin && run) ||
838 (vsock && unixsocket) ||
839 (vsock && listen_stdin) ||
840 (vsock && run)) {
841 fprintf (stderr,
842 "%s: -p, --run, -s, -U or --vsock options cannot be used"
843 "in this combination\n",
844 program_name);
845 exit (EXIT_FAILURE);
848 /* Lock the process into memory if requested. */
849 if (swap) {
850 #ifdef HAVE_MLOCKALL
851 if (mlockall (MCL_CURRENT | MCL_FUTURE) == -1) {
852 fprintf (stderr, "%s: --swap: mlockall: %m\n", program_name);
853 exit (EXIT_FAILURE);
855 debug ("mlockall done");
856 #else
857 fprintf (stderr, "%s: mlockall (--swap option) "
858 "is not supported on this platform\n");
859 exit (EXIT_FAILURE);
860 #endif
863 /* Socket activation -- we are handling connections on pre-opened
864 * file descriptors [FIRST_SOCKET_ACTIVATION_FD ..
865 * FIRST_SOCKET_ACTIVATION_FD+nr_socks-1].
867 if (socket_activation) {
868 nr_socks = socket_activation;
869 debug ("using socket activation, nr_socks = %zu", nr_socks);
870 socks = malloc (sizeof (int) * nr_socks);
871 if (socks == NULL) {
872 perror ("malloc");
873 exit (EXIT_FAILURE);
875 for (i = 0; i < nr_socks; ++i)
876 socks[i] = FIRST_SOCKET_ACTIVATION_FD + i;
877 change_user ();
878 write_pidfile ();
879 accept_incoming_connections (socks, nr_socks);
880 return;
883 /* Handling a single connection on stdin/stdout. */
884 if (listen_stdin) {
885 change_user ();
886 write_pidfile ();
887 threadlocal_new_server_thread ();
888 handle_single_connection (0, 1);
889 return;
892 /* Handling multiple connections on TCP/IP, Unix domain socket or
893 * AF_VSOCK.
895 if (unixsocket)
896 socks = bind_unix_socket (&nr_socks);
897 else if (vsock)
898 socks = bind_vsock (&nr_socks);
899 else
900 socks = bind_tcpip_socket (&nr_socks);
902 run_command ();
903 change_user ();
904 fork_into_background ();
905 write_pidfile ();
906 accept_incoming_connections (socks, nr_socks);
909 static void
910 write_pidfile (void)
912 int fd;
913 pid_t pid;
914 char pidstr[64];
915 size_t len;
917 if (!pidfile)
918 return;
920 pid = getpid ();
921 snprintf (pidstr, sizeof pidstr, "%d\n", pid);
922 len = strlen (pidstr);
924 fd = open (pidfile, O_WRONLY|O_TRUNC|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644);
925 if (fd == -1) {
926 perror (pidfile);
927 exit (EXIT_FAILURE);
930 if (write (fd, pidstr, len) < len ||
931 close (fd) == -1) {
932 perror (pidfile);
933 exit (EXIT_FAILURE);
936 debug ("written pidfile %s", pidfile);
939 /* When parsing plugin and filter config key=value from the command
940 * line, is the key a simple alphanumeric with period, underscore or
941 * dash?
943 static bool
944 is_config_key (const char *key, size_t len)
946 static const char allowed_first[] =
947 "abcdefghijklmnopqrstuvwxyz"
948 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
949 static const char allowed[] =
950 "._-"
951 "0123456789"
952 "abcdefghijklmnopqrstuvwxyz"
953 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
955 if (len == 0)
956 return false;
958 if (strchr (allowed_first, key[0]) == NULL)
959 return false;
961 /* This works in context of the caller since key[len] == '='. */
962 if (strspn (key, allowed) != len)
963 return false;
965 return true;