Add APIs for talking to init via /dev/initctl

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@redhat.com>
This commit is contained in:
Daniel P. Berrange 2012-11-28 12:17:31 +00:00
parent a21f51121d
commit c4ef575c97
7 changed files with 214 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -1363,6 +1363,10 @@ virFileTouch;
virFileUpdatePerm;
# virinitctl.h
virInitctlSetRunLevel;
# virkeycode.h
virKeycodeSetTypeFromString;
virKeycodeSetTypeToString;

163
src/util/virinitctl.c Normal file
View File

@ -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
* <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel P. Berrange <berrange@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;
};
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;
}

43
src/util/virinitctl.h Normal file
View File

@ -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
* <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel P. Berrange <berrange@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,
VIR_INITCTL_RUNLEVEL_LAST
};
int virInitctlSetRunLevel(virInitctlRunLevel level,
const char *vroot);
#endif

View File

@ -117,6 +117,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST,
"SSH transport layer", /* 50 */
"Lock Space",
"Init control",
)