mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-22 11:22:23 +00:00
Make LXC I/O controller process a parent of the container process
This commit is contained in:
parent
518c2144e5
commit
763dacda97
21
ChangeLog
21
ChangeLog
@ -1,3 +1,24 @@
|
||||
Wed Aug 13 11:48:36 BST 2008 Daniel Berrange <berrange@redhat.com>
|
||||
|
||||
* configure.in: Add check for termios.h
|
||||
* src/util.h, src/util.c: Add virFileOpenTty and helpers
|
||||
for creating/deleting/reading PID files
|
||||
* src/lxc_conf.h, src/lxc_conf.c, src/lxc_container.c,
|
||||
src/lxc_container.h, src/lxc_controller.c,
|
||||
src/lxc_controller.h, src/lxc_driver.c: Re-arrange
|
||||
container launch process so that the I/O helper is
|
||||
a direct parent of the container process. Daemonize
|
||||
container so it survives restarts of libvirtd.
|
||||
|
||||
Wed Aug 13 11:23:36 BST 2008 Daniel Berrange <berrange@redhat.com>
|
||||
|
||||
Re-arrange code between LXC driver files
|
||||
* src/lxc_container.c, src/lxc_container.h,
|
||||
src/lxc_controller.h, src/lxc_container.c,
|
||||
src/lxc_driver.c: Move code for I/O handling into
|
||||
a seprate lxc_controller module, and move code for
|
||||
creating containers into lcx_container module.
|
||||
|
||||
Wed Aug 13 10:55:36 BST 2008 Daniel Berrange <berrange@redhat.com>
|
||||
|
||||
* src/lxc_conf.h, src/lxc_conf.c, src/lxc_container.h,
|
||||
|
@ -68,7 +68,7 @@ dnl Availability of various common functions (non-fatal if missing).
|
||||
AC_CHECK_FUNCS([cfmakeraw regexec uname sched_getaffinity])
|
||||
|
||||
dnl Availability of various common headers (non-fatal if missing).
|
||||
AC_CHECK_HEADERS([pwd.h paths.h sys/syslimits.h sys/utsname.h sys/wait.h winsock2.h sched.h])
|
||||
AC_CHECK_HEADERS([pwd.h paths.h sys/syslimits.h sys/utsname.h sys/wait.h winsock2.h sched.h termios.h])
|
||||
|
||||
dnl Where are the XDR functions?
|
||||
dnl If portablexdr is installed, prefer that.
|
||||
|
195
src/lxc_conf.c
195
src/lxc_conf.c
@ -833,25 +833,24 @@ static lxc_vm_t * lxcLoadConfig(lxc_driver_t *driver,
|
||||
strncpy(vm->configFileBase, file, PATH_MAX);
|
||||
vm->configFile[PATH_MAX-1] = '\0';
|
||||
|
||||
if (lxcLoadTtyPid(driver, vm) < 0) {
|
||||
DEBUG0("failed to load tty pid");
|
||||
}
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
int lxcLoadDriverConfig(lxc_driver_t *driver)
|
||||
{
|
||||
/* Set the container configuration directory */
|
||||
driver->configDir = strdup(SYSCONF_DIR "/libvirt/lxc");
|
||||
if (NULL == driver->configDir) {
|
||||
lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, "configDir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
driver->stateDir = strdup(LOCAL_STATE_DIR "/run/libvirt/lxc");
|
||||
if ((driver->configDir = strdup(SYSCONF_DIR "/libvirt/lxc")) == NULL)
|
||||
goto no_memory;
|
||||
if ((driver->stateDir = strdup(LOCAL_STATE_DIR "/run/libvirt/lxc")) == NULL)
|
||||
goto no_memory;
|
||||
if ((driver->logDir = strdup(LOCAL_STATE_DIR "/log/libvirt/lxc")) == NULL)
|
||||
goto no_memory;
|
||||
|
||||
return 0;
|
||||
|
||||
no_memory:
|
||||
lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, "configDir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int lxcLoadContainerConfigFile(lxc_driver_t *driver,
|
||||
@ -1012,9 +1011,7 @@ void lxcFreeVMDef(lxc_vm_def_t *vmdef)
|
||||
curNet = vmdef->nets;
|
||||
while (curNet) {
|
||||
nextNet = curNet->next;
|
||||
printf("Freeing %s:%s\n", curNet->parentVeth, curNet->containerVeth);
|
||||
VIR_FREE(curNet->parentVeth);
|
||||
VIR_FREE(curNet->containerVeth);
|
||||
VIR_FREE(curNet->txName);
|
||||
VIR_FREE(curNet);
|
||||
curNet = nextNet;
|
||||
@ -1106,176 +1103,4 @@ int lxcDeleteConfig(virConnectPtr conn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lxcStoreTtyPid:
|
||||
* @driver: pointer to driver
|
||||
* @vm: Ptr to VM
|
||||
*
|
||||
* Stores the pid of the tty forward process contained in vm->pid
|
||||
* LOCAL_STATE_DIR/run/libvirt/lxc/{container_name}.pid
|
||||
*
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
int lxcStoreTtyPid(const lxc_driver_t *driver, lxc_vm_t *vm)
|
||||
{
|
||||
int rc = -1;
|
||||
int fd;
|
||||
FILE *file = NULL;
|
||||
|
||||
if (vm->ttyPidFile[0] == 0x00) {
|
||||
if ((rc = virFileMakePath(driver->stateDir))) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("cannot create lxc state directory %s: %s"),
|
||||
driver->stateDir, strerror(rc));
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (virFileBuildPath(driver->stateDir, vm->def->name, ".pid",
|
||||
vm->ttyPidFile, PATH_MAX) < 0) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("cannot construct tty pid file path"));
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
if ((fd = open(vm->ttyPidFile,
|
||||
O_WRONLY | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR)) < 0) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("cannot create tty pid file %s: %s"),
|
||||
vm->ttyPidFile, strerror(errno));
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!(file = fdopen(fd, "w"))) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("cannot fdopen tty pid file %s: %s"),
|
||||
vm->ttyPidFile, strerror(errno));
|
||||
|
||||
if (close(fd) < 0) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to close tty pid file %s: %s"),
|
||||
vm->ttyPidFile, strerror(errno));
|
||||
}
|
||||
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (fprintf(file, "%d", vm->pid) < 0) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("cannot write tty pid file %s: %s"),
|
||||
vm->ttyPidFile, strerror(errno));
|
||||
|
||||
goto fclose_error_out;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
fclose_error_out:
|
||||
if (fclose(file) < 0) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to close tty pid file %s: %s"),
|
||||
vm->ttyPidFile, strerror(errno));
|
||||
}
|
||||
|
||||
error_out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* lxcLoadTtyPid:
|
||||
* @driver: pointer to driver
|
||||
* @vm: Ptr to VM
|
||||
*
|
||||
* Loads the pid of the tty forward process from the pid file.
|
||||
* LOCAL_STATE_DIR/run/libvirt/lxc/{container_name}.pid
|
||||
*
|
||||
* Returns
|
||||
* > 0 - pid of tty process
|
||||
* 0 - no tty pid file
|
||||
* -1 - error
|
||||
*/
|
||||
int lxcLoadTtyPid(const lxc_driver_t *driver, lxc_vm_t *vm)
|
||||
{
|
||||
int rc = -1;
|
||||
FILE *file;
|
||||
|
||||
if (vm->ttyPidFile[0] == 0x00) {
|
||||
if ((rc = virFileMakePath(driver->stateDir))) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("cannot create lxc state directory %s: %s"),
|
||||
driver->stateDir, strerror(rc));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (virFileBuildPath(driver->stateDir, vm->def->name, ".pid",
|
||||
vm->ttyPidFile, PATH_MAX) < 0) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("cannot construct tty pid file path"));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(file = fopen(vm->ttyPidFile, "r"))) {
|
||||
if (ENOENT == errno) {
|
||||
rc = 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("cannot open tty pid file %s: %s"),
|
||||
vm->ttyPidFile, strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (fscanf(file, "%d", &(vm->pid)) < 0) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("cannot read tty pid file %s: %s"),
|
||||
vm->ttyPidFile, strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (fclose(file) < 0) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to close tty pid file %s: %s"),
|
||||
vm->ttyPidFile, strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = vm->pid;
|
||||
|
||||
cleanup:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* lxcDeleteTtyPid:
|
||||
* @vm: Ptr to VM
|
||||
*
|
||||
* Unlinks the tty pid file for the vm
|
||||
* LOCAL_STATE_DIR/run/libvirt/lxc/{container_name}.pid
|
||||
*
|
||||
* Returns on 0 success or -1 in case of error
|
||||
*/
|
||||
int lxcDeleteTtyPidFile(const lxc_vm_t *vm)
|
||||
{
|
||||
if (vm->ttyPidFile[0] == 0x00) {
|
||||
goto no_file;
|
||||
}
|
||||
|
||||
if (unlink(vm->ttyPidFile) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
goto no_file;
|
||||
}
|
||||
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("cannot remove ttyPidFile %s: %s"), vm->ttyPidFile,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
no_file:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* WITH_LXC */
|
||||
|
@ -46,7 +46,6 @@ typedef struct __lxc_net_def lxc_net_def_t;
|
||||
struct __lxc_net_def {
|
||||
int type;
|
||||
char *parentVeth; /* veth device in parent namespace */
|
||||
char *containerVeth; /* veth device in container namespace */
|
||||
char *txName; /* bridge or network name */
|
||||
|
||||
lxc_net_def_t *next;
|
||||
@ -87,12 +86,11 @@ typedef struct __lxc_vm lxc_vm_t;
|
||||
struct __lxc_vm {
|
||||
int pid;
|
||||
int state;
|
||||
int monitor;
|
||||
|
||||
char configFile[PATH_MAX];
|
||||
char configFileBase[PATH_MAX];
|
||||
|
||||
char ttyPidFile[PATH_MAX];
|
||||
|
||||
lxc_vm_def_t *def;
|
||||
|
||||
lxc_vm_t *next;
|
||||
@ -103,8 +101,9 @@ struct __lxc_driver {
|
||||
lxc_vm_t *vms;
|
||||
int nactivevms;
|
||||
int ninactivevms;
|
||||
char* configDir;
|
||||
char* stateDir;
|
||||
char *configDir;
|
||||
char *stateDir;
|
||||
char *logDir;
|
||||
int have_netns;
|
||||
};
|
||||
|
||||
@ -154,9 +153,6 @@ int lxcDeleteConfig(virConnectPtr conn,
|
||||
lxc_driver_t *driver,
|
||||
const char *configFile,
|
||||
const char *name);
|
||||
int lxcStoreTtyPid(const lxc_driver_t *driver, lxc_vm_t *vm);
|
||||
int lxcLoadTtyPid(const lxc_driver_t *driver, lxc_vm_t *vm);
|
||||
int lxcDeleteTtyPidFile(const lxc_vm_t *vm);
|
||||
|
||||
void lxcError(virConnectPtr conn,
|
||||
virDomainPtr dom,
|
||||
|
@ -69,6 +69,8 @@ typedef char lxc_message_t;
|
||||
typedef struct __lxc_child_argv lxc_child_argv_t;
|
||||
struct __lxc_child_argv {
|
||||
lxc_vm_def_t *config;
|
||||
unsigned int nveths;
|
||||
char **veths;
|
||||
int monitor;
|
||||
char *ttyPath;
|
||||
};
|
||||
@ -171,8 +173,7 @@ error_out:
|
||||
*
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
int lxcContainerSendContinue(virConnectPtr conn,
|
||||
int control)
|
||||
int lxcContainerSendContinue(int control)
|
||||
{
|
||||
int rc = -1;
|
||||
lxc_message_t msg = LXC_CONTINUE_MSG;
|
||||
@ -180,7 +181,7 @@ int lxcContainerSendContinue(virConnectPtr conn,
|
||||
|
||||
writeCount = safewrite(control, &msg, sizeof(msg));
|
||||
if (writeCount != sizeof(msg)) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("unable to send container continue message: %s"),
|
||||
strerror(errno));
|
||||
goto error_out;
|
||||
@ -230,21 +231,22 @@ static int lxcContainerWaitForContinue(int control)
|
||||
*
|
||||
* Returns 0 on success or nonzero in case of error
|
||||
*/
|
||||
static int lxcContainerEnableInterfaces(const lxc_vm_def_t *def)
|
||||
static int lxcContainerEnableInterfaces(unsigned int nveths,
|
||||
char **veths)
|
||||
{
|
||||
int rc = 0;
|
||||
const lxc_net_def_t *net;
|
||||
unsigned int i;
|
||||
|
||||
for (net = def->nets; net; net = net->next) {
|
||||
DEBUG("Enabling %s", net->containerVeth);
|
||||
rc = vethInterfaceUpOrDown(net->containerVeth, 1);
|
||||
for (i = 0 ; i < nveths ; i++) {
|
||||
DEBUG("Enabling %s", veths[i]);
|
||||
rc = vethInterfaceUpOrDown(veths[i], 1);
|
||||
if (0 != rc) {
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
/* enable lo device only if there were other net devices */
|
||||
if (def->nets)
|
||||
if (veths)
|
||||
rc = vethInterfaceUpOrDown("lo", 1);
|
||||
|
||||
error_out:
|
||||
@ -311,7 +313,7 @@ static int lxcContainerChild( void *data )
|
||||
return -1;
|
||||
|
||||
/* enable interfaces */
|
||||
if (lxcContainerEnableInterfaces(vmDef) < 0)
|
||||
if (lxcContainerEnableInterfaces(argv->nveths, argv->veths) < 0)
|
||||
return -1;
|
||||
|
||||
/* this function will only return if an error occured */
|
||||
@ -320,7 +322,6 @@ static int lxcContainerChild( void *data )
|
||||
|
||||
/**
|
||||
* lxcContainerStart:
|
||||
* @conn: pointer to connection
|
||||
* @driver: pointer to driver structure
|
||||
* @vm: pointer to virtual machine structure
|
||||
*
|
||||
@ -328,8 +329,9 @@ static int lxcContainerChild( void *data )
|
||||
*
|
||||
* Returns PID of container on success or -1 in case of error
|
||||
*/
|
||||
int lxcContainerStart(virConnectPtr conn,
|
||||
lxc_vm_def_t *def,
|
||||
int lxcContainerStart(lxc_vm_def_t *def,
|
||||
unsigned int nveths,
|
||||
char **veths,
|
||||
int control,
|
||||
char *ttyPath)
|
||||
{
|
||||
@ -337,12 +339,11 @@ int lxcContainerStart(virConnectPtr conn,
|
||||
int flags;
|
||||
int stacksize = getpagesize() * 4;
|
||||
char *stack, *stacktop;
|
||||
lxc_child_argv_t args = { def, control, ttyPath };
|
||||
lxc_child_argv_t args = { def, nveths, veths, control, ttyPath };
|
||||
|
||||
/* allocate a stack for the container */
|
||||
if (VIR_ALLOC_N(stack, stacksize) < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_NO_MEMORY,
|
||||
_("unable to allocate container stack"));
|
||||
lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
|
||||
return -1;
|
||||
}
|
||||
stacktop = stack + stacksize;
|
||||
@ -357,7 +358,7 @@ int lxcContainerStart(virConnectPtr conn,
|
||||
DEBUG("clone() returned, %d", pid);
|
||||
|
||||
if (pid < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("clone() failed, %s"), strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
@ -32,11 +32,11 @@ enum {
|
||||
LXC_CONTAINER_FEATURE_NET = (1 << 0),
|
||||
};
|
||||
|
||||
int lxcContainerSendContinue(virConnectPtr conn,
|
||||
int control);
|
||||
int lxcContainerSendContinue(int control);
|
||||
|
||||
int lxcContainerStart(virConnectPtr conn,
|
||||
lxc_vm_def_t *def,
|
||||
int lxcContainerStart(lxc_vm_def_t *def,
|
||||
unsigned int nveths,
|
||||
char **veths,
|
||||
int control,
|
||||
char *ttyPath);
|
||||
|
||||
|
@ -26,16 +26,27 @@
|
||||
#ifdef WITH_LXC
|
||||
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <paths.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "lxc_conf.h"
|
||||
#include "lxc_container.h"
|
||||
#include "lxc_controller.h"
|
||||
#include "veth.h"
|
||||
#include "memory.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
|
||||
#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
|
||||
|
||||
|
||||
/**
|
||||
* lxcFdForward:
|
||||
@ -91,7 +102,10 @@ typedef struct _lxcTtyForwardFd_t {
|
||||
*
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
int lxcControllerMain(int appPty, int contPty)
|
||||
static int lxcControllerMain(int monitor,
|
||||
int client,
|
||||
int appPty,
|
||||
int contPty)
|
||||
{
|
||||
int rc = -1;
|
||||
int epollFd;
|
||||
@ -120,41 +134,77 @@ int lxcControllerMain(int appPty, int contPty)
|
||||
memset(&epollEvent, 0x00, sizeof(epollEvent));
|
||||
epollEvent.events = EPOLLIN|EPOLLET; /* edge triggered */
|
||||
epollEvent.data.fd = appPty;
|
||||
epollEvent.data.u32 = 0; /* fdArray position */
|
||||
if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, appPty, &epollEvent)) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("epoll_ctl(appPty) failed: %s"), strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
epollEvent.data.fd = contPty;
|
||||
epollEvent.data.u32 = 1; /* fdArray position */
|
||||
if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, contPty, &epollEvent)) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("epoll_ctl(contPty) failed: %s"), strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
epollEvent.events = EPOLLIN;
|
||||
epollEvent.data.fd = monitor;
|
||||
if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, monitor, &epollEvent)) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("epoll_ctl(contPty) failed: %s"), strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
epollEvent.events = EPOLLHUP;
|
||||
epollEvent.data.fd = client;
|
||||
if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, client, &epollEvent)) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("epoll_ctl(contPty) failed: %s"), strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
/* if active fd's, return if no events, else wait forever */
|
||||
timeout = (numActive > 0) ? 0 : -1;
|
||||
numEvents = epoll_wait(epollFd, &epollEvent, 1, timeout);
|
||||
if (0 < numEvents) {
|
||||
if (epollEvent.events & EPOLLIN) {
|
||||
curFdOff = epollEvent.data.u32;
|
||||
if (!fdArray[curFdOff].active) {
|
||||
fdArray[curFdOff].active = 1;
|
||||
++numActive;
|
||||
if (numEvents > 0) {
|
||||
if (epollEvent.data.fd == monitor) {
|
||||
int fd = accept(monitor, NULL, 0);
|
||||
if (client != -1) { /* Already connected, so kick new one out */
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
} else if (epollEvent.events & EPOLLHUP) {
|
||||
DEBUG("EPOLLHUP from fd %d", epollEvent.data.fd);
|
||||
continue;
|
||||
client = fd;
|
||||
epollEvent.events = EPOLLHUP;
|
||||
epollEvent.data.fd = client;
|
||||
if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, client, &epollEvent)) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("epoll_ctl(contPty) failed: %s"), strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
} else if (client != -1 && epollEvent.data.fd == client) {
|
||||
if (0 > epoll_ctl(epollFd, EPOLL_CTL_DEL, client, &epollEvent)) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("epoll_ctl(contPty) failed: %s"), strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
close(client);
|
||||
client = -1;
|
||||
} else {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("error event %d"), epollEvent.events);
|
||||
goto cleanup;
|
||||
if (epollEvent.events & EPOLLIN) {
|
||||
curFdOff = epollEvent.data.fd == appPty ? 0 : 1;
|
||||
if (!fdArray[curFdOff].active) {
|
||||
fdArray[curFdOff].active = 1;
|
||||
++numActive;
|
||||
}
|
||||
} else if (epollEvent.events & EPOLLHUP) {
|
||||
DEBUG("EPOLLHUP from fd %d", epollEvent.data.fd);
|
||||
continue;
|
||||
} else {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("error event %d"), epollEvent.events);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (0 == numEvents) {
|
||||
if (2 == numActive) {
|
||||
/* both fds active, toggle between the two */
|
||||
@ -202,4 +252,255 @@ cleanup:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* lxcControllerMoveInterfaces
|
||||
* @nveths: number of interfaces
|
||||
* @veths: interface names
|
||||
* @container: pid of container
|
||||
*
|
||||
* Moves network interfaces into a container's namespace
|
||||
*
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
static int lxcControllerMoveInterfaces(unsigned int nveths,
|
||||
char **veths,
|
||||
pid_t container)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < nveths ; i++)
|
||||
if (moveInterfaceToNetNs(veths[i], container) < 0) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to move interface %s to ns %d"),
|
||||
veths[i], container);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* lxcCleanupInterfaces:
|
||||
* @conn: pointer to connection
|
||||
* @vm: pointer to virtual machine structure
|
||||
*
|
||||
* Cleans up the container interfaces by deleting the veth device pairs.
|
||||
*
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
static int lxcControllerCleanupInterfaces(unsigned int nveths,
|
||||
char **veths)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < nveths ; i++)
|
||||
if (vethDelete(veths[i]) < 0)
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to delete veth: %s"), veths[i]);
|
||||
/* will continue to try to cleanup any other interfaces */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
lxcControllerRun(const char *stateDir,
|
||||
lxc_vm_def_t *def,
|
||||
unsigned int nveths,
|
||||
char **veths,
|
||||
int monitor,
|
||||
int client,
|
||||
int appPty)
|
||||
{
|
||||
int rc = -1;
|
||||
int control[2] = { -1, -1};
|
||||
int containerPty;
|
||||
char *containerPtyPath;
|
||||
pid_t container = -1;
|
||||
|
||||
if (socketpair(PF_UNIX, SOCK_STREAM, 0, control) < 0) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("sockpair failed: %s"), strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (virFileOpenTty(&containerPty,
|
||||
&containerPtyPath,
|
||||
0) < 0) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to allocate tty: %s"), strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((container = lxcContainerStart(def,
|
||||
nveths,
|
||||
veths,
|
||||
control[1],
|
||||
containerPtyPath)) < 0)
|
||||
goto cleanup;
|
||||
close(control[1]);
|
||||
control[1] = -1;
|
||||
|
||||
if (lxcControllerMoveInterfaces(nveths, veths, container) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (lxcContainerSendContinue(control[0]) < 0)
|
||||
goto cleanup;
|
||||
|
||||
rc = lxcControllerMain(monitor, client, appPty, containerPty);
|
||||
|
||||
cleanup:
|
||||
if (control[0] != -1)
|
||||
close(control[0]);
|
||||
if (control[1] != -1)
|
||||
close(control[1]);
|
||||
VIR_FREE(containerPtyPath);
|
||||
if (containerPty != -1)
|
||||
close(containerPty);
|
||||
|
||||
kill(container, SIGTERM);
|
||||
waitpid(container, NULL, 0);
|
||||
lxcControllerCleanupInterfaces(nveths, veths);
|
||||
virFileDeletePid(stateDir, def->name);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int lxcControllerStart(const char *stateDir,
|
||||
lxc_vm_def_t *def,
|
||||
unsigned int nveths,
|
||||
char **veths,
|
||||
int monitor,
|
||||
int appPty,
|
||||
int logfd)
|
||||
{
|
||||
pid_t pid;
|
||||
int rc;
|
||||
int status, null;
|
||||
int open_max, i;
|
||||
int client;
|
||||
struct sigaction sig_action;
|
||||
|
||||
if ((pid = fork()) < 0)
|
||||
return -1;
|
||||
|
||||
if (pid > 0) {
|
||||
/* Original caller waits for first child to exit */
|
||||
while (1) {
|
||||
rc = waitpid(pid, &status, 0);
|
||||
if (rc < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
if (rc != pid) {
|
||||
fprintf(stderr,
|
||||
_("Unexpected pid %d != %d from waitpid\n"),
|
||||
rc, pid);
|
||||
return -1;
|
||||
}
|
||||
if (WIFEXITED(status) &&
|
||||
WEXITSTATUS(status) == 0)
|
||||
return 0;
|
||||
else {
|
||||
fprintf(stderr,
|
||||
_("Unexpected status %d from pid %d\n"),
|
||||
status, pid);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* First child is running here */
|
||||
|
||||
/* Clobber all libvirtd's signal handlers so they
|
||||
* don't affect us
|
||||
*/
|
||||
sig_action.sa_handler = SIG_DFL;
|
||||
sig_action.sa_flags = 0;
|
||||
sigemptyset(&sig_action.sa_mask);
|
||||
|
||||
sigaction(SIGHUP, &sig_action, NULL);
|
||||
sigaction(SIGINT, &sig_action, NULL);
|
||||
sigaction(SIGQUIT, &sig_action, NULL);
|
||||
sigaction(SIGTERM, &sig_action, NULL);
|
||||
sigaction(SIGCHLD, &sig_action, NULL);
|
||||
|
||||
sig_action.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &sig_action, NULL);
|
||||
|
||||
|
||||
/* Don't hold onto any cwd we inherit from libvirtd either */
|
||||
if (chdir("/") < 0) {
|
||||
fprintf(stderr, _("Unable to change to root dir: %s\n"),
|
||||
strerror(errno));
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
if (setsid() < 0) {
|
||||
fprintf(stderr, _("Unable to become session leader: %s\n"),
|
||||
strerror(errno));
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
if ((null = open(_PATH_DEVNULL, O_RDONLY)) < 0) {
|
||||
fprintf(stderr, _("Unable to open %s: %s\n"),
|
||||
_PATH_DEVNULL, strerror(errno));
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
open_max = sysconf (_SC_OPEN_MAX);
|
||||
for (i = 0; i < open_max; i++)
|
||||
if (i != appPty &&
|
||||
i != monitor &&
|
||||
i != logfd &&
|
||||
i != null)
|
||||
close(i);
|
||||
|
||||
if (dup2(null, STDIN_FILENO) < 0 ||
|
||||
dup2(logfd, STDOUT_FILENO) < 0 ||
|
||||
dup2(logfd, STDERR_FILENO) < 0) {
|
||||
fprintf(stderr, _("Unable to redirect stdio: %s\n"),
|
||||
strerror(errno));
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
close(null);
|
||||
close(logfd);
|
||||
|
||||
/* Now fork the real controller process */
|
||||
if ((pid = fork()) < 0) {
|
||||
fprintf(stderr, _("Unable to fork controller: %s\n"),
|
||||
strerror(errno));
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
if ((rc = virFileWritePid(stateDir, def->name, pid)) != 0) {
|
||||
fprintf(stderr, _("Unable to write pid file: %s\n"),
|
||||
strerror(rc));
|
||||
_exit(-1);
|
||||
}
|
||||
/* First child now exits, allowing originall caller to
|
||||
* complete their waitpid & continue */
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
/* This is real controller running finally... */
|
||||
|
||||
/* Accept initial client which is the libvirtd daemon */
|
||||
if ((client = accept(monitor, NULL, 0))) {
|
||||
fprintf(stderr, _("Failed connection from LXC driver: %s\n"),
|
||||
strerror(errno));
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
/* Controlling libvirtd LXC driver now knows
|
||||
what our PID is, and is able to cleanup after
|
||||
us from now on */
|
||||
_exit(lxcControllerRun(stateDir, def, nveths, veths, monitor, client, appPty));
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -26,7 +26,15 @@
|
||||
|
||||
#ifdef WITH_LXC
|
||||
|
||||
int lxcControllerMain(int appPty, int contPty);
|
||||
#include "lxc_conf.h"
|
||||
|
||||
int lxcControllerStart(const char *stateDir,
|
||||
lxc_vm_def_t *def,
|
||||
unsigned int nveths,
|
||||
char **veths,
|
||||
int monitor,
|
||||
int appPty,
|
||||
int logfd);
|
||||
|
||||
#endif /* WITH_LXC */
|
||||
|
||||
|
639
src/lxc_driver.c
639
src/lxc_driver.c
@ -31,22 +31,23 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <termios.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
#include <wait.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "lxc_conf.h"
|
||||
#include "lxc_container.h"
|
||||
#include "lxc_driver.h"
|
||||
#include "lxc_controller.h"
|
||||
#include "driver.h"
|
||||
#include "internal.h"
|
||||
#include "memory.h"
|
||||
#include "util.h"
|
||||
#include "memory.h"
|
||||
#include "bridge.h"
|
||||
#include "qemu_conf.h"
|
||||
#include "veth.h"
|
||||
#include "event.h"
|
||||
|
||||
|
||||
/* debug macros */
|
||||
#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
|
||||
@ -284,8 +285,6 @@ static int lxcDomainUndefine(virDomainPtr dom)
|
||||
|
||||
vm->configFile[0] = '\0';
|
||||
|
||||
lxcDeleteTtyPidFile(vm);
|
||||
|
||||
lxcRemoveInactiveVM(driver, vm);
|
||||
|
||||
return 0;
|
||||
@ -339,10 +338,60 @@ static char *lxcDomainDumpXML(virDomainPtr dom,
|
||||
return lxcGenerateXML(dom->conn, driver, vm, vm->def);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* lxcVmCleanup:
|
||||
* @vm: Ptr to VM to clean up
|
||||
*
|
||||
* waitpid() on the container process. kill and wait the tty process
|
||||
* This is called by both lxcDomainDestroy and lxcSigHandler when a
|
||||
* container exits.
|
||||
*
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
static int lxcVMCleanup(virConnectPtr conn,
|
||||
lxc_driver_t *driver,
|
||||
lxc_vm_t * vm)
|
||||
{
|
||||
int rc = -1;
|
||||
int waitRc;
|
||||
int childStatus = -1;
|
||||
|
||||
while (((waitRc = waitpid(vm->pid, &childStatus, 0)) == -1) &&
|
||||
errno == EINTR)
|
||||
; /* empty */
|
||||
|
||||
if ((waitRc != vm->pid) && (errno != ECHILD)) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("waitpid failed to wait for container %d: %d %s"),
|
||||
vm->pid, waitRc, strerror(errno));
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
if (WIFEXITED(childStatus)) {
|
||||
rc = WEXITSTATUS(childStatus);
|
||||
DEBUG("container exited with rc: %d", rc);
|
||||
}
|
||||
|
||||
virEventRemoveHandle(vm->monitor);
|
||||
close(vm->monitor);
|
||||
|
||||
virFileDeletePid(driver->stateDir, vm->def->name);
|
||||
|
||||
vm->state = VIR_DOMAIN_SHUTOFF;
|
||||
vm->pid = -1;
|
||||
vm->def->id = -1;
|
||||
vm->monitor = -1;
|
||||
driver->nactivevms--;
|
||||
driver->ninactivevms++;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* lxcSetupInterfaces:
|
||||
* @conn: pointer to connection
|
||||
* @vm: pointer to virtual machine structure
|
||||
* @def: pointer to virtual machine structure
|
||||
*
|
||||
* Sets up the container interfaces by creating the veth device pairs and
|
||||
* attaching the parent end to the appropriate bridge. The container end
|
||||
@ -351,24 +400,21 @@ static char *lxcDomainDumpXML(virDomainPtr dom,
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
static int lxcSetupInterfaces(virConnectPtr conn,
|
||||
lxc_vm_t *vm)
|
||||
lxc_vm_def_t *def,
|
||||
unsigned int *nveths,
|
||||
char ***veths)
|
||||
{
|
||||
int rc = -1;
|
||||
lxc_driver_t *driver = conn->privateData;
|
||||
struct qemud_driver *networkDriver =
|
||||
(struct qemud_driver *)(conn->networkPrivateData);
|
||||
lxc_net_def_t *net = vm->def->nets;
|
||||
char* bridge;
|
||||
lxc_net_def_t *net;
|
||||
char *bridge = NULL;
|
||||
char parentVeth[PATH_MAX] = "";
|
||||
char containerVeth[PATH_MAX] = "";
|
||||
brControl *brctl = NULL;
|
||||
|
||||
if ((vm->def->nets != NULL) && (driver->have_netns == 0)) {
|
||||
lxcError(conn, NULL, VIR_ERR_NO_SUPPORT,
|
||||
_("System lacks NETNS support"));
|
||||
if (brInit(&brctl) != 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (net = vm->def->nets; net; net = net->next) {
|
||||
for (net = def->nets; net; net = net->next) {
|
||||
if (LXC_NET_NETWORK == net->type) {
|
||||
virNetworkPtr network = virNetworkLookupByName(conn, net->txName);
|
||||
if (!network) {
|
||||
@ -378,7 +424,6 @@ static int lxcSetupInterfaces(virConnectPtr conn,
|
||||
bridge = virNetworkGetBridgeName(network);
|
||||
|
||||
virNetworkFree(network);
|
||||
|
||||
} else {
|
||||
bridge = net->txName;
|
||||
}
|
||||
@ -394,9 +439,6 @@ static int lxcSetupInterfaces(virConnectPtr conn,
|
||||
if (NULL != net->parentVeth) {
|
||||
strcpy(parentVeth, net->parentVeth);
|
||||
}
|
||||
if (NULL != net->containerVeth) {
|
||||
strcpy(containerVeth, net->containerVeth);
|
||||
}
|
||||
DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, containerVeth);
|
||||
if (0 != (rc = vethCreate(parentVeth, PATH_MAX, containerVeth, PATH_MAX))) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
@ -406,24 +448,18 @@ static int lxcSetupInterfaces(virConnectPtr conn,
|
||||
if (NULL == net->parentVeth) {
|
||||
net->parentVeth = strdup(parentVeth);
|
||||
}
|
||||
if (NULL == net->containerVeth) {
|
||||
net->containerVeth = strdup(containerVeth);
|
||||
}
|
||||
if (VIR_REALLOC_N(*veths, (*nveths)+1) < 0)
|
||||
goto error_exit;
|
||||
if (((*veths)[(*nveths)++] = strdup(containerVeth)) == NULL)
|
||||
goto error_exit;
|
||||
|
||||
if ((NULL == net->parentVeth) || (NULL == net->containerVeth)) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
if (NULL == net->parentVeth) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to allocate veth names"));
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
if (!(networkDriver->brctl) && (rc = brInit(&(networkDriver->brctl)))) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("cannot initialize bridge support: %s"),
|
||||
strerror(rc));
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
if (0 != (rc = brAddInterface(networkDriver->brctl, bridge, parentVeth))) {
|
||||
if (0 != (rc = brAddInterface(brctl, bridge, parentVeth))) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to add %s device to %s: %s"),
|
||||
parentVeth,
|
||||
@ -433,7 +469,7 @@ static int lxcSetupInterfaces(virConnectPtr conn,
|
||||
}
|
||||
|
||||
if (0 != (rc = vethInterfaceUpOrDown(parentVeth, 1))) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to enable parent ns veth device: %d"), rc);
|
||||
goto error_exit;
|
||||
}
|
||||
@ -443,136 +479,144 @@ static int lxcSetupInterfaces(virConnectPtr conn,
|
||||
rc = 0;
|
||||
|
||||
error_exit:
|
||||
brShutdown(brctl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* lxcMoveInterfacesToNetNs:
|
||||
* @conn: pointer to connection
|
||||
* @vm: pointer to virtual machine structure
|
||||
*
|
||||
* Starts a container process by calling clone() with the namespace flags
|
||||
*
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
static int lxcMoveInterfacesToNetNs(virConnectPtr conn,
|
||||
const lxc_vm_t *vm)
|
||||
static int lxcMonitorServer(virConnectPtr conn,
|
||||
lxc_driver_t * driver,
|
||||
lxc_vm_t *vm)
|
||||
{
|
||||
int rc = -1;
|
||||
lxc_net_def_t *net;
|
||||
char *sockpath = NULL;
|
||||
int fd;
|
||||
struct sockaddr_un addr;
|
||||
|
||||
for (net = vm->def->nets; net; net = net->next) {
|
||||
if (0 != moveInterfaceToNetNs(net->containerVeth, vm->def->id)) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to move interface %s to ns %d"),
|
||||
net->containerVeth, vm->def->id);
|
||||
goto error_exit;
|
||||
}
|
||||
if (asprintf(&sockpath, "%s/%s.sock",
|
||||
driver->stateDir, vm->def->name) < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
error_exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* lxcCleanupInterfaces:
|
||||
* @conn: pointer to connection
|
||||
* @vm: pointer to virtual machine structure
|
||||
*
|
||||
* Cleans up the container interfaces by deleting the veth device pairs.
|
||||
*
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
static int lxcCleanupInterfaces(const lxc_vm_t *vm)
|
||||
{
|
||||
int rc = -1;
|
||||
lxc_net_def_t *net;
|
||||
|
||||
for (net = vm->def->nets; net; net = net->next) {
|
||||
if (0 != (rc = vethDelete(net->parentVeth))) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to delete veth: %s"), net->parentVeth);
|
||||
/* will continue to try to cleanup any other interfaces */
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* lxcOpenTty:
|
||||
* @conn: pointer to connection
|
||||
* @ttymaster: pointer to int. On success, set to fd for master end
|
||||
* @ttyName: On success, will point to string slave end of tty. Caller
|
||||
* must free when done (such as in lxcFreeVM).
|
||||
*
|
||||
* Opens and configures container tty.
|
||||
*
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
static int lxcOpenTty(virConnectPtr conn,
|
||||
int *ttymaster,
|
||||
char **ttyName,
|
||||
int rawmode)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
*ttymaster = posix_openpt(O_RDWR|O_NOCTTY|O_NONBLOCK);
|
||||
if (*ttymaster < 0) {
|
||||
if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("posix_openpt failed: %s"), strerror(errno));
|
||||
goto cleanup;
|
||||
_("failed to create server socket: %s"),
|
||||
strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (unlockpt(*ttymaster) < 0) {
|
||||
unlink(sockpath);
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path));
|
||||
|
||||
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("unlockpt failed: %s"), strerror(errno));
|
||||
goto cleanup;
|
||||
_("failed to bind server socket: %s"),
|
||||
strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
if (listen(fd, 30 /* backlog */ ) < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to listen server socket: %s"),
|
||||
strerror(errno));
|
||||
goto error;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (rawmode) {
|
||||
struct termios ttyAttr;
|
||||
if (tcgetattr(*ttymaster, &ttyAttr) < 0) {
|
||||
VIR_FREE(sockpath);
|
||||
return fd;
|
||||
|
||||
error:
|
||||
VIR_FREE(sockpath);
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int lxcMonitorClient(virConnectPtr conn,
|
||||
lxc_driver_t * driver,
|
||||
lxc_vm_t *vm)
|
||||
{
|
||||
char *sockpath = NULL;
|
||||
int fd;
|
||||
struct sockaddr_un addr;
|
||||
|
||||
if (asprintf(&sockpath, "%s/%s.sock",
|
||||
driver->stateDir, vm->def->name) < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to create client socket: %s"),
|
||||
strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path));
|
||||
|
||||
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("failed to connect to client socket: %s"),
|
||||
strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
VIR_FREE(sockpath);
|
||||
return fd;
|
||||
|
||||
error:
|
||||
VIR_FREE(sockpath);
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int lxcVmTerminate(virConnectPtr conn,
|
||||
lxc_driver_t *driver,
|
||||
lxc_vm_t *vm,
|
||||
int signum)
|
||||
{
|
||||
if (signum == 0)
|
||||
signum = SIGINT;
|
||||
|
||||
if (kill(vm->pid, signum) < 0) {
|
||||
if (errno != ESRCH) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
"tcgetattr() failed: %s", strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cfmakeraw(&ttyAttr);
|
||||
|
||||
if (tcsetattr(*ttymaster, TCSADRAIN, &ttyAttr) < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
"tcsetattr failed: %s", strerror(errno));
|
||||
goto cleanup;
|
||||
_("failed to kill pid %d: %s"),
|
||||
vm->pid, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ttyName) {
|
||||
char tempTtyName[PATH_MAX];
|
||||
if (0 != ptsname_r(*ttymaster, tempTtyName, sizeof(tempTtyName))) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("ptsname_r failed: %s"), strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
vm->state = VIR_DOMAIN_SHUTDOWN;
|
||||
|
||||
if ((*ttyName = strdup(tempTtyName)) == NULL) {
|
||||
lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL);
|
||||
goto cleanup;
|
||||
}
|
||||
return lxcVMCleanup(conn, driver, vm);
|
||||
}
|
||||
|
||||
static void lxcMonitorEvent(int fd,
|
||||
int events ATTRIBUTE_UNUSED,
|
||||
void *data)
|
||||
{
|
||||
lxc_driver_t *driver = data;
|
||||
lxc_vm_t *vm = driver->vms;
|
||||
|
||||
while (vm) {
|
||||
if (vm->monitor == fd)
|
||||
break;
|
||||
vm = vm->next;
|
||||
}
|
||||
if (!vm) {
|
||||
virEventRemoveHandle(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
cleanup:
|
||||
if (rc != 0 &&
|
||||
*ttymaster != -1) {
|
||||
close(*ttymaster);
|
||||
}
|
||||
|
||||
return rc;
|
||||
if (lxcVmTerminate(NULL, driver, vm, SIGINT) < 0)
|
||||
virEventRemoveHandle(fd);
|
||||
}
|
||||
|
||||
|
||||
@ -591,80 +635,106 @@ static int lxcVmStart(virConnectPtr conn,
|
||||
lxc_vm_t * vm)
|
||||
{
|
||||
int rc = -1;
|
||||
int sockpair[2] = { -1, -1 };
|
||||
int containerTty, parentTty;
|
||||
char *containerTtyPath = NULL;
|
||||
unsigned int i;
|
||||
int monitor;
|
||||
int parentTty;
|
||||
char *logfile = NULL;
|
||||
int logfd = -1;
|
||||
unsigned int nveths = 0;
|
||||
char **veths = NULL;
|
||||
|
||||
if (virFileMakePath(driver->logDir) < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("cannot create log directory %s: %s"),
|
||||
driver->logDir, strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (asprintf(&logfile, "%s/%s.log",
|
||||
driver->logDir, vm->def->name) < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((monitor = lxcMonitorServer(conn, driver, vm)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* open parent tty */
|
||||
VIR_FREE(vm->def->tty);
|
||||
if (lxcOpenTty(conn, &parentTty, &vm->def->tty, 1) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* open container tty */
|
||||
if (lxcOpenTty(conn, &containerTty, &containerTtyPath, 0) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* fork process to handle the tty io forwarding */
|
||||
if ((vm->pid = fork()) < 0) {
|
||||
if (virFileOpenTty(&parentTty, &vm->def->tty, 1) < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("unable to fork tty forwarding process: %s"),
|
||||
_("failed to allocate tty: %s"),
|
||||
strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (vm->pid == 0) {
|
||||
/* child process calls forward routine */
|
||||
lxcControllerMain(parentTty, containerTty);
|
||||
}
|
||||
|
||||
if (lxcStoreTtyPid(driver, vm)) {
|
||||
DEBUG0("unable to store tty pid");
|
||||
}
|
||||
|
||||
close(parentTty);
|
||||
close(containerTty);
|
||||
|
||||
if (0 != (rc = lxcSetupInterfaces(conn, vm))) {
|
||||
if (lxcSetupInterfaces(conn, vm->def, &nveths, &veths) != 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* create a socket pair to send continue message to the container once */
|
||||
/* we've completed the post clone configuration */
|
||||
if (0 != socketpair(PF_UNIX, SOCK_STREAM, 0, sockpair)) {
|
||||
if ((logfd = open(logfile, O_WRONLY | O_TRUNC | O_CREAT,
|
||||
S_IRUSR|S_IWUSR)) < 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("sockpair failed: %s"), strerror(errno));
|
||||
_("failed to open %s: %s"), logfile,
|
||||
strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* check this rc */
|
||||
|
||||
vm->def->id = lxcContainerStart(conn,
|
||||
vm->def,
|
||||
sockpair[1],
|
||||
containerTtyPath);
|
||||
if (vm->def->id == -1)
|
||||
if (lxcControllerStart(driver->stateDir,
|
||||
vm->def, nveths, veths,
|
||||
monitor, parentTty, logfd) < 0)
|
||||
goto cleanup;
|
||||
lxcSaveConfig(conn, driver, vm, vm->def);
|
||||
/* Close the server side of the monitor, now owned
|
||||
* by the controller process */
|
||||
close(monitor);
|
||||
monitor = -1;
|
||||
|
||||
rc = lxcMoveInterfacesToNetNs(conn, vm);
|
||||
if (rc != 0)
|
||||
/* Connect to the controller as a client *first* because
|
||||
* this will block until the child has written their
|
||||
* pid file out to disk */
|
||||
if ((vm->monitor = lxcMonitorClient(conn, driver, vm)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
rc = lxcContainerSendContinue(conn, sockpair[0]);
|
||||
if (rc != 0)
|
||||
/* And get its pid */
|
||||
if ((rc = virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) != 0) {
|
||||
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("Failed to read pid file %s/%s.pid: %s"),
|
||||
driver->stateDir, vm->def->name, strerror(rc));
|
||||
rc = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
vm->def->id = vm->pid;
|
||||
vm->state = VIR_DOMAIN_RUNNING;
|
||||
driver->ninactivevms--;
|
||||
driver->nactivevms++;
|
||||
|
||||
cleanup:
|
||||
if (sockpair[0] != -1) close(sockpair[0]);
|
||||
if (sockpair[1] != -1) close(sockpair[1]);
|
||||
VIR_FREE(containerTtyPath);
|
||||
if (virEventAddHandle(vm->monitor,
|
||||
POLLERR | POLLHUP,
|
||||
lxcMonitorEvent,
|
||||
driver) < 0) {
|
||||
lxcVmTerminate(conn, driver, vm, 0);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
cleanup:
|
||||
for (i = 0 ; i < nveths ; i++) {
|
||||
if (rc != 0)
|
||||
vethDelete(veths[i]);
|
||||
VIR_FREE(veths[i]);
|
||||
}
|
||||
if (monitor != -1)
|
||||
close(monitor);
|
||||
if (rc != 0 && vm->monitor != -1) {
|
||||
close(vm->monitor);
|
||||
vm->monitor = -1;
|
||||
}
|
||||
if (parentTty != -1)
|
||||
close(parentTty);
|
||||
if (logfd != -1)
|
||||
close(logfd);
|
||||
VIR_FREE(logfile);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -752,105 +822,18 @@ return_point:
|
||||
*/
|
||||
static int lxcDomainShutdown(virDomainPtr dom)
|
||||
{
|
||||
int rc = -1;
|
||||
lxc_driver_t *driver = (lxc_driver_t*)dom->conn->privateData;
|
||||
lxc_vm_t *vm = lxcFindVMByID(driver, dom->id);
|
||||
|
||||
if (!vm) {
|
||||
lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
|
||||
_("no domain with id %d"), dom->id);
|
||||
goto error_out;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 > (kill(vm->def->id, SIGINT))) {
|
||||
if (ESRCH != errno) {
|
||||
lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
|
||||
_("sending SIGTERM failed: %s"), strerror(errno));
|
||||
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
vm->state = VIR_DOMAIN_SHUTDOWN;
|
||||
|
||||
rc = 0;
|
||||
|
||||
error_out:
|
||||
return rc;
|
||||
return lxcVmTerminate(dom->conn, driver, vm, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* lxcVmCleanup:
|
||||
* @vm: Ptr to VM to clean up
|
||||
*
|
||||
* waitpid() on the container process. kill and wait the tty process
|
||||
* This is called by boh lxcDomainDestroy and lxcSigHandler when a
|
||||
* container exits.
|
||||
*
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
static int lxcVMCleanup(lxc_driver_t *driver, lxc_vm_t * vm)
|
||||
{
|
||||
int rc = -1;
|
||||
int waitRc;
|
||||
int childStatus = -1;
|
||||
|
||||
/* if this fails, we'll continue. it will report any errors */
|
||||
lxcCleanupInterfaces(vm);
|
||||
|
||||
while (((waitRc = waitpid(vm->def->id, &childStatus, 0)) == -1) &&
|
||||
errno == EINTR);
|
||||
|
||||
if ((waitRc != vm->def->id) && (errno != ECHILD)) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("waitpid failed to wait for container %d: %d %s"),
|
||||
vm->def->id, waitRc, strerror(errno));
|
||||
goto kill_tty;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
if (WIFEXITED(childStatus)) {
|
||||
rc = WEXITSTATUS(childStatus);
|
||||
DEBUG("container exited with rc: %d", rc);
|
||||
}
|
||||
|
||||
kill_tty:
|
||||
if (2 > vm->pid) {
|
||||
DEBUG("not killing tty process with pid %d", vm->pid);
|
||||
goto tty_error_out;
|
||||
}
|
||||
|
||||
if (0 > (kill(vm->pid, SIGKILL))) {
|
||||
if (ESRCH != errno) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("sending SIGKILL to tty process failed: %s"),
|
||||
strerror(errno));
|
||||
|
||||
goto tty_error_out;
|
||||
}
|
||||
}
|
||||
|
||||
while (((waitRc = waitpid(vm->pid, &childStatus, 0)) == -1) &&
|
||||
errno == EINTR);
|
||||
|
||||
if ((waitRc != vm->pid) && (errno != ECHILD)) {
|
||||
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||
_("waitpid failed to wait for tty %d: %d %s"),
|
||||
vm->pid, waitRc, strerror(errno));
|
||||
}
|
||||
|
||||
tty_error_out:
|
||||
vm->state = VIR_DOMAIN_SHUTOFF;
|
||||
vm->pid = -1;
|
||||
lxcDeleteTtyPidFile(vm);
|
||||
vm->def->id = -1;
|
||||
driver->nactivevms--;
|
||||
driver->ninactivevms++;
|
||||
lxcSaveConfig(NULL, driver, vm, vm->def);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* lxcDomainDestroy:
|
||||
@ -862,31 +845,16 @@ tty_error_out:
|
||||
*/
|
||||
static int lxcDomainDestroy(virDomainPtr dom)
|
||||
{
|
||||
int rc = -1;
|
||||
lxc_driver_t *driver = (lxc_driver_t*)dom->conn->privateData;
|
||||
lxc_vm_t *vm = lxcFindVMByID(driver, dom->id);
|
||||
|
||||
if (!vm) {
|
||||
lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
|
||||
_("no domain with id %d"), dom->id);
|
||||
goto error_out;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 > (kill(vm->def->id, SIGKILL))) {
|
||||
if (ESRCH != errno) {
|
||||
lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
|
||||
_("sending SIGKILL failed: %s"), strerror(errno));
|
||||
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
vm->state = VIR_DOMAIN_SHUTDOWN;
|
||||
|
||||
rc = lxcVMCleanup(driver, vm);
|
||||
|
||||
error_out:
|
||||
return rc;
|
||||
return lxcVmTerminate(dom->conn, driver, vm, SIGKILL);
|
||||
}
|
||||
|
||||
static int lxcCheckNetNsSupport(void)
|
||||
@ -907,6 +875,7 @@ static int lxcCheckNetNsSupport(void)
|
||||
static int lxcStartup(void)
|
||||
{
|
||||
uid_t uid = getuid();
|
||||
lxc_vm_t *vm;
|
||||
|
||||
/* Check that the user is root */
|
||||
if (0 != uid) {
|
||||
@ -935,6 +904,36 @@ static int lxcStartup(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
vm = lxc_driver->vms;
|
||||
while (vm) {
|
||||
int rc;
|
||||
if ((vm->monitor = lxcMonitorClient(NULL, lxc_driver, vm)) < 0) {
|
||||
vm = vm->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Read pid from controller */
|
||||
if ((rc = virFileReadPid(lxc_driver->stateDir, vm->def->name, &vm->pid)) != 0) {
|
||||
close(vm->monitor);
|
||||
vm->monitor = -1;
|
||||
vm = vm->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vm->pid != 0) {
|
||||
vm->def->id = vm->pid;
|
||||
vm->state = VIR_DOMAIN_RUNNING;
|
||||
lxc_driver->ninactivevms--;
|
||||
lxc_driver->nactivevms++;
|
||||
} else {
|
||||
vm->def->id = -1;
|
||||
close(vm->monitor);
|
||||
vm->monitor = -1;
|
||||
}
|
||||
|
||||
vm = vm->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -942,6 +941,7 @@ static void lxcFreeDriver(lxc_driver_t *driver)
|
||||
{
|
||||
VIR_FREE(driver->configDir);
|
||||
VIR_FREE(driver->stateDir);
|
||||
VIR_FREE(driver->logDir);
|
||||
VIR_FREE(driver);
|
||||
}
|
||||
|
||||
@ -978,37 +978,6 @@ lxcActive(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lxcSigHandler:
|
||||
* @siginfo: Pointer to siginfo_t structure
|
||||
*
|
||||
* Handles signals received by libvirtd. Currently this is used to
|
||||
* catch SIGCHLD from an exiting container.
|
||||
*
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
static int lxcSigHandler(siginfo_t *siginfo)
|
||||
{
|
||||
int rc = -1;
|
||||
lxc_vm_t *vm;
|
||||
|
||||
if (siginfo->si_signo == SIGCHLD) {
|
||||
vm = lxcFindVMByID(lxc_driver, siginfo->si_pid);
|
||||
|
||||
if (NULL == vm) {
|
||||
DEBUG("Ignoring SIGCHLD from non-container process %d\n",
|
||||
siginfo->si_pid);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = lxcVMCleanup(lxc_driver, vm);
|
||||
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Function Tables */
|
||||
static virDriver lxcDriver = {
|
||||
@ -1079,7 +1048,7 @@ static virStateDriver lxcStateDriver = {
|
||||
lxcShutdown,
|
||||
NULL, /* reload */
|
||||
lxcActive,
|
||||
lxcSigHandler
|
||||
NULL,
|
||||
};
|
||||
|
||||
int lxcRegister(void)
|
||||
|
166
src/util.c
166
src/util.c
@ -37,6 +37,9 @@
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#if HAVE_TERMIOS_H
|
||||
#include <termios.h>
|
||||
#endif
|
||||
#include "c-ctype.h"
|
||||
|
||||
#ifdef HAVE_PATHS_H
|
||||
@ -472,6 +475,169 @@ int virFileBuildPath(const char *dir,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __linux__
|
||||
int virFileOpenTty(int *ttymaster,
|
||||
char **ttyName,
|
||||
int rawmode)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
if ((*ttymaster = posix_openpt(O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (unlockpt(*ttymaster) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (grantpt(*ttymaster) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (rawmode) {
|
||||
struct termios ttyAttr;
|
||||
if (tcgetattr(*ttymaster, &ttyAttr) < 0)
|
||||
goto cleanup;
|
||||
|
||||
cfmakeraw(&ttyAttr);
|
||||
|
||||
if (tcsetattr(*ttymaster, TCSADRAIN, &ttyAttr) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (ttyName) {
|
||||
char tempTtyName[PATH_MAX];
|
||||
if (ptsname_r(*ttymaster, tempTtyName, sizeof(tempTtyName)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((*ttyName = strdup(tempTtyName)) == NULL) {
|
||||
errno = ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
cleanup:
|
||||
if (rc != 0 &&
|
||||
*ttymaster != -1) {
|
||||
close(*ttymaster);
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
||||
}
|
||||
#else
|
||||
int virFileOpenTty(int *ttymaster ATTRIBUTE_UNUSED,
|
||||
char **ttyName ATTRIBUTE_UNUSED,
|
||||
int rawmode ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int virFileWritePid(const char *dir,
|
||||
const char *name,
|
||||
pid_t pid)
|
||||
{
|
||||
int rc;
|
||||
int fd;
|
||||
FILE *file = NULL;
|
||||
char *pidfile = NULL;
|
||||
|
||||
if ((rc = virFileMakePath(dir)))
|
||||
goto cleanup;
|
||||
|
||||
if (asprintf(&pidfile, "%s/%s.pid", dir, name) < 0) {
|
||||
rc = ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((fd = open(pidfile,
|
||||
O_WRONLY | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR)) < 0) {
|
||||
rc = errno;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!(file = fdopen(fd, "w"))) {
|
||||
rc = errno;
|
||||
close(fd);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (fprintf(file, "%d", pid) < 0) {
|
||||
rc = errno;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
cleanup:
|
||||
if (file &&
|
||||
fclose(file) < 0) {
|
||||
rc = errno;
|
||||
}
|
||||
|
||||
VIR_FREE(pidfile);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int virFileReadPid(const char *dir,
|
||||
const char *name,
|
||||
pid_t *pid)
|
||||
{
|
||||
int rc;
|
||||
FILE *file;
|
||||
char *pidfile = NULL;
|
||||
*pid = 0;
|
||||
if (asprintf(&pidfile, "%s/%s.pid", dir, name) < 0) {
|
||||
rc = ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!(file = fopen(pidfile, "r"))) {
|
||||
rc = errno;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (fscanf(file, "%d", pid) != 1) {
|
||||
rc = EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (fclose(file) < 0) {
|
||||
rc = errno;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
cleanup:
|
||||
VIR_FREE(pidfile);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int virFileDeletePid(const char *dir,
|
||||
const char *name)
|
||||
{
|
||||
int rc = 0;
|
||||
char *pidfile = NULL;
|
||||
|
||||
if (asprintf(&pidfile, "%s/%s.pid", dir, name) < 0) {
|
||||
rc = errno;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (unlink(pidfile) < 0 && errno != ENOENT)
|
||||
rc = errno;
|
||||
|
||||
cleanup:
|
||||
VIR_FREE(pidfile);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Like strtol, but produce an "int" result, and check more carefully.
|
||||
Return 0 upon success; return -1 to indicate failure.
|
||||
When END_PTR is NULL, the byte after the final valid digit must be NUL.
|
||||
|
12
src/util.h
12
src/util.h
@ -58,6 +58,18 @@ int virFileBuildPath(const char *dir,
|
||||
char *buf,
|
||||
unsigned int buflen);
|
||||
|
||||
int virFileOpenTty(int *ttymaster,
|
||||
char **ttyName,
|
||||
int rawmode);
|
||||
|
||||
int virFileWritePid(const char *dir,
|
||||
const char *name,
|
||||
pid_t pid);
|
||||
int virFileReadPid(const char *dir,
|
||||
const char *name,
|
||||
pid_t *pid);
|
||||
int virFileDeletePid(const char *dir,
|
||||
const char *name);
|
||||
|
||||
int __virStrToLong_i(char const *s,
|
||||
char **end_ptr,
|
||||
|
Loading…
x
Reference in New Issue
Block a user