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
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
46 #include <sys/types.h>
48 #include <sys/socket.h>
50 #ifdef HAVE_SYS_MMAN_H
54 #ifdef HAVE_LINUX_VM_SOCKETS_H
55 #include <linux/vm_sockets.h>
63 #include "nbd-protocol.h"
65 #include "exit-with-parent.h"
67 #ifdef ENABLE_LIBFUZZER
68 #define main fuzzer_main
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
;
114 /* --{short,long}-options remain undocumented */
116 #include "synopsis.c"
119 "Please read the nbdkit(1) manual page for full usage.\n");
123 display_version (void)
125 printf ("%s %s\n", PACKAGE_NAME
, PACKAGE_VERSION
);
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");
142 printf ("selinux=no\n");
144 printf ("%s=%s\n", "sysconfdir", sysconfdir
);
146 printf ("tls=yes\n");
150 printf ("%s=%s\n", "version", PACKAGE_VERSION
);
154 main (int argc
, char *argv
[])
157 bool help
= false, version
= false, dump_plugin
= false;
158 int tls_set_on_cli
= false;
160 const char *filename
;
162 static struct filter_filename
{
163 struct filter_filename
*next
;
164 const char *filename
;
165 } *filter_filenames
= NULL
;
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. */
174 if (fcntl (STDIN_FILENO
, F_GETFL
) == -1 ||
175 fcntl (STDOUT_FILENO
, F_GETFL
) == -1) {
176 perror ("expecting stdin/stdout to be opened");
180 #if !ENABLE_LIBFUZZER
183 static bool main_called
= false;
190 /* The default setting for TLS depends on whether we were
191 * compiled with GnuTLS.
199 /* Returns 0 if no socket activation, or the number of FDs. */
200 socket_activation
= get_socket_activation ();
203 c
= getopt_long (argc
, argv
, short_options
, long_options
, NULL
);
208 case DUMP_CONFIG_OPTION
:
212 case DUMP_PLUGIN_OPTION
:
216 case EXIT_WITH_PARENT_OPTION
:
217 #ifdef HAVE_EXIT_WITH_PARENT
218 exit_with_parent
= true;
223 "%s: --exit-with-parent is not implemented "
224 "for this operating system\n",
231 struct filter_filename
*t
;
233 t
= malloc (sizeof *t
);
238 t
->next
= filter_filenames
;
239 t
->filename
= optarg
;
240 filter_filenames
= t
;
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
;
252 fprintf (stderr
, "%s: "
253 "--log must be \"stderr\", \"syslog\" or \"null\"\n",
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
);
268 if (socket_activation
) {
269 fprintf (stderr
, "%s: cannot use socket activation with --run flag\n",
277 case SELINUX_LABEL_OPTION
:
278 selinux_label
= optarg
;
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
]);
293 tls_set_on_cli
= true;
294 if (strcasecmp (optarg
, "require") == 0 ||
295 strcasecmp (optarg
, "required") == 0 ||
296 strcasecmp (optarg
, "force") == 0)
299 tls
= nbdkit_parse_bool (optarg
);
305 case TLS_CERTIFICATES_OPTION
:
306 tls_certificates_dir
= optarg
;
313 case TLS_VERIFY_PEER_OPTION
:
314 tls_verify_peer
= true;
322 fprintf (stderr
, "%s: AF_VSOCK is not supported on this platform\n",
328 add_debug_flag (optarg
);
333 if (strnlen (exportname
, NBD_MAX_STRING
+ 1) > NBD_MAX_STRING
) {
334 nbdkit_error ("export name too long");
337 /* TODO: Check that name is valid UTF-8? */
350 if (socket_activation
) {
351 fprintf (stderr
, "%s: cannot use socket activation with -i flag\n",
358 case MASK_HANDSHAKE_OPTION
:
359 if (nbdkit_parse_unsigned ("mask-handshake",
360 optarg
, &mask_handshake
) == -1)
377 pidfile
= nbdkit_absolute_path (optarg
);
383 if (socket_activation
) {
384 fprintf (stderr
, "%s: cannot use socket activation with -p flag\n",
396 if (socket_activation
) {
397 fprintf (stderr
, "%s: cannot use socket activation with -s flag\n",
405 if (nbdkit_parse_unsigned ("threads", optarg
, &threads
) == -1)
407 /* XXX Worth a maximimum limit on threads? */
411 if (socket_activation
) {
412 fprintf (stderr
, "%s: cannot use socket activation with -U flag\n",
416 if (strcmp (optarg
, "-") == 0)
417 unixsocket
= make_random_fifo ();
419 unixsocket
= nbdkit_absolute_path (optarg
);
420 if (unixsocket
== NULL
)
446 /* No extra parameters. */
447 if (optind
>= argc
) {
457 /* Incorrect use of --dump-plugin. */
459 "%s: use 'nbdkit plugin --dump-plugin' or\n"
460 "'nbdkit /path/to/plugin.so --dump-plugin'\n",
465 /* Otherwise this is an error. */
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
);
474 /* Oldstyle protocol + exportname not allowed. */
475 if (!newstyle
&& exportname
!= NULL
) {
477 "%s: cannot use oldstyle protocol (-o) and exportname (-e)\n",
482 /* If exportname was not set on the command line, use "". */
483 if (exportname
== NULL
)
486 /* --tls=require and oldstyle won't work. */
487 if (tls
== 2 && !newstyle
) {
489 "%s: cannot use oldstyle protocol (-o) and require TLS\n",
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).
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
);
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");
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.
533 CLEANUP_FREE
char *script
;
535 if (asprintf (&script
,
536 "%s/nbdkit-%s-plugin", plugindir
, filename
) == -1) {
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
547 argv
[0] = argv
[optind
-1];
548 for (i
= optind
; i
<= argc
; i
++)
550 execv (script
, argv
);
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
);
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
;
575 /* Apply nbdkit.* flags for the server. */
576 apply_debug_flags (NULL
, "nbdkit");
578 /* Check all debug flags were used, and free them. */
585 for_each_backend (b
) {
589 backend
->free (backend
);
598 for_each_backend (b
) {
599 printf ("%s", b
->name
);
600 if ((v
= b
->version (b
)) != NULL
)
604 backend
->free (backend
);
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 */
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
]);
630 "%s: expecting key=value on the command line but got: %s\n",
631 program_name
, argv
[optind
]);
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
646 backend
->dump_fields (backend
);
647 backend
->free (backend
);
651 backend
->config_complete (backend
);
653 /* Select the correct thread model based on config. */
654 lock_init_thread_model ();
657 #if !ENABLE_LIBFUZZER
663 backend
->free (backend
);
670 unlink (random_fifo
);
674 if (random_fifo_dir
) {
675 rmdir (random_fifo_dir
);
676 free (random_fifo_dir
);
682 /* Note: Don't exit here, otherwise this won't work when compiled
688 /* Implementation of '-U -' */
690 make_random_fifo (void)
692 char template[] = "/tmp/nbdkitXXXXXX";
695 if (mkdtemp (template) == NULL
) {
700 random_fifo_dir
= strdup (template);
701 if (random_fifo_dir
== NULL
) {
706 if (asprintf (&random_fifo
, "%s/socket", template) == -1) {
711 sock
= strdup (random_fifo
);
720 static struct backend
*
721 open_plugin_so (size_t i
, const char *name
, int short_name
)
724 char *filename
= (char *) name
;
725 bool free_filename
= false;
727 struct nbdkit_plugin
*(*plugin_init
) (void);
731 /* Short names are rewritten relative to the plugindir. */
732 if (asprintf (&filename
,
733 "%s/nbdkit-%s-plugin.so", plugindir
, name
) == -1) {
737 free_filename
= true;
740 dl
= dlopen (filename
, RTLD_NOW
|RTLD_GLOBAL
);
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 (),
751 /* Initialize the plugin. See dlopen(3) to understand C weirdness. */
753 *(void **) (&plugin_init
) = dlsym (dl
, "plugin_init");
754 if ((error
= dlerror ()) != NULL
) {
755 fprintf (stderr
, "%s: %s: %s\n", program_name
, name
, error
);
759 fprintf (stderr
, "%s: %s: invalid plugin_init\n", program_name
, name
);
763 /* Register the plugin. */
764 ret
= plugin_register (i
, filename
, dl
, plugin_init
);
772 static struct backend
*
773 open_filter_so (struct backend
*next
, size_t i
,
774 const char *name
, int short_name
)
777 char *filename
= (char *) name
;
778 bool free_filename
= false;
780 struct nbdkit_filter
*(*filter_init
) (void);
784 /* Short names are rewritten relative to the filterdir. */
785 if (asprintf (&filename
,
786 "%s/nbdkit-%s-filter.so", filterdir
, name
) == -1) {
790 free_filename
= true;
793 dl
= dlopen (filename
, RTLD_NOW
|RTLD_GLOBAL
);
795 fprintf (stderr
, "%s: error: cannot open filter '%s': %s\n",
796 program_name
, name
, dlerror ());
800 /* Initialize the filter. See dlopen(3) to understand C weirdness. */
802 *(void **) (&filter_init
) = dlsym (dl
, "filter_init");
803 if ((error
= dlerror ()) != NULL
) {
804 fprintf (stderr
, "%s: %s: %s\n", program_name
, name
, error
);
808 fprintf (stderr
, "%s: %s: invalid filter_init\n", program_name
, name
);
812 /* Register the filter. */
813 ret
= filter_register (next
, i
, filename
, dl
, filter_init
);
828 /* If the user has mixed up -p/--run/-s/-U/--vsock options, then
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
) ||
842 "%s: -p, --run, -s, -U or --vsock options cannot be used"
843 "in this combination\n",
848 /* Lock the process into memory if requested. */
851 if (mlockall (MCL_CURRENT
| MCL_FUTURE
) == -1) {
852 fprintf (stderr
, "%s: --swap: mlockall: %m\n", program_name
);
855 debug ("mlockall done");
857 fprintf (stderr
, "%s: mlockall (--swap option) "
858 "is not supported on this platform\n");
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
);
875 for (i
= 0; i
< nr_socks
; ++i
)
876 socks
[i
] = FIRST_SOCKET_ACTIVATION_FD
+ i
;
879 accept_incoming_connections (socks
, nr_socks
);
883 /* Handling a single connection on stdin/stdout. */
887 threadlocal_new_server_thread ();
888 handle_single_connection (0, 1);
892 /* Handling multiple connections on TCP/IP, Unix domain socket or
896 socks
= bind_unix_socket (&nr_socks
);
898 socks
= bind_vsock (&nr_socks
);
900 socks
= bind_tcpip_socket (&nr_socks
);
904 fork_into_background ();
906 accept_incoming_connections (socks
, nr_socks
);
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);
930 if (write (fd
, pidstr
, len
) < len
||
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
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
[] =
952 "abcdefghijklmnopqrstuvwxyz"
953 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
958 if (strchr (allowed_first
, key
[0]) == NULL
)
961 /* This works in context of the caller since key[len] == '='. */
962 if (strspn (key
, allowed
) != len
)