[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 6 of 7 v4] blktap3/tapback: Introduce the tapback daemon
This patch introduces the core of the tapback daemon, the user space daemon that acts as a device's back-end, essentially most of blkback in user space. Similar to blkback, the daemon monitors XenStore for device creation/removal requests and front-end state changes, and acts in response to them (communicates to the front-end necessary information, switches the back-end's state etc.). The daemon creates/destroys tapdisk processes as needed. Signed-off-by: Thanos Makatos <thanos.makatos@xxxxxxxxxx> --- Changed since v1: * minor code clean up Changed since v2: * Introduce function XenbusState2str for printing XenbusStates in a more human-friendly way. * Don't print the log identify if the tapback is run in non-daemon mode, in order to make debug output more concise. Changed since v3: * Remove some debug print as they spam the output. * Use function abs() to report errors as some error codes are negated. * Create the control socket. diff --git a/tools/blktap3/tapback/tapback.c b/tools/blktap3/tapback/tapback.c new file mode 100644 --- /dev/null +++ b/tools/blktap3/tapback/tapback.c @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2012 Citrix Ltd. + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * This file contains the core of the tapback daemon, the user space daemon + * that acts as a device's back-end. + */ + +/* + * TODO Some of these includes may be useless. + * TODO Replace hard-coding strings with defines/const string. + */ +#include <stdlib.h> +#include <stdarg.h> +#include <assert.h> +#include <syslog.h> +#include <fcntl.h> +#include <unistd.h> +#include <libgen.h> +#include <getopt.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <syslog.h> + +#include "blktap3.h" +#include "stdio.h" /* TODO tap-ctl.h needs to include stdio.h */ +#include "tap-ctl.h" +#include "tapback.h" +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <signal.h> + +void tapback_log(int prio, const char *fmt, ...); +void (*tapback_vlog) (int prio, const char *fmt, va_list ap); + +struct _blktap3_daemon blktap3_daemon; + +char *XenbusState2str(const XenbusState xbs) +{ + static char * const str[] = { + [XenbusStateUnknown] = "unknown", + [XenbusStateInitialising] = "initialising", + [XenbusStateInitWait] = "init wait", + [XenbusStateInitialised] = "initialised", + [XenbusStateConnected] = "connected", + [XenbusStateClosing] = "closing", + [XenbusStateClosed] = "closed", + [XenbusStateReconfiguring] = "reconfiguring", + [XenbusStateReconfigured] = "reconfigured" + }; + return str[xbs]; +} + +/** + * Read changes that occurred on the "backend/<backend name>" XenStore path + * or one of the front-end paths and act accordingly. + */ +static inline void +tapback_read_watch(void) +{ + char **watch = NULL, *path = NULL, *token = NULL; + unsigned int n = 0; + int err = 0, _abort = 0; + + /* read the change */ + watch = xs_read_watch(blktap3_daemon.xs, &n); + path = watch[XS_WATCH_PATH]; + token = watch[XS_WATCH_TOKEN]; + + /* + * TODO Put the body of "again:" into a loop instead of using goto. + */ +again: + if (!(blktap3_daemon.xst = xs_transaction_start(blktap3_daemon.xs))) { + WARN("error starting transaction\n"); + goto fail; + } + + /* + * The token indicates which XenStore watch triggered, the front-end one or + * the back-end one. + */ + if (!strcmp(token, BLKTAP3_FRONTEND_TOKEN)) { + err = tapback_backend_handle_otherend_watch(path); + } else if (!strcmp(token, BLKTAP3_BACKEND_TOKEN)) { + err = tapback_backend_handle_backend_watch(path); + } else { + WARN("invalid token \'%s\'\n", token); + err = EINVAL; + } + + _abort = !!err; + if (_abort) + /* TODO Some functions return +err, others -err */ + DBG("aborting transaction: %s\n", strerror(abs(err))); + + err = xs_transaction_end(blktap3_daemon.xs, blktap3_daemon.xst, _abort); + blktap3_daemon.xst = 0; + if (!err) { + err = -errno; + /* + * This is OK according to xs_transaction_end's semantics. + */ + if (EAGAIN == errno) + goto again; + DBG("error ending transaction: %s\n", strerror(err)); + } + +fail: + free(watch); + return; +} + +static void +tapback_backend_destroy(void) +{ + int err; + + if (blktap3_daemon.xs) { + xs_daemon_close(blktap3_daemon.xs); + blktap3_daemon.xs = NULL; + } + + err = unlink(TAPBACK_CTL_SOCK_PATH); + if (err == -1 && errno != ENOENT) { + err = errno; + WARN("failed to remove %s: %s\n", TAPBACK_CTL_SOCK_PATH, strerror(err)); + } +} + +static void +signal_cb(int signum) { + + assert(signum == SIGINT || signum == SIGTERM); + + /* TODO Check whether there are active VBDs? */ + tapback_backend_destroy(); + exit(0); +} + +/** + * Initializes the back-end descriptor. There is one back-end per tapback + * process. Also, it initiates a watch to XenStore on backend/<backend name>. + * + * @returns 0 on success, an error code otherwise + */ +static inline int +tapback_backend_create(void) +{ + int err; + struct sockaddr_un local; + int len; + + TAILQ_INIT(&blktap3_daemon.devices); + blktap3_daemon.xst = XBT_NULL; + blktap3_daemon.ctrl_sock = -1; + + if (!(blktap3_daemon.xs = xs_daemon_open())) { + err = EINVAL; + goto fail; + } + + /* + * Watch the back-end. + */ + if (!xs_watch(blktap3_daemon.xs, BLKTAP3_BACKEND_PATH, + BLKTAP3_BACKEND_TOKEN)) { + err = errno; + goto fail; + } + + if (SIG_ERR == signal(SIGINT, signal_cb) || + SIG_ERR == signal(SIGTERM, signal_cb)) { + WARN("failed to register signal handlers\n"); + err = EINVAL; + goto fail; + } + + /* + * Create the control socket. + * XXX We don't listen for connections as we don't yet support any control + * commands. + */ + blktap3_daemon.ctrl_sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (blktap3_daemon.ctrl_sock == -1) { + err = errno; + WARN("failed to create control socket: %s\n", strerror(errno)); + goto fail; + } + local.sun_family = AF_UNIX; + strcpy(local.sun_path, TAPBACK_CTL_SOCK_PATH); + err = unlink(local.sun_path); + if (err && errno != ENOENT) { + err = errno; + WARN("failed to remove %s: %s\n", local.sun_path, strerror(err)); + goto fail; + } + len = strlen(local.sun_path) + sizeof(local.sun_family); + err = bind(blktap3_daemon.ctrl_sock, (struct sockaddr *)&local, len); + if (err == -1) { + err = errno; + WARN("failed to bind to %s: %s\n", local.sun_path, strerror(err)); + goto fail; + } + + return 0; + +fail: + tapback_backend_destroy(); + + return err; +} + +/** + * Runs the daemon. + * + * Watches backend/<backend name> and the front-end devices. + */ +static inline int +tapback_backend_run(void) +{ + const int fd = xs_fileno(blktap3_daemon.xs); + int err; + + do { + fd_set rfds; + int nfds = 0; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + /* poll the fd for changes in the XenStore path we're interested in */ + if ((nfds = select(fd + 1, &rfds, NULL, NULL, NULL)) < 0) { + perror("error monitoring XenStore"); + err = -errno; + break; + } + + if (FD_ISSET(fd, &rfds)) + tapback_read_watch(); + DBG("--\n"); + } while (1); + + return err; +} + +static char *blkback_ident = NULL; + +static void +blkback_vlog_fprintf(const int prio, const char * const fmt, va_list ap) +{ + static const char *strprio[] = { + [LOG_DEBUG] = "DBG", + [LOG_INFO] = "INF", + [LOG_WARNING] = "WRN" + }; + + assert(LOG_DEBUG == prio || LOG_INFO == prio || LOG_WARNING == prio); + assert(strprio[prio]); + + fprintf(stderr, "%s[%s] ", blkback_ident, strprio[prio]); + vfprintf(stderr, fmt, ap); +} + +/** + * Print tapback's usage instructions. + */ +static void +usage(FILE * const stream, const char * const prog) +{ + assert(stream); + assert(prog); + + fprintf(stream, + "usage: %s\n" + "\t[-D|--debug]\n" + "\t[-h|--help]\n", prog); +} + +int main(int argc, char **argv) +{ + const char *prog = NULL; + int opt_debug = 0; + int err = 0; + + prog = basename(argv[0]); + + opt_debug = 0; + + do { + const struct option longopts[] = { + {"help", 0, NULL, 'h'}, + {"debug", 0, NULL, 'D'}, + }; + int c; + + c = getopt_long(argc, argv, "h:D", longopts, NULL); + if (c < 0) + break; + + switch (c) { + case 'h': + usage(stdout, prog); + return 0; + case 'D': + opt_debug = 1; + break; + case '?': + goto usage; + } + } while (1); + + if (opt_debug) { + blkback_ident = ""; + tapback_vlog = blkback_vlog_fprintf; + } + else { + blkback_ident = BLKTAP3_BACKEND_TOKEN; + openlog(blkback_ident, 0, LOG_DAEMON); + } + + if (!opt_debug) { + if ((err = daemon(0, 0))) { + err = -errno; + goto fail; + } + } + + if ((err = tapback_backend_create())) { + WARN("error creating blkback: %s\n", strerror(err)); + goto fail; + } + + err = tapback_backend_run(); + + tapback_backend_destroy(); + +fail: + return err ? -err : 0; + +usage: + usage(stderr, prog); + return 1; +} _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |