X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=qemu-nbd.c;h=a8cb39e51043a9f2731e1c8df26cfe1d7a3835ee;hb=e326767b455e031a9bd187adfcbaca4362cb9f40;hp=d75ca514820760a6852d9f4983788634a28402a7;hpb=6d58d50e5376d8143802a3102289744e660302f0;p=mirror_qemu.git diff --git a/qemu-nbd.c b/qemu-nbd.c index d75ca51482..a8cb39e510 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -21,26 +21,36 @@ #include #include -#include "qapi/error.h" #include "qemu-common.h" +#include "qapi/error.h" #include "qemu/cutils.h" #include "sysemu/block-backend.h" #include "block/block_int.h" #include "block/nbd.h" #include "qemu/main-loop.h" +#include "qemu/module.h" +#include "qemu/option.h" #include "qemu/error-report.h" #include "qemu/config-file.h" #include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/systemd.h" #include "block/snapshot.h" +#include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" #include "qom/object_interfaces.h" #include "io/channel-socket.h" +#include "io/net-listener.h" #include "crypto/init.h" #include "trace/control.h" #include "qemu-version.h" +#ifdef __linux__ +#define HAVE_NBD_DEVICE 1 +#else +#define HAVE_NBD_DEVICE 0 +#endif + #define SOCKET_PATH "/var/lock/qemu-nbd-%s" #define QEMU_NBD_OPT_CACHE 256 #define QEMU_NBD_OPT_AIO 257 @@ -50,11 +60,12 @@ #define QEMU_NBD_OPT_TLSCREDS 261 #define QEMU_NBD_OPT_IMAGE_OPTS 262 #define QEMU_NBD_OPT_FORK 263 +#define QEMU_NBD_OPT_TLSAUTHZ 264 +#define QEMU_NBD_OPT_PID_FILE 265 #define MBR_SIZE 512 -static NBDExport *exp; -static bool newproto; +static NBDExport *export; static int verbose; static char *srcpath; static SocketAddress *saddr; @@ -62,15 +73,16 @@ static int persistent = 0; static enum { RUNNING, TERMINATE, TERMINATING, TERMINATED } state; static int shared = 1; static int nb_fds; -static QIOChannelSocket *server_ioc; -static int server_watch = -1; +static QIONetListener *server; static QCryptoTLSCreds *tlscreds; +static const char *tlsauthz; static void usage(const char *name) { (printf) ( "Usage: %s [OPTIONS] FILE\n" -"QEMU Disk Network Block Device Server\n" +" or: %s -L [OPTIONS]\n" +"QEMU Disk Network Block Device Utility\n" "\n" " -h, --help display this help and exit\n" " -V, --version output version information and exit\n" @@ -83,25 +95,31 @@ static void usage(const char *name) " -e, --shared=NUM device can be shared by NUM clients (default '1')\n" " -t, --persistent don't exit on the last connection\n" " -v, --verbose display extra debugging information\n" -" -x, --export-name=NAME expose export by name\n" -" -D, --description=TEXT with -x, also export a human-readable description\n" +" -x, --export-name=NAME expose export by name (default is empty string)\n" +" -D, --description=TEXT export a human-readable description\n" "\n" "Exposing part of the image:\n" " -o, --offset=OFFSET offset into the image\n" " -P, --partition=NUM only expose partition NUM\n" +" -B, --bitmap=NAME expose a persistent dirty bitmap\n" "\n" "General purpose options:\n" +" -L, --list list exports available from another NBD server\n" " --object type,id=ID,... define an object such as 'secret' for providing\n" " passwords and/or encryption keys\n" +" --tls-creds=ID use id of an earlier --object to provide TLS\n" +" --tls-authz=ID use id of an earlier --object to provide\n" +" authorization\n" " -T, --trace [[enable=]][,events=][,file=]\n" " specify tracing options\n" " --fork fork off the server process and exit the parent\n" " once the server is running\n" -#ifdef __linux__ +" --pid-file=PATH store the server's process ID in the given file\n" +#if HAVE_NBD_DEVICE +"\n" "Kernel NBD client support:\n" " -c, --connect=DEV connect FILE to the local NBD device DEV\n" " -d, --disconnect disconnect the specified device\n" -"\n" #endif "\n" "Block device options:\n" @@ -123,13 +141,13 @@ static void usage(const char *name) " --image-opts treat FILE as a full set of image options\n" "\n" QEMU_HELP_BOTTOM "\n" - , name, NBD_DEFAULT_PORT, "DEVICE"); + , name, name, NBD_DEFAULT_PORT, "DEVICE"); } static void version(const char *name) { printf( -"%s " QEMU_VERSION QEMU_PKGVERSION "\n" +"%s " QEMU_FULL_VERSION "\n" "Written by Anthony Liguori.\n" "\n" QEMU_COPYRIGHT "\n" @@ -168,7 +186,7 @@ static void read_partition(uint8_t *p, struct partition_record *r) } static int find_partition(BlockBackend *blk, int partition, - off_t *offset, off_t *size) + uint64_t *offset, uint64_t *size) { struct partition_record mbr[4]; uint8_t data[MBR_SIZE]; @@ -235,6 +253,80 @@ static void termsig_handler(int signum) } +static int qemu_nbd_client_list(SocketAddress *saddr, QCryptoTLSCreds *tls, + const char *hostname) +{ + int ret = EXIT_FAILURE; + int rc; + Error *err = NULL; + QIOChannelSocket *sioc; + NBDExportInfo *list; + int i, j; + + sioc = qio_channel_socket_new(); + if (qio_channel_socket_connect_sync(sioc, saddr, &err) < 0) { + error_report_err(err); + return EXIT_FAILURE; + } + rc = nbd_receive_export_list(QIO_CHANNEL(sioc), tls, hostname, &list, + &err); + if (rc < 0) { + if (err) { + error_report_err(err); + } + goto out; + } + printf("exports available: %d\n", rc); + for (i = 0; i < rc; i++) { + printf(" export: '%s'\n", list[i].name); + if (list[i].description && *list[i].description) { + printf(" description: %s\n", list[i].description); + } + if (list[i].flags & NBD_FLAG_HAS_FLAGS) { + static const char *const flag_names[] = { + [NBD_FLAG_READ_ONLY_BIT] = "readonly", + [NBD_FLAG_SEND_FLUSH_BIT] = "flush", + [NBD_FLAG_SEND_FUA_BIT] = "fua", + [NBD_FLAG_ROTATIONAL_BIT] = "rotational", + [NBD_FLAG_SEND_TRIM_BIT] = "trim", + [NBD_FLAG_SEND_WRITE_ZEROES_BIT] = "zeroes", + [NBD_FLAG_SEND_DF_BIT] = "df", + [NBD_FLAG_CAN_MULTI_CONN_BIT] = "multi", + [NBD_FLAG_SEND_RESIZE_BIT] = "resize", + [NBD_FLAG_SEND_CACHE_BIT] = "cache", + }; + + printf(" size: %" PRIu64 "\n", list[i].size); + printf(" flags: 0x%x (", list[i].flags); + for (size_t bit = 0; bit < ARRAY_SIZE(flag_names); bit++) { + if (flag_names[bit] && (list[i].flags & (1 << bit))) { + printf(" %s", flag_names[bit]); + } + } + printf(" )\n"); + } + if (list[i].min_block) { + printf(" min block: %u\n", list[i].min_block); + printf(" opt block: %u\n", list[i].opt_block); + printf(" max block: %u\n", list[i].max_block); + } + if (list[i].n_contexts) { + printf(" available meta contexts: %d\n", list[i].n_contexts); + for (j = 0; j < list[i].n_contexts; j++) { + printf(" %s\n", list[i].contexts[j]); + } + } + } + nbd_free_export_list(list, rc); + + ret = EXIT_SUCCESS; + out: + object_unref(OBJECT(sioc)); + return ret; +} + + +#if HAVE_NBD_DEVICE static void *show_parts(void *arg) { char *device = arg; @@ -255,7 +347,7 @@ static void *show_parts(void *arg) static void *nbd_client_thread(void *arg) { char *device = arg; - NBDExportInfo info = { .request_sizes = false, }; + NBDExportInfo info = { .request_sizes = false, .name = g_strdup("") }; QIOChannelSocket *sioc; int fd; int ret; @@ -270,7 +362,7 @@ static void *nbd_client_thread(void *arg) goto out; } - ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, + ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, NULL, NULL, &info, &local_error); if (ret < 0) { if (local_error) { @@ -309,6 +401,7 @@ static void *nbd_client_thread(void *arg) } close(fd); object_unref(OBJECT(sioc)); + g_free(info.name); kill(getpid(), SIGTERM); return (void *) EXIT_SUCCESS; @@ -317,16 +410,18 @@ out_fd: out_socket: object_unref(OBJECT(sioc)); out: + g_free(info.name); kill(getpid(), SIGTERM); return (void *) EXIT_FAILURE; } +#endif /* HAVE_NBD_DEVICE */ static int nbd_can_accept(void) { return state == RUNNING && nb_fds < shared; } -static void nbd_export_closed(NBDExport *exp) +static void nbd_export_closed(NBDExport *export) { assert(state == TERMINATING); state = TERMINATED; @@ -344,44 +439,24 @@ static void nbd_client_closed(NBDClient *client, bool negotiated) nbd_client_put(client); } -static gboolean nbd_accept(QIOChannel *ioc, GIOCondition cond, gpointer opaque) +static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, + gpointer opaque) { - QIOChannelSocket *cioc; - - cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), - NULL); - if (!cioc) { - return TRUE; - } - if (state >= TERMINATE) { - object_unref(OBJECT(cioc)); - return TRUE; + return; } nb_fds++; nbd_update_server_watch(); - nbd_client_new(newproto ? NULL : exp, cioc, - tlscreds, NULL, nbd_client_closed); - object_unref(OBJECT(cioc)); - - return TRUE; + nbd_client_new(cioc, tlscreds, tlsauthz, nbd_client_closed); } static void nbd_update_server_watch(void) { if (nbd_can_accept()) { - if (server_watch == -1) { - server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc), - G_IO_IN, - nbd_accept, - NULL, NULL); - } + qio_net_listener_set_client_func(server, nbd_accept, NULL, NULL); } else { - if (server_watch != -1) { - g_source_remove(server_watch); - server_watch = -1; - } + qio_net_listener_set_client_func(server, NULL, NULL, NULL); } } @@ -433,7 +508,8 @@ static QemuOptsList qemu_object_opts = { -static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) +static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, bool list, + Error **errp) { Object *obj; QCryptoTLSCreds *creds; @@ -453,10 +529,18 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) return NULL; } - if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { - error_setg(errp, - "Expecting TLS credentials with a server endpoint"); - return NULL; + if (list) { + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + error_setg(errp, + "Expecting TLS credentials with a client endpoint"); + return NULL; + } + } else { + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + error_setg(errp, + "Expecting TLS credentials with a server endpoint"); + return NULL; + } } object_ref(obj); return creds; @@ -479,7 +563,8 @@ static void setup_address_and_port(const char **address, const char **port) static const char *socket_activation_validate_opts(const char *device, const char *sockpath, const char *address, - const char *port) + const char *port, + bool list) { if (device != NULL) { return "NBD device can't be set when using socket activation"; @@ -497,24 +582,34 @@ static const char *socket_activation_validate_opts(const char *device, return "TCP port number can't be set when using socket activation"; } + if (list) { + return "List mode is incompatible with socket activation"; + } + return NULL; } +static void qemu_nbd_shutdown(void) +{ + job_cancel_sync_all(); + bdrv_close_all(); +} + int main(int argc, char **argv) { BlockBackend *blk; BlockDriverState *bs; - off_t dev_offset = 0; + uint64_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; + int64_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:"; + const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:L"; struct option lopt[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, @@ -524,8 +619,10 @@ int main(int argc, char **argv) { "offset", required_argument, NULL, 'o' }, { "read-only", no_argument, NULL, 'r' }, { "partition", required_argument, NULL, 'P' }, + { "bitmap", required_argument, NULL, 'B' }, { "connect", required_argument, NULL, 'c' }, { "disconnect", no_argument, NULL, 'd' }, + { "list", no_argument, NULL, 'L' }, { "snapshot", no_argument, NULL, 's' }, { "load-snapshot", required_argument, NULL, 'l' }, { "nocache", no_argument, NULL, 'n' }, @@ -542,16 +639,17 @@ int main(int argc, char **argv) { "export-name", required_argument, NULL, 'x' }, { "description", required_argument, NULL, 'D' }, { "tls-creds", required_argument, NULL, QEMU_NBD_OPT_TLSCREDS }, + { "tls-authz", required_argument, NULL, QEMU_NBD_OPT_TLSAUTHZ }, { "image-opts", no_argument, NULL, QEMU_NBD_OPT_IMAGE_OPTS }, { "trace", required_argument, NULL, 'T' }, { "fork", no_argument, NULL, QEMU_NBD_OPT_FORK }, + { "pid-file", required_argument, NULL, QEMU_NBD_OPT_PID_FILE }, { NULL, 0, NULL, 0 } }; int ch; int opt_ind = 0; - char *end; int flags = BDRV_O_RDWR; - int partition = -1; + int partition = 0; int ret = 0; bool seen_cache = false; bool seen_discard = false; @@ -561,15 +659,18 @@ int main(int argc, char **argv) Error *local_err = NULL; BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; QDict *options = NULL; - const char *export_name = NULL; + const char *export_name = NULL; /* defaults to "" later for server mode */ const char *export_description = NULL; + const char *bitmap = NULL; const char *tlscredsid = NULL; bool imageOpts = false; bool writethrough = true; char *trace_file = NULL; bool fork_process = false; + bool list = false; int old_stderr = -1; unsigned socket_activation; + const char *pid_file_name = NULL; /* The client thread uses SIGTERM to interrupt the server. A signal * handler ensures that "qemu-nbd -v -c" exits with a nice status code. @@ -583,6 +684,7 @@ int main(int argc, char **argv) signal(SIGPIPE, SIG_IGN); #endif + error_init(argv[0]); module_call_init(MODULE_INIT_TRACE); qcrypto_init(&error_fatal); @@ -661,13 +763,8 @@ int main(int argc, char **argv) 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); + if (qemu_strtou64(optarg, NULL, 0, &dev_offset) < 0) { + error_report("Invalid offset '%s'", optarg); exit(EXIT_FAILURE); } break; @@ -689,16 +786,17 @@ int main(int argc, char **argv) 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); + warn_report("The '-P' option is deprecated; use --image-opts with " + "a raw device wrapper for subset exports instead"); + if (qemu_strtoi(optarg, NULL, 0, &partition) < 0 || + partition < 1 || partition > 8) { + error_report("Invalid partition '%s'", optarg); exit(EXIT_FAILURE); } break; + case 'B': + bitmap = optarg; + break; case 'k': sockpath = optarg; if (sockpath[0] != '/') { @@ -713,15 +811,11 @@ int main(int argc, char **argv) device = optarg; break; case 'e': - shared = strtol(optarg, &end, 0); - if (*end) { + if (qemu_strtoi(optarg, NULL, 0, &shared) < 0 || + shared < 1) { 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; @@ -767,23 +861,47 @@ int main(int argc, char **argv) g_free(trace_file); trace_file = trace_opt_parse(optarg); break; + case QEMU_NBD_OPT_TLSAUTHZ: + tlsauthz = optarg; + break; case QEMU_NBD_OPT_FORK: fork_process = true; break; + case 'L': + list = true; + break; + case QEMU_NBD_OPT_PID_FILE: + pid_file_name = optarg; + break; } } - if ((argc - optind) != 1) { + if (list) { + if (argc != optind) { + error_report("List mode is incompatible with a file name"); + exit(EXIT_FAILURE); + } + if (export_name || export_description || dev_offset || partition || + device || disconnect || fmt || sn_id_or_name || bitmap || + seen_aio || seen_discard || seen_cache) { + error_report("List mode is incompatible with per-device settings"); + exit(EXIT_FAILURE); + } + if (fork_process) { + error_report("List mode is incompatible with forking"); + exit(EXIT_FAILURE); + } + } else if ((argc - optind) != 1) { error_report("Invalid number of arguments"); error_printf("Try `%s --help' for more information.\n", argv[0]); exit(EXIT_FAILURE); + } else if (!export_name) { + export_name = ""; } - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - NULL, NULL)) { - exit(EXIT_FAILURE); - } + qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, &error_fatal); if (!trace_init_backends()) { exit(1); @@ -797,7 +915,8 @@ int main(int argc, char **argv) } else { /* Using socket activation - check user didn't use -p etc. */ const char *err_msg = socket_activation_validate_opts(device, sockpath, - bindto, port); + bindto, port, + list); if (err_msg != NULL) { error_report("%s", err_msg); exit(EXIT_FAILURE); @@ -820,19 +939,34 @@ int main(int argc, char **argv) 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 = ""; + if (tlsauthz && list) { + error_report("TLS authorization is incompatible with export list"); + exit(EXIT_FAILURE); } - tlscreds = nbd_get_tls_creds(tlscredsid, &local_err); + tlscreds = nbd_get_tls_creds(tlscredsid, list, &local_err); if (local_err) { error_report("Failed to get TLS creds %s", error_get_pretty(local_err)); exit(EXIT_FAILURE); } + } else { + if (tlsauthz) { + error_report("--tls-authz is not permitted without --tls-creds"); + exit(EXIT_FAILURE); + } + } + + if (list) { + saddr = nbd_build_socket_address(sockpath, bindto, port); + return qemu_nbd_client_list(saddr, tlscreds, bindto); } +#if !HAVE_NBD_DEVICE + if (disconnect || device) { + error_report("Kernel /dev/nbdN support not available"); + exit(EXIT_FAILURE); + } +#else /* HAVE_NBD_DEVICE */ if (disconnect) { int nbdfd = open(argv[optind], O_RDWR); if (nbdfd < 0) { @@ -848,6 +982,7 @@ int main(int argc, char **argv) return 0; } +#endif if ((device && !verbose) || fork_process) { int stderr_fd[2]; @@ -869,10 +1004,11 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } else if (pid == 0) { close(stderr_fd[0]); + + old_stderr = dup(STDERR_FILENO); 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)); @@ -915,23 +1051,29 @@ int main(int argc, char **argv) snprintf(sockpath, 128, SOCKET_PATH, basename(device)); } + server = qio_net_listener_new(); 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)); + if (qio_net_listener_open_sync(server, saddr, &local_err) < 0) { + object_unref(OBJECT(server)); error_report_err(local_err); - return 1; + exit(EXIT_FAILURE); } } else { + size_t i; /* 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); + for (i = 0; i < socket_activation; i++) { + QIOChannelSocket *sioc; + sioc = qio_channel_socket_new_fd(FIRST_SOCKET_ACTIVATION_FD + i, + &local_err); + if (sioc == NULL) { + object_unref(OBJECT(server)); + error_report("Failed to use socket activation: %s", + error_get_pretty(local_err)); + exit(EXIT_FAILURE); + } + qio_net_listener_add(server, sioc); + object_unref(OBJECT(sioc)); } } @@ -940,7 +1082,7 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } bdrv_init(); - atexit(bdrv_close_all); + atexit(qemu_nbd_shutdown); srcpath = argv[optind]; if (imageOpts) { @@ -997,38 +1139,46 @@ int main(int argc, char **argv) } 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); + error_report("Offset (%" PRIu64 ") has to be smaller than the image " + "size (%" PRId64 ")", dev_offset, fd_size); exit(EXIT_FAILURE); } fd_size -= dev_offset; - if (partition != -1) { - ret = find_partition(blk, partition, &dev_offset, &fd_size); + if (partition) { + uint64_t limit; + + if (dev_offset) { + error_report("Cannot request partition and offset together"); + exit(EXIT_FAILURE); + } + ret = find_partition(blk, partition, &dev_offset, &limit); if (ret < 0) { error_report("Could not find partition %d: %s", partition, strerror(-ret)); exit(EXIT_FAILURE); } + /* + * MBR partition limits are (32-bit << 9); this assert lets + * the compiler know that we can't overflow 64 bits. + */ + assert(dev_offset + limit >= dev_offset); + if (dev_offset + limit > fd_size) { + error_report("Discovered partition %d at offset %" PRIu64 + " size %" PRIu64 ", but size exceeds file length %" + PRId64, partition, dev_offset, limit, fd_size); + exit(EXIT_FAILURE); + } + fd_size = limit; } - 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); - } + export = nbd_export_new(bs, dev_offset, fd_size, export_name, + export_description, bitmap, nbdflags, + nbd_export_closed, writethrough, NULL, + &error_fatal); if (device) { +#if HAVE_NBD_DEVICE int ret; ret = pthread_create(&client_thread, NULL, nbd_client_thread, device); @@ -1036,6 +1186,7 @@ int main(int argc, char **argv) error_report("Failed to create client thread: %s", strerror(ret)); exit(EXIT_FAILURE); } +#endif } else { /* Shut up GCC warnings. */ memset(&client_thread, 0, sizeof(client_thread)); @@ -1043,6 +1194,10 @@ int main(int argc, char **argv) nbd_update_server_watch(); + if (pid_file_name) { + qemu_write_pidfile(pid_file_name, &error_fatal); + } + /* now when the initialization is (almost) complete, chdir("/") * to free any busy filesystems */ if (chdir("/") < 0) { @@ -1061,9 +1216,9 @@ int main(int argc, char **argv) main_loop_wait(false); if (state == TERMINATE) { state = TERMINATING; - nbd_export_close(exp); - nbd_export_put(exp); - exp = NULL; + nbd_export_close(export); + nbd_export_put(export); + export = NULL; } } while (state != TERMINATED);