rust: Logically group example code
[nbdkit.git] / server / main.c
blobb7053671e27cb0648b226928a757c77fb33c2453
1 /* nbdkit
2 * Copyright (C) 2013-2020 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 <sys/types.h>
46 #include <sys/stat.h>
48 #ifdef HAVE_SYS_MMAN_H
49 #include <sys/mman.h>
50 #endif
52 #ifdef HAVE_SYS_SOCKET_H
53 #include <sys/socket.h>
54 #endif
56 #ifdef HAVE_LINUX_VM_SOCKETS_H
57 #include <linux/vm_sockets.h>
58 #elif HAVE_SYS_VSOCK_H
59 #include <sys/vsock.h>
60 #endif
62 #include <pthread.h>
64 #include <dlfcn.h>
66 #include "ascii-string.h"
67 #include "exit-with-parent.h"
68 #include "nbd-protocol.h"
69 #include "realpath.h"
70 #include "strndup.h"
71 #include "syslog.h"
72 #include "utils.h"
74 #include "internal.h"
75 #include "options.h"
77 #ifdef ENABLE_LIBFUZZER
78 #define main fuzzer_main
79 #endif
81 static char *make_random_fifo (void);
82 static struct backend *open_plugin_so (size_t i, const char *filename, int short_name);
83 static struct backend *open_filter_so (struct backend *next, size_t i, const char *filename, int short_name);
84 static void start_serving (void);
85 static void write_pidfile (void);
86 static bool is_config_key (const char *key, size_t len);
87 static void error_if_stdio_closed (void);
88 static void switch_stdio (void);
89 static void winsock_init (void);
91 int tcpip_sock_af = AF_UNSPEC; /* -4, -6 */
92 struct debug_flag *debug_flags; /* -D */
93 bool exit_with_parent; /* --exit-with-parent */
94 const char *export_name; /* -e */
95 bool foreground; /* -f */
96 const char *ipaddr; /* -i */
97 enum log_to log_to = LOG_TO_DEFAULT; /* --log */
98 unsigned mask_handshake = ~0U; /* --mask-handshake */
99 bool newstyle = true; /* false = -o, true = -n */
100 bool no_sr; /* --no-sr */
101 char *pidfile; /* -P */
102 const char *port; /* -p */
103 bool read_only; /* -r */
104 const char *run; /* --run */
105 bool listen_stdin; /* -s */
106 const char *selinux_label; /* --selinux-label */
107 bool swap; /* --swap */
108 unsigned threads; /* -t */
109 int tls; /* --tls : 0=off 1=on 2=require */
110 const char *tls_certificates_dir; /* --tls-certificates */
111 const char *tls_psk; /* --tls-psk */
112 bool tls_verify_peer; /* --tls-verify-peer */
113 char *unixsocket; /* -U */
114 const char *user, *group; /* -u & -g */
115 bool verbose; /* -v */
116 bool vsock; /* --vsock */
117 unsigned int socket_activation; /* $LISTEN_FDS and $LISTEN_PID set */
118 bool configured; /* .config_complete done */
119 int saved_stdin = -1; /* dup'd stdin during -s/--run */
120 int saved_stdout = -1; /* dup'd stdout during -s/--run */
122 /* The linked list of zero or more filters, and one plugin. */
123 struct backend *top;
125 static char *random_fifo_dir = NULL;
126 static char *random_fifo = NULL;
128 static void
129 usage (void)
131 /* --{short,long}-options remain undocumented */
132 const char *opt_list =
133 #include "synopsis.c"
135 printf ("%s\n", opt_list);
136 printf ("Please read the nbdkit(1) manual page for full usage.\n");
139 static void
140 display_version (void)
142 if (strcmp (NBDKIT_VERSION_EXTRA, "") == 0)
143 printf ("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
144 else
145 printf ("%s %s (%s)\n",
146 PACKAGE_NAME, PACKAGE_VERSION, NBDKIT_VERSION_EXTRA);
149 static void
150 dump_config (void)
152 CLEANUP_FREE char *binary = NULL;
154 #ifdef __linux__
155 binary = realpath ("/proc/self/exe", NULL);
156 #else
157 #ifdef WIN32
158 /* GetModuleFileNameA has a crappy interface that prevents us from
159 * getting the length of the path so we just have to guess at an
160 * upper limit here. It will at least truncate it properly with \0.
161 * _get_pgmptr would be a better alternative except that it isn't
162 * implemented in MinGW. XXX
164 binary = malloc (256);
165 if (!GetModuleFileNameA (NULL, binary, 256)) {
166 free (binary);
167 binary = NULL;
169 #endif
170 #endif
172 if (binary != NULL)
173 printf ("%s=%s\n", "binary", binary);
174 printf ("%s=%s\n", "bindir", bindir);
175 printf ("%s=%s\n", "filterdir", filterdir);
176 printf ("%s=%s\n", "host_cpu", host_cpu);
177 printf ("%s=%s\n", "host_os", host_os);
178 printf ("%s=%s\n", "libdir", libdir);
179 printf ("%s=%s\n", "mandir", mandir);
180 printf ("%s=%s\n", "name", PACKAGE_NAME);
181 printf ("%s=%s\n", "plugindir", plugindir);
182 printf ("%s=%s\n", "root_tls_certificates_dir", root_tls_certificates_dir);
183 printf ("%s=%s\n", "sbindir", sbindir);
184 #ifdef HAVE_LIBSELINUX
185 printf ("selinux=yes\n");
186 #else
187 printf ("selinux=no\n");
188 #endif
189 printf ("%s=%s\n", "sysconfdir", sysconfdir);
190 #ifdef HAVE_GNUTLS
191 printf ("tls=yes\n");
192 #else
193 printf ("tls=no\n");
194 #endif
195 printf ("%s=%s\n", "version", PACKAGE_VERSION);
196 if (strcmp (NBDKIT_VERSION_EXTRA, "") != 0)
197 printf ("%s=%s\n", "version_extra", NBDKIT_VERSION_EXTRA);
198 printf ("%s=%d\n", "version_major", NBDKIT_VERSION_MAJOR);
199 printf ("%s=%d\n", "version_minor", NBDKIT_VERSION_MINOR);
200 #ifdef HAVE_LIBZSTD
201 printf ("zstd=yes\n");
202 #else
203 printf ("zstd=no\n");
204 #endif
208 main (int argc, char *argv[])
210 int c;
211 bool help = false, version = false, dump_plugin = false;
212 int tls_set_on_cli = false;
213 bool short_name;
214 const char *filename;
215 char *p;
216 static struct filter_filename {
217 struct filter_filename *next;
218 const char *filename;
219 } *filter_filenames = NULL;
220 size_t i;
221 const char *magic_config_key;
223 error_if_stdio_closed ();
224 winsock_init ();
226 #if !ENABLE_LIBFUZZER
227 threadlocal_init ();
228 #else
229 static bool main_called = false;
230 if (!main_called) {
231 threadlocal_init ();
232 main_called = true;
234 #endif
236 /* The default setting for TLS depends on whether we were
237 * compiled with GnuTLS.
239 #ifdef HAVE_GNUTLS
240 tls = 1;
241 #else
242 tls = 0;
243 #endif
245 /* Returns 0 if no socket activation, or the number of FDs. */
246 socket_activation = get_socket_activation ();
248 for (;;) {
249 c = getopt_long (argc, argv, short_options, long_options, NULL);
250 if (c == -1)
251 break;
253 switch (c) {
254 case DUMP_CONFIG_OPTION:
255 dump_config ();
256 exit (EXIT_SUCCESS);
258 case DUMP_PLUGIN_OPTION:
259 dump_plugin = true;
260 break;
262 case EXIT_WITH_PARENT_OPTION:
263 #ifdef HAVE_EXIT_WITH_PARENT
264 exit_with_parent = true;
265 foreground = true;
266 break;
267 #else
268 fprintf (stderr,
269 "%s: --exit-with-parent is not implemented "
270 "for this operating system\n",
271 program_name);
272 exit (EXIT_FAILURE);
273 #endif
275 case FILTER_OPTION:
277 struct filter_filename *t;
279 t = malloc (sizeof *t);
280 if (t == NULL) {
281 perror ("malloc");
282 exit (EXIT_FAILURE);
284 t->next = filter_filenames;
285 t->filename = optarg;
286 filter_filenames = t;
288 break;
290 case LOG_OPTION:
291 if (strcmp (optarg, "stderr") == 0)
292 log_to = LOG_TO_STDERR;
293 else if (strcmp (optarg, "syslog") == 0)
294 log_to = LOG_TO_SYSLOG;
295 else if (strcmp (optarg, "null") == 0)
296 log_to = LOG_TO_NULL;
297 else {
298 fprintf (stderr, "%s: "
299 "--log must be \"stderr\", \"syslog\" or \"null\"\n",
300 program_name);
301 exit (EXIT_FAILURE);
303 break;
305 case LONG_OPTIONS_OPTION:
306 for (i = 0; long_options[i].name != NULL; ++i) {
307 if (strcmp (long_options[i].name, "long-options") != 0 &&
308 strcmp (long_options[i].name, "short-options") != 0)
309 printf ("--%s\n", long_options[i].name);
311 exit (EXIT_SUCCESS);
313 case RUN_OPTION:
314 if (socket_activation) {
315 fprintf (stderr, "%s: cannot use socket activation with --run flag\n",
316 program_name);
317 exit (EXIT_FAILURE);
319 run = optarg;
320 foreground = true;
321 break;
323 case SELINUX_LABEL_OPTION:
324 selinux_label = optarg;
325 break;
327 case SHORT_OPTIONS_OPTION:
328 for (i = 0; short_options[i]; ++i) {
329 if (short_options[i] != ':')
330 printf ("-%c\n", short_options[i]);
332 exit (EXIT_SUCCESS);
334 case SWAP_OPTION:
335 swap = 1;
336 break;
338 case TLS_OPTION:
339 tls_set_on_cli = true;
340 if (ascii_strcasecmp (optarg, "require") == 0 ||
341 ascii_strcasecmp (optarg, "required") == 0 ||
342 ascii_strcasecmp (optarg, "force") == 0)
343 tls = 2;
344 else {
345 tls = nbdkit_parse_bool (optarg);
346 if (tls == -1)
347 exit (EXIT_FAILURE);
349 break;
351 case TLS_CERTIFICATES_OPTION:
352 tls_certificates_dir = optarg;
353 break;
355 case TLS_PSK_OPTION:
356 tls_psk = optarg;
357 break;
359 case TLS_VERIFY_PEER_OPTION:
360 tls_verify_peer = true;
361 break;
363 case VSOCK_OPTION:
364 #if defined(AF_VSOCK) && defined(VMADDR_CID_ANY)
365 vsock = true;
366 break;
367 #else
368 fprintf (stderr, "%s: AF_VSOCK is not supported on this platform\n",
369 program_name);
370 exit (EXIT_FAILURE);
371 #endif
373 case '4':
374 tcpip_sock_af = AF_INET;
375 break;
377 case '6':
378 tcpip_sock_af = AF_INET6;
379 break;
381 case 'D':
382 add_debug_flag (optarg);
383 break;
385 case 'e':
386 export_name = optarg;
387 break;
389 case 'f':
390 foreground = true;
391 break;
393 case 'g':
394 group = optarg;
395 break;
397 case 'i':
398 if (socket_activation) {
399 fprintf (stderr, "%s: cannot use socket activation with -i flag\n",
400 program_name);
401 exit (EXIT_FAILURE);
403 ipaddr = optarg;
404 break;
406 case MASK_HANDSHAKE_OPTION:
407 if (nbdkit_parse_unsigned ("mask-handshake",
408 optarg, &mask_handshake) == -1)
409 exit (EXIT_FAILURE);
410 break;
412 case 'n':
413 newstyle = true;
414 break;
416 case NO_SR_OPTION:
417 no_sr = true;
418 break;
420 case 'o':
421 newstyle = false;
422 break;
424 case 'P':
425 pidfile = nbdkit_absolute_path (optarg);
426 if (pidfile == NULL)
427 exit (EXIT_FAILURE);
428 break;
430 case 'p':
431 if (socket_activation) {
432 fprintf (stderr, "%s: cannot use socket activation with -p flag\n",
433 program_name);
434 exit (EXIT_FAILURE);
436 port = optarg;
437 break;
439 case 'r':
440 read_only = true;
441 break;
443 case 's':
444 if (socket_activation) {
445 fprintf (stderr, "%s: cannot use socket activation with -s flag\n",
446 program_name);
447 exit (EXIT_FAILURE);
449 listen_stdin = true;
450 #ifdef WIN32
451 /* This could be implemented with a bit of work. The problem
452 * currently is that we try to use recv() on the stdio file
453 * descriptor which winsock does not support (nor Linux in
454 * fact). We would need to implement a test to see if the file
455 * descriptor is a socket or not and use either read or recv as
456 * appropriate.
458 NOT_IMPLEMENTED_ON_WINDOWS ("-s");
459 #endif
460 break;
462 case 't':
463 if (nbdkit_parse_unsigned ("threads", optarg, &threads) == -1)
464 exit (EXIT_FAILURE);
465 /* XXX Worth a maximimum limit on threads? */
466 break;
468 case 'U':
469 if (socket_activation) {
470 fprintf (stderr, "%s: cannot use socket activation with -U flag\n",
471 program_name);
472 exit (EXIT_FAILURE);
474 if (strcmp (optarg, "-") == 0)
475 unixsocket = make_random_fifo ();
476 else
477 unixsocket = nbdkit_absolute_path (optarg);
478 if (unixsocket == NULL)
479 exit (EXIT_FAILURE);
480 break;
482 case 'u':
483 user = optarg;
484 break;
486 case 'v':
487 verbose = true;
488 break;
490 case 'V':
491 version = true;
492 break;
494 case HELP_OPTION:
495 help = true;
496 break;
498 default:
499 usage ();
500 exit (EXIT_FAILURE);
504 /* No extra parameters. */
505 if (optind >= argc) {
506 if (help) {
507 usage ();
508 exit (EXIT_SUCCESS);
510 if (version) {
511 display_version ();
512 exit (EXIT_SUCCESS);
514 if (dump_plugin) {
515 /* Incorrect use of --dump-plugin. */
516 fprintf (stderr,
517 "%s: use 'nbdkit plugin --dump-plugin' or\n"
518 "'nbdkit /path/to/plugin." SOEXT " --dump-plugin' or\n"
519 "if you want to find out about the server use --dump-config\n",
520 program_name);
521 exit (EXIT_FAILURE);
524 /* Otherwise this is an error. */
525 fprintf (stderr,
526 "%s: no plugins given on the command line.\n"
527 "Use '%s --help' or "
528 "read the nbdkit(1) manual page for documentation.\n",
529 program_name, program_name);
530 exit (EXIT_FAILURE);
533 /* --tls=require and oldstyle won't work. */
534 if (tls == 2 && !newstyle) {
535 fprintf (stderr,
536 "%s: cannot use oldstyle protocol (-o) and require TLS\n",
537 program_name);
538 exit (EXIT_FAILURE);
541 /* Set the umask to a known value. This makes the behaviour of
542 * plugins when creating files more predictable, and also removes an
543 * implicit dependency on umask when calling mkstemp(3).
545 umask (0022);
547 /* If we will or might use syslog. */
548 if (log_to == LOG_TO_SYSLOG || log_to == LOG_TO_DEFAULT)
549 openlog (program_name, LOG_PID, 0);
551 /* Print the version in debug output, right after syslog initialization. */
552 if (strcmp (NBDKIT_VERSION_EXTRA, "") == 0)
553 debug ("%s %s", PACKAGE_NAME, PACKAGE_VERSION);
554 else
555 debug ("%s %s (%s)", PACKAGE_NAME, PACKAGE_VERSION, NBDKIT_VERSION_EXTRA);
557 /* Initialize TLS. */
558 crypto_init (tls_set_on_cli);
559 assert (tls != -1);
561 /* Implement --exit-with-parent early in case plugin initialization
562 * takes a long time and the parent exits during that time.
564 #ifdef HAVE_EXIT_WITH_PARENT
565 if (exit_with_parent) {
566 if (set_exit_with_parent () == -1) {
567 perror ("nbdkit: --exit-with-parent");
568 exit (EXIT_FAILURE);
571 #endif
573 /* If the user has mixed up -p/--run/-s/-U/--vsock options, then
574 * give an error.
576 * XXX Actually the server could easily be extended to handle both
577 * TCP/IP and Unix sockets, or even multiple TCP/IP ports.
579 if ((port && unixsocket) ||
580 (port && listen_stdin) ||
581 (unixsocket && listen_stdin) ||
582 (listen_stdin && run) ||
583 (listen_stdin && dump_plugin) ||
584 (vsock && unixsocket) ||
585 (vsock && listen_stdin)) {
586 fprintf (stderr,
587 "%s: --dump-plugin, -p, --run, -s, -U or --vsock options "
588 "cannot be used in this combination\n",
589 program_name);
590 exit (EXIT_FAILURE);
593 /* The remaining command line arguments are the plugin name and
594 * parameters. If --help, --version or --dump-plugin were specified
595 * then we open the plugin so that we can display the per-plugin
596 * help/version/plugin information.
598 filename = argv[optind++];
599 short_name = is_short_name (filename);
601 /* Is there an executable script located in the plugindir?
602 * If so we simply execute it with the current command line.
604 if (short_name) {
605 struct stat statbuf;
606 CLEANUP_FREE char *script;
608 if (asprintf (&script,
609 "%s/nbdkit-%s-plugin", plugindir, filename) == -1) {
610 perror ("asprintf");
611 exit (EXIT_FAILURE);
614 if (stat (script, &statbuf) == 0 &&
615 (statbuf.st_mode & S_IXUSR) != 0) {
616 /* We're going to execute the plugin directly.
617 * Replace argv[0] with argv[optind-1] and move further arguments
618 * down the list.
620 argv[0] = argv[optind-1];
621 for (i = optind; i <= argc; i++)
622 argv[i-1] = argv[i];
623 execv (script, argv);
624 perror (script);
625 exit (EXIT_FAILURE);
629 /* Open the plugin (first) and then wrap the plugin with the
630 * filters. The filters are wrapped in reverse order that they
631 * appear on the command line so that in the end ‘top’ points to
632 * the first filter on the command line.
634 top = open_plugin_so (0, filename, short_name);
635 i = 1;
636 while (filter_filenames) {
637 struct filter_filename *t = filter_filenames;
639 filename = t->filename;
640 short_name = is_short_name (filename);
642 top = open_filter_so (top, i++, filename, short_name);
644 filter_filenames = t->next;
645 free (t);
648 /* Apply nbdkit.* flags for the server. */
649 apply_debug_flags (RTLD_DEFAULT, "nbdkit");
651 /* Check all debug flags were used, and free them. */
652 free_debug_flags ();
654 if (help) {
655 struct backend *b;
657 usage ();
658 for_each_backend (b) {
659 printf ("\n");
660 b->usage (b);
662 top->free (top);
663 exit (EXIT_SUCCESS);
666 if (version) {
667 const char *v;
668 struct backend *b;
670 display_version ();
671 for_each_backend (b) {
672 printf ("%s", b->name);
673 if ((v = b->version (b)) != NULL)
674 printf (" %s", v);
675 printf ("\n");
677 top->free (top);
678 exit (EXIT_SUCCESS);
681 /* Call config and config_complete to parse the parameters.
683 * If the plugin provides magic_config_key then any "bare" values
684 * (ones not containing "=") are prefixed with this key.
686 * For backwards compatibility with old plugins, and to support
687 * scripting languages, if magic_config_key == NULL then if the
688 * first parameter is bare it is prefixed with the key "script", and
689 * any other bare parameters are errors.
691 * Keys must live for the life of nbdkit. Since we want to avoid
692 * modifying argv (so that /proc/PID/cmdline remains sane) but we
693 * need to create a key from argv[i] = "key=value" we must intern
694 * the keys, which are then freed at the end of main().
696 magic_config_key = top->magic_config_key (top);
697 for (i = 0; optind < argc; ++i, ++optind) {
698 size_t n;
700 p = strchr (argv[optind], '=');
701 n = p - argv[optind];
702 if (p && is_config_key (argv[optind], n)) { /* Is it key=value? */
703 const char *key = nbdkit_strndup_intern (argv[optind], n);
704 if (key == NULL)
705 exit (EXIT_FAILURE);
706 top->config (top, key, p+1);
708 else if (magic_config_key == NULL) {
709 if (i == 0) /* magic script parameter */
710 top->config (top, "script", argv[optind]);
711 else {
712 fprintf (stderr,
713 "%s: expecting key=value on the command line but got: %s\n",
714 program_name, argv[optind]);
715 exit (EXIT_FAILURE);
718 else { /* magic config key */
719 top->config (top, magic_config_key, argv[optind]);
723 /* This must run after parsing the parameters so that the script can
724 * be loaded for scripting languages. But it must be called before
725 * config_complete so that the plugin doesn't check for missing
726 * parameters.
728 if (dump_plugin) {
729 top->dump_fields (top);
730 top->free (top);
731 free_interns ();
732 exit (EXIT_SUCCESS);
735 top->config_complete (top);
737 /* Select the correct thread model based on config. */
738 lock_init_thread_model ();
740 /* Tell the plugin that we are about to start serving. This must be
741 * called before we change user, fork, or open any sockets.
743 top->get_ready (top);
745 switch_stdio ();
746 configured = true;
748 start_serving ();
750 top->cleanup (top);
751 top->free (top);
752 top = NULL;
754 free (unixsocket);
755 free (pidfile);
757 if (random_fifo) {
758 unlink (random_fifo);
759 free (random_fifo);
762 if (random_fifo_dir) {
763 rmdir (random_fifo_dir);
764 free (random_fifo_dir);
767 crypto_free ();
768 close_quit_pipe ();
770 free_interns ();
772 /* Note: Don't exit here, otherwise this won't work when compiled
773 * for libFuzzer.
775 return EXIT_SUCCESS;
778 /* Implementation of '-U -' */
779 static char *
780 make_random_fifo (void)
782 char *sock;
784 random_fifo_dir = make_temporary_directory ();
785 if (random_fifo_dir == NULL) {
786 perror ("make_temporary_directory");
787 return NULL;
790 if (asprintf (&random_fifo, "%s" DIR_SEPARATOR_STR "socket",
791 random_fifo_dir) == -1) {
792 perror ("asprintf");
793 goto error;
796 sock = strdup (random_fifo);
797 if (sock == NULL) {
798 perror ("strdup");
799 goto error;
802 return sock;
804 error:
805 free (random_fifo);
806 random_fifo = NULL;
807 rmdir (random_fifo_dir);
808 free (random_fifo_dir);
809 random_fifo_dir = NULL;
810 return NULL;
813 static struct backend *
814 open_plugin_so (size_t i, const char *name, int short_name)
816 struct backend *ret;
817 char *filename = (char *) name;
818 bool free_filename = false;
819 void *dl;
820 struct nbdkit_plugin *(*plugin_init) (void);
821 char *error;
823 if (short_name) {
824 /* Short names are rewritten relative to the plugindir. */
825 if (asprintf (&filename,
826 "%s/nbdkit-%s-plugin." SOEXT, plugindir, name) == -1) {
827 perror ("asprintf");
828 exit (EXIT_FAILURE);
830 free_filename = true;
833 dl = dlopen (filename, RTLD_NOW|RTLD_GLOBAL);
834 if (dl == NULL) {
835 fprintf (stderr,
836 "%s: error: cannot open plugin '%s': %s\n"
837 "Use '%s --help' or "
838 "read the nbdkit(1) manual page for documentation.\n",
839 program_name, name, dlerror (),
840 program_name);
841 exit (EXIT_FAILURE);
844 /* Initialize the plugin. See dlopen(3) to understand C weirdness. */
845 dlerror ();
846 plugin_init = dlsym (dl, "plugin_init");
847 if ((error = dlerror ()) != NULL) {
848 fprintf (stderr, "%s: %s: %s\n", program_name, name, error);
849 exit (EXIT_FAILURE);
851 if (!plugin_init) {
852 fprintf (stderr, "%s: %s: invalid plugin_init\n", program_name, name);
853 exit (EXIT_FAILURE);
856 /* Register the plugin. */
857 ret = plugin_register (i, filename, dl, plugin_init);
859 if (free_filename)
860 free (filename);
862 return ret;
865 static struct backend *
866 open_filter_so (struct backend *next, size_t i,
867 const char *name, int short_name)
869 struct backend *ret;
870 char *filename = (char *) name;
871 bool free_filename = false;
872 void *dl;
873 struct nbdkit_filter *(*filter_init) (void);
874 char *error;
876 if (short_name) {
877 /* Short names are rewritten relative to the filterdir. */
878 if (asprintf (&filename,
879 "%s/nbdkit-%s-filter." SOEXT, filterdir, name) == -1) {
880 perror ("asprintf");
881 exit (EXIT_FAILURE);
883 free_filename = true;
886 dl = dlopen (filename, RTLD_NOW|RTLD_GLOBAL);
887 if (dl == NULL) {
888 fprintf (stderr, "%s: error: cannot open filter '%s': %s\n",
889 program_name, name, dlerror ());
890 exit (EXIT_FAILURE);
893 /* Initialize the filter. See dlopen(3) to understand C weirdness. */
894 dlerror ();
895 filter_init = dlsym (dl, "filter_init");
896 if ((error = dlerror ()) != NULL) {
897 fprintf (stderr, "%s: %s: %s\n", program_name, name, error);
898 exit (EXIT_FAILURE);
900 if (!filter_init) {
901 fprintf (stderr, "%s: %s: invalid filter_init\n", program_name, name);
902 exit (EXIT_FAILURE);
905 /* Register the filter. */
906 ret = filter_register (next, i, filename, dl, filter_init);
908 if (free_filename)
909 free (filename);
911 return ret;
914 static void
915 start_serving (void)
917 sockets socks = empty_vector;
918 size_t i;
920 set_up_quit_pipe ();
921 #if !ENABLE_LIBFUZZER
922 set_up_signals ();
923 #endif
925 /* Lock the process into memory if requested. */
926 if (swap) {
927 #ifdef HAVE_MLOCKALL
928 if (mlockall (MCL_CURRENT | MCL_FUTURE) == -1) {
929 fprintf (stderr, "%s: --swap: mlockall: %m\n", program_name);
930 exit (EXIT_FAILURE);
932 debug ("mlockall done");
933 #else
934 fprintf (stderr, "%s: mlockall (--swap option) "
935 "is not supported on this platform\n", program_name);
936 exit (EXIT_FAILURE);
937 #endif
940 /* Socket activation: the ‘socket_activation’ variable (> 0) is the
941 * number of file descriptors from FIRST_SOCKET_ACTIVATION_FD to
942 * FIRST_SOCKET_ACTIVATION_FD+socket_activation-1.
944 if (socket_activation) {
945 if (sockets_reserve (&socks, socket_activation) == -1) {
946 perror ("realloc");
947 exit (EXIT_FAILURE);
949 for (i = 0; i < socket_activation; ++i) {
950 int s = FIRST_SOCKET_ACTIVATION_FD + i, r;
951 /* This can't fail because of the reservation above. */
952 r = sockets_append (&socks, s);
953 assert (r == 0);
955 debug ("using socket activation, nr_socks = %zu", socks.len);
956 change_user ();
957 write_pidfile ();
958 top->after_fork (top);
959 accept_incoming_connections (&socks);
960 return;
963 /* Handling a single connection on stdin/stdout. */
964 if (listen_stdin) {
965 change_user ();
966 write_pidfile ();
967 top->after_fork (top);
968 threadlocal_new_server_thread ();
969 handle_single_connection (saved_stdin, saved_stdout);
970 return;
973 /* Handling multiple connections on TCP/IP, Unix domain socket or
974 * AF_VSOCK.
976 if (unixsocket)
977 bind_unix_socket (&socks);
978 else if (vsock)
979 bind_vsock (&socks);
980 else
981 bind_tcpip_socket (&socks);
983 run_command ();
984 change_user ();
985 fork_into_background ();
986 write_pidfile ();
987 top->after_fork (top);
988 accept_incoming_connections (&socks);
991 static void
992 write_pidfile (void)
994 int fd;
995 pid_t pid;
996 char pidstr[64];
997 size_t len;
999 if (!pidfile)
1000 return;
1002 pid = getpid ();
1003 /* Don't put a trailing \n after the PID file on Windows. It is
1004 * turned into \r\n which causes problems if you process the file
1005 * using a Unix tool like bash, especially when running the test
1006 * suite.
1008 snprintf (pidstr, sizeof pidstr, "%d"
1009 #ifndef WIN32
1010 "\n"
1011 #endif
1012 , (int) pid);
1013 len = strlen (pidstr);
1015 fd = open (pidfile, O_WRONLY|O_TRUNC|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644);
1016 if (fd == -1) {
1017 perror (pidfile);
1018 exit (EXIT_FAILURE);
1021 if (write (fd, pidstr, len) < len ||
1022 close (fd) == -1) {
1023 perror (pidfile);
1024 exit (EXIT_FAILURE);
1027 debug ("written pidfile %s", pidfile);
1030 /* When parsing plugin and filter config key=value from the command
1031 * line, is the key a simple alphanumeric with period, underscore or
1032 * dash?
1034 static bool
1035 is_config_key (const char *key, size_t len)
1037 static const char allowed_first[] =
1038 "abcdefghijklmnopqrstuvwxyz"
1039 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1040 static const char allowed[] =
1041 "._-"
1042 "0123456789"
1043 "abcdefghijklmnopqrstuvwxyz"
1044 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1046 if (len == 0)
1047 return false;
1049 if (strchr (allowed_first, key[0]) == NULL)
1050 return false;
1052 /* This works in context of the caller since key[len] == '='. */
1053 if (strspn (key, allowed) != len)
1054 return false;
1056 return true;
1059 /* Refuse to run if stdin/out/err are closed, whether or not -s is used. */
1060 static void
1061 error_if_stdio_closed (void)
1063 #ifdef F_GETFL
1064 if (fcntl (STDERR_FILENO, F_GETFL) == -1) {
1065 /* Nowhere we can report the error. Oh well. */
1066 exit (EXIT_FAILURE);
1068 if (fcntl (STDIN_FILENO, F_GETFL) == -1 ||
1069 fcntl (STDOUT_FILENO, F_GETFL) == -1) {
1070 perror ("expecting stdin/stdout to be opened");
1071 exit (EXIT_FAILURE);
1073 #endif
1076 /* Sanitize stdin/stdout to /dev/null, after saving the originals
1077 * when needed. We are still single-threaded at this point, and
1078 * already checked that stdin/out were open, so we don't have to
1079 * worry about other threads accidentally grabbing our intended fds,
1080 * or races on FD_CLOEXEC. POSIX says that 'fflush(NULL)' is
1081 * supposed to reset the underlying offset of seekable stdin, but
1082 * glibc is buggy and requires an explicit fflush(stdin) as
1083 * well. https://sourceware.org/bugzilla/show_bug.cgi?id=12799
1085 static void
1086 switch_stdio (void)
1088 #if defined(F_DUPFD_CLOEXEC) || defined(F_DUPFD)
1089 fflush (stdin);
1090 fflush (NULL);
1091 if (listen_stdin || run) {
1092 #ifndef F_DUPFD_CLOEXEC
1093 #define F_DUPFD_CLOEXEC F_DUPFD
1094 #endif
1095 saved_stdin = fcntl (STDIN_FILENO, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
1096 saved_stdout = fcntl (STDOUT_FILENO, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
1097 #if F_DUPFD == F_DUPFD_CLOEXEC
1098 saved_stdin = set_cloexec (saved_stdin);
1099 saved_stdout = set_cloexec (saved_stdout);
1100 #endif
1101 if (saved_stdin == -1 || saved_stdout == -1) {
1102 perror ("fcntl");
1103 exit (EXIT_FAILURE);
1106 #endif
1107 #ifndef WIN32
1108 close (STDIN_FILENO);
1109 close (STDOUT_FILENO);
1110 if (open ("/dev/null", O_RDONLY) != STDIN_FILENO ||
1111 open ("/dev/null", O_WRONLY) != STDOUT_FILENO) {
1112 perror ("open");
1113 exit (EXIT_FAILURE);
1115 #endif
1118 /* On Windows the Winsock library must be initialized early.
1119 * https://docs.microsoft.com/en-us/windows/win32/winsock/initializing-winsock
1121 static void
1122 winsock_init (void)
1124 #ifdef WIN32
1125 WSADATA wsaData;
1126 int result;
1128 result = WSAStartup (MAKEWORD (2, 2), &wsaData);
1129 if (result != 0) {
1130 fprintf (stderr, "WSAStartup failed: %d\n", result);
1131 exit (EXIT_FAILURE);
1133 #endif