diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index a87768349b..4d796200f4 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -114,6 +114,7 @@ typedef enum { VIR_FROM_SSH = 50, /* Error from libssh2 connection transport */ VIR_FROM_LOCKSPACE = 51, /* Error from lockspace */ + VIR_FROM_INITCTL = 52, /* Error from initctl device communication */ # ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST diff --git a/po/POTFILES.in b/po/POTFILES.in index ec59efbe51..77bc04b937 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -153,6 +153,7 @@ src/util/virauthconfig.c src/util/virdbus.c src/util/virfile.c src/util/virhash.c +src/util/virinitctl.c src/util/virkeyfile.c src/util/virlockspace.c src/util/virnetdev.c diff --git a/src/Makefile.am b/src/Makefile.am index 627dbb5c46..6401decdfa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -94,6 +94,7 @@ UTIL_SOURCES = \ util/virdbus.c util/virdbus.h \ util/virhash.c util/virhash.h \ util/virhashcode.c util/virhashcode.h \ + util/virinitctl.c util/virinitctl.h \ util/virkeycode.c util/virkeycode.h \ util/virkeyfile.c util/virkeyfile.h \ util/virkeymaps.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 5b056504bc..93a21cc5cc 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1363,6 +1363,10 @@ virFileTouch; virFileUpdatePerm; +# virinitctl.h +virInitctlSetRunLevel; + + # virkeycode.h virKeycodeSetTypeFromString; virKeycodeSetTypeToString; diff --git a/src/util/virinitctl.c b/src/util/virinitctl.c new file mode 100644 index 0000000000..cdd3dc0482 --- /dev/null +++ b/src/util/virinitctl.c @@ -0,0 +1,163 @@ +/* + * virinitctl.c: API for talking to init systems via initctl + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Authors: + * Daniel P. Berrange + */ + +#include + +#include +#include + +#include "internal.h" +#include "virinitctl.h" +#include "virterror_internal.h" +#include "util.h" +#include "memory.h" +#include "virfile.h" + +#define VIR_FROM_THIS VIR_FROM_INITCTL + +/* These constants & struct definitions are taken from + * systemd, under terms of LGPLv2+ + * + * initreq.h Interface to talk to init through /dev/initctl. + * + * Copyright (C) 1995-2004 Miquel van Smoorenburg + */ + +#if defined(__FreeBSD_kernel__) +# define VIR_INITCTL_FIFO "/etc/.initctl" +#else +# define VIR_INITCTL_FIFO "/dev/initctl" +#endif + +#define VIR_INITCTL_MAGIC 0x03091969 +#define VIR_INITCTL_CMD_START 0 +#define VIR_INITCTL_CMD_RUNLVL 1 +#define VIR_INITCTL_CMD_POWERFAIL 2 +#define VIR_INITCTL_CMD_POWERFAILNOW 3 +#define VIR_INITCTL_CMD_POWEROK 4 +#define VIR_INITCTL_CMD_BSD 5 +#define VIR_INITCTL_CMD_SETENV 6 +#define VIR_INITCTL_CMD_UNSETENV 7 + +#define VIR_INITCTL_CMD_CHANGECONS 12345 + +#ifdef MAXHOSTNAMELEN +# define VIR_INITCTL_RQ_HLEN MAXHOSTNAMELEN +#else +# define VIR_INITCTL_RQ_HLEN 64 +#endif + +/* +* This is what BSD 4.4 uses when talking to init. +* Linux doesn't use this right now. +*/ +struct virInitctlRequestBSD { + char gen_id[8]; /* Beats me.. telnetd uses "fe" */ + char tty_id[16]; /* Tty name minus /dev/tty */ + char host[VIR_INITCTL_RQ_HLEN]; /* Hostname */ + char term_type[16]; /* Terminal type */ + int signal; /* Signal to send */ + int pid_value; /* Process to send to */ + char exec_name[128]; /* Program to execute */ + char reserved[128]; /* For future expansion. */ +}; + + +/* + * Because of legacy interfaces, "runlevel" and "sleeptime" + * aren't in a separate struct in the union. + * + * The weird sizes are because init expects the whole + * struct to be 384 bytes. + */ +struct virInitctlRequest { + int magic; /* Magic number */ + int cmd; /* What kind of request */ + int runlevel; /* Runlevel to change to */ + int sleeptime; /* Time between TERM and KILL */ + union { + struct virInitctlRequestBSD bsd; + char data[368]; + } i; +}; + +verify(sizeof(struct virInitctlRequest) == 384); + +/* + * Send a message to init to change the runlevel + * + * Returns 1 on success, 0 if initctl does not exist, -1 on error + */ +int virInitctlSetRunLevel(virInitctlRunLevel level, + const char *vroot) +{ + struct virInitctlRequest req; + int fd = -1; + char *path = NULL; + int ret = -1; + + memset(&req, 0, sizeof(req)); + + req.magic = VIR_INITCTL_MAGIC; + req.sleeptime = 0; + req.cmd = VIR_INITCTL_CMD_RUNLVL; + /* Yes it is an 'int' field, but wants a numeric character. Go figure */ + req.runlevel = '0' + level; + + if (vroot) { + if (virAsprintf(&path, "%s/%s", vroot, VIR_INITCTL_FIFO) < 0) { + virReportOOMError(); + return -1; + } + } else { + if (!(path = strdup(VIR_INITCTL_FIFO))) { + virReportOOMError(); + return -1; + } + } + + if ((fd = open(path, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY)) < 0) { + if (errno == ENOENT) { + ret = 0; + goto cleanup; + } + virReportSystemError(errno, + _("Cannot open init control %s"), + path); + goto cleanup; + } + + if (safewrite(fd, &req, sizeof(req)) != sizeof(req)) { + virReportSystemError(errno, + _("Failed to send request to init control %s"), + path); + goto cleanup; + } + + ret = 1; + +cleanup: + VIR_FREE(path); + VIR_FORCE_CLOSE(fd); + return ret; +} diff --git a/src/util/virinitctl.h b/src/util/virinitctl.h new file mode 100644 index 0000000000..862539fcd6 --- /dev/null +++ b/src/util/virinitctl.h @@ -0,0 +1,43 @@ +/* + * virinitctl.h: API for talking to init systems via initctl + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Authors: + * Daniel P. Berrange + */ + +#ifndef __VIR_INITCTL_H__ +# define __VIR_INITCTL_H__ + +typedef enum virInitctlRunLevel virInitctlRunLevel; +enum virInitctlRunLevel { + VIR_INITCTL_RUNLEVEL_POWEROFF = 0, + VIR_INITCTL_RUNLEVEL_1 = 1, + VIR_INITCTL_RUNLEVEL_2 = 2, + VIR_INITCTL_RUNLEVEL_3 = 3, + VIR_INITCTL_RUNLEVEL_4 = 4, + VIR_INITCTL_RUNLEVEL_5 = 5, + VIR_INITCTL_RUNLEVEL_REBOOT = 6, + + VIR_INITCTL_RUNLEVEL_LAST +}; + +int virInitctlSetRunLevel(virInitctlRunLevel level, + const char *vroot); + +#endif diff --git a/src/util/virterror.c b/src/util/virterror.c index 213188e215..1142c40a63 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -117,6 +117,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST, "SSH transport layer", /* 50 */ "Lock Space", + "Init control", )