[libvirt] [PATCH 1/3] Add APIs for talking to init via /dev/initctl

Daniel P. Berrange berrange at redhat.com
Wed Nov 28 13:30:34 UTC 2012


From: "Daniel P. Berrange" <berrange at redhat.com>

To be able todo controlled shutdown/reboot of containers an
API to talk to init via /dev/initctl is required. Fortunately
this is quite straightforward to implement, and is supported
by both sysvinit and systemd. Upstart support for /dev/initctl
is unclear.

Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
---
 include/libvirt/virterror.h |   1 +
 po/POTFILES.in              |   1 +
 src/Makefile.am             |   1 +
 src/libvirt_private.syms    |   4 ++
 src/util/virinitctl.c       | 161 ++++++++++++++++++++++++++++++++++++++++++++
 src/util/virinitctl.h       |  41 +++++++++++
 src/util/virterror.c        |   1 +
 7 files changed, 210 insertions(+)
 create mode 100644 src/util/virinitctl.c
 create mode 100644 src/util/virinitctl.h

diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index a877683..4d79620 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 ec59efb..77bc04b 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 627dbb5..6401dec 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 2573b8a..f308b55 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1356,6 +1356,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 0000000..c70ea3a
--- /dev/null
+++ b/src/util/virinitctl.c
@@ -0,0 +1,161 @@
+/*
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/param.h>
+#include <fcntl.h>
+
+#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;
+};
+
+
+/*
+ * 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;
+    req.runlevel = 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_NDELAY|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 0000000..09c078f
--- /dev/null
+++ b/src/util/virinitctl.h
@@ -0,0 +1,41 @@
+/*
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#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,
+};
+
+int virInitctlSetRunLevel(virInitctlRunLevel level,
+                          const char *vroot);
+
+#endif
diff --git a/src/util/virterror.c b/src/util/virterror.c
index 213188e..1142c40 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",
     )
 
 
-- 
1.7.11.7




More information about the libvir-list mailing list