| /* | 
 |  * QEMU storage daemon | 
 |  * | 
 |  * Copyright (c) 2003-2008 Fabrice Bellard | 
 |  * Copyright (c) 2019 Kevin Wolf <kwolf@redhat.com> | 
 |  * | 
 |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
 |  * of this software and associated documentation files (the "Software"), to deal | 
 |  * in the Software without restriction, including without limitation the rights | 
 |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
 |  * copies of the Software, and to permit persons to whom the Software is | 
 |  * furnished to do so, subject to the following conditions: | 
 |  * | 
 |  * The above copyright notice and this permission notice shall be included in | 
 |  * all copies or substantial portions of the Software. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
 |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
 |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
 |  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
 |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
 |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
 |  * THE SOFTWARE. | 
 |  */ | 
 |  | 
 | #include "qemu/osdep.h" | 
 |  | 
 | #include <getopt.h> | 
 |  | 
 | #include "block/block.h" | 
 | #include "block/nbd.h" | 
 | #include "chardev/char.h" | 
 | #include "crypto/init.h" | 
 | #include "monitor/monitor.h" | 
 | #include "monitor/monitor-internal.h" | 
 |  | 
 | #include "qapi/error.h" | 
 | #include "qapi/qapi-visit-block-core.h" | 
 | #include "qapi/qapi-visit-block-export.h" | 
 | #include "qapi/qapi-visit-control.h" | 
 | #include "qapi/qmp/qdict.h" | 
 | #include "qapi/qmp/qstring.h" | 
 | #include "qapi/qobject-input-visitor.h" | 
 |  | 
 | #include "qemu/help-texts.h" | 
 | #include "qemu-version.h" | 
 | #include "qemu/cutils.h" | 
 | #include "qemu/config-file.h" | 
 | #include "qemu/error-report.h" | 
 | #include "qemu/help_option.h" | 
 | #include "qemu/job.h" | 
 | #include "qemu/log.h" | 
 | #include "qemu/main-loop.h" | 
 | #include "qemu/module.h" | 
 | #include "qemu/option.h" | 
 | #include "qom/object_interfaces.h" | 
 |  | 
 | #include "storage-daemon/qapi/qapi-commands.h" | 
 | #include "storage-daemon/qapi/qapi-init-commands.h" | 
 |  | 
 | #include "sysemu/runstate.h" | 
 | #include "trace/control.h" | 
 |  | 
 | static const char *pid_file; | 
 | static char *pid_file_realpath; | 
 | static volatile bool exit_requested = false; | 
 |  | 
 | void qemu_system_killed(int signal, pid_t pid) | 
 | { | 
 |     exit_requested = true; | 
 | } | 
 |  | 
 | void qmp_quit(Error **errp) | 
 | { | 
 |     exit_requested = true; | 
 | } | 
 |  | 
 | static void help(void) | 
 | { | 
 |     printf( | 
 | "Usage: %s [options]\n" | 
 | "QEMU storage daemon\n" | 
 | "\n" | 
 | "  -h, --help             display this help and exit\n" | 
 | "  -T, --trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n" | 
 | "                         specify tracing options\n" | 
 | "  -V, --version          output version information and exit\n" | 
 | "\n" | 
 | "  --blockdev [driver=]<driver>[,node-name=<N>][,discard=ignore|unmap]\n" | 
 | "             [,cache.direct=on|off][,cache.no-flush=on|off]\n" | 
 | "             [,read-only=on|off][,auto-read-only=on|off]\n" | 
 | "             [,force-share=on|off][,detect-zeroes=on|off|unmap]\n" | 
 | "             [,driver specific parameters...]\n" | 
 | "                         configure a block backend\n" | 
 | "\n" | 
 | "  --chardev <options>    configure a character device backend\n" | 
 | "                         (see the qemu(1) man page for possible options)\n" | 
 | "\n" | 
 | "  --daemonize            daemonize the process, and have the parent exit\n" | 
 | "                         once startup is complete\n" | 
 | "\n" | 
 | "  --export [type=]nbd,id=<id>,node-name=<node-name>[,name=<export-name>]\n" | 
 | "           [,writable=on|off][,bitmap=<name>]\n" | 
 | "                         export the specified block node over NBD\n" | 
 | "                         (requires --nbd-server)\n" | 
 | "\n" | 
 | #ifdef CONFIG_FUSE | 
 | "  --export [type=]fuse,id=<id>,node-name=<node-name>,mountpoint=<file>\n" | 
 | "           [,growable=on|off][,writable=on|off][,allow-other=on|off|auto]\n" | 
 | "                         export the specified block node over FUSE\n" | 
 | "\n" | 
 | #endif /* CONFIG_FUSE */ | 
 | #ifdef CONFIG_VHOST_USER_BLK_SERVER | 
 | "  --export [type=]vhost-user-blk,id=<id>,node-name=<node-name>,\n" | 
 | "           addr.type=unix,addr.path=<socket-path>[,writable=on|off]\n" | 
 | "           [,logical-block-size=<block-size>][,num-queues=<num-queues>]\n" | 
 | "                         export the specified block node as a\n" | 
 | "                         vhost-user-blk device over UNIX domain socket\n" | 
 | "  --export [type=]vhost-user-blk,id=<id>,node-name=<node-name>,\n" | 
 | "           addr.type=fd,addr.str=<fd>[,writable=on|off]\n" | 
 | "           [,logical-block-size=<block-size>][,num-queues=<num-queues>]\n" | 
 | "                         export the specified block node as a\n" | 
 | "                         vhost-user-blk device over file descriptor\n" | 
 | "\n" | 
 | #endif /* CONFIG_VHOST_USER_BLK_SERVER */ | 
 | #ifdef CONFIG_VDUSE_BLK_EXPORT | 
 | "  --export [type=]vduse-blk,id=<id>,node-name=<node-name>\n" | 
 | "           ,name=<vduse-name>[,writable=on|off]\n" | 
 | "           [,num-queues=<num-queues>][,queue-size=<queue-size>]\n" | 
 | "           [,logical-block-size=<logical-block-size>]\n" | 
 | "           [,serial=<serial-number>]\n" | 
 | "                         export the specified block node as a\n" | 
 | "                         vduse-blk device\n" | 
 | "\n" | 
 | #endif /* CONFIG_VDUSE_BLK_EXPORT */ | 
 | "  --monitor [chardev=]name[,mode=control][,pretty[=on|off]]\n" | 
 | "                         configure a QMP monitor\n" | 
 | "\n" | 
 | "  --nbd-server addr.type=inet,addr.host=<host>,addr.port=<port>\n" | 
 | "               [,tls-creds=<id>][,tls-authz=<id>][,max-connections=<n>]\n" | 
 | "  --nbd-server addr.type=unix,addr.path=<path>\n" | 
 | "               [,tls-creds=<id>][,tls-authz=<id>][,max-connections=<n>]\n" | 
 | "                         start an NBD server for exporting block nodes\n" | 
 | "\n" | 
 | "  --object help          list object types that can be added\n" | 
 | "  --object <type>,help   list properties for the given object type\n" | 
 | "  --object <type>[,<property>=<value>...]\n" | 
 | "                         create a new object of type <type>, setting\n" | 
 | "                         properties in the order they are specified. Note\n" | 
 | "                         that the 'id' property must be set.\n" | 
 | "                         See the qemu(1) man page for documentation of the\n" | 
 | "                         objects that can be added.\n" | 
 | "\n" | 
 | "  --pidfile <path>       write process ID to a file after startup\n" | 
 | "\n" | 
 | QEMU_HELP_BOTTOM "\n", | 
 |     g_get_prgname()); | 
 | } | 
 |  | 
 | enum { | 
 |     OPTION_BLOCKDEV = 256, | 
 |     OPTION_CHARDEV, | 
 |     OPTION_DAEMONIZE, | 
 |     OPTION_EXPORT, | 
 |     OPTION_MONITOR, | 
 |     OPTION_NBD_SERVER, | 
 |     OPTION_OBJECT, | 
 |     OPTION_PIDFILE, | 
 | }; | 
 |  | 
 | extern QemuOptsList qemu_chardev_opts; | 
 |  | 
 | static void init_qmp_commands(void) | 
 | { | 
 |     qmp_init_marshal(&qmp_commands); | 
 |  | 
 |     QTAILQ_INIT(&qmp_cap_negotiation_commands); | 
 |     qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", | 
 |                          qmp_marshal_qmp_capabilities, | 
 |                          QCO_ALLOW_PRECONFIG, 0); | 
 | } | 
 |  | 
 | static int getopt_set_loc(int argc, char **argv, const char *optstring, | 
 |                           const struct option *longopts) | 
 | { | 
 |     int c, save_index; | 
 |  | 
 |     optarg = NULL; | 
 |     save_index = optind; | 
 |     c = getopt_long(argc, argv, optstring, longopts, NULL); | 
 |     if (optarg) { | 
 |         loc_set_cmdline(argv, save_index, MAX(1, optind - save_index)); | 
 |     } | 
 |     return c; | 
 | } | 
 |  | 
 | /** | 
 |  * Process QSD command-line arguments. | 
 |  * | 
 |  * This is done in two passes: | 
 |  * | 
 |  * First (@pre_init_pass is true), we do a pass where all global | 
 |  * arguments pertaining to the QSD process (like --help or --daemonize) | 
 |  * are processed.  This pass is done before most of the QEMU-specific | 
 |  * initialization steps (e.g. initializing the block layer or QMP), and | 
 |  * so must only process arguments that are not really QEMU-specific. | 
 |  * | 
 |  * Second (@pre_init_pass is false), we (sequentially) process all | 
 |  * QEMU/QSD-specific arguments.  Many of these arguments are effectively | 
 |  * translated to QMP commands (like --blockdev for blockdev-add, or | 
 |  * --export for block-export-add). | 
 |  */ | 
 | static void process_options(int argc, char *argv[], bool pre_init_pass) | 
 | { | 
 |     int c; | 
 |  | 
 |     static const struct option long_options[] = { | 
 |         {"blockdev", required_argument, NULL, OPTION_BLOCKDEV}, | 
 |         {"chardev", required_argument, NULL, OPTION_CHARDEV}, | 
 |         {"daemonize", no_argument, NULL, OPTION_DAEMONIZE}, | 
 |         {"export", required_argument, NULL, OPTION_EXPORT}, | 
 |         {"help", no_argument, NULL, 'h'}, | 
 |         {"monitor", required_argument, NULL, OPTION_MONITOR}, | 
 |         {"nbd-server", required_argument, NULL, OPTION_NBD_SERVER}, | 
 |         {"object", required_argument, NULL, OPTION_OBJECT}, | 
 |         {"pidfile", required_argument, NULL, OPTION_PIDFILE}, | 
 |         {"trace", required_argument, NULL, 'T'}, | 
 |         {"version", no_argument, NULL, 'V'}, | 
 |         {0, 0, 0, 0} | 
 |     }; | 
 |  | 
 |     /* | 
 |      * In contrast to the system emulator, QEMU-specific options are processed | 
 |      * in the order they are given on the command lines. This means that things | 
 |      * must be defined first before they can be referenced in another option. | 
 |      */ | 
 |     optind = 1; | 
 |     while ((c = getopt_set_loc(argc, argv, "-hT:V", long_options)) != -1) { | 
 |         bool handle_option_pre_init; | 
 |  | 
 |         /* Should this argument be processed in the pre-init pass? */ | 
 |         handle_option_pre_init = | 
 |             c == '?' || | 
 |             c == 'h' || | 
 |             c == 'V' || | 
 |             c == OPTION_DAEMONIZE || | 
 |             c == OPTION_PIDFILE; | 
 |  | 
 |         /* Process every option only in its respective pass */ | 
 |         if (pre_init_pass != handle_option_pre_init) { | 
 |             continue; | 
 |         } | 
 |  | 
 |         switch (c) { | 
 |         case '?': | 
 |             exit(EXIT_FAILURE); | 
 |         case 'h': | 
 |             help(); | 
 |             exit(EXIT_SUCCESS); | 
 |         case 'T': | 
 |             trace_opt_parse(optarg); | 
 |             trace_init_file(); | 
 |             break; | 
 |         case 'V': | 
 |             printf("qemu-storage-daemon version " | 
 |                    QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n"); | 
 |             exit(EXIT_SUCCESS); | 
 |         case OPTION_BLOCKDEV: | 
 |             { | 
 |                 Visitor *v; | 
 |                 BlockdevOptions *options; | 
 |  | 
 |                 v = qobject_input_visitor_new_str(optarg, "driver", | 
 |                                                   &error_fatal); | 
 |  | 
 |                 visit_type_BlockdevOptions(v, NULL, &options, &error_fatal); | 
 |                 visit_free(v); | 
 |  | 
 |                 qmp_blockdev_add(options, &error_fatal); | 
 |                 qapi_free_BlockdevOptions(options); | 
 |                 break; | 
 |             } | 
 |         case OPTION_CHARDEV: | 
 |             { | 
 |                 /* TODO This interface is not stable until we QAPIfy it */ | 
 |                 QemuOpts *opts = qemu_opts_parse_noisily(&qemu_chardev_opts, | 
 |                                                          optarg, true); | 
 |                 if (opts == NULL) { | 
 |                     exit(EXIT_FAILURE); | 
 |                 } | 
 |  | 
 |                 if (!qemu_chr_new_from_opts(opts, NULL, &error_fatal)) { | 
 |                     /* No error, but NULL returned means help was printed */ | 
 |                     exit(EXIT_SUCCESS); | 
 |                 } | 
 |                 qemu_opts_del(opts); | 
 |                 break; | 
 |             } | 
 |         case OPTION_DAEMONIZE: | 
 |             if (os_set_daemonize(true) < 0) { | 
 |                 /* | 
 |                  * --daemonize is parsed before monitor_init_globals(), so | 
 |                  * error_report() does not work yet | 
 |                  */ | 
 |                 fprintf(stderr, "--daemonize not supported in this build\n"); | 
 |                 exit(EXIT_FAILURE); | 
 |             } | 
 |             break; | 
 |         case OPTION_EXPORT: | 
 |             { | 
 |                 Visitor *v; | 
 |                 BlockExportOptions *export; | 
 |  | 
 |                 v = qobject_input_visitor_new_str(optarg, "type", &error_fatal); | 
 |                 visit_type_BlockExportOptions(v, NULL, &export, &error_fatal); | 
 |                 visit_free(v); | 
 |  | 
 |                 qmp_block_export_add(export, &error_fatal); | 
 |                 qapi_free_BlockExportOptions(export); | 
 |                 break; | 
 |             } | 
 |         case OPTION_MONITOR: | 
 |             { | 
 |                 Visitor *v; | 
 |                 MonitorOptions *monitor; | 
 |  | 
 |                 v = qobject_input_visitor_new_str(optarg, "chardev", | 
 |                                                   &error_fatal); | 
 |                 visit_type_MonitorOptions(v, NULL, &monitor, &error_fatal); | 
 |                 visit_free(v); | 
 |  | 
 |                 /* TODO Catch duplicate monitor IDs */ | 
 |                 monitor_init(monitor, false, &error_fatal); | 
 |                 qapi_free_MonitorOptions(monitor); | 
 |                 break; | 
 |             } | 
 |         case OPTION_NBD_SERVER: | 
 |             { | 
 |                 Visitor *v; | 
 |                 NbdServerOptions *options; | 
 |  | 
 |                 v = qobject_input_visitor_new_str(optarg, NULL, &error_fatal); | 
 |                 visit_type_NbdServerOptions(v, NULL, &options, &error_fatal); | 
 |                 visit_free(v); | 
 |  | 
 |                 nbd_server_start_options(options, &error_fatal); | 
 |                 qapi_free_NbdServerOptions(options); | 
 |                 break; | 
 |             } | 
 |         case OPTION_OBJECT: | 
 |             user_creatable_process_cmdline(optarg); | 
 |             break; | 
 |         case OPTION_PIDFILE: | 
 |             pid_file = optarg; | 
 |             break; | 
 |         case 1: | 
 |             error_report("Unexpected argument"); | 
 |             exit(EXIT_FAILURE); | 
 |         default: | 
 |             g_assert_not_reached(); | 
 |         } | 
 |     } | 
 |     loc_set_none(); | 
 | } | 
 |  | 
 | static void pid_file_cleanup(void) | 
 | { | 
 |     unlink(pid_file_realpath); | 
 | } | 
 |  | 
 | static void pid_file_init(void) | 
 | { | 
 |     Error *err = NULL; | 
 |  | 
 |     if (!pid_file) { | 
 |         return; | 
 |     } | 
 |  | 
 |     if (!qemu_write_pidfile(pid_file, &err)) { | 
 |         error_reportf_err(err, "cannot create PID file: "); | 
 |         exit(EXIT_FAILURE); | 
 |     } | 
 |  | 
 |     pid_file_realpath = g_malloc(PATH_MAX); | 
 |     if (!realpath(pid_file, pid_file_realpath)) { | 
 |         error_report("cannot resolve PID file path: %s: %s", | 
 |                      pid_file, strerror(errno)); | 
 |         unlink(pid_file); | 
 |         exit(EXIT_FAILURE); | 
 |     } | 
 |  | 
 |     atexit(pid_file_cleanup); | 
 | } | 
 |  | 
 | int main(int argc, char *argv[]) | 
 | { | 
 | #ifdef CONFIG_POSIX | 
 |     signal(SIGPIPE, SIG_IGN); | 
 | #endif | 
 |  | 
 |     error_init(argv[0]); | 
 |     qemu_init_exec_dir(argv[0]); | 
 |     os_setup_signal_handling(); | 
 |  | 
 |     process_options(argc, argv, true); | 
 |  | 
 |     os_daemonize(); | 
 |  | 
 |     module_call_init(MODULE_INIT_QOM); | 
 |     module_call_init(MODULE_INIT_TRACE); | 
 |     qemu_add_opts(&qemu_trace_opts); | 
 |     qcrypto_init(&error_fatal); | 
 |     bdrv_init(); | 
 |     monitor_init_globals(); | 
 |     init_qmp_commands(); | 
 |  | 
 |     if (!trace_init_backends()) { | 
 |         return EXIT_FAILURE; | 
 |     } | 
 |     qemu_set_log(LOG_TRACE, &error_fatal); | 
 |  | 
 |     qemu_init_main_loop(&error_fatal); | 
 |     process_options(argc, argv, false); | 
 |  | 
 |     /* | 
 |      * Write the pid file after creating chardevs, exports, and NBD servers but | 
 |      * before accepting connections. This ordering is documented. Do not change | 
 |      * it. | 
 |      */ | 
 |     pid_file_init(); | 
 |     os_setup_post(); | 
 |  | 
 |     while (!exit_requested) { | 
 |         main_loop_wait(false); | 
 |     } | 
 |  | 
 |     blk_exp_close_all(); | 
 |     bdrv_drain_all_begin(); | 
 |     job_cancel_sync_all(); | 
 |     bdrv_close_all(); | 
 |  | 
 |     monitor_cleanup(); | 
 |     qemu_chr_cleanup(); | 
 |     user_creatable_cleanup(); | 
 |  | 
 |     return EXIT_SUCCESS; | 
 | } |