/* * Parse incoming URI and populate *options with the host * and device information */ static int vxhs_parse_uri(const char *filename, QDict *options) { URI *uri = NULL; char *port; int ret = 0; trace_vxhs_parse_uri_filename(filename); uri = uri_parse(filename); if (!uri || !uri->server || !uri->path) { uri_free(uri); return -EINVAL; } qdict_put_str(options, VXHS_OPT_SERVER ".host", uri->server); if (uri->port) { port = g_strdup_printf("%d", uri->port); qdict_put_str(options, VXHS_OPT_SERVER ".port", port); g_free(port); } qdict_put_str(options, "vdisk-id", uri->path); trace_vxhs_parse_uri_hostinfo(uri->server, uri->port); uri_free(uri); return ret; }
/* test commands that return an error due to invalid parameters */ static void test_dispatch_cmd_failure(void) { QDict *req = qdict_new(); QDict *args = qdict_new(); QDict *resp; qdict_put_str(req, "execute", "user_def_cmd2"); resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp != NULL); assert(qdict_haskey(resp, "error")); qobject_unref(resp); qobject_unref(req); /* check that with extra arguments it throws an error */ req = qdict_new(); qdict_put_int(args, "a", 66); qdict_put(req, "arguments", args); qdict_put_str(req, "execute", "user_def_cmd"); resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp != NULL); assert(qdict_haskey(resp, "error")); qobject_unref(resp); qobject_unref(req); }
/* test commands that involve both input parameters and return values */ static void test_dispatch_cmd_io(void) { QDict *req = qdict_new(); QDict *args = qdict_new(); QDict *args3 = qdict_new(); QDict *ud1a = qdict_new(); QDict *ud1b = qdict_new(); QDict *ret, *ret_dict, *ret_dict_dict, *ret_dict_dict_userdef; QDict *ret_dict_dict2, *ret_dict_dict2_userdef; QNum *ret3; int64_t val; qdict_put_int(ud1a, "integer", 42); qdict_put_str(ud1a, "string", "hello"); qdict_put_int(ud1b, "integer", 422); qdict_put_str(ud1b, "string", "hello2"); qdict_put(args, "ud1a", ud1a); qdict_put(args, "ud1b", ud1b); qdict_put(req, "arguments", args); qdict_put_str(req, "execute", "user_def_cmd2"); ret = qobject_to(QDict, test_qmp_dispatch(req)); assert(!strcmp(qdict_get_str(ret, "string0"), "blah1")); ret_dict = qdict_get_qdict(ret, "dict1"); assert(!strcmp(qdict_get_str(ret_dict, "string1"), "blah2")); ret_dict_dict = qdict_get_qdict(ret_dict, "dict2"); ret_dict_dict_userdef = qdict_get_qdict(ret_dict_dict, "userdef"); assert(qdict_get_int(ret_dict_dict_userdef, "integer") == 42); assert(!strcmp(qdict_get_str(ret_dict_dict_userdef, "string"), "hello")); assert(!strcmp(qdict_get_str(ret_dict_dict, "string"), "blah3")); ret_dict_dict2 = qdict_get_qdict(ret_dict, "dict3"); ret_dict_dict2_userdef = qdict_get_qdict(ret_dict_dict2, "userdef"); assert(qdict_get_int(ret_dict_dict2_userdef, "integer") == 422); assert(!strcmp(qdict_get_str(ret_dict_dict2_userdef, "string"), "hello2")); assert(!strcmp(qdict_get_str(ret_dict_dict2, "string"), "blah4")); qobject_unref(ret); qdict_put_int(args3, "a", 66); qdict_put(req, "arguments", args3); qdict_put_str(req, "execute", "guest-get-time"); ret3 = qobject_to(QNum, test_qmp_dispatch(req)); g_assert(qnum_get_try_int(ret3, &val)); g_assert_cmpint(val, ==, 66); qobject_unref(ret3); qobject_unref(req); }
/* test generated deallocation on an object whose construction was prematurely * terminated due to an error */ static void test_dealloc_partial(void) { static const char text[] = "don't leak me"; UserDefTwo *ud2 = NULL; Error *err = NULL; /* create partial object */ { QDict *ud2_dict; Visitor *v; ud2_dict = qdict_new(); qdict_put_str(ud2_dict, "string0", text); v = qobject_input_visitor_new(QOBJECT(ud2_dict)); visit_type_UserDefTwo(v, NULL, &ud2, &err); visit_free(v); qobject_unref(ud2_dict); } /* verify that visit_type_XXX() cleans up properly on error */ error_free_or_abort(&err); assert(!ud2); /* Manually create a partial object, leaving ud2->dict1 at NULL */ ud2 = g_new0(UserDefTwo, 1); ud2->string0 = g_strdup(text); /* tear down partial object */ qapi_free_UserDefTwo(ud2); }
static int openfile(char *name, int flags, bool writethrough, bool force_share, QDict *opts) { Error *local_err = NULL; if (qemuio_blk) { error_report("file open already, try 'help close'"); qobject_unref(opts); return 1; } if (force_share) { if (!opts) { opts = qdict_new(); } if (qdict_haskey(opts, BDRV_OPT_FORCE_SHARE) && strcmp(qdict_get_str(opts, BDRV_OPT_FORCE_SHARE), "on")) { error_report("-U conflicts with image options"); qobject_unref(opts); return 1; } qdict_put_str(opts, BDRV_OPT_FORCE_SHARE, "on"); } qemuio_blk = blk_new_open(name, NULL, opts, flags, &local_err); if (!qemuio_blk) { error_reportf_err(local_err, "can't open%s%s: ", name ? " device " : "", name ?: ""); return 1; }
/* test commands that involve both input parameters and return values */ static void test_dispatch_cmd_io(void) { QDict *req = qdict_new(); QDict *args = qdict_new(); QDict *args3 = qdict_new(); QDict *ud1a = qdict_new(); QDict *ud1b = qdict_new(); QDict *ret, *ret_dict, *ret_dict_dict, *ret_dict_dict_userdef; QDict *ret_dict_dict2, *ret_dict_dict2_userdef; QInt *ret3; qdict_put_int(ud1a, "integer", 42); qdict_put_str(ud1a, "string", "hello"); qdict_put_int(ud1b, "integer", 422); qdict_put_str(ud1b, "string", "hello2"); qdict_put(args, "ud1a", ud1a); qdict_put(args, "ud1b", ud1b); qdict_put(req, "arguments", args); qdict_put_str(req, "execute", "user_def_cmd2"); ret = qobject_to_qdict(test_qmp_dispatch(req)); assert(!strcmp(qdict_get_str(ret, "string0"), "blah1")); ret_dict = qdict_get_qdict(ret, "dict1"); assert(!strcmp(qdict_get_str(ret_dict, "string1"), "blah2")); ret_dict_dict = qdict_get_qdict(ret_dict, "dict2"); ret_dict_dict_userdef = qdict_get_qdict(ret_dict_dict, "userdef"); assert(qdict_get_int(ret_dict_dict_userdef, "integer") == 42); assert(!strcmp(qdict_get_str(ret_dict_dict_userdef, "string"), "hello")); assert(!strcmp(qdict_get_str(ret_dict_dict, "string"), "blah3")); ret_dict_dict2 = qdict_get_qdict(ret_dict, "dict3"); ret_dict_dict2_userdef = qdict_get_qdict(ret_dict_dict2, "userdef"); assert(qdict_get_int(ret_dict_dict2_userdef, "integer") == 422); assert(!strcmp(qdict_get_str(ret_dict_dict2_userdef, "string"), "hello2")); assert(!strcmp(qdict_get_str(ret_dict_dict2, "string"), "blah4")); QDECREF(ret); qdict_put_int(args3, "a", 66); qdict_put(req, "arguments", args3); qdict_put_str(req, "execute", "guest-get-time"); ret3 = qobject_to_qint(test_qmp_dispatch(req)); assert(qint_get_int(ret3) == 66); QDECREF(ret3); QDECREF(req); }
/* test commands with no input and no return value */ static void test_dispatch_cmd(void) { QDict *req = qdict_new(); QDict *resp; qdict_put_str(req, "execute", "user_def_cmd"); resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp != NULL); assert(!qdict_haskey(resp, "error")); qobject_unref(resp); qobject_unref(req); }
static QObject *make_qobject(void) { QDict *qdict = qdict_new(); QList *list = qlist_new(); qdict_put_int(qdict, "foo", 42); qdict_put_str(qdict, "bar", "hello world"); qdict_put_null(qdict, "baz"); qlist_append_int(list, 43); qlist_append_int(list, 44); qlist_append_bool(list, true); qdict_put(qdict, "bee", list); return QOBJECT(qdict); }
static QList *qom_list_types(const char *implements, bool abstract) { QDict *resp; QList *ret; QDict *args = qdict_new(); qdict_put_bool(args, "abstract", abstract); if (implements) { qdict_put_str(args, "implements", implements); } resp = qmp("{'execute': 'qom-list-types'," " 'arguments': %p }", args); g_assert(qdict_haskey(resp, "return")); ret = qdict_get_qlist(resp, "return"); QINCREF(ret); QDECREF(resp); return ret; }
int main(int argc, char **argv) { BlockBackend *blk; BlockDriverState *bs; off_t dev_offset = 0; uint16_t nbdflags = 0; bool disconnect = false; const char *bindto = NULL; const char *port = NULL; char *sockpath = NULL; char *device = NULL; off_t fd_size; QemuOpts *sn_opts = NULL; const char *sn_id_or_name = NULL; const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:"; struct option lopt[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, { "bind", required_argument, NULL, 'b' }, { "port", required_argument, NULL, 'p' }, { "socket", required_argument, NULL, 'k' }, { "offset", required_argument, NULL, 'o' }, { "read-only", no_argument, NULL, 'r' }, { "partition", required_argument, NULL, 'P' }, { "connect", required_argument, NULL, 'c' }, { "disconnect", no_argument, NULL, 'd' }, { "snapshot", no_argument, NULL, 's' }, { "load-snapshot", required_argument, NULL, 'l' }, { "nocache", no_argument, NULL, 'n' }, { "cache", required_argument, NULL, QEMU_NBD_OPT_CACHE }, { "aio", required_argument, NULL, QEMU_NBD_OPT_AIO }, { "discard", required_argument, NULL, QEMU_NBD_OPT_DISCARD }, { "detect-zeroes", required_argument, NULL, QEMU_NBD_OPT_DETECT_ZEROES }, { "shared", required_argument, NULL, 'e' }, { "format", required_argument, NULL, 'f' }, { "persistent", no_argument, NULL, 't' }, { "verbose", no_argument, NULL, 'v' }, { "object", required_argument, NULL, QEMU_NBD_OPT_OBJECT }, { "export-name", required_argument, NULL, 'x' }, { "description", required_argument, NULL, 'D' }, { "tls-creds", required_argument, NULL, QEMU_NBD_OPT_TLSCREDS }, { "image-opts", no_argument, NULL, QEMU_NBD_OPT_IMAGE_OPTS }, { "trace", required_argument, NULL, 'T' }, { "fork", no_argument, NULL, QEMU_NBD_OPT_FORK }, { NULL, 0, NULL, 0 } }; int ch; int opt_ind = 0; char *end; int flags = BDRV_O_RDWR; int partition = -1; int ret = 0; bool seen_cache = false; bool seen_discard = false; bool seen_aio = false; pthread_t client_thread; const char *fmt = NULL; Error *local_err = NULL; BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; QDict *options = NULL; const char *export_name = NULL; const char *export_description = NULL; const char *tlscredsid = NULL; bool imageOpts = false; bool writethrough = true; char *trace_file = NULL; bool fork_process = false; int old_stderr = -1; unsigned socket_activation; /* The client thread uses SIGTERM to interrupt the server. A signal * handler ensures that "qemu-nbd -v -c" exits with a nice status code. */ struct sigaction sa_sigterm; memset(&sa_sigterm, 0, sizeof(sa_sigterm)); sa_sigterm.sa_handler = termsig_handler; sigaction(SIGTERM, &sa_sigterm, NULL); #ifdef CONFIG_POSIX signal(SIGPIPE, SIG_IGN); #endif module_call_init(MODULE_INIT_TRACE); qcrypto_init(&error_fatal); module_call_init(MODULE_INIT_QOM); qemu_add_opts(&qemu_object_opts); qemu_add_opts(&qemu_trace_opts); qemu_init_exec_dir(argv[0]); while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { case 's': flags |= BDRV_O_SNAPSHOT; break; case 'n': optarg = (char *) "none"; /* fallthrough */ case QEMU_NBD_OPT_CACHE: if (seen_cache) { error_report("-n and --cache can only be specified once"); exit(EXIT_FAILURE); } seen_cache = true; if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) == -1) { error_report("Invalid cache mode `%s'", optarg); exit(EXIT_FAILURE); } break; case QEMU_NBD_OPT_AIO: if (seen_aio) { error_report("--aio can only be specified once"); exit(EXIT_FAILURE); } seen_aio = true; if (!strcmp(optarg, "native")) { flags |= BDRV_O_NATIVE_AIO; } else if (!strcmp(optarg, "threads")) { /* this is the default */ } else { error_report("invalid aio mode `%s'", optarg); exit(EXIT_FAILURE); } break; case QEMU_NBD_OPT_DISCARD: if (seen_discard) { error_report("--discard can only be specified once"); exit(EXIT_FAILURE); } seen_discard = true; if (bdrv_parse_discard_flags(optarg, &flags) == -1) { error_report("Invalid discard mode `%s'", optarg); exit(EXIT_FAILURE); } break; case QEMU_NBD_OPT_DETECT_ZEROES: detect_zeroes = qapi_enum_parse(BlockdevDetectZeroesOptions_lookup, optarg, BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX, BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, &local_err); if (local_err) { error_reportf_err(local_err, "Failed to parse detect_zeroes mode: "); exit(EXIT_FAILURE); } if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && !(flags & BDRV_O_UNMAP)) { error_report("setting detect-zeroes to unmap is not allowed " "without setting discard operation to unmap"); exit(EXIT_FAILURE); } break; case 'b': bindto = optarg; break; case 'p': port = optarg; break; case 'o': dev_offset = strtoll (optarg, &end, 0); if (*end) { error_report("Invalid offset `%s'", optarg); exit(EXIT_FAILURE); } if (dev_offset < 0) { error_report("Offset must be positive `%s'", optarg); exit(EXIT_FAILURE); } break; case 'l': if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts, optarg, false); if (!sn_opts) { error_report("Failed in parsing snapshot param `%s'", optarg); exit(EXIT_FAILURE); } } else { sn_id_or_name = optarg; } /* fall through */ case 'r': nbdflags |= NBD_FLAG_READ_ONLY; flags &= ~BDRV_O_RDWR; break; case 'P': partition = strtol(optarg, &end, 0); if (*end) { error_report("Invalid partition `%s'", optarg); exit(EXIT_FAILURE); } if (partition < 1 || partition > 8) { error_report("Invalid partition %d", partition); exit(EXIT_FAILURE); } break; case 'k': sockpath = optarg; if (sockpath[0] != '/') { error_report("socket path must be absolute"); exit(EXIT_FAILURE); } break; case 'd': disconnect = true; break; case 'c': device = optarg; break; case 'e': shared = strtol(optarg, &end, 0); if (*end) { error_report("Invalid shared device number '%s'", optarg); exit(EXIT_FAILURE); } if (shared < 1) { error_report("Shared device number must be greater than 0"); exit(EXIT_FAILURE); } break; case 'f': fmt = optarg; break; case 't': persistent = 1; break; case 'x': export_name = optarg; break; case 'D': export_description = optarg; break; case 'v': verbose = 1; break; case 'V': version(argv[0]); exit(0); break; case 'h': usage(argv[0]); exit(0); break; case '?': error_report("Try `%s --help' for more information.", argv[0]); exit(EXIT_FAILURE); case QEMU_NBD_OPT_OBJECT: { QemuOpts *opts; opts = qemu_opts_parse_noisily(&qemu_object_opts, optarg, true); if (!opts) { exit(EXIT_FAILURE); } } break; case QEMU_NBD_OPT_TLSCREDS: tlscredsid = optarg; break; case QEMU_NBD_OPT_IMAGE_OPTS: imageOpts = true; break; case 'T': g_free(trace_file); trace_file = trace_opt_parse(optarg); break; case QEMU_NBD_OPT_FORK: fork_process = true; break; } } if ((argc - optind) != 1) { error_report("Invalid number of arguments"); error_printf("Try `%s --help' for more information.\n", argv[0]); exit(EXIT_FAILURE); } if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, NULL, NULL)) { exit(EXIT_FAILURE); } if (!trace_init_backends()) { exit(1); } trace_init_file(trace_file); qemu_set_log(LOG_TRACE); socket_activation = check_socket_activation(); if (socket_activation == 0) { setup_address_and_port(&bindto, &port); } else { /* Using socket activation - check user didn't use -p etc. */ const char *err_msg = socket_activation_validate_opts(device, sockpath, bindto, port); if (err_msg != NULL) { error_report("%s", err_msg); exit(EXIT_FAILURE); } /* qemu-nbd can only listen on a single socket. */ if (socket_activation > 1) { error_report("qemu-nbd does not support socket activation with %s > 1", "LISTEN_FDS"); exit(EXIT_FAILURE); } } if (tlscredsid) { if (sockpath) { error_report("TLS is only supported with IPv4/IPv6"); exit(EXIT_FAILURE); } if (device) { error_report("TLS is not supported with a host device"); exit(EXIT_FAILURE); } if (!export_name) { /* Set the default NBD protocol export name, since * we *must* use new style protocol for TLS */ export_name = ""; } tlscreds = nbd_get_tls_creds(tlscredsid, &local_err); if (local_err) { error_report("Failed to get TLS creds %s", error_get_pretty(local_err)); exit(EXIT_FAILURE); } } if (disconnect) { int nbdfd = open(argv[optind], O_RDWR); if (nbdfd < 0) { error_report("Cannot open %s: %s", argv[optind], strerror(errno)); exit(EXIT_FAILURE); } nbd_disconnect(nbdfd); close(nbdfd); printf("%s disconnected\n", argv[optind]); return 0; } if ((device && !verbose) || fork_process) { int stderr_fd[2]; pid_t pid; int ret; if (qemu_pipe(stderr_fd) < 0) { error_report("Error setting up communication pipe: %s", strerror(errno)); exit(EXIT_FAILURE); } /* Now daemonize, but keep a communication channel open to * print errors and exit with the proper status code. */ pid = fork(); if (pid < 0) { error_report("Failed to fork: %s", strerror(errno)); exit(EXIT_FAILURE); } else if (pid == 0) { close(stderr_fd[0]); ret = qemu_daemon(1, 0); /* Temporarily redirect stderr to the parent's pipe... */ old_stderr = dup(STDERR_FILENO); dup2(stderr_fd[1], STDERR_FILENO); if (ret < 0) { error_report("Failed to daemonize: %s", strerror(errno)); exit(EXIT_FAILURE); } /* ... close the descriptor we inherited and go on. */ close(stderr_fd[1]); } else { bool errors = false; char *buf; /* In the parent. Print error messages from the child until * it closes the pipe. */ close(stderr_fd[1]); buf = g_malloc(1024); while ((ret = read(stderr_fd[0], buf, 1024)) > 0) { errors = true; ret = qemu_write_full(STDERR_FILENO, buf, ret); if (ret < 0) { exit(EXIT_FAILURE); } } if (ret < 0) { error_report("Cannot read from daemon: %s", strerror(errno)); exit(EXIT_FAILURE); } /* Usually the daemon should not print any message. * Exit with zero status in that case. */ exit(errors); } } if (device != NULL && sockpath == NULL) { sockpath = g_malloc(128); snprintf(sockpath, 128, SOCKET_PATH, basename(device)); } if (socket_activation == 0) { server_ioc = qio_channel_socket_new(); saddr = nbd_build_socket_address(sockpath, bindto, port); if (qio_channel_socket_listen_sync(server_ioc, saddr, &local_err) < 0) { object_unref(OBJECT(server_ioc)); error_report_err(local_err); return 1; } } else { /* See comment in check_socket_activation above. */ assert(socket_activation == 1); server_ioc = qio_channel_socket_new_fd(FIRST_SOCKET_ACTIVATION_FD, &local_err); if (server_ioc == NULL) { error_report("Failed to use socket activation: %s", error_get_pretty(local_err)); exit(EXIT_FAILURE); } } if (qemu_init_main_loop(&local_err)) { error_report_err(local_err); exit(EXIT_FAILURE); } bdrv_init(); atexit(bdrv_close_all); srcpath = argv[optind]; if (imageOpts) { QemuOpts *opts; if (fmt) { error_report("--image-opts and -f are mutually exclusive"); exit(EXIT_FAILURE); } opts = qemu_opts_parse_noisily(&file_opts, srcpath, true); if (!opts) { qemu_opts_reset(&file_opts); exit(EXIT_FAILURE); } options = qemu_opts_to_qdict(opts, NULL); qemu_opts_reset(&file_opts); blk = blk_new_open(NULL, NULL, options, flags, &local_err); } else { if (fmt) { options = qdict_new(); qdict_put_str(options, "driver", fmt); } blk = blk_new_open(srcpath, NULL, options, flags, &local_err); } if (!blk) { error_reportf_err(local_err, "Failed to blk_new_open '%s': ", argv[optind]); exit(EXIT_FAILURE); } bs = blk_bs(blk); blk_set_enable_write_cache(blk, !writethrough); if (sn_opts) { ret = bdrv_snapshot_load_tmp(bs, qemu_opt_get(sn_opts, SNAPSHOT_OPT_ID), qemu_opt_get(sn_opts, SNAPSHOT_OPT_NAME), &local_err); } else if (sn_id_or_name) { ret = bdrv_snapshot_load_tmp_by_id_or_name(bs, sn_id_or_name, &local_err); } if (ret < 0) { error_reportf_err(local_err, "Failed to load snapshot: "); exit(EXIT_FAILURE); } bs->detect_zeroes = detect_zeroes; fd_size = blk_getlength(blk); if (fd_size < 0) { error_report("Failed to determine the image length: %s", strerror(-fd_size)); exit(EXIT_FAILURE); } if (dev_offset >= fd_size) { error_report("Offset (%lld) has to be smaller than the image size " "(%lld)", (long long int)dev_offset, (long long int)fd_size); exit(EXIT_FAILURE); } fd_size -= dev_offset; if (partition != -1) { ret = find_partition(blk, partition, &dev_offset, &fd_size); if (ret < 0) { error_report("Could not find partition %d: %s", partition, strerror(-ret)); exit(EXIT_FAILURE); } } exp = nbd_export_new(bs, dev_offset, fd_size, nbdflags, nbd_export_closed, writethrough, NULL, &local_err); if (!exp) { error_report_err(local_err); exit(EXIT_FAILURE); } if (export_name) { nbd_export_set_name(exp, export_name); nbd_export_set_description(exp, export_description); newproto = true; } else if (export_description) { error_report("Export description requires an export name"); exit(EXIT_FAILURE); } if (device) { int ret; ret = pthread_create(&client_thread, NULL, nbd_client_thread, device); if (ret != 0) { error_report("Failed to create client thread: %s", strerror(ret)); exit(EXIT_FAILURE); } } else { /* Shut up GCC warnings. */ memset(&client_thread, 0, sizeof(client_thread)); } nbd_update_server_watch(); /* now when the initialization is (almost) complete, chdir("/") * to free any busy filesystems */ if (chdir("/") < 0) { error_report("Could not chdir to root directory: %s", strerror(errno)); exit(EXIT_FAILURE); } if (fork_process) { dup2(old_stderr, STDERR_FILENO); close(old_stderr); } state = RUNNING; do { main_loop_wait(false); if (state == TERMINATE) { state = TERMINATING; nbd_export_close(exp); nbd_export_put(exp); exp = NULL; } } while (state != TERMINATED); blk_unref(blk); if (sockpath) { unlink(sockpath); } qemu_opts_del(sn_opts); if (device) { void *ret; pthread_join(client_thread, &ret); exit(ret != NULL); } else { exit(EXIT_SUCCESS); } }
static void qemu_rbd_parse_filename(const char *filename, QDict *options, Error **errp) { const char *start; char *p, *buf; QList *keypairs = NULL; char *found_str; if (!strstart(filename, "rbd:", &start)) { error_setg(errp, "File name must start with 'rbd:'"); return; } buf = g_strdup(start); p = buf; found_str = qemu_rbd_next_tok(p, '/', &p); if (!p) { error_setg(errp, "Pool name is required"); goto done; } qemu_rbd_unescape(found_str); qdict_put_str(options, "pool", found_str); if (strchr(p, '@')) { found_str = qemu_rbd_next_tok(p, '@', &p); qemu_rbd_unescape(found_str); qdict_put_str(options, "image", found_str); found_str = qemu_rbd_next_tok(p, ':', &p); qemu_rbd_unescape(found_str); qdict_put_str(options, "snapshot", found_str); } else { found_str = qemu_rbd_next_tok(p, ':', &p); qemu_rbd_unescape(found_str); qdict_put_str(options, "image", found_str); } if (!p) { goto done; } /* The following are essentially all key/value pairs, and we treat * 'id' and 'conf' a bit special. Key/value pairs may be in any order. */ while (p) { char *name, *value; name = qemu_rbd_next_tok(p, '=', &p); if (!p) { error_setg(errp, "conf option %s has no value", name); break; } qemu_rbd_unescape(name); value = qemu_rbd_next_tok(p, ':', &p); qemu_rbd_unescape(value); if (!strcmp(name, "conf")) { qdict_put_str(options, "conf", value); } else if (!strcmp(name, "id")) { qdict_put_str(options, "user", value); } else { /* * We pass these internally to qemu_rbd_set_keypairs(), so * we can get away with the simpler list of [ "key1", * "value1", "key2", "value2" ] rather than a raw dict * { "key1": "value1", "key2": "value2" } where we can't * guarantee order, or even a more correct but complex * [ { "key1": "value1" }, { "key2": "value2" } ] */ if (!keypairs) { keypairs = qlist_new(); } qlist_append_str(keypairs, name); qlist_append_str(keypairs, value); } } if (keypairs) { qdict_put(options, "=keyvalue-pairs", qobject_to_json(QOBJECT(keypairs))); } done: g_free(buf); QDECREF(keypairs); return; }
static int blk_connect(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); int index, qflags; bool readonly = true; bool writethrough = true; int order, ring_ref; unsigned int ring_size, max_grants; unsigned int i; trace_xen_disk_connect(xendev->name); /* read-only ? */ if (blkdev->directiosafe) { qflags = BDRV_O_NOCACHE | BDRV_O_NATIVE_AIO; } else { qflags = 0; writethrough = false; } if (strcmp(blkdev->mode, "w") == 0) { qflags |= BDRV_O_RDWR; readonly = false; } if (blkdev->feature_discard) { qflags |= BDRV_O_UNMAP; } /* init qemu block driver */ index = (xendev->dev - 202 * 256) / 16; blkdev->dinfo = drive_get(IF_XEN, 0, index); if (!blkdev->dinfo) { Error *local_err = NULL; QDict *options = NULL; if (strcmp(blkdev->fileproto, "<unset>")) { options = qdict_new(); qdict_put_str(options, "driver", blkdev->fileproto); } /* setup via xenbus -> create new block driver instance */ xen_pv_printf(xendev, 2, "create new bdrv (xenbus setup)\n"); blkdev->blk = blk_new_open(blkdev->filename, NULL, options, qflags, &local_err); if (!blkdev->blk) { xen_pv_printf(xendev, 0, "error: %s\n", error_get_pretty(local_err)); error_free(local_err); return -1; } blk_set_enable_write_cache(blkdev->blk, !writethrough); } else { /* setup via qemu cmdline -> already setup for us */ xen_pv_printf(xendev, 2, "get configured bdrv (cmdline setup)\n"); blkdev->blk = blk_by_legacy_dinfo(blkdev->dinfo); if (blk_is_read_only(blkdev->blk) && !readonly) { xen_pv_printf(xendev, 0, "Unexpected read-only drive"); blkdev->blk = NULL; return -1; } /* blkdev->blk is not create by us, we get a reference * so we can blk_unref() unconditionally */ blk_ref(blkdev->blk); } blk_attach_dev_legacy(blkdev->blk, blkdev); blkdev->file_size = blk_getlength(blkdev->blk); if (blkdev->file_size < 0) { BlockDriverState *bs = blk_bs(blkdev->blk); const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL; xen_pv_printf(xendev, 1, "blk_getlength: %d (%s) | drv %s\n", (int)blkdev->file_size, strerror(-blkdev->file_size), drv_name ?: "-"); blkdev->file_size = 0; }
static void qdict_crumple_test_bad_inputs(void) { QDict *src, *nested; Error *error = NULL; src = qdict_new(); /* rule.0 can't be both a string and a dict */ qdict_put_str(src, "rule.0", "fred"); qdict_put_str(src, "rule.0.policy", "allow"); g_assert(qdict_crumple(src, &error) == NULL); g_assert(error != NULL); error_free(error); error = NULL; qobject_unref(src); src = qdict_new(); /* rule can't be both a list and a dict */ qdict_put_str(src, "rule.0", "fred"); qdict_put_str(src, "rule.a", "allow"); g_assert(qdict_crumple(src, &error) == NULL); g_assert(error != NULL); error_free(error); error = NULL; qobject_unref(src); src = qdict_new(); /* The input should be flat, ie no dicts or lists */ nested = qdict_new(); qdict_put(nested, "x", qdict_new()); qdict_put(src, "rule.a", nested); qdict_put_str(src, "rule.b", "allow"); g_assert(qdict_crumple(src, &error) == NULL); g_assert(error != NULL); error_free(error); error = NULL; qobject_unref(src); src = qdict_new(); /* List indexes must not have gaps */ qdict_put_str(src, "rule.0", "deny"); qdict_put_str(src, "rule.3", "allow"); g_assert(qdict_crumple(src, &error) == NULL); g_assert(error != NULL); error_free(error); error = NULL; qobject_unref(src); src = qdict_new(); /* List indexes must be in %zu format */ qdict_put_str(src, "rule.0", "deny"); qdict_put_str(src, "rule.+1", "allow"); g_assert(qdict_crumple(src, &error) == NULL); g_assert(error != NULL); error_free(error); error = NULL; qobject_unref(src); }
static void qdict_rename_keys_test(void) { QDict *dict = qdict_new(); QDict *copy; QDictRenames *renames; Error *local_err = NULL; qdict_put_str(dict, "abc", "foo"); qdict_put_str(dict, "abcdef", "bar"); qdict_put_int(dict, "number", 42); qdict_put_bool(dict, "flag", true); qdict_put_null(dict, "nothing"); /* Empty rename list */ renames = (QDictRenames[]) { { NULL, "this can be anything" } }; copy = qdict_clone_shallow(dict); qdict_rename_keys(copy, renames, &error_abort); g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); g_assert_cmpint(qdict_count_entries(copy), ==, 5); qobject_unref(copy); /* Simple rename of all entries */ renames = (QDictRenames[]) { { "abc", "str1" }, { "abcdef", "str2" }, { "number", "int" }, { "flag", "bool" }, { "nothing", "null" }, { NULL , NULL } }; copy = qdict_clone_shallow(dict); qdict_rename_keys(copy, renames, &error_abort); g_assert(!qdict_haskey(copy, "abc")); g_assert(!qdict_haskey(copy, "abcdef")); g_assert(!qdict_haskey(copy, "number")); g_assert(!qdict_haskey(copy, "flag")); g_assert(!qdict_haskey(copy, "nothing")); g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo"); g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar"); g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42); g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true); g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); g_assert_cmpint(qdict_count_entries(copy), ==, 5); qobject_unref(copy); /* Renames are processed top to bottom */ renames = (QDictRenames[]) { { "abc", "tmp" }, { "abcdef", "abc" }, { "number", "abcdef" }, { "flag", "number" }, { "nothing", "flag" }, { "tmp", "nothing" }, { NULL , NULL } }; copy = qdict_clone_shallow(dict); qdict_rename_keys(copy, renames, &error_abort); g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo"); g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar"); g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42); g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true); g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL); g_assert(!qdict_haskey(copy, "tmp")); g_assert_cmpint(qdict_count_entries(copy), ==, 5); qobject_unref(copy); /* Conflicting rename */ renames = (QDictRenames[]) { { "abcdef", "abc" }, { NULL , NULL } }; copy = qdict_clone_shallow(dict); qdict_rename_keys(copy, renames, &local_err); g_assert(local_err != NULL); error_free(local_err); local_err = NULL; g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); g_assert_cmpint(qdict_count_entries(copy), ==, 5); qobject_unref(copy); /* Renames in an empty dict */ renames = (QDictRenames[]) { { "abcdef", "abc" }, { NULL , NULL } }; qobject_unref(dict); dict = qdict_new(); qdict_rename_keys(dict, renames, &error_abort); g_assert(qdict_first(dict) == NULL); qobject_unref(dict); }
static void qdict_crumple_test_recursive(void) { QDict *src, *dst, *rule, *vnc, *acl, *listen; QDict *empty, *empty_dict, *empty_list_0; QList *rules, *empty_list, *empty_dict_a; src = qdict_new(); qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); qdict_put_str(src, "vnc.listen.port", "5901"); qdict_put_str(src, "vnc.acl.rules.0.match", "fred"); qdict_put_str(src, "vnc.acl.rules.0.policy", "allow"); qdict_put_str(src, "vnc.acl.rules.1.match", "bob"); qdict_put_str(src, "vnc.acl.rules.1.policy", "deny"); qdict_put_str(src, "vnc.acl.default", "deny"); qdict_put_str(src, "vnc.acl..name", "acl0"); qdict_put_str(src, "vnc.acl.rule..name", "acl0"); qdict_put(src, "empty.dict.a", qlist_new()); qdict_put(src, "empty.list.0", qdict_new()); dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); g_assert(dst); g_assert_cmpint(qdict_size(dst), ==, 2); vnc = qdict_get_qdict(dst, "vnc"); g_assert(vnc); g_assert_cmpint(qdict_size(vnc), ==, 3); listen = qdict_get_qdict(vnc, "listen"); g_assert(listen); g_assert_cmpint(qdict_size(listen), ==, 2); g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr")); g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port")); acl = qdict_get_qdict(vnc, "acl"); g_assert(acl); g_assert_cmpint(qdict_size(acl), ==, 3); rules = qdict_get_qlist(acl, "rules"); g_assert(rules); g_assert_cmpint(qlist_size(rules), ==, 2); rule = qobject_to(QDict, qlist_pop(rules)); g_assert(rule); g_assert_cmpint(qdict_size(rule), ==, 2); g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); qobject_unref(rule); rule = qobject_to(QDict, qlist_pop(rules)); g_assert(rule); g_assert_cmpint(qdict_size(rule), ==, 2); g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); qobject_unref(rule); /* With recursive crumpling, we should see all names unescaped */ g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); empty = qdict_get_qdict(dst, "empty"); g_assert(empty); g_assert_cmpint(qdict_size(empty), ==, 2); empty_dict = qdict_get_qdict(empty, "dict"); g_assert(empty_dict); g_assert_cmpint(qdict_size(empty_dict), ==, 1); empty_dict_a = qdict_get_qlist(empty_dict, "a"); g_assert(empty_dict_a && qlist_empty(empty_dict_a)); empty_list = qdict_get_qlist(empty, "list"); g_assert(empty_list); g_assert_cmpint(qlist_size(empty_list), ==, 1); empty_list_0 = qobject_to(QDict, qlist_pop(empty_list)); g_assert(empty_list_0); g_assert_cmpint(qdict_size(empty_list_0), ==, 0); qobject_unref(src); qobject_unref(dst); }