static void test_io_channel_setup_sync(SocketAddress *listen_addr, SocketAddress *connect_addr, QIOChannel **src, QIOChannel **dst) { QIOChannelSocket *lioc; lioc = qio_channel_socket_new(); qio_channel_socket_listen_sync(lioc, listen_addr, &error_abort); if (listen_addr->type == SOCKET_ADDRESS_TYPE_INET) { SocketAddress *laddr = qio_channel_socket_get_local_address( lioc, &error_abort); g_free(connect_addr->u.inet.port); connect_addr->u.inet.port = g_strdup(laddr->u.inet.port); qapi_free_SocketAddress(laddr); } *src = QIO_CHANNEL(qio_channel_socket_new()); qio_channel_socket_connect_sync( QIO_CHANNEL_SOCKET(*src), connect_addr, &error_abort); qio_channel_set_delay(*src, false); qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); g_assert(*dst); test_io_channel_set_socket_bufs(*src, *dst); object_unref(OBJECT(lioc)); }
static void test_io_channel_setup_async(SocketAddress *listen_addr, SocketAddress *connect_addr, QIOChannel **srv, QIOChannel **src, QIOChannel **dst) { QIOChannelSocket *lioc; struct TestIOChannelData data; data.loop = g_main_loop_new(g_main_context_default(), TRUE); lioc = qio_channel_socket_new(); qio_channel_socket_listen_async( lioc, listen_addr, test_io_channel_complete, &data, NULL, NULL); g_main_loop_run(data.loop); g_main_context_iteration(g_main_context_default(), FALSE); g_assert(!data.err); if (listen_addr->type == SOCKET_ADDRESS_TYPE_INET) { SocketAddress *laddr = qio_channel_socket_get_local_address( lioc, &error_abort); g_free(connect_addr->u.inet.port); connect_addr->u.inet.port = g_strdup(laddr->u.inet.port); qapi_free_SocketAddress(laddr); } *src = QIO_CHANNEL(qio_channel_socket_new()); qio_channel_socket_connect_async( QIO_CHANNEL_SOCKET(*src), connect_addr, test_io_channel_complete, &data, NULL, NULL); g_main_loop_run(data.loop); g_main_context_iteration(g_main_context_default(), FALSE); g_assert(!data.err); qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); g_assert(*dst); qio_channel_set_delay(*src, false); test_io_channel_set_socket_bufs(*src, *dst); *srv = QIO_CHANNEL(lioc); g_main_loop_unref(data.loop); }
static int tcp_chr_wait_connected(Chardev *chr, Error **errp) { SocketChardev *s = SOCKET_CHARDEV(chr); QIOChannelSocket *sioc; /* It can't wait on s->connected, since it is set asynchronously * in TLS and telnet cases, only wait for an accepted socket */ while (!s->ioc) { if (s->is_listen) { error_report("QEMU waiting for connection on: %s", chr->filename); qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), true, NULL); tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr); qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL); } else { sioc = qio_channel_socket_new(); tcp_chr_set_client_ioc_name(chr, sioc); if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) { object_unref(OBJECT(sioc)); return -1; } tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); } } return 0; }
QIOChannelSocket * qio_channel_socket_new_fd(int fd, Error **errp) { QIOChannelSocket *ioc; ioc = qio_channel_socket_new(); if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { object_unref(OBJECT(ioc)); return NULL; } trace_qio_channel_socket_new_fd(ioc, fd); return ioc; }
void qmp_nbd_server_start(SocketAddress *addr, bool has_tls_creds, const char *tls_creds, Error **errp) { if (nbd_server) { error_setg(errp, "NBD server already running"); return; } nbd_server = g_new0(NBDServerData, 1); nbd_server->watch = -1; nbd_server->listen_ioc = qio_channel_socket_new(); qio_channel_set_name(QIO_CHANNEL(nbd_server->listen_ioc), "nbd-listener"); if (qio_channel_socket_listen_sync( nbd_server->listen_ioc, addr, errp) < 0) { goto error; } if (has_tls_creds) { nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp); if (!nbd_server->tlscreds) { goto error; } /* TODO SOCKET_ADDRESS_KIND_FD where fd has AF_INET or AF_INET6 */ if (addr->type != SOCKET_ADDRESS_KIND_INET) { error_setg(errp, "TLS is only supported with IPv4/IPv6"); goto error; } } nbd_server->watch = qio_channel_add_watch( QIO_CHANNEL(nbd_server->listen_ioc), G_IO_IN, nbd_accept, NULL, NULL); return; error: nbd_server_free(nbd_server); nbd_server = NULL; }
QIOChannelSocket * qio_channel_socket_accept(QIOChannelSocket *ioc, Error **errp) { QIOChannelSocket *cioc; cioc = qio_channel_socket_new(); cioc->remoteAddrLen = sizeof(ioc->remoteAddr); cioc->localAddrLen = sizeof(ioc->localAddr); retry: trace_qio_channel_socket_accept(ioc); cioc->fd = qemu_accept(ioc->fd, (struct sockaddr *)&cioc->remoteAddr, &cioc->remoteAddrLen); if (cioc->fd < 0) { trace_qio_channel_socket_accept_fail(ioc); if (errno == EINTR) { goto retry; } goto error; } if (getsockname(cioc->fd, (struct sockaddr *)&cioc->localAddr, &cioc->localAddrLen) < 0) { error_setg_errno(errp, errno, "Unable to query local socket address"); goto error; } #ifndef WIN32 if (cioc->localAddr.ss_family == AF_UNIX) { QIOChannel *ioc_local = QIO_CHANNEL(cioc); qio_channel_set_feature(ioc_local, QIO_CHANNEL_FEATURE_FD_PASS); } #endif /* WIN32 */ trace_qio_channel_socket_accept_complete(ioc, cioc, cioc->fd); return cioc; error: object_unref(OBJECT(cioc)); return NULL; }
static gboolean socket_reconnect_timeout(gpointer opaque) { Chardev *chr = CHARDEV(opaque); SocketChardev *s = SOCKET_CHARDEV(opaque); QIOChannelSocket *sioc; s->reconnect_timer = 0; if (chr->be_open) { return false; } sioc = qio_channel_socket_new(); tcp_chr_set_client_ioc_name(chr, sioc); qio_channel_socket_connect_async(sioc, s->addr, qemu_chr_socket_connected, chr, NULL); return false; }
static void socket_start_outgoing_migration(MigrationState *s, SocketAddress *saddr, Error **errp) { QIOChannelSocket *sioc = qio_channel_socket_new(); struct SocketConnectData *data = g_new0(struct SocketConnectData, 1); data->s = s; if (saddr->type == SOCKET_ADDRESS_KIND_INET) { data->hostname = g_strdup(saddr->u.inet.data->host); } qio_channel_set_name(QIO_CHANNEL(sioc), "migration-socket-outgoing"); qio_channel_socket_connect_async(sioc, saddr, socket_outgoing_migration, data, socket_connect_data_free); qapi_free_SocketAddress(saddr); }
static void socket_start_incoming_migration(SocketAddress *saddr, Error **errp) { QIOChannelSocket *listen_ioc = qio_channel_socket_new(); qio_channel_set_name(QIO_CHANNEL(listen_ioc), "migration-socket-listener"); if (qio_channel_socket_listen_sync(listen_ioc, saddr, errp) < 0) { object_unref(OBJECT(listen_ioc)); qapi_free_SocketAddress(saddr); return; } qio_channel_add_watch(QIO_CHANNEL(listen_ioc), G_IO_IN, socket_accept_incoming_migration, listen_ioc, (GDestroyNotify)object_unref); qapi_free_SocketAddress(saddr); }
static void test_io_channel_ipv4(bool async) { SocketAddress *listen_addr = g_new0(SocketAddress, 1); SocketAddress *connect_addr = g_new0(SocketAddress, 1); listen_addr->type = SOCKET_ADDRESS_TYPE_INET; listen_addr->u.inet = (InetSocketAddress) { .host = g_strdup("127.0.0.1"), .port = NULL, /* Auto-select */ }; connect_addr->type = SOCKET_ADDRESS_TYPE_INET; connect_addr->u.inet = (InetSocketAddress) { .host = g_strdup("127.0.0.1"), .port = NULL, /* Filled in later */ }; test_io_channel(async, listen_addr, connect_addr, false); qapi_free_SocketAddress(listen_addr); qapi_free_SocketAddress(connect_addr); } static void test_io_channel_ipv4_sync(void) { return test_io_channel_ipv4(false); } static void test_io_channel_ipv4_async(void) { return test_io_channel_ipv4(true); } static void test_io_channel_ipv6(bool async) { SocketAddress *listen_addr = g_new0(SocketAddress, 1); SocketAddress *connect_addr = g_new0(SocketAddress, 1); listen_addr->type = SOCKET_ADDRESS_TYPE_INET; listen_addr->u.inet = (InetSocketAddress) { .host = g_strdup("::1"), .port = NULL, /* Auto-select */ }; connect_addr->type = SOCKET_ADDRESS_TYPE_INET; connect_addr->u.inet = (InetSocketAddress) { .host = g_strdup("::1"), .port = NULL, /* Filled in later */ }; test_io_channel(async, listen_addr, connect_addr, false); qapi_free_SocketAddress(listen_addr); qapi_free_SocketAddress(connect_addr); } static void test_io_channel_ipv6_sync(void) { return test_io_channel_ipv6(false); } static void test_io_channel_ipv6_async(void) { return test_io_channel_ipv6(true); } #ifndef _WIN32 static void test_io_channel_unix(bool async) { SocketAddress *listen_addr = g_new0(SocketAddress, 1); SocketAddress *connect_addr = g_new0(SocketAddress, 1); #define TEST_SOCKET "test-io-channel-socket.sock" listen_addr->type = SOCKET_ADDRESS_TYPE_UNIX; listen_addr->u.q_unix.path = g_strdup(TEST_SOCKET); connect_addr->type = SOCKET_ADDRESS_TYPE_UNIX; connect_addr->u.q_unix.path = g_strdup(TEST_SOCKET); test_io_channel(async, listen_addr, connect_addr, true); qapi_free_SocketAddress(listen_addr); qapi_free_SocketAddress(connect_addr); g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS) == FALSE); } static void test_io_channel_unix_sync(void) { return test_io_channel_unix(false); } static void test_io_channel_unix_async(void) { return test_io_channel_unix(true); } static void test_io_channel_unix_fd_pass(void) { SocketAddress *listen_addr = g_new0(SocketAddress, 1); SocketAddress *connect_addr = g_new0(SocketAddress, 1); QIOChannel *src, *dst; int testfd; int fdsend[3]; int *fdrecv = NULL; size_t nfdrecv = 0; size_t i; char bufsend[12], bufrecv[12]; struct iovec iosend[1], iorecv[1]; #define TEST_SOCKET "test-io-channel-socket.sock" #define TEST_FILE "test-io-channel-socket.txt" testfd = open(TEST_FILE, O_RDWR|O_TRUNC|O_CREAT, 0700); g_assert(testfd != -1); fdsend[0] = testfd; fdsend[1] = testfd; fdsend[2] = testfd; listen_addr->type = SOCKET_ADDRESS_TYPE_UNIX; listen_addr->u.q_unix.path = g_strdup(TEST_SOCKET); connect_addr->type = SOCKET_ADDRESS_TYPE_UNIX; connect_addr->u.q_unix.path = g_strdup(TEST_SOCKET); test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst); memcpy(bufsend, "Hello World", G_N_ELEMENTS(bufsend)); iosend[0].iov_base = bufsend; iosend[0].iov_len = G_N_ELEMENTS(bufsend); iorecv[0].iov_base = bufrecv; iorecv[0].iov_len = G_N_ELEMENTS(bufrecv); g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); qio_channel_writev_full(src, iosend, G_N_ELEMENTS(iosend), fdsend, G_N_ELEMENTS(fdsend), &error_abort); qio_channel_readv_full(dst, iorecv, G_N_ELEMENTS(iorecv), &fdrecv, &nfdrecv, &error_abort); g_assert(nfdrecv == G_N_ELEMENTS(fdsend)); /* Each recvd FD should be different from sent FD */ for (i = 0; i < nfdrecv; i++) { g_assert_cmpint(fdrecv[i], !=, testfd); } /* Each recvd FD should be different from each other */ g_assert_cmpint(fdrecv[0], !=, fdrecv[1]); g_assert_cmpint(fdrecv[0], !=, fdrecv[2]); g_assert_cmpint(fdrecv[1], !=, fdrecv[2]); /* Check the I/O buf we sent at the same time matches */ g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0); /* Write some data into the FD we received */ g_assert(write(fdrecv[0], bufsend, G_N_ELEMENTS(bufsend)) == G_N_ELEMENTS(bufsend)); /* Read data from the original FD and make sure it matches */ memset(bufrecv, 0, G_N_ELEMENTS(bufrecv)); g_assert(lseek(testfd, 0, SEEK_SET) == 0); g_assert(read(testfd, bufrecv, G_N_ELEMENTS(bufrecv)) == G_N_ELEMENTS(bufrecv)); g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0); object_unref(OBJECT(src)); object_unref(OBJECT(dst)); qapi_free_SocketAddress(listen_addr); qapi_free_SocketAddress(connect_addr); unlink(TEST_SOCKET); unlink(TEST_FILE); close(testfd); for (i = 0; i < nfdrecv; i++) { close(fdrecv[i]); } g_free(fdrecv); } static void test_io_channel_unix_listen_cleanup(void) { QIOChannelSocket *ioc; struct sockaddr_un un; int sock; #define TEST_SOCKET "test-io-channel-socket.sock" ioc = qio_channel_socket_new(); /* Manually bind ioc without calling the qio api to avoid setting * the LISTEN feature */ sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; snprintf(un.sun_path, sizeof(un.sun_path), "%s", TEST_SOCKET); unlink(TEST_SOCKET); bind(sock, (struct sockaddr *)&un, sizeof(un)); ioc->fd = sock; ioc->localAddrLen = sizeof(ioc->localAddr); getsockname(sock, (struct sockaddr *)&ioc->localAddr, &ioc->localAddrLen); g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS)); object_unref(OBJECT(ioc)); g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS)); unlink(TEST_SOCKET); }
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 *nbd_client_thread(void *arg) { char *device = arg; NBDExportInfo info = { .request_sizes = false, }; QIOChannelSocket *sioc; int fd; int ret; pthread_t show_parts_thread; Error *local_error = NULL; sioc = qio_channel_socket_new(); if (qio_channel_socket_connect_sync(sioc, saddr, &local_error) < 0) { error_report_err(local_error); goto out; } ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, NULL, NULL, NULL, &info, &local_error); if (ret < 0) { if (local_error) { error_report_err(local_error); } goto out_socket; } fd = open(device, O_RDWR); if (fd < 0) { /* Linux-only, we can use %m in printf. */ error_report("Failed to open %s: %m", device); goto out_socket; } ret = nbd_init(fd, sioc, &info, &local_error); if (ret < 0) { error_report_err(local_error); goto out_fd; } /* update partition table */ pthread_create(&show_parts_thread, NULL, show_parts, device); if (verbose) { fprintf(stderr, "NBD device %s is now connected to %s\n", device, srcpath); } else { /* Close stderr so that the qemu-nbd process exits. */ dup2(STDOUT_FILENO, STDERR_FILENO); } ret = nbd_client(fd); if (ret) { goto out_fd; } close(fd); object_unref(OBJECT(sioc)); kill(getpid(), SIGTERM); return (void *) EXIT_SUCCESS; out_fd: close(fd); out_socket: object_unref(OBJECT(sioc)); out: kill(getpid(), SIGTERM); return (void *) EXIT_FAILURE; } static int nbd_can_accept(void) { return state == RUNNING && nb_fds < shared; } static void nbd_export_closed(NBDExport *exp) { assert(state == TERMINATING); state = TERMINATED; }
static void *nbd_client_thread(void *arg) { char *device = arg; off_t size; uint32_t nbdflags; QIOChannelSocket *sioc; int fd; int ret; pthread_t show_parts_thread; Error *local_error = NULL; sioc = qio_channel_socket_new(); if (qio_channel_socket_connect_sync(sioc, saddr, &local_error) < 0) { error_report_err(local_error); goto out; } ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, &nbdflags, NULL, NULL, NULL, &size, &local_error); if (ret < 0) { if (local_error) { error_report_err(local_error); } goto out_socket; } fd = open(device, O_RDWR); if (fd < 0) { /* Linux-only, we can use %m in printf. */ error_report("Failed to open %s: %m", device); goto out_socket; } ret = nbd_init(fd, sioc, nbdflags, size); if (ret < 0) { goto out_fd; } /* update partition table */ pthread_create(&show_parts_thread, NULL, show_parts, device); if (verbose) { fprintf(stderr, "NBD device %s is now connected to %s\n", device, srcpath); } else { /* Close stderr so that the qemu-nbd process exits. */ dup2(STDOUT_FILENO, STDERR_FILENO); } ret = nbd_client(fd); if (ret) { goto out_fd; } close(fd); object_unref(OBJECT(sioc)); kill(getpid(), SIGTERM); return (void *) EXIT_SUCCESS; out_fd: close(fd); out_socket: object_unref(OBJECT(sioc)); out: kill(getpid(), SIGTERM); return (void *) EXIT_FAILURE; }
static void qmp_chardev_open_socket(Chardev *chr, ChardevBackend *backend, bool *be_opened, Error **errp) { SocketChardev *s = SOCKET_CHARDEV(chr); ChardevSocket *sock = backend->u.socket.data; SocketAddress *addr = sock->addr; bool do_nodelay = sock->has_nodelay ? sock->nodelay : false; bool is_listen = sock->has_server ? sock->server : true; bool is_telnet = sock->has_telnet ? sock->telnet : false; bool is_waitconnect = sock->has_wait ? sock->wait : false; int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0; QIOChannelSocket *sioc = NULL; s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX; s->is_listen = is_listen; s->is_telnet = is_telnet; s->do_nodelay = do_nodelay; if (sock->tls_creds) { Object *creds; creds = object_resolve_path_component( object_get_objects_root(), sock->tls_creds); if (!creds) { error_setg(errp, "No TLS credentials with id '%s'", sock->tls_creds); goto error; } s->tls_creds = (QCryptoTLSCreds *) object_dynamic_cast(creds, TYPE_QCRYPTO_TLS_CREDS); if (!s->tls_creds) { error_setg(errp, "Object with id '%s' is not TLS credentials", sock->tls_creds); goto error; } object_ref(OBJECT(s->tls_creds)); if (is_listen) { if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { error_setg(errp, "%s", "Expected TLS credentials for server endpoint"); goto error; } } else { if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { error_setg(errp, "%s", "Expected TLS credentials for client endpoint"); goto error; } } } s->addr = QAPI_CLONE(SocketAddress, sock->addr); qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE); if (s->is_unix) { qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS); } /* be isn't opened until we get a connection */ *be_opened = false; chr->filename = SocketAddress_to_str("disconnected:", addr, is_listen, is_telnet); if (is_listen) { if (is_telnet) { s->do_telnetopt = 1; } } else if (reconnect > 0) { s->reconnect_time = reconnect; } if (s->reconnect_time) { sioc = qio_channel_socket_new(); tcp_chr_set_client_ioc_name(chr, sioc); qio_channel_socket_connect_async(sioc, s->addr, qemu_chr_socket_connected, chr, NULL); } else { if (s->is_listen) { char *name; sioc = qio_channel_socket_new(); name = g_strdup_printf("chardev-tcp-listener-%s", chr->label); qio_channel_set_name(QIO_CHANNEL(sioc), name); g_free(name); if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) { goto error; } s->listen_ioc = sioc; if (is_waitconnect && qemu_chr_wait_connected(chr, errp) < 0) { return; } if (!s->ioc) { s->listen_tag = qio_channel_add_watch( QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL); } } else if (qemu_chr_wait_connected(chr, errp) < 0) { goto error; } } return; error: if (sioc) { object_unref(OBJECT(sioc)); } }
static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend, Error **errp) { const char *host = qemu_opt_get(opts, "host"); const char *port = qemu_opt_get(opts, "port"); const char *localaddr = qemu_opt_get(opts, "localaddr"); const char *localport = qemu_opt_get(opts, "localport"); bool has_local = false; SocketAddressLegacy *addr; ChardevUdp *udp; backend->type = CHARDEV_BACKEND_KIND_UDP; if (host == NULL || strlen(host) == 0) { host = "localhost"; } if (port == NULL || strlen(port) == 0) { error_setg(errp, "chardev: udp: remote port not specified"); return; } if (localport == NULL || strlen(localport) == 0) { localport = "0"; } else { has_local = true; } if (localaddr == NULL || strlen(localaddr) == 0) { localaddr = ""; } else { has_local = true; } udp = backend->u.udp.data = g_new0(ChardevUdp, 1); qemu_chr_parse_common(opts, qapi_ChardevUdp_base(udp)); addr = g_new0(SocketAddressLegacy, 1); addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET; addr->u.inet.data = g_new(InetSocketAddress, 1); *addr->u.inet.data = (InetSocketAddress) { .host = g_strdup(host), .port = g_strdup(port), .has_ipv4 = qemu_opt_get(opts, "ipv4"), .ipv4 = qemu_opt_get_bool(opts, "ipv4", 0), .has_ipv6 = qemu_opt_get(opts, "ipv6"), .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0), }; udp->remote = addr; if (has_local) { udp->has_local = true; addr = g_new0(SocketAddressLegacy, 1); addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET; addr->u.inet.data = g_new(InetSocketAddress, 1); *addr->u.inet.data = (InetSocketAddress) { .host = g_strdup(localaddr), .port = g_strdup(localport), }; udp->local = addr; } } static void qmp_chardev_open_udp(Chardev *chr, ChardevBackend *backend, bool *be_opened, Error **errp) { ChardevUdp *udp = backend->u.udp.data; SocketAddress *local_addr = socket_address_flatten(udp->local); SocketAddress *remote_addr = socket_address_flatten(udp->remote); QIOChannelSocket *sioc = qio_channel_socket_new(); char *name; UdpChardev *s = UDP_CHARDEV(chr); int ret; ret = qio_channel_socket_dgram_sync(sioc, local_addr, remote_addr, errp); qapi_free_SocketAddress(local_addr); qapi_free_SocketAddress(remote_addr); if (ret < 0) { object_unref(OBJECT(sioc)); return; } name = g_strdup_printf("chardev-udp-%s", chr->label); qio_channel_set_name(QIO_CHANNEL(sioc), name); g_free(name); s->ioc = QIO_CHANNEL(sioc); /* be isn't opened until we get a connection */ *be_opened = false; }