2 * Copyright (C) 2013-2014 Red Hat Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of Red Hat nor the names of its contributors may be
17 * used to endorse or promote products derived from this software without
18 * specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
30 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 #include <sys/types.h>
53 #include "nbdkit-plugin.h"
56 static void open_plugin_so (const char *filename
);
57 static void start_serving (void);
58 static void set_up_signals (void);
59 static void change_user (void);
60 static void write_pidfile (void);
61 static void fork_into_background (void);
62 static uid_t
parseuser (const char *);
63 static gid_t
parsegroup (const char *);
65 int foreground
; /* -f */
66 const char *ipaddr
; /* -i */
67 int listen_stdin
; /* -s */
68 char *pidfile
; /* -P */
69 const char *port
; /* -p */
70 int readonly
; /* -r */
71 char *unixsocket
; /* -U */
72 const char *user
, *group
; /* -u & -g */
77 enum { HELP_OPTION
= CHAR_MAX
+ 1 };
79 static const char *short_options
= "fg:i:p:P:rsu:U:vV";
80 static const struct option long_options
[] = {
81 { "help", 0, NULL
, HELP_OPTION
},
82 { "foreground", 0, NULL
, 'f' },
83 { "no-fork", 0, NULL
, 'f' },
84 { "group", 1, NULL
, 'g' },
85 { "ip-addr", 1, NULL
, 'i' },
86 { "ipaddr", 1, NULL
, 'i' },
87 { "pid-file", 1, NULL
, 'P' },
88 { "pidfile", 1, NULL
, 'P' },
89 { "port", 1, NULL
, 'p' },
90 { "read-only", 0, NULL
, 'r' },
91 { "readonly", 0, NULL
, 'r' },
92 { "single", 0, NULL
, 's' },
93 { "stdin", 0, NULL
, 's' },
94 { "unix", 1, NULL
, 'U' },
95 { "user", 1, NULL
, 'u' },
96 { "verbose", 0, NULL
, 'v' },
97 { "version", 0, NULL
, 'V' },
103 printf ("nbdkit [-f] [-g GROUP] [-i IPADDR] [-P PIDFILE] [-p PORT]\n"
104 " [-r] [-s] [-U SOCKET] [-u USER] [-v] [-V]\n"
105 " PLUGIN [key=value [key=value [...]]]\n"
107 "Please read the nbdkit(1) manual page for full usage.\n");
111 display_version (void)
113 printf ("%s %s\n", PACKAGE_NAME
, PACKAGE_VERSION
);
117 main (int argc
, char *argv
[])
121 int help
= 0, version
= 0;
126 c
= getopt_long (argc
, argv
, short_options
, long_options
, &option_index
);
131 case 0: /* options which are long only */
132 fprintf (stderr
, "%s: unknown long option: %s (%d)\n",
133 program_name
, long_options
[option_index
].name
, option_index
);
149 pidfile
= nbdkit_absolute_path (optarg
);
167 unixsocket
= nbdkit_absolute_path (optarg
);
168 if (unixsocket
== NULL
)
194 /* No extra parameters. */
195 if (optind
>= argc
) {
205 /* Otherwise this is an error. */
207 "%s: no plugins given on the command line.\nRead nbdkit(1) for documentation.\n",
212 /* Remaining command line arguments define the plugins and plugin
213 * configuration. If --help or --version was specified, we still
214 * partially parse these in order that we can display the per-plugin
215 * help/version information. In future (when the new protocol and
216 * export names are permitted) we will allow multiple plugins to be
217 * given, but at the moment only one plugin is allowed.
219 while (optind
< argc
) {
220 const char *filename
= argv
[optind
];
223 open_plugin_so (filename
);
225 /* Find key=value configuration parameters for this plugin. */
227 while (optind
< argc
&& (p
= strchr (argv
[optind
], '=')) != NULL
) {
232 plugin_config (argv
[optind
], p
+1);
239 printf ("\n%s:\n\n", filename
);
250 plugin_config_complete ();
252 /* If we supported export names, then we'd continue in the loop
253 * here, but at the moment only one plugin may be used per server
254 * so exit if there are any more.
258 fprintf (stderr
, "%s: this server only supports a single plugin\n",
275 open_plugin_so (const char *name
)
277 char *filename
= (char *) name
;
278 int free_filename
= 0;
280 struct nbdkit_plugin
*(*plugin_init
) (void);
283 if (strchr (name
, '.') == NULL
&& strchr (name
, '/') == NULL
) {
284 /* Short names are rewritten relative to libdir. */
285 if (asprintf (&filename
, "%s/nbdkit-%s-plugin.so", plugindir
, name
) == -1) {
292 dl
= dlopen (filename
, RTLD_NOW
|RTLD_LOCAL
);
294 fprintf (stderr
, "%s: %s: %s\n", program_name
, filename
, dlerror ());
298 /* Initialize the plugin. See dlopen(3) to understand C weirdness. */
300 *(void **) (&plugin_init
) = dlsym (dl
, "plugin_init");
301 if ((error
= dlerror ()) != NULL
) {
302 fprintf (stderr
, "%s: %s: %s\n", program_name
, name
, dlerror ());
306 fprintf (stderr
, "%s: %s: invalid plugin_init\n", program_name
, name
);
310 /* Register the plugin. */
311 plugin_register (filename
, dl
, plugin_init
);
323 /* If the user has mixed up -p/-U/-s options, then give an error.
325 * XXX Actually the server could easily be extended to handle both
326 * TCP/IP and Unix sockets, or even multiple TCP/IP ports.
328 if ((port
&& unixsocket
) || (port
&& listen_stdin
) ||
329 (unixsocket
&& listen_stdin
)) {
330 fprintf (stderr
, "%s: -p, -U and -s options cannot appear at the same time\n",
337 /* Handling a single connection on stdin/stdout. */
341 tls_new_server_thread ();
342 if (handle_single_connection (0, 1) == -1)
347 /* Handling multiple connections on TCP/IP or a Unix domain socket. */
349 socks
= bind_unix_socket (&nr_socks
);
351 socks
= bind_tcpip_socket (&nr_socks
);
354 fork_into_background ();
356 accept_incoming_connections (socks
, nr_socks
);
358 free_listening_sockets (socks
, nr_socks
);
362 handle_quit (int sig
)
368 set_up_signals (void)
372 memset (&sa
, 0, sizeof sa
);
373 sa
.sa_flags
= SA_RESTART
;
374 sa
.sa_handler
= handle_quit
;
375 sigaction (SIGINT
, &sa
, NULL
);
376 sigaction (SIGQUIT
, &sa
, NULL
);
377 sigaction (SIGTERM
, &sa
, NULL
);
378 sigaction (SIGHUP
, &sa
, NULL
);
380 memset (&sa
, 0, sizeof sa
);
381 sa
.sa_flags
= SA_RESTART
;
382 sa
.sa_handler
= SIG_IGN
;
383 sigaction (SIGPIPE
, &sa
, NULL
);
390 gid_t gid
= parsegroup (group
);
392 if (setgid (gid
) == -1) {
397 /* Kill supplemental groups from parent process. */
398 if (setgroups (1, &gid
) == -1) {
399 perror ("setgroups");
403 debug ("changed group to %s", group
);
407 uid_t uid
= parseuser (user
);
409 if (setuid (uid
) == -1) {
414 debug ("changed user to %s", user
);
430 snprintf (pidstr
, sizeof pidstr
, "%d\n", pid
);
431 len
= strlen (pidstr
);
433 fd
= open (pidfile
, O_WRONLY
|O_TRUNC
|O_CREAT
|O_CLOEXEC
|O_NOCTTY
, 0644);
439 if (write (fd
, pidstr
, len
) < len
||
445 debug ("written pidfile %s", pidfile
);
449 fork_into_background (void)
462 if (pid
> 0) /* Parent process exits. */
467 /* Close stdin/stdout and redirect to /dev/null. */
470 open ("/dev/null", O_RDONLY
);
471 open ("/dev/null", O_WRONLY
);
473 /* If not verbose, set stderr to the same as stdout as well. */
477 debug ("forked into background (new pid = %d)", getpid ());
481 parseuser (const char *id
)
494 if (sscanf (id
, "%d", &val
) == 1)
497 fprintf (stderr
, "%s: -u option: %s is not a valid user name or uid",
499 if (saved_errno
!= 0)
500 fprintf (stderr
, " (getpwnam error: %s)", strerror (saved_errno
));
501 fprintf (stderr
, "\n");
509 parsegroup (const char *id
)
522 if (sscanf (id
, "%d", &val
) == 1)
525 fprintf (stderr
, "%s: -g option: %s is not a valid group name or gid",
527 if (saved_errno
!= 0)
528 fprintf (stderr
, " (getgrnam error: %s)", strerror (saved_errno
));
529 fprintf (stderr
, "\n");