2008-07-03 18:23:51 +08:00
|
|
|
/*
|
2008-05-28 05:13:40 +08:00
|
|
|
* Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
|
|
|
|
*
|
|
|
|
* Network Block Device
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; under version 2 of the License.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2009-07-17 04:47:01 +08:00
|
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
2008-05-28 05:13:40 +08:00
|
|
|
*/
|
|
|
|
|
2016-01-30 01:50:05 +08:00
|
|
|
#include "qemu/osdep.h"
|
include/qemu/osdep.h: Don't include qapi/error.h
Commit 57cb38b included qapi/error.h into qemu/osdep.h to get the
Error typedef. Since then, we've moved to include qemu/osdep.h
everywhere. Its file comment explains: "To avoid getting into
possible circular include dependencies, this file should not include
any other QEMU headers, with the exceptions of config-host.h,
compiler.h, os-posix.h and os-win32.h, all of which are doing a
similar job to this file and are under similar constraints."
qapi/error.h doesn't do a similar job, and it doesn't adhere to
similar constraints: it includes qapi-types.h. That's in excess of
100KiB of crap most .c files don't actually need.
Add the typedef to qemu/typedefs.h, and include that instead of
qapi/error.h. Include qapi/error.h in .c files that need it and don't
get it now. Include qapi-types.h in qom/object.h for uint16List.
Update scripts/clean-includes accordingly. Update it further to match
reality: replace config.h by config-target.h, add sysemu/os-posix.h,
sysemu/os-win32.h. Update the list of includes in the qemu/osdep.h
comment quoted above similarly.
This reduces the number of objects depending on qapi/error.h from "all
of them" to less than a third. Unfortunately, the number depending on
qapi-types.h shrinks only a little. More work is needed for that one.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
[Fix compilation without the spice devel packages. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-03-14 16:01:28 +08:00
|
|
|
#include "qapi/error.h"
|
2011-09-08 23:55:32 +08:00
|
|
|
#include "qemu-common.h"
|
2016-03-21 01:16:19 +08:00
|
|
|
#include "qemu/cutils.h"
|
block: New BlockBackend
A block device consists of a frontend device model and a backend.
A block backend has a tree of block drivers doing the actual work.
The tree is managed by the block layer.
We currently use a single abstraction BlockDriverState both for tree
nodes and the backend as a whole. Drawbacks:
* Its API includes both stuff that makes sense only at the block
backend level (root of the tree) and stuff that's only for use
within the block layer. This makes the API bigger and more complex
than necessary. Moreover, it's not obvious which interfaces are
meant for device models, and which really aren't.
* Since device models keep a reference to their backend, the backend
object can't just be destroyed. But for media change, we need to
replace the tree. Our solution is to make the BlockDriverState
generic, with actual driver state in a separate object, pointed to
by member opaque. That lets us replace the tree by deinitializing
and reinitializing its root. This special need of the root makes
the data structure awkward everywhere in the tree.
The general plan is to separate the APIs into "block backend", for use
by device models, monitor and whatever other code dealing with block
backends, and "block driver", for use by the block layer and whatever
other code (if any) dealing with trees and tree nodes.
Code dealing with block backends, device models in particular, should
become completely oblivious of BlockDriverState. This should let us
clean up both APIs, and the tree data structures.
This commit is a first step. It creates a minimal "block backend"
API: type BlockBackend and functions to create, destroy and find them.
BlockBackend objects are created and destroyed exactly when root
BlockDriverState objects are created and destroyed. "Root" in the
sense of "in bdrv_states". They're not yet used for anything; that'll
come shortly.
A root BlockDriverState is created with bdrv_new_root(), so where to
create a BlockBackend is obvious. Where these roots get destroyed
isn't always as obvious.
It is obvious in qemu-img.c, qemu-io.c and qemu-nbd.c, and in error
paths of blockdev_init(), blk_connect(). That leaves destruction of
objects successfully created by blockdev_init() and blk_connect().
blockdev_init() is used only by drive_new() and qmp_blockdev_add().
Objects created by the latter are currently indestructible (see commit
48f364d "blockdev: Refuse to drive_del something added with
blockdev-add" and commit 2d246f0 "blockdev: Introduce
DriveInfo.enable_auto_del"). Objects created by the former get
destroyed by drive_del().
Objects created by blk_connect() get destroyed by blk_disconnect().
BlockBackend is reference-counted. Its reference count never exceeds
one so far, but that's going to change.
In drive_del(), the BB's reference count is surely one now. The BDS's
reference count is greater than one when something else is holding a
reference, such as a block job. In this case, the BB is destroyed
right away, but the BDS lives on until all extra references get
dropped.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2014-10-07 19:59:04 +08:00
|
|
|
#include "sysemu/block-backend.h"
|
2014-08-14 01:20:18 +08:00
|
|
|
#include "block/block_int.h"
|
2012-12-18 01:19:44 +08:00
|
|
|
#include "block/nbd.h"
|
2013-08-21 23:02:47 +08:00
|
|
|
#include "qemu/main-loop.h"
|
2014-02-17 21:43:51 +08:00
|
|
|
#include "qemu/error-report.h"
|
2016-02-11 02:41:00 +08:00
|
|
|
#include "qemu/config-file.h"
|
2016-03-16 00:22:36 +08:00
|
|
|
#include "qemu/bswap.h"
|
2016-06-17 22:44:12 +08:00
|
|
|
#include "qemu/log.h"
|
2017-03-16 23:29:45 +08:00
|
|
|
#include "qemu/systemd.h"
|
2013-12-04 17:10:55 +08:00
|
|
|
#include "block/snapshot.h"
|
2014-08-14 01:20:18 +08:00
|
|
|
#include "qapi/util.h"
|
2015-03-18 01:29:20 +08:00
|
|
|
#include "qapi/qmp/qstring.h"
|
2016-02-11 02:41:00 +08:00
|
|
|
#include "qom/object_interfaces.h"
|
2016-02-11 02:41:02 +08:00
|
|
|
#include "io/channel-socket.h"
|
2016-04-06 19:12:06 +08:00
|
|
|
#include "crypto/init.h"
|
2016-06-17 22:44:12 +08:00
|
|
|
#include "trace/control.h"
|
2008-05-28 05:13:40 +08:00
|
|
|
|
|
|
|
#include <getopt.h>
|
2009-12-23 23:34:04 +08:00
|
|
|
#include <libgen.h>
|
2011-11-04 22:51:21 +08:00
|
|
|
#include <pthread.h>
|
2008-07-03 18:23:51 +08:00
|
|
|
|
2014-08-14 01:20:19 +08:00
|
|
|
#define SOCKET_PATH "/var/lock/qemu-nbd-%s"
|
2016-02-17 18:10:21 +08:00
|
|
|
#define QEMU_NBD_OPT_CACHE 256
|
|
|
|
#define QEMU_NBD_OPT_AIO 257
|
|
|
|
#define QEMU_NBD_OPT_DISCARD 258
|
|
|
|
#define QEMU_NBD_OPT_DETECT_ZEROES 259
|
|
|
|
#define QEMU_NBD_OPT_OBJECT 260
|
|
|
|
#define QEMU_NBD_OPT_TLSCREDS 261
|
|
|
|
#define QEMU_NBD_OPT_IMAGE_OPTS 262
|
2016-09-29 04:46:42 +08:00
|
|
|
#define QEMU_NBD_OPT_FORK 263
|
2008-05-28 05:13:40 +08:00
|
|
|
|
2016-05-07 00:26:42 +08:00
|
|
|
#define MBR_SIZE 512
|
|
|
|
|
2011-09-19 20:03:37 +08:00
|
|
|
static NBDExport *exp;
|
2016-02-11 02:41:08 +08:00
|
|
|
static bool newproto;
|
2008-10-26 21:43:07 +08:00
|
|
|
static int verbose;
|
2011-11-04 22:51:21 +08:00
|
|
|
static char *srcpath;
|
2017-04-26 15:36:41 +08:00
|
|
|
static SocketAddress *saddr;
|
2012-09-18 19:31:56 +08:00
|
|
|
static int persistent = 0;
|
|
|
|
static enum { RUNNING, TERMINATE, TERMINATING, TERMINATED } state;
|
2011-09-12 23:28:11 +08:00
|
|
|
static int shared = 1;
|
|
|
|
static int nb_fds;
|
2016-02-11 02:41:02 +08:00
|
|
|
static QIOChannelSocket *server_ioc;
|
|
|
|
static int server_watch = -1;
|
2016-02-11 02:41:13 +08:00
|
|
|
static QCryptoTLSCreds *tlscreds;
|
2008-05-28 05:13:40 +08:00
|
|
|
|
|
|
|
static void usage(const char *name)
|
|
|
|
{
|
2012-07-18 20:50:52 +08:00
|
|
|
(printf) (
|
2008-05-28 05:13:40 +08:00
|
|
|
"Usage: %s [OPTIONS] FILE\n"
|
|
|
|
"QEMU Disk Network Block Device Server\n"
|
|
|
|
"\n"
|
2014-08-14 01:20:19 +08:00
|
|
|
" -h, --help display this help and exit\n"
|
|
|
|
" -V, --version output version information and exit\n"
|
2012-07-18 20:50:52 +08:00
|
|
|
"\n"
|
|
|
|
"Connection properties:\n"
|
2014-08-14 01:20:19 +08:00
|
|
|
" -p, --port=PORT port to listen on (default `%d')\n"
|
|
|
|
" -b, --bind=IFACE interface to bind to (default `0.0.0.0')\n"
|
|
|
|
" -k, --socket=PATH path to the unix socket\n"
|
|
|
|
" (default '"SOCKET_PATH"')\n"
|
|
|
|
" -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"
|
2016-04-06 10:02:08 +08:00
|
|
|
" -x, --export-name=NAME expose export by name\n"
|
2016-10-15 02:33:03 +08:00
|
|
|
" -D, --description=TEXT with -x, also export a human-readable description\n"
|
2008-05-28 05:13:40 +08:00
|
|
|
"\n"
|
2012-07-18 20:50:52 +08:00
|
|
|
"Exposing part of the image:\n"
|
2014-08-14 01:20:19 +08:00
|
|
|
" -o, --offset=OFFSET offset into the image\n"
|
|
|
|
" -P, --partition=NUM only expose partition NUM\n"
|
2012-07-18 20:50:52 +08:00
|
|
|
"\n"
|
2016-02-11 02:41:00 +08:00
|
|
|
"General purpose options:\n"
|
|
|
|
" --object type,id=ID,... define an object such as 'secret' for providing\n"
|
|
|
|
" passwords and/or encryption keys\n"
|
2016-06-17 22:44:12 +08:00
|
|
|
" -T, --trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n"
|
|
|
|
" specify tracing options\n"
|
2016-09-29 04:46:42 +08:00
|
|
|
" --fork fork off the server process and exit the parent\n"
|
|
|
|
" once the server is running\n"
|
2012-07-18 20:50:52 +08:00
|
|
|
#ifdef __linux__
|
|
|
|
"Kernel NBD client support:\n"
|
2014-08-14 01:20:19 +08:00
|
|
|
" -c, --connect=DEV connect FILE to the local NBD device DEV\n"
|
|
|
|
" -d, --disconnect disconnect the specified device\n"
|
2012-07-18 20:50:52 +08:00
|
|
|
"\n"
|
|
|
|
#endif
|
|
|
|
"\n"
|
|
|
|
"Block device options:\n"
|
2014-08-14 01:20:19 +08:00
|
|
|
" -f, --format=FORMAT set image format (raw, qcow2, ...)\n"
|
|
|
|
" -r, --read-only export read-only\n"
|
|
|
|
" -s, --snapshot use FILE as an external snapshot, create a temporary\n"
|
|
|
|
" file with backing_file=FILE, redirect the write to\n"
|
|
|
|
" the temporary one\n"
|
2013-12-04 17:10:55 +08:00
|
|
|
" -l, --load-snapshot=SNAPSHOT_PARAM\n"
|
2014-08-14 01:20:19 +08:00
|
|
|
" load an internal snapshot inside FILE and export it\n"
|
|
|
|
" as an read-only device, SNAPSHOT_PARAM format is\n"
|
|
|
|
" 'snapshot.id=[ID],snapshot.name=[NAME]', or\n"
|
|
|
|
" '[ID_OR_NAME]'\n"
|
|
|
|
" -n, --nocache disable host cache\n"
|
|
|
|
" --cache=MODE set cache mode (none, writeback, ...)\n"
|
|
|
|
" --aio=MODE set AIO mode (native or threads)\n"
|
2014-08-14 01:20:18 +08:00
|
|
|
" --discard=MODE set discard mode (ignore, unmap)\n"
|
2015-08-01 03:12:54 +08:00
|
|
|
" --detect-zeroes=MODE set detect-zeroes mode (off, on, unmap)\n"
|
2016-02-17 18:10:19 +08:00
|
|
|
" --image-opts treat FILE as a full set of image options\n"
|
2012-07-18 20:50:52 +08:00
|
|
|
"\n"
|
|
|
|
"Report bugs to <qemu-devel@nongnu.org>\n"
|
2010-09-18 02:37:25 +08:00
|
|
|
, name, NBD_DEFAULT_PORT, "DEVICE");
|
2008-05-28 05:13:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void version(const char *name)
|
|
|
|
{
|
|
|
|
printf(
|
2008-07-19 02:06:23 +08:00
|
|
|
"%s version 0.0.1\n"
|
2008-05-28 05:13:40 +08:00
|
|
|
"Written by Anthony Liguori.\n"
|
|
|
|
"\n"
|
|
|
|
"Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>.\n"
|
|
|
|
"This is free software; see the source for copying conditions. There is NO\n"
|
|
|
|
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
|
2008-07-19 02:06:23 +08:00
|
|
|
, name);
|
2008-05-28 05:13:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
struct partition_record
|
|
|
|
{
|
|
|
|
uint8_t bootable;
|
|
|
|
uint8_t start_head;
|
|
|
|
uint32_t start_cylinder;
|
|
|
|
uint8_t start_sector;
|
|
|
|
uint8_t system;
|
|
|
|
uint8_t end_head;
|
|
|
|
uint8_t end_cylinder;
|
|
|
|
uint8_t end_sector;
|
|
|
|
uint32_t start_sector_abs;
|
|
|
|
uint32_t nb_sectors_abs;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void read_partition(uint8_t *p, struct partition_record *r)
|
|
|
|
{
|
|
|
|
r->bootable = p[0];
|
|
|
|
r->start_head = p[1];
|
|
|
|
r->start_cylinder = p[3] | ((p[2] << 2) & 0x0300);
|
|
|
|
r->start_sector = p[2] & 0x3f;
|
|
|
|
r->system = p[4];
|
|
|
|
r->end_head = p[5];
|
|
|
|
r->end_cylinder = p[7] | ((p[6] << 2) & 0x300);
|
|
|
|
r->end_sector = p[6] & 0x3f;
|
2015-02-26 02:08:23 +08:00
|
|
|
|
2016-06-10 23:00:36 +08:00
|
|
|
r->start_sector_abs = ldl_le_p(p + 8);
|
|
|
|
r->nb_sectors_abs = ldl_le_p(p + 12);
|
2008-05-28 05:13:40 +08:00
|
|
|
}
|
|
|
|
|
2014-11-18 19:21:19 +08:00
|
|
|
static int find_partition(BlockBackend *blk, int partition,
|
2008-05-28 05:13:40 +08:00
|
|
|
off_t *offset, off_t *size)
|
|
|
|
{
|
|
|
|
struct partition_record mbr[4];
|
2016-05-07 00:26:42 +08:00
|
|
|
uint8_t data[MBR_SIZE];
|
2008-05-28 05:13:40 +08:00
|
|
|
int i;
|
|
|
|
int ext_partnum = 4;
|
2010-05-03 05:50:25 +08:00
|
|
|
int ret;
|
2008-05-28 05:13:40 +08:00
|
|
|
|
2016-05-07 00:26:42 +08:00
|
|
|
ret = blk_pread(blk, 0, data, sizeof(data));
|
|
|
|
if (ret < 0) {
|
2015-12-18 23:35:10 +08:00
|
|
|
error_report("error while reading: %s", strerror(-ret));
|
2015-12-18 23:35:04 +08:00
|
|
|
exit(EXIT_FAILURE);
|
2010-05-03 05:50:25 +08:00
|
|
|
}
|
2008-05-28 05:13:40 +08:00
|
|
|
|
|
|
|
if (data[510] != 0x55 || data[511] != 0xaa) {
|
2012-03-05 15:56:10 +08:00
|
|
|
return -EINVAL;
|
2008-05-28 05:13:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
read_partition(&data[446 + 16 * i], &mbr[i]);
|
|
|
|
|
2015-02-26 02:08:15 +08:00
|
|
|
if (!mbr[i].system || !mbr[i].nb_sectors_abs) {
|
2008-05-28 05:13:40 +08:00
|
|
|
continue;
|
2015-02-26 02:08:15 +08:00
|
|
|
}
|
2008-05-28 05:13:40 +08:00
|
|
|
|
|
|
|
if (mbr[i].system == 0xF || mbr[i].system == 0x5) {
|
|
|
|
struct partition_record ext[4];
|
2016-05-07 00:26:42 +08:00
|
|
|
uint8_t data1[MBR_SIZE];
|
2008-05-28 05:13:40 +08:00
|
|
|
int j;
|
|
|
|
|
2016-05-07 00:26:42 +08:00
|
|
|
ret = blk_pread(blk, mbr[i].start_sector_abs * MBR_SIZE,
|
|
|
|
data1, sizeof(data1));
|
|
|
|
if (ret < 0) {
|
2015-12-18 23:35:10 +08:00
|
|
|
error_report("error while reading: %s", strerror(-ret));
|
2015-12-18 23:35:04 +08:00
|
|
|
exit(EXIT_FAILURE);
|
2010-05-03 05:50:25 +08:00
|
|
|
}
|
2008-05-28 05:13:40 +08:00
|
|
|
|
|
|
|
for (j = 0; j < 4; j++) {
|
|
|
|
read_partition(&data1[446 + 16 * j], &ext[j]);
|
2015-02-26 02:08:15 +08:00
|
|
|
if (!ext[j].system || !ext[j].nb_sectors_abs) {
|
2008-05-28 05:13:40 +08:00
|
|
|
continue;
|
2015-02-26 02:08:15 +08:00
|
|
|
}
|
2008-05-28 05:13:40 +08:00
|
|
|
|
|
|
|
if ((ext_partnum + j + 1) == partition) {
|
|
|
|
*offset = (uint64_t)ext[j].start_sector_abs << 9;
|
|
|
|
*size = (uint64_t)ext[j].nb_sectors_abs << 9;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ext_partnum += 4;
|
|
|
|
} else if ((i + 1) == partition) {
|
|
|
|
*offset = (uint64_t)mbr[i].start_sector_abs << 9;
|
|
|
|
*size = (uint64_t)mbr[i].nb_sectors_abs << 9;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-05 15:56:10 +08:00
|
|
|
return -ENOENT;
|
2008-05-28 05:13:40 +08:00
|
|
|
}
|
|
|
|
|
2011-11-04 22:51:19 +08:00
|
|
|
static void termsig_handler(int signum)
|
|
|
|
{
|
2016-04-14 18:20:15 +08:00
|
|
|
atomic_cmpxchg(&state, RUNNING, TERMINATE);
|
2011-09-12 23:28:11 +08:00
|
|
|
qemu_notify_event();
|
2011-11-04 22:51:19 +08:00
|
|
|
}
|
|
|
|
|
2014-02-17 21:43:51 +08:00
|
|
|
|
2011-11-04 22:51:21 +08:00
|
|
|
static void *show_parts(void *arg)
|
2008-07-03 18:23:51 +08:00
|
|
|
{
|
2011-12-06 16:07:00 +08:00
|
|
|
char *device = arg;
|
2011-11-04 22:51:21 +08:00
|
|
|
int nbd;
|
|
|
|
|
|
|
|
/* linux just needs an open() to trigger
|
|
|
|
* the partition table update
|
|
|
|
* but remember to load the module with max_part != 0 :
|
|
|
|
* modprobe nbd max_part=63
|
|
|
|
*/
|
|
|
|
nbd = open(device, O_RDWR);
|
2012-03-07 18:05:34 +08:00
|
|
|
if (nbd >= 0) {
|
2011-11-04 22:51:21 +08:00
|
|
|
close(nbd);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-07-03 18:23:51 +08:00
|
|
|
|
2011-11-04 22:51:21 +08:00
|
|
|
static void *nbd_client_thread(void *arg)
|
|
|
|
{
|
2011-12-06 16:07:00 +08:00
|
|
|
char *device = arg;
|
2011-11-04 22:51:21 +08:00
|
|
|
off_t size;
|
2016-07-22 03:34:46 +08:00
|
|
|
uint16_t nbdflags;
|
2016-02-11 02:41:02 +08:00
|
|
|
QIOChannelSocket *sioc;
|
|
|
|
int fd;
|
2011-11-04 22:51:21 +08:00
|
|
|
int ret;
|
|
|
|
pthread_t show_parts_thread;
|
2015-01-27 10:02:59 +08:00
|
|
|
Error *local_error = NULL;
|
2011-11-04 22:51:21 +08:00
|
|
|
|
2016-02-11 02:41:02 +08:00
|
|
|
sioc = qio_channel_socket_new();
|
|
|
|
if (qio_channel_socket_connect_sync(sioc,
|
|
|
|
saddr,
|
|
|
|
&local_error) < 0) {
|
2015-09-16 21:52:23 +08:00
|
|
|
error_report_err(local_error);
|
2012-01-05 21:16:07 +08:00
|
|
|
goto out;
|
|
|
|
}
|
2011-11-04 22:51:21 +08:00
|
|
|
|
2016-02-11 02:41:04 +08:00
|
|
|
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, &nbdflags,
|
2016-02-11 02:41:11 +08:00
|
|
|
NULL, NULL, NULL,
|
2015-02-26 02:08:25 +08:00
|
|
|
&size, &local_error);
|
2012-03-07 18:05:34 +08:00
|
|
|
if (ret < 0) {
|
2015-01-27 10:02:59 +08:00
|
|
|
if (local_error) {
|
2015-12-18 23:35:07 +08:00
|
|
|
error_report_err(local_error);
|
2015-01-27 10:02:59 +08:00
|
|
|
}
|
2014-03-15 01:10:54 +08:00
|
|
|
goto out_socket;
|
2011-11-04 22:51:21 +08:00
|
|
|
}
|
|
|
|
|
2011-12-06 16:07:00 +08:00
|
|
|
fd = open(device, O_RDWR);
|
2012-03-07 18:05:34 +08:00
|
|
|
if (fd < 0) {
|
2011-12-06 16:07:00 +08:00
|
|
|
/* Linux-only, we can use %m in printf. */
|
2015-12-18 23:35:18 +08:00
|
|
|
error_report("Failed to open %s: %m", device);
|
2014-03-15 01:10:54 +08:00
|
|
|
goto out_socket;
|
2011-12-06 16:07:00 +08:00
|
|
|
}
|
|
|
|
|
2017-05-26 19:09:13 +08:00
|
|
|
ret = nbd_init(fd, sioc, nbdflags, size, &local_error);
|
2012-03-07 18:05:34 +08:00
|
|
|
if (ret < 0) {
|
2017-05-26 19:09:13 +08:00
|
|
|
error_report_err(local_error);
|
2014-03-15 01:10:54 +08:00
|
|
|
goto out_fd;
|
2011-11-04 22:51:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* update partition table */
|
2011-12-06 16:07:00 +08:00
|
|
|
pthread_create(&show_parts_thread, NULL, show_parts, device);
|
2011-11-04 22:51:21 +08:00
|
|
|
|
2011-11-04 22:51:22 +08:00
|
|
|
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);
|
|
|
|
}
|
2011-11-04 22:51:21 +08:00
|
|
|
|
|
|
|
ret = nbd_client(fd);
|
|
|
|
if (ret) {
|
2014-03-15 01:10:54 +08:00
|
|
|
goto out_fd;
|
2008-07-03 18:23:51 +08:00
|
|
|
}
|
2011-11-04 22:51:21 +08:00
|
|
|
close(fd);
|
2016-02-11 02:41:02 +08:00
|
|
|
object_unref(OBJECT(sioc));
|
2011-11-04 22:51:21 +08:00
|
|
|
kill(getpid(), SIGTERM);
|
|
|
|
return (void *) EXIT_SUCCESS;
|
|
|
|
|
2014-03-15 01:10:54 +08:00
|
|
|
out_fd:
|
|
|
|
close(fd);
|
|
|
|
out_socket:
|
2016-02-11 02:41:02 +08:00
|
|
|
object_unref(OBJECT(sioc));
|
2011-11-04 22:51:21 +08:00
|
|
|
out:
|
|
|
|
kill(getpid(), SIGTERM);
|
|
|
|
return (void *) EXIT_FAILURE;
|
2008-07-03 18:23:51 +08:00
|
|
|
}
|
|
|
|
|
2015-05-19 18:50:59 +08:00
|
|
|
static int nbd_can_accept(void)
|
2011-09-12 23:28:11 +08:00
|
|
|
{
|
|
|
|
return nb_fds < shared;
|
|
|
|
}
|
|
|
|
|
2012-09-18 19:31:56 +08:00
|
|
|
static void nbd_export_closed(NBDExport *exp)
|
|
|
|
{
|
|
|
|
assert(state == TERMINATING);
|
|
|
|
state = TERMINATED;
|
|
|
|
}
|
|
|
|
|
2016-02-11 02:41:02 +08:00
|
|
|
static void nbd_update_server_watch(void);
|
2015-05-19 18:50:59 +08:00
|
|
|
|
2011-09-19 20:33:23 +08:00
|
|
|
static void nbd_client_closed(NBDClient *client)
|
2011-09-12 23:28:11 +08:00
|
|
|
{
|
2011-09-19 20:33:23 +08:00
|
|
|
nb_fds--;
|
2012-09-18 19:31:56 +08:00
|
|
|
if (nb_fds == 0 && !persistent && state == RUNNING) {
|
|
|
|
state = TERMINATE;
|
|
|
|
}
|
2016-02-11 02:41:02 +08:00
|
|
|
nbd_update_server_watch();
|
2012-09-18 19:31:56 +08:00
|
|
|
nbd_client_put(client);
|
2011-09-12 23:28:11 +08:00
|
|
|
}
|
|
|
|
|
2016-02-11 02:41:02 +08:00
|
|
|
static gboolean nbd_accept(QIOChannel *ioc, GIOCondition cond, gpointer opaque)
|
2011-09-12 23:28:11 +08:00
|
|
|
{
|
2016-02-11 02:41:02 +08:00
|
|
|
QIOChannelSocket *cioc;
|
2011-09-12 23:28:11 +08:00
|
|
|
|
2016-02-11 02:41:02 +08:00
|
|
|
cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
|
|
|
|
NULL);
|
|
|
|
if (!cioc) {
|
|
|
|
return TRUE;
|
2014-03-15 01:10:54 +08:00
|
|
|
}
|
|
|
|
|
2012-09-18 19:31:56 +08:00
|
|
|
if (state >= TERMINATE) {
|
2016-02-11 02:41:02 +08:00
|
|
|
object_unref(OBJECT(cioc));
|
|
|
|
return TRUE;
|
2012-09-18 19:31:56 +08:00
|
|
|
}
|
|
|
|
|
2016-01-14 16:41:01 +08:00
|
|
|
nb_fds++;
|
2016-02-11 02:41:02 +08:00
|
|
|
nbd_update_server_watch();
|
2016-02-11 02:41:11 +08:00
|
|
|
nbd_client_new(newproto ? NULL : exp, cioc,
|
2016-02-11 02:41:13 +08:00
|
|
|
tlscreds, NULL, nbd_client_closed);
|
2016-02-11 02:41:02 +08:00
|
|
|
object_unref(OBJECT(cioc));
|
|
|
|
|
|
|
|
return TRUE;
|
2011-09-12 23:28:11 +08:00
|
|
|
}
|
|
|
|
|
2016-02-11 02:41:02 +08:00
|
|
|
static void nbd_update_server_watch(void)
|
2015-05-19 18:50:59 +08:00
|
|
|
{
|
|
|
|
if (nbd_can_accept()) {
|
2016-02-11 02:41:02 +08:00
|
|
|
if (server_watch == -1) {
|
|
|
|
server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc),
|
|
|
|
G_IO_IN,
|
|
|
|
nbd_accept,
|
|
|
|
NULL, NULL);
|
|
|
|
}
|
2015-05-19 18:50:59 +08:00
|
|
|
} else {
|
2016-02-11 02:41:02 +08:00
|
|
|
if (server_watch != -1) {
|
|
|
|
g_source_remove(server_watch);
|
|
|
|
server_watch = -1;
|
|
|
|
}
|
2015-05-19 18:50:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-16 21:52:23 +08:00
|
|
|
|
2017-04-26 15:36:41 +08:00
|
|
|
static SocketAddress *nbd_build_socket_address(const char *sockpath,
|
2015-09-16 21:52:23 +08:00
|
|
|
const char *bindto,
|
|
|
|
const char *port)
|
|
|
|
{
|
2017-04-26 15:36:41 +08:00
|
|
|
SocketAddress *saddr;
|
2015-09-16 21:52:23 +08:00
|
|
|
|
2017-04-26 15:36:41 +08:00
|
|
|
saddr = g_new0(SocketAddress, 1);
|
2015-09-16 21:52:23 +08:00
|
|
|
if (sockpath) {
|
2017-04-26 15:36:41 +08:00
|
|
|
saddr->type = SOCKET_ADDRESS_TYPE_UNIX;
|
|
|
|
saddr->u.q_unix.path = g_strdup(sockpath);
|
2015-09-16 21:52:23 +08:00
|
|
|
} else {
|
2016-03-04 00:16:48 +08:00
|
|
|
InetSocketAddress *inet;
|
2017-04-26 15:36:41 +08:00
|
|
|
saddr->type = SOCKET_ADDRESS_TYPE_INET;
|
|
|
|
inet = &saddr->u.inet;
|
2016-03-04 00:16:48 +08:00
|
|
|
inet->host = g_strdup(bindto);
|
2015-09-16 21:52:23 +08:00
|
|
|
if (port) {
|
2016-03-04 00:16:48 +08:00
|
|
|
inet->port = g_strdup(port);
|
2015-09-16 21:52:23 +08:00
|
|
|
} else {
|
2016-03-04 00:16:48 +08:00
|
|
|
inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
|
2015-09-16 21:52:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return saddr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-17 18:10:19 +08:00
|
|
|
static QemuOptsList file_opts = {
|
|
|
|
.name = "file",
|
|
|
|
.implied_opt_name = "file",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(file_opts.head),
|
|
|
|
.desc = {
|
|
|
|
/* no elements => accept any params */
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2016-02-11 02:41:00 +08:00
|
|
|
static QemuOptsList qemu_object_opts = {
|
|
|
|
.name = "object",
|
|
|
|
.implied_opt_name = "qom-type",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
|
|
|
|
.desc = {
|
|
|
|
{ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-02-11 02:41:13 +08:00
|
|
|
|
|
|
|
static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
|
|
|
|
{
|
|
|
|
Object *obj;
|
|
|
|
QCryptoTLSCreds *creds;
|
|
|
|
|
|
|
|
obj = object_resolve_path_component(
|
|
|
|
object_get_objects_root(), id);
|
|
|
|
if (!obj) {
|
|
|
|
error_setg(errp, "No TLS credentials with id '%s'",
|
|
|
|
id);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
creds = (QCryptoTLSCreds *)
|
|
|
|
object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
|
|
|
|
if (!creds) {
|
|
|
|
error_setg(errp, "Object with id '%s' is not TLS credentials",
|
|
|
|
id);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-02-04 18:03:17 +08:00
|
|
|
static void setup_address_and_port(const char **address, const char **port)
|
|
|
|
{
|
|
|
|
if (*address == NULL) {
|
|
|
|
*address = "0.0.0.0";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*port == NULL) {
|
|
|
|
*port = stringify(NBD_DEFAULT_PORT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check socket parameters compatibility when socket activation is used.
|
|
|
|
*/
|
|
|
|
static const char *socket_activation_validate_opts(const char *device,
|
|
|
|
const char *sockpath,
|
|
|
|
const char *address,
|
|
|
|
const char *port)
|
|
|
|
{
|
|
|
|
if (device != NULL) {
|
|
|
|
return "NBD device can't be set when using socket activation";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sockpath != NULL) {
|
|
|
|
return "Unix socket can't be set when using socket activation";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (address != NULL) {
|
|
|
|
return "The interface can't be set when using socket activation";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (port != NULL) {
|
|
|
|
return "TCP port number can't be set when using socket activation";
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-02-11 02:41:13 +08:00
|
|
|
|
2008-05-28 05:13:40 +08:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
block: New BlockBackend
A block device consists of a frontend device model and a backend.
A block backend has a tree of block drivers doing the actual work.
The tree is managed by the block layer.
We currently use a single abstraction BlockDriverState both for tree
nodes and the backend as a whole. Drawbacks:
* Its API includes both stuff that makes sense only at the block
backend level (root of the tree) and stuff that's only for use
within the block layer. This makes the API bigger and more complex
than necessary. Moreover, it's not obvious which interfaces are
meant for device models, and which really aren't.
* Since device models keep a reference to their backend, the backend
object can't just be destroyed. But for media change, we need to
replace the tree. Our solution is to make the BlockDriverState
generic, with actual driver state in a separate object, pointed to
by member opaque. That lets us replace the tree by deinitializing
and reinitializing its root. This special need of the root makes
the data structure awkward everywhere in the tree.
The general plan is to separate the APIs into "block backend", for use
by device models, monitor and whatever other code dealing with block
backends, and "block driver", for use by the block layer and whatever
other code (if any) dealing with trees and tree nodes.
Code dealing with block backends, device models in particular, should
become completely oblivious of BlockDriverState. This should let us
clean up both APIs, and the tree data structures.
This commit is a first step. It creates a minimal "block backend"
API: type BlockBackend and functions to create, destroy and find them.
BlockBackend objects are created and destroyed exactly when root
BlockDriverState objects are created and destroyed. "Root" in the
sense of "in bdrv_states". They're not yet used for anything; that'll
come shortly.
A root BlockDriverState is created with bdrv_new_root(), so where to
create a BlockBackend is obvious. Where these roots get destroyed
isn't always as obvious.
It is obvious in qemu-img.c, qemu-io.c and qemu-nbd.c, and in error
paths of blockdev_init(), blk_connect(). That leaves destruction of
objects successfully created by blockdev_init() and blk_connect().
blockdev_init() is used only by drive_new() and qmp_blockdev_add().
Objects created by the latter are currently indestructible (see commit
48f364d "blockdev: Refuse to drive_del something added with
blockdev-add" and commit 2d246f0 "blockdev: Introduce
DriveInfo.enable_auto_del"). Objects created by the former get
destroyed by drive_del().
Objects created by blk_connect() get destroyed by blk_disconnect().
BlockBackend is reference-counted. Its reference count never exceeds
one so far, but that's going to change.
In drive_del(), the BB's reference count is surely one now. The BDS's
reference count is greater than one when something else is holding a
reference, such as a block job. In this case, the BB is destroyed
right away, but the BDS lives on until all extra references get
dropped.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2014-10-07 19:59:04 +08:00
|
|
|
BlockBackend *blk;
|
2008-05-28 05:13:40 +08:00
|
|
|
BlockDriverState *bs;
|
|
|
|
off_t dev_offset = 0;
|
2016-07-22 03:34:46 +08:00
|
|
|
uint16_t nbdflags = 0;
|
2008-07-03 18:23:51 +08:00
|
|
|
bool disconnect = false;
|
2017-02-04 18:03:17 +08:00
|
|
|
const char *bindto = NULL;
|
2015-09-16 21:52:23 +08:00
|
|
|
const char *port = NULL;
|
|
|
|
char *sockpath = NULL;
|
2011-12-06 16:07:00 +08:00
|
|
|
char *device = NULL;
|
2008-05-28 05:13:40 +08:00
|
|
|
off_t fd_size;
|
2013-12-04 17:10:55 +08:00
|
|
|
QemuOpts *sn_opts = NULL;
|
|
|
|
const char *sn_id_or_name = NULL;
|
2016-10-15 02:33:03 +08:00
|
|
|
const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:";
|
2008-05-28 05:13:40 +08:00
|
|
|
struct option lopt[] = {
|
2016-02-17 18:10:22 +08:00
|
|
|
{ "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' },
|
2016-10-15 02:33:03 +08:00
|
|
|
{ "description", required_argument, NULL, 'D' },
|
2016-02-17 18:10:22 +08:00
|
|
|
{ "tls-creds", required_argument, NULL, QEMU_NBD_OPT_TLSCREDS },
|
|
|
|
{ "image-opts", no_argument, NULL, QEMU_NBD_OPT_IMAGE_OPTS },
|
2016-06-17 22:44:12 +08:00
|
|
|
{ "trace", required_argument, NULL, 'T' },
|
2016-09-29 04:46:42 +08:00
|
|
|
{ "fork", no_argument, NULL, QEMU_NBD_OPT_FORK },
|
2009-08-01 05:16:51 +08:00
|
|
|
{ NULL, 0, NULL, 0 }
|
2008-05-28 05:13:40 +08:00
|
|
|
};
|
|
|
|
int ch;
|
|
|
|
int opt_ind = 0;
|
|
|
|
char *end;
|
2010-01-17 22:48:13 +08:00
|
|
|
int flags = BDRV_O_RDWR;
|
2008-05-28 05:13:40 +08:00
|
|
|
int partition = -1;
|
2015-02-06 02:58:19 +08:00
|
|
|
int ret = 0;
|
2012-07-18 20:57:15 +08:00
|
|
|
bool seen_cache = false;
|
2013-02-08 21:06:13 +08:00
|
|
|
bool seen_discard = false;
|
2012-07-18 20:57:15 +08:00
|
|
|
bool seen_aio = false;
|
2011-11-04 22:51:21 +08:00
|
|
|
pthread_t client_thread;
|
2013-03-19 19:20:20 +08:00
|
|
|
const char *fmt = NULL;
|
2013-09-05 20:45:29 +08:00
|
|
|
Error *local_err = NULL;
|
2014-08-14 01:20:18 +08:00
|
|
|
BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
|
2015-02-06 02:58:19 +08:00
|
|
|
QDict *options = NULL;
|
2016-02-11 02:41:08 +08:00
|
|
|
const char *export_name = NULL;
|
2016-10-15 02:33:03 +08:00
|
|
|
const char *export_description = NULL;
|
2016-02-11 02:41:13 +08:00
|
|
|
const char *tlscredsid = NULL;
|
2016-02-17 18:10:19 +08:00
|
|
|
bool imageOpts = false;
|
2016-03-14 18:43:28 +08:00
|
|
|
bool writethrough = true;
|
2016-06-17 22:44:12 +08:00
|
|
|
char *trace_file = NULL;
|
2016-09-29 04:46:42 +08:00
|
|
|
bool fork_process = false;
|
|
|
|
int old_stderr = -1;
|
2017-02-04 18:03:17 +08:00
|
|
|
unsigned socket_activation;
|
2008-05-28 05:13:40 +08:00
|
|
|
|
2011-11-04 22:51:21 +08:00
|
|
|
/* The client thread uses SIGTERM to interrupt the server. A signal
|
|
|
|
* handler ensures that "qemu-nbd -v -c" exits with a nice status code.
|
|
|
|
*/
|
2011-11-04 22:51:19 +08:00
|
|
|
struct sigaction sa_sigterm;
|
|
|
|
memset(&sa_sigterm, 0, sizeof(sa_sigterm));
|
|
|
|
sa_sigterm.sa_handler = termsig_handler;
|
|
|
|
sigaction(SIGTERM, &sa_sigterm, NULL);
|
2016-04-06 19:12:06 +08:00
|
|
|
|
2016-10-04 21:35:52 +08:00
|
|
|
module_call_init(MODULE_INIT_TRACE);
|
2016-05-12 22:10:04 +08:00
|
|
|
qcrypto_init(&error_fatal);
|
2016-04-06 19:12:06 +08:00
|
|
|
|
2016-02-11 02:41:00 +08:00
|
|
|
module_call_init(MODULE_INIT_QOM);
|
|
|
|
qemu_add_opts(&qemu_object_opts);
|
2016-06-17 22:44:12 +08:00
|
|
|
qemu_add_opts(&qemu_trace_opts);
|
2014-02-10 14:48:51 +08:00
|
|
|
qemu_init_exec_dir(argv[0]);
|
2011-11-04 22:51:19 +08:00
|
|
|
|
2008-05-28 05:13:40 +08:00
|
|
|
while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
|
|
|
|
switch (ch) {
|
|
|
|
case 's':
|
2008-07-03 19:47:46 +08:00
|
|
|
flags |= BDRV_O_SNAPSHOT;
|
|
|
|
break;
|
|
|
|
case 'n':
|
2012-07-18 20:57:15 +08:00
|
|
|
optarg = (char *) "none";
|
|
|
|
/* fallthrough */
|
|
|
|
case QEMU_NBD_OPT_CACHE:
|
|
|
|
if (seen_cache) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("-n and --cache can only be specified once");
|
|
|
|
exit(EXIT_FAILURE);
|
2012-07-18 20:57:15 +08:00
|
|
|
}
|
|
|
|
seen_cache = true;
|
2016-03-14 18:43:28 +08:00
|
|
|
if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) == -1) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Invalid cache mode `%s'", optarg);
|
|
|
|
exit(EXIT_FAILURE);
|
2012-07-18 20:57:15 +08:00
|
|
|
}
|
2008-05-28 05:13:40 +08:00
|
|
|
break;
|
2012-07-18 20:57:15 +08:00
|
|
|
case QEMU_NBD_OPT_AIO:
|
|
|
|
if (seen_aio) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("--aio can only be specified once");
|
|
|
|
exit(EXIT_FAILURE);
|
2012-07-18 20:57:15 +08:00
|
|
|
}
|
|
|
|
seen_aio = true;
|
|
|
|
if (!strcmp(optarg, "native")) {
|
|
|
|
flags |= BDRV_O_NATIVE_AIO;
|
|
|
|
} else if (!strcmp(optarg, "threads")) {
|
|
|
|
/* this is the default */
|
|
|
|
} else {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("invalid aio mode `%s'", optarg);
|
|
|
|
exit(EXIT_FAILURE);
|
2012-07-18 20:57:15 +08:00
|
|
|
}
|
|
|
|
break;
|
2013-02-08 21:06:13 +08:00
|
|
|
case QEMU_NBD_OPT_DISCARD:
|
|
|
|
if (seen_discard) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("--discard can only be specified once");
|
|
|
|
exit(EXIT_FAILURE);
|
2013-02-08 21:06:13 +08:00
|
|
|
}
|
|
|
|
seen_discard = true;
|
|
|
|
if (bdrv_parse_discard_flags(optarg, &flags) == -1) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Invalid discard mode `%s'", optarg);
|
|
|
|
exit(EXIT_FAILURE);
|
2013-02-08 21:06:13 +08:00
|
|
|
}
|
|
|
|
break;
|
2014-08-14 01:20:18 +08:00
|
|
|
case QEMU_NBD_OPT_DETECT_ZEROES:
|
|
|
|
detect_zeroes =
|
|
|
|
qapi_enum_parse(BlockdevDetectZeroesOptions_lookup,
|
|
|
|
optarg,
|
qapi: Don't let implicit enum MAX member collide
Now that we guarantee the user doesn't have any enum values
beginning with a single underscore, we can use that for our
own purposes. Renaming ENUM_MAX to ENUM__MAX makes it obvious
that the sentinel is generated.
This patch was mostly generated by applying a temporary patch:
|diff --git a/scripts/qapi.py b/scripts/qapi.py
|index e6d014b..b862ec9 100644
|--- a/scripts/qapi.py
|+++ b/scripts/qapi.py
|@@ -1570,6 +1570,7 @@ const char *const %(c_name)s_lookup[] = {
| max_index = c_enum_const(name, 'MAX', prefix)
| ret += mcgen('''
| [%(max_index)s] = NULL,
|+// %(max_index)s
| };
| ''',
| max_index=max_index)
then running:
$ cat qapi-{types,event}.c tests/test-qapi-types.c |
sed -n 's,^// \(.*\)MAX,s|\1MAX|\1_MAX|g,p' > list
$ git grep -l _MAX | xargs sed -i -f list
The only things not generated are the changes in scripts/qapi.py.
Rejecting enum members named 'MAX' is now useless, and will be dropped
in the next patch.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1447836791-369-23-git-send-email-eblake@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
[Rebased to current master, commit message tweaked]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-11-18 16:52:57 +08:00
|
|
|
BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX,
|
2014-08-14 01:20:18 +08:00
|
|
|
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
|
|
|
|
&local_err);
|
|
|
|
if (local_err) {
|
2015-12-18 23:35:14 +08:00
|
|
|
error_reportf_err(local_err,
|
|
|
|
"Failed to parse detect_zeroes mode: ");
|
2015-12-18 23:35:04 +08:00
|
|
|
exit(EXIT_FAILURE);
|
2014-08-14 01:20:18 +08:00
|
|
|
}
|
|
|
|
if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
|
|
|
|
!(flags & BDRV_O_UNMAP)) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("setting detect-zeroes to unmap is not allowed "
|
|
|
|
"without setting discard operation to unmap");
|
|
|
|
exit(EXIT_FAILURE);
|
2014-08-14 01:20:18 +08:00
|
|
|
}
|
|
|
|
break;
|
2008-05-28 05:13:40 +08:00
|
|
|
case 'b':
|
|
|
|
bindto = optarg;
|
|
|
|
break;
|
|
|
|
case 'p':
|
2015-09-16 21:52:23 +08:00
|
|
|
port = optarg;
|
2008-05-28 05:13:40 +08:00
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
dev_offset = strtoll (optarg, &end, 0);
|
|
|
|
if (*end) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Invalid offset `%s'", optarg);
|
|
|
|
exit(EXIT_FAILURE);
|
2008-05-28 05:13:40 +08:00
|
|
|
}
|
|
|
|
if (dev_offset < 0) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Offset must be positive `%s'", optarg);
|
|
|
|
exit(EXIT_FAILURE);
|
2008-05-28 05:13:40 +08:00
|
|
|
}
|
|
|
|
break;
|
2013-12-04 17:10:55 +08:00
|
|
|
case 'l':
|
|
|
|
if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) {
|
QemuOpts: Wean off qerror_report_err()
qerror_report_err() is a transitional interface to help with
converting existing monitor commands to QMP. It should not be used
elsewhere.
The only remaining user in qemu-option.c is qemu_opts_parse(). Is it
used in QMP context? If not, we can simply replace
qerror_report_err() by error_report_err().
The uses in qemu-img.c, qemu-io.c, qemu-nbd.c and under tests/ are
clearly not in QMP context.
The uses in vl.c aren't either, because the only QMP command handlers
there are qmp_query_status() and qmp_query_machines(), and they don't
call it.
Remaining uses:
* drive_def(): Command line -drive and such, HMP drive_add and pci_add
* hmp_chardev_add(): HMP chardev-add
* monitor_parse_command(): HMP core
* tmp_config_parse(): Command line -tpmdev
* net_host_device_add(): HMP host_net_add
* net_client_parse(): Command line -net and -netdev
* qemu_global_option(): Command line -global
* vnc_parse_func(): Command line -display, -vnc, default display, HMP
change, QMP change. Bummer.
* qemu_pci_hot_add_nic(): HMP pci_add
* usb_net_init(): Command line -usbdevice, HMP usb_add
Propagate errors through qemu_opts_parse(). Create a convenience
function qemu_opts_parse_noisily() that passes errors to
error_report_err(). Switch all non-QMP users outside tests to it.
That leaves vnc_parse_func(). Propagate errors through it. Since I'm
touching it anyway, rename it to vnc_parse().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-02-13 19:50:26 +08:00
|
|
|
sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts,
|
|
|
|
optarg, false);
|
2013-12-04 17:10:55 +08:00
|
|
|
if (!sn_opts) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Failed in parsing snapshot param `%s'",
|
|
|
|
optarg);
|
|
|
|
exit(EXIT_FAILURE);
|
2013-12-04 17:10:55 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sn_id_or_name = optarg;
|
|
|
|
}
|
|
|
|
/* fall through */
|
2008-05-28 05:13:40 +08:00
|
|
|
case 'r':
|
2011-09-08 23:24:54 +08:00
|
|
|
nbdflags |= NBD_FLAG_READ_ONLY;
|
2010-03-14 21:19:57 +08:00
|
|
|
flags &= ~BDRV_O_RDWR;
|
2008-05-28 05:13:40 +08:00
|
|
|
break;
|
|
|
|
case 'P':
|
|
|
|
partition = strtol(optarg, &end, 0);
|
2014-08-14 01:20:19 +08:00
|
|
|
if (*end) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Invalid partition `%s'", optarg);
|
|
|
|
exit(EXIT_FAILURE);
|
2014-08-14 01:20:19 +08:00
|
|
|
}
|
|
|
|
if (partition < 1 || partition > 8) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Invalid partition %d", partition);
|
|
|
|
exit(EXIT_FAILURE);
|
2014-08-14 01:20:19 +08:00
|
|
|
}
|
2008-05-28 05:13:40 +08:00
|
|
|
break;
|
2008-07-03 18:23:51 +08:00
|
|
|
case 'k':
|
2011-11-04 22:51:20 +08:00
|
|
|
sockpath = optarg;
|
2014-08-14 01:20:19 +08:00
|
|
|
if (sockpath[0] != '/') {
|
2015-12-18 23:35:19 +08:00
|
|
|
error_report("socket path must be absolute");
|
2015-12-18 23:35:04 +08:00
|
|
|
exit(EXIT_FAILURE);
|
2014-08-14 01:20:19 +08:00
|
|
|
}
|
2008-07-03 18:23:51 +08:00
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
disconnect = true;
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
device = optarg;
|
|
|
|
break;
|
2008-07-03 20:45:02 +08:00
|
|
|
case 'e':
|
|
|
|
shared = strtol(optarg, &end, 0);
|
|
|
|
if (*end) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Invalid shared device number '%s'", optarg);
|
|
|
|
exit(EXIT_FAILURE);
|
2008-07-03 20:45:02 +08:00
|
|
|
}
|
|
|
|
if (shared < 1) {
|
2015-12-18 23:35:19 +08:00
|
|
|
error_report("Shared device number must be greater than 0");
|
2015-12-18 23:35:04 +08:00
|
|
|
exit(EXIT_FAILURE);
|
2008-07-03 20:45:02 +08:00
|
|
|
}
|
|
|
|
break;
|
2013-03-19 19:20:20 +08:00
|
|
|
case 'f':
|
|
|
|
fmt = optarg;
|
|
|
|
break;
|
2014-08-14 01:20:19 +08:00
|
|
|
case 't':
|
|
|
|
persistent = 1;
|
|
|
|
break;
|
2016-02-11 02:41:08 +08:00
|
|
|
case 'x':
|
|
|
|
export_name = optarg;
|
|
|
|
break;
|
2016-10-15 02:33:03 +08:00
|
|
|
case 'D':
|
|
|
|
export_description = optarg;
|
|
|
|
break;
|
2008-05-28 05:13:40 +08:00
|
|
|
case 'v':
|
|
|
|
verbose = 1;
|
|
|
|
break;
|
|
|
|
case 'V':
|
|
|
|
version(argv[0]);
|
|
|
|
exit(0);
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
usage(argv[0]);
|
|
|
|
exit(0);
|
|
|
|
break;
|
|
|
|
case '?':
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Try `%s --help' for more information.", argv[0]);
|
|
|
|
exit(EXIT_FAILURE);
|
2016-02-11 02:41:00 +08:00
|
|
|
case QEMU_NBD_OPT_OBJECT: {
|
|
|
|
QemuOpts *opts;
|
|
|
|
opts = qemu_opts_parse_noisily(&qemu_object_opts,
|
|
|
|
optarg, true);
|
|
|
|
if (!opts) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
} break;
|
2016-02-11 02:41:13 +08:00
|
|
|
case QEMU_NBD_OPT_TLSCREDS:
|
|
|
|
tlscredsid = optarg;
|
|
|
|
break;
|
2016-02-17 18:10:19 +08:00
|
|
|
case QEMU_NBD_OPT_IMAGE_OPTS:
|
|
|
|
imageOpts = true;
|
|
|
|
break;
|
2016-06-17 22:44:12 +08:00
|
|
|
case 'T':
|
|
|
|
g_free(trace_file);
|
|
|
|
trace_file = trace_opt_parse(optarg);
|
|
|
|
break;
|
2016-09-29 04:46:42 +08:00
|
|
|
case QEMU_NBD_OPT_FORK:
|
|
|
|
fork_process = true;
|
|
|
|
break;
|
2008-05-28 05:13:40 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((argc - optind) != 1) {
|
2015-12-18 23:35:24 +08:00
|
|
|
error_report("Invalid number of arguments");
|
|
|
|
error_printf("Try `%s --help' for more information.\n", argv[0]);
|
2015-12-18 23:35:04 +08:00
|
|
|
exit(EXIT_FAILURE);
|
2008-05-28 05:13:40 +08:00
|
|
|
}
|
|
|
|
|
2016-02-11 02:41:00 +08:00
|
|
|
if (qemu_opts_foreach(&qemu_object_opts,
|
|
|
|
user_creatable_add_opts_foreach,
|
qom: -object error messages lost location, restore it
qemu_opts_foreach() runs its callback with the error location set to
the option's location. Any errors the callback reports use the
option's location automatically.
Commit 90998d5 moved the actual error reporting from "inside"
qemu_opts_foreach() to after it. Here's a typical hunk:
if (qemu_opts_foreach(qemu_find_opts("object"),
- object_create,
- object_create_initial, NULL)) {
+ user_creatable_add_opts_foreach,
+ object_create_initial, &err)) {
+ error_report_err(err);
exit(1);
}
Before, object_create() reports from within qemu_opts_foreach(), using
the option's location. Afterwards, we do it after
qemu_opts_foreach(), using whatever location happens to be current
there. Commonly a "none" location.
This is because Error objects don't have location information.
Problematic.
Reproducer:
$ qemu-system-x86_64 -nodefaults -display none -object secret,id=foo,foo=bar
qemu-system-x86_64: Property '.foo' not found
Note no location. This commit restores it:
qemu-system-x86_64: -object secret,id=foo,foo=bar: Property '.foo' not found
Note that the qemu_opts_foreach() bug just fixed could mask the bug
here: if the location it leaves dangling hasn't been clobbered, yet,
it's the correct one.
Reported-by: Eric Blake <eblake@redhat.com>
Cc: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1461767349-15329-4-git-send-email-armbru@redhat.com>
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[Paragraph on Error added to commit message]
2016-04-27 22:29:09 +08:00
|
|
|
NULL, NULL)) {
|
2016-02-11 02:41:00 +08:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2016-06-17 22:44:12 +08:00
|
|
|
if (!trace_init_backends()) {
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
trace_init_file(trace_file);
|
|
|
|
qemu_set_log(LOG_TRACE);
|
|
|
|
|
2017-02-04 18:03:17 +08:00
|
|
|
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);
|
|
|
|
}
|
2017-03-16 23:29:45 +08:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
2017-02-04 18:03:17 +08:00
|
|
|
}
|
|
|
|
|
2016-02-11 02:41:13 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-03 18:23:51 +08:00
|
|
|
if (disconnect) {
|
2016-02-11 02:41:02 +08:00
|
|
|
int nbdfd = open(argv[optind], O_RDWR);
|
|
|
|
if (nbdfd < 0) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Cannot open %s: %s", argv[optind],
|
|
|
|
strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
2012-03-07 18:05:34 +08:00
|
|
|
}
|
2016-02-11 02:41:02 +08:00
|
|
|
nbd_disconnect(nbdfd);
|
2008-07-03 18:23:51 +08:00
|
|
|
|
2016-02-11 02:41:02 +08:00
|
|
|
close(nbdfd);
|
2008-07-03 18:23:51 +08:00
|
|
|
|
|
|
|
printf("%s disconnected\n", argv[optind]);
|
|
|
|
|
2014-08-14 01:20:19 +08:00
|
|
|
return 0;
|
2008-07-03 18:23:51 +08:00
|
|
|
}
|
|
|
|
|
2016-09-29 04:46:42 +08:00
|
|
|
if ((device && !verbose) || fork_process) {
|
2011-11-04 22:51:22 +08:00
|
|
|
int stderr_fd[2];
|
|
|
|
pid_t pid;
|
|
|
|
int ret;
|
|
|
|
|
2012-03-07 18:05:34 +08:00
|
|
|
if (qemu_pipe(stderr_fd) < 0) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Error setting up communication pipe: %s",
|
|
|
|
strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
2011-11-04 22:51:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Now daemonize, but keep a communication channel open to
|
|
|
|
* print errors and exit with the proper status code.
|
|
|
|
*/
|
|
|
|
pid = fork();
|
2015-02-26 02:08:22 +08:00
|
|
|
if (pid < 0) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Failed to fork: %s", strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
2015-02-26 02:08:22 +08:00
|
|
|
} else if (pid == 0) {
|
2011-11-04 22:51:22 +08:00
|
|
|
close(stderr_fd[0]);
|
2012-01-16 22:37:44 +08:00
|
|
|
ret = qemu_daemon(1, 0);
|
2011-11-04 22:51:22 +08:00
|
|
|
|
|
|
|
/* Temporarily redirect stderr to the parent's pipe... */
|
2016-09-29 04:46:42 +08:00
|
|
|
old_stderr = dup(STDERR_FILENO);
|
2011-11-04 22:51:22 +08:00
|
|
|
dup2(stderr_fd[1], STDERR_FILENO);
|
2012-03-07 18:05:34 +08:00
|
|
|
if (ret < 0) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Failed to daemonize: %s", strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
2011-11-04 22:51:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ... 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);
|
2012-03-07 18:05:34 +08:00
|
|
|
if (ret < 0) {
|
2011-11-04 22:51:22 +08:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
2012-03-07 18:05:34 +08:00
|
|
|
if (ret < 0) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Cannot read from daemon: %s",
|
|
|
|
strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
2011-11-04 22:51:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Usually the daemon should not print any message.
|
|
|
|
* Exit with zero status in that case.
|
|
|
|
*/
|
|
|
|
exit(errors);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-06 16:07:00 +08:00
|
|
|
if (device != NULL && sockpath == NULL) {
|
|
|
|
sockpath = g_malloc(128);
|
|
|
|
snprintf(sockpath, 128, SOCKET_PATH, basename(device));
|
2008-07-03 18:23:51 +08:00
|
|
|
}
|
|
|
|
|
2017-02-04 18:03:17 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2015-09-16 21:52:23 +08:00
|
|
|
|
2014-09-18 19:30:49 +08:00
|
|
|
if (qemu_init_main_loop(&local_err)) {
|
2015-02-12 20:55:05 +08:00
|
|
|
error_report_err(local_err);
|
2014-09-18 19:30:49 +08:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2011-11-04 22:51:24 +08:00
|
|
|
bdrv_init();
|
|
|
|
atexit(bdrv_close_all);
|
|
|
|
|
2016-02-17 18:10:19 +08:00
|
|
|
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);
|
2016-03-17 02:54:38 +08:00
|
|
|
blk = blk_new_open(NULL, NULL, options, flags, &local_err);
|
2016-02-17 18:10:19 +08:00
|
|
|
} else {
|
|
|
|
if (fmt) {
|
|
|
|
options = qdict_new();
|
2017-04-28 05:58:17 +08:00
|
|
|
qdict_put_str(options, "driver", fmt);
|
2016-02-17 18:10:19 +08:00
|
|
|
}
|
2016-03-17 02:54:38 +08:00
|
|
|
blk = blk_new_open(srcpath, NULL, options, flags, &local_err);
|
2013-03-19 19:20:20 +08:00
|
|
|
}
|
|
|
|
|
2015-02-06 02:58:19 +08:00
|
|
|
if (!blk) {
|
2015-12-18 23:35:14 +08:00
|
|
|
error_reportf_err(local_err, "Failed to blk_new_open '%s': ",
|
|
|
|
argv[optind]);
|
2015-12-18 23:35:04 +08:00
|
|
|
exit(EXIT_FAILURE);
|
2011-11-04 22:51:24 +08:00
|
|
|
}
|
2015-02-06 02:58:19 +08:00
|
|
|
bs = blk_bs(blk);
|
2011-11-04 22:51:24 +08:00
|
|
|
|
2016-03-14 18:43:28 +08:00
|
|
|
blk_set_enable_write_cache(blk, !writethrough);
|
|
|
|
|
2013-12-04 17:10:55 +08:00
|
|
|
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) {
|
2015-12-18 23:35:14 +08:00
|
|
|
error_reportf_err(local_err, "Failed to load snapshot: ");
|
2015-12-18 23:35:04 +08:00
|
|
|
exit(EXIT_FAILURE);
|
2013-12-04 17:10:55 +08:00
|
|
|
}
|
|
|
|
|
2014-08-14 01:20:18 +08:00
|
|
|
bs->detect_zeroes = detect_zeroes;
|
2014-11-18 19:21:19 +08:00
|
|
|
fd_size = blk_getlength(blk);
|
2015-02-26 02:08:21 +08:00
|
|
|
if (fd_size < 0) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Failed to determine the image length: %s",
|
|
|
|
strerror(-fd_size));
|
|
|
|
exit(EXIT_FAILURE);
|
2015-02-26 02:08:21 +08:00
|
|
|
}
|
2011-11-04 22:51:24 +08:00
|
|
|
|
2016-10-06 05:40:20 +08:00
|
|
|
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;
|
|
|
|
|
2012-03-05 15:56:10 +08:00
|
|
|
if (partition != -1) {
|
2014-11-18 19:21:19 +08:00
|
|
|
ret = find_partition(blk, partition, &dev_offset, &fd_size);
|
2012-03-05 15:56:10 +08:00
|
|
|
if (ret < 0) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Could not find partition %d: %s", partition,
|
2015-12-18 23:35:10 +08:00
|
|
|
strerror(-ret));
|
2015-12-18 23:35:04 +08:00
|
|
|
exit(EXIT_FAILURE);
|
2012-03-05 15:56:10 +08:00
|
|
|
}
|
2011-11-04 22:51:24 +08:00
|
|
|
}
|
|
|
|
|
2016-07-06 17:22:39 +08:00
|
|
|
exp = nbd_export_new(bs, dev_offset, fd_size, nbdflags, nbd_export_closed,
|
|
|
|
writethrough, NULL, &local_err);
|
2015-02-26 02:08:21 +08:00
|
|
|
if (!exp) {
|
2015-12-18 23:35:05 +08:00
|
|
|
error_report_err(local_err);
|
2015-12-18 23:35:04 +08:00
|
|
|
exit(EXIT_FAILURE);
|
2015-02-26 02:08:21 +08:00
|
|
|
}
|
2016-02-11 02:41:08 +08:00
|
|
|
if (export_name) {
|
|
|
|
nbd_export_set_name(exp, export_name);
|
2016-10-15 02:33:03 +08:00
|
|
|
nbd_export_set_description(exp, export_description);
|
2016-02-11 02:41:08 +08:00
|
|
|
newproto = true;
|
2016-10-15 02:33:03 +08:00
|
|
|
} else if (export_description) {
|
|
|
|
error_report("Export description requires an export name");
|
|
|
|
exit(EXIT_FAILURE);
|
2016-02-11 02:41:08 +08:00
|
|
|
}
|
2008-07-03 20:45:02 +08:00
|
|
|
|
2011-11-04 22:51:23 +08:00
|
|
|
if (device) {
|
|
|
|
int ret;
|
|
|
|
|
2011-12-06 16:07:00 +08:00
|
|
|
ret = pthread_create(&client_thread, NULL, nbd_client_thread, device);
|
2011-11-04 22:51:23 +08:00
|
|
|
if (ret != 0) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Failed to create client thread: %s", strerror(ret));
|
|
|
|
exit(EXIT_FAILURE);
|
2011-11-04 22:51:23 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Shut up GCC warnings. */
|
|
|
|
memset(&client_thread, 0, sizeof(client_thread));
|
|
|
|
}
|
|
|
|
|
2016-02-11 02:41:02 +08:00
|
|
|
nbd_update_server_watch();
|
2008-05-28 05:13:40 +08:00
|
|
|
|
2012-01-16 22:37:44 +08:00
|
|
|
/* now when the initialization is (almost) complete, chdir("/")
|
|
|
|
* to free any busy filesystems */
|
|
|
|
if (chdir("/") < 0) {
|
2015-12-18 23:35:04 +08:00
|
|
|
error_report("Could not chdir to root directory: %s",
|
|
|
|
strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
2012-01-16 22:37:44 +08:00
|
|
|
}
|
|
|
|
|
2016-09-29 04:46:42 +08:00
|
|
|
if (fork_process) {
|
|
|
|
dup2(old_stderr, STDERR_FILENO);
|
|
|
|
close(old_stderr);
|
|
|
|
}
|
|
|
|
|
2012-09-18 19:31:56 +08:00
|
|
|
state = RUNNING;
|
2008-07-03 20:45:02 +08:00
|
|
|
do {
|
2011-09-12 23:28:11 +08:00
|
|
|
main_loop_wait(false);
|
2012-09-18 19:31:56 +08:00
|
|
|
if (state == TERMINATE) {
|
|
|
|
state = TERMINATING;
|
|
|
|
nbd_export_close(exp);
|
|
|
|
nbd_export_put(exp);
|
|
|
|
exp = NULL;
|
|
|
|
}
|
|
|
|
} while (state != TERMINATED);
|
2008-05-28 05:13:40 +08:00
|
|
|
|
block: New BlockBackend
A block device consists of a frontend device model and a backend.
A block backend has a tree of block drivers doing the actual work.
The tree is managed by the block layer.
We currently use a single abstraction BlockDriverState both for tree
nodes and the backend as a whole. Drawbacks:
* Its API includes both stuff that makes sense only at the block
backend level (root of the tree) and stuff that's only for use
within the block layer. This makes the API bigger and more complex
than necessary. Moreover, it's not obvious which interfaces are
meant for device models, and which really aren't.
* Since device models keep a reference to their backend, the backend
object can't just be destroyed. But for media change, we need to
replace the tree. Our solution is to make the BlockDriverState
generic, with actual driver state in a separate object, pointed to
by member opaque. That lets us replace the tree by deinitializing
and reinitializing its root. This special need of the root makes
the data structure awkward everywhere in the tree.
The general plan is to separate the APIs into "block backend", for use
by device models, monitor and whatever other code dealing with block
backends, and "block driver", for use by the block layer and whatever
other code (if any) dealing with trees and tree nodes.
Code dealing with block backends, device models in particular, should
become completely oblivious of BlockDriverState. This should let us
clean up both APIs, and the tree data structures.
This commit is a first step. It creates a minimal "block backend"
API: type BlockBackend and functions to create, destroy and find them.
BlockBackend objects are created and destroyed exactly when root
BlockDriverState objects are created and destroyed. "Root" in the
sense of "in bdrv_states". They're not yet used for anything; that'll
come shortly.
A root BlockDriverState is created with bdrv_new_root(), so where to
create a BlockBackend is obvious. Where these roots get destroyed
isn't always as obvious.
It is obvious in qemu-img.c, qemu-io.c and qemu-nbd.c, and in error
paths of blockdev_init(), blk_connect(). That leaves destruction of
objects successfully created by blockdev_init() and blk_connect().
blockdev_init() is used only by drive_new() and qmp_blockdev_add().
Objects created by the latter are currently indestructible (see commit
48f364d "blockdev: Refuse to drive_del something added with
blockdev-add" and commit 2d246f0 "blockdev: Introduce
DriveInfo.enable_auto_del"). Objects created by the former get
destroyed by drive_del().
Objects created by blk_connect() get destroyed by blk_disconnect().
BlockBackend is reference-counted. Its reference count never exceeds
one so far, but that's going to change.
In drive_del(), the BB's reference count is surely one now. The BDS's
reference count is greater than one when something else is holding a
reference, such as a block job. In this case, the BB is destroyed
right away, but the BDS lives on until all extra references get
dropped.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2014-10-07 19:59:04 +08:00
|
|
|
blk_unref(blk);
|
2011-11-04 22:51:20 +08:00
|
|
|
if (sockpath) {
|
|
|
|
unlink(sockpath);
|
|
|
|
}
|
2008-05-28 05:13:40 +08:00
|
|
|
|
2014-09-29 22:07:55 +08:00
|
|
|
qemu_opts_del(sn_opts);
|
2013-12-04 17:10:55 +08:00
|
|
|
|
2011-11-04 22:51:21 +08:00
|
|
|
if (device) {
|
|
|
|
void *ret;
|
|
|
|
pthread_join(client_thread, &ret);
|
|
|
|
exit(ret != NULL);
|
|
|
|
} else {
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
2008-05-28 05:13:40 +08:00
|
|
|
}
|