/*
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();
}