/* Simple prototype Xen Store Daemon providing simple tree-like database. Copyright (C) 2005 Rusty Russell IBM Corporation 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, see . */ #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_SYSTEMD) #include #endif #include #include "utils.h" #include "core.h" #include "osdep.h" #include "talloc.h" static int reopen_log_pipe0_pollfd_idx = -1; static int reopen_log_pipe[2]; static int sock_pollfd_idx = -1; static int sock = -1; static void write_pidfile(const char *pidfile) { char buf[100]; int len; int fd; fd = open(pidfile, O_RDWR | O_CREAT, 0600); if (fd == -1) barf_perror("Opening pid file %s", pidfile); /* We exit silently if daemon already running. */ if (lockf(fd, F_TLOCK, 0) == -1) exit(0); len = snprintf(buf, sizeof(buf), "%ld\n", (long)getpid()); if (write(fd, buf, len) != len) barf_perror("Writing pid file %s", pidfile); close(fd); } /* Stevens. */ static void daemonize(void) { pid_t pid; /* Separate from our parent via fork, so init inherits us. */ if ((pid = fork()) < 0) barf_perror("Failed to fork daemon"); if (pid != 0) exit(0); /* Session leader so ^C doesn't whack us. */ setsid(); /* Let session leader exit so child cannot regain CTTY */ if ((pid = fork()) < 0) barf_perror("Failed to fork daemon"); if (pid != 0) exit(0); /* Move off any mount points we might be in. */ if (chdir("/") == -1) barf_perror("Failed to chdir"); /* Discard our parent's old-fashioned umask prejudices. */ umask(0); } /* * Signal handler for SIGHUP, which requests that the trace log is reopened * (in the main loop). A single byte is written to reopen_log_pipe, to awaken * the poll() in the main loop. */ static void trigger_reopen_log(int signal __attribute__((unused))) { char c = 'A'; int dummy; dummy = write(reopen_log_pipe[1], &c, 1); } void finish_daemonize(void) { int devnull = open("/dev/null", O_RDWR); if (devnull == -1) barf_perror("Could not open /dev/null\n"); dup2(devnull, STDIN_FILENO); dup2(devnull, STDOUT_FILENO); dup2(devnull, STDERR_FILENO); close(devnull); } static void init_pipe(void) { int flags; unsigned int i; if (pipe(reopen_log_pipe)) { barf_perror("pipe"); } for (i = 0; i < 2; i++) { flags = fcntl(reopen_log_pipe[i], F_GETFD); if (flags < 0) barf_perror("pipe get flags"); flags |= FD_CLOEXEC; if (fcntl(reopen_log_pipe[i], F_SETFD, flags) < 0) barf_perror("pipe set flags"); } } void unmap_xenbus(void *interface) { munmap(interface, getpagesize()); } static evtchn_port_t get_xenbus_evtchn(void) { int fd; int rc; evtchn_port_t port; char str[20]; fd = open(XENSTORED_PORT_DEV, O_RDONLY); if (fd == -1) return -1; rc = read(fd, str, sizeof(str) - 1); if (rc == -1) { int err = errno; close(fd); errno = err; return -1; } str[rc] = '\0'; port = strtoul(str, NULL, 0); close(fd); return port; } /* * dom0 xenstored uses get_xenbus_evtchn() to lookup with XENSTORED_PORT_DEV. * * Any other existing domains from dom0less/Hyperlaunch will have * the event channel in the xenstore page, so lookup here isn't necessary. */ evtchn_port_t get_domain_evtchn(unsigned int domid) { if (domid == store_domid) return get_xenbus_evtchn(); return 0; } void *xenbus_map(void) { int fd; void *addr; fd = open(XENSTORED_KVA_DEV, O_RDWR); if (fd == -1) return NULL; addr = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (addr == MAP_FAILED) addr = NULL; close(fd); return addr; } static int writefd(struct connection *conn, const void *data, unsigned int len) { int rc; while ((rc = write(conn->fd, data, len)) < 0) { if (errno == EAGAIN) { rc = 0; break; } if (errno != EINTR) break; } return rc; } static int readfd(struct connection *conn, void *data, unsigned int len) { int rc; while ((rc = read(conn->fd, data, len)) < 0) { if (errno == EAGAIN) { rc = 0; break; } if (errno != EINTR) break; } /* Reading zero length means we're done with this connection. */ if ((rc == 0) && (len != 0)) { errno = EBADF; rc = -1; } return rc; } static bool socket_can_process(struct connection *conn, int mask) { if (conn->pollfd_idx == -1) return false; if (poll_fds[conn->pollfd_idx].revents & ~(POLLIN | POLLOUT)) { talloc_free(conn); return false; } return (poll_fds[conn->pollfd_idx].revents & mask); } static bool socket_can_write(struct connection *conn) { return socket_can_process(conn, POLLOUT); } static bool socket_can_read(struct connection *conn) { return socket_can_process(conn, POLLIN); } static const struct interface_funcs socket_funcs = { .write = writefd, .read = readfd, .can_write = socket_can_write, .can_read = socket_can_read, }; static void accept_connection(int sock) { int fd; struct connection *conn; fd = accept(sock, NULL, NULL); if (fd < 0) return; conn = new_connection(&socket_funcs); if (conn) { conn->fd = fd; conn->id = store_domid; } else close(fd); } static void destroy_fds(void) { if (sock >= 0) close(sock); } static void init_sockets(void) { struct sockaddr_un addr; const char *soc_str = xenstore_daemon_path(); if (!soc_str) barf_perror("Failed to obtain xs domain socket"); /* Create sockets for them to listen to. */ atexit(destroy_fds); sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) barf_perror("Could not create socket"); /* FIXME: Be more sophisticated, don't mug running daemon. */ unlink(soc_str); addr.sun_family = AF_UNIX; if (strlen(soc_str) >= sizeof(addr.sun_path)) barf_perror("socket string '%s' too long", soc_str); strcpy(addr.sun_path, soc_str); if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) barf_perror("Could not bind socket to %s", soc_str); if (chmod(soc_str, 0600) != 0) barf_perror("Could not chmod sockets"); if (listen(sock, 1) != 0) barf_perror("Could not listen on sockets"); } struct connection *add_socket_connection(int fd) { struct connection *conn; conn = new_connection(&socket_funcs); if (!conn) barf("error restoring connection"); conn->fd = fd; return conn; } void early_init(bool live_update, bool dofork, const char *pidfile) { reopen_log(); /* Make sure xenstored directories exist. */ /* Errors ignored here, will be reported when we open files */ mkdir(xenstore_daemon_rundir(), 0755); mkdir(XENSTORE_LIB_DIR, 0755); if (dofork) { openlog("xenstored", 0, LOG_DAEMON); if (!live_update) daemonize(); } if (pidfile) write_pidfile(pidfile); /* Don't kill us with SIGPIPE. */ signal(SIGPIPE, SIG_IGN); signal(SIGHUP, trigger_reopen_log); if (!live_update) init_sockets(); init_pipe(); } void set_special_fds(void) { if (reopen_log_pipe[0] != -1) reopen_log_pipe0_pollfd_idx = set_fd(reopen_log_pipe[0], POLLIN|POLLPRI); if (sock != -1) sock_pollfd_idx = set_fd(sock, POLLIN|POLLPRI); } void handle_special_fds(void) { if (reopen_log_pipe0_pollfd_idx != -1) { if (poll_fds[reopen_log_pipe0_pollfd_idx].revents & ~POLLIN) { close(reopen_log_pipe[0]); close(reopen_log_pipe[1]); init_pipe(); } else if (poll_fds[reopen_log_pipe0_pollfd_idx].revents & POLLIN) { char c; if (read(reopen_log_pipe[0], &c, 1) != 1) barf_perror("read failed"); reopen_log(); } reopen_log_pipe0_pollfd_idx = -1; } if (sock_pollfd_idx != -1) { if (poll_fds[sock_pollfd_idx].revents & ~POLLIN) { barf_perror("sock poll failed"); } else if (poll_fds[sock_pollfd_idx].revents & POLLIN) { accept_connection(sock); sock_pollfd_idx = -1; } } } void late_init(bool live_update) { #if defined(HAVE_SYSTEMD) if (!live_update) { sd_notify(1, "READY=1"); fprintf(stderr, "xenstored is ready\n"); } #endif } int get_socket_fd(void) { return sock; } void set_socket_fd(int fd) { sock = fd; } xenevtchn_handle *evtchn_fdopen(int fd) { return xenevtchn_fdopen(NULL, fd, 0); } int evtchn_rebind(int port) { return 0; } const char *xenstore_rundir(void) { return xenstore_daemon_rundir(); }