/* * QEMU storage daemon * * Copyright (c) 2003-2008 Fabrice Bellard * Copyright (c) 2019 Kevin Wolf * * 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 #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/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 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=]][,events=][,file=]\n" " specify tracing options\n" " -V, --version output version information and exit\n" "\n" " --blockdev [driver=][,node-name=][,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 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=,node-name=[,name=]\n" " [,writable=on|off][,bitmap=]\n" " export the specified block node over NBD\n" " (requires --nbd-server)\n" "\n" #ifdef CONFIG_FUSE " --export [type=]fuse,id=,node-name=,mountpoint=\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=,node-name=,\n" " addr.type=unix,addr.path=[,writable=on|off]\n" " [,logical-block-size=][,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=,node-name=,\n" " addr.type=fd,addr.str=[,writable=on|off]\n" " [,logical-block-size=][,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 */ " --monitor [chardev=]name[,mode=control][,pretty[=on|off]]\n" " configure a QMP monitor\n" "\n" " --nbd-server addr.type=inet,addr.host=,addr.port=\n" " [,tls-creds=][,tls-authz=][,max-connections=]\n" " --nbd-server addr.type=unix,addr.path=\n" " [,tls-creds=][,tls-authz=][,max-connections=]\n" " start an NBD server for exporting block nodes\n" "\n" " --object help list object types that can be added\n" " --object ,help list properties for the given object type\n" " --object [,=...]\n" " create a new object of 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 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) { error_report("--daemonize not supported in this build"); 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); } 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); } 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_core(); 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; }