moved netns code from daemon/src to netns at the top level, updated files to account for location change
This commit is contained in:
parent
e4a0069bc3
commit
d799390c4a
32 changed files with 14 additions and 14 deletions
8
netns/.gitignore
vendored
Normal file
8
netns/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
*.o
|
||||
Makefile
|
||||
Makefile.in
|
||||
build
|
||||
netns
|
||||
vcmd
|
||||
version.h
|
||||
vnoded
|
2
netns/MANIFEST.in
Normal file
2
netns/MANIFEST.in
Normal file
|
@ -0,0 +1,2 @@
|
|||
include *.h
|
||||
include sbin/*
|
73
netns/Makefile.am
Normal file
73
netns/Makefile.am
Normal file
|
@ -0,0 +1,73 @@
|
|||
# CORE
|
||||
# (c)2010-2013 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||
#
|
||||
# Makefile for building netns.
|
||||
#
|
||||
|
||||
AM_CFLAGS = -Wall -fno-strict-aliasing -O3 -g @libev_CFLAGS@
|
||||
SETUPPY = setup.py
|
||||
SETUPPYFLAGS = -v
|
||||
# -DDEBUG
|
||||
|
||||
SRC_COMMON = vnode_msg.c vnode_cmd.c vnode_chnl.c vnode_io.c \
|
||||
vnode_msg.h vnode_cmd.h vnode_chnl.h vnode_io.h \
|
||||
vnode_tlv.h myerr.h netns.h
|
||||
SRC_VNODED = vnoded_main.c vnode_server.c netns.c \
|
||||
vnode_server.h
|
||||
SRC_VCMD = vcmd_main.c vnode_client.c \
|
||||
vnode_client.h
|
||||
SRC_NETNS = netns_main.c netns.c netns.h
|
||||
|
||||
sbin_PROGRAMS = vnoded vcmd netns
|
||||
vnoded_LDADD = @libev_LIBS@
|
||||
vnoded_SOURCES = ${SRC_COMMON} ${SRC_VNODED}
|
||||
vcmd_LDADD = @libev_LIBS@
|
||||
vcmd_SOURCES = ${SRC_COMMON} ${SRC_VCMD}
|
||||
netns_SOURCES = ${SRC_NETNS}
|
||||
|
||||
# this triggers automake to run setup.py for building the Python libraries
|
||||
# actual library names are netns.so and vcmd.so
|
||||
# SOURCES line prevents 'make dist' from looking for a 'libnetns.c' file
|
||||
noinst_LIBRARIES = libnetns.a
|
||||
libnetns_a_SOURCES = netnsmodule.c vcmdmodule.c
|
||||
libnetns.a:
|
||||
SBINDIR=@SBINDIR@ LDFLAGS="$(LDFLAGS) @libev_LIBS@" CFLAGS="$(CFLAGS) @libev_CFLAGS@" $(PYTHON) setup.py build_ext
|
||||
|
||||
# Python libraries install
|
||||
install-exec-local:
|
||||
$(MKDIR_P) ${DESTDIR}/${pythondir}
|
||||
$(MKDIR_P) ${DESTDIR}/${pyexecdir}
|
||||
SBINDIR=${DESTDIR}/@SBINDIR@ PYTHONPATH=${DESTDIR}/${pythondir} $(PYTHON) $(SETUPPY) $(SETUPPYFLAGS) install \
|
||||
--prefix=${DESTDIR}/${pyprefix} \
|
||||
--install-purelib=${DESTDIR}/${pythondir} \
|
||||
--install-platlib=${DESTDIR}/${pyexecdir} \
|
||||
--no-compile
|
||||
|
||||
# Python libraries uninstall
|
||||
uninstall-hook:
|
||||
rm -f ${pyexecdir}/core_python_netns-1.0-py${PYTHON_VERSION}.egg-info
|
||||
rm -f ${pyexecdir}/netns.so
|
||||
rm -f ${pyexecdir}/vcmd.so
|
||||
|
||||
# Python libraries cleanup
|
||||
clean-local: clean-local-check
|
||||
.PHONY: clean-local-check
|
||||
clean-local-check:
|
||||
-rm -rf build
|
||||
|
||||
rpmbuild.sh:
|
||||
echo SBINDIR=@SBINDIR@ LDFLAGS="$(LDFLAGS)" CFLAGS="$(CFLAGS) @libev_CFLAGS@" $(PYTHON) setup.py build > rpmbuild.sh
|
||||
chmod a+x rpmbuild.sh
|
||||
|
||||
rpm: rpmbuild.sh
|
||||
$(PYTHON) setup.py bdist_rpm --build-script=rpmbuild.sh --requires="libev" --build-requires="libev-devel"
|
||||
|
||||
# extra cruft to remove
|
||||
DISTCLEANFILES = Makefile.in rpmbuild.sh MANIFEST
|
||||
|
||||
# include source files for Python libraries with distribution tarball
|
||||
EXTRA_DIST = setup.py MANIFEST.in
|
||||
|
80
netns/myerr.h
Normal file
80
netns/myerr.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* myerr.h
|
||||
*
|
||||
* Custom error printing macros.
|
||||
*/
|
||||
|
||||
#ifndef _MYERR_H_
|
||||
#define _MYERR_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
static void __myerrprintf(const char *func, const char *file, const int line,
|
||||
FILE *stream, const char *fmt, ...)
|
||||
{
|
||||
extern const char *__progname;
|
||||
va_list ap;
|
||||
pid_t pid;
|
||||
struct timeval tv;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
pid = getpid();
|
||||
if (gettimeofday(&tv, NULL))
|
||||
{
|
||||
fprintf(stream, "%s[%u]: %s[%s:%d]: ", __progname, pid, func, file, line);
|
||||
}
|
||||
else
|
||||
{
|
||||
char timestr[9];
|
||||
strftime(timestr, sizeof(timestr), "%H:%M:%S", localtime(&tv.tv_sec));
|
||||
fprintf(stream, "%s[%u]: %s.%06ld %s[%s:%d]: ",
|
||||
__progname, pid, timestr, tv.tv_usec, func, file, line);
|
||||
}
|
||||
|
||||
vfprintf(stream, fmt, ap);
|
||||
fputs("\n", stream);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#define INFO(fmt, args...) \
|
||||
__myerrprintf(__func__, __FILE__, __LINE__, \
|
||||
stdout, fmt, ##args)
|
||||
|
||||
#define WARNX(fmt, args...) \
|
||||
__myerrprintf(__func__, __FILE__, __LINE__, \
|
||||
stderr, fmt, ##args)
|
||||
|
||||
#define WARN(fmt, args...) \
|
||||
__myerrprintf(__func__, __FILE__, __LINE__, \
|
||||
stderr, fmt ": %s", ##args, strerror(errno))
|
||||
|
||||
#define ERRX(eval, fmt, args...) \
|
||||
do { \
|
||||
WARNX(fmt, ##args); \
|
||||
exit(eval); \
|
||||
} while (0)
|
||||
|
||||
#define ERR(eval, fmt, args...) \
|
||||
do { \
|
||||
WARN(fmt, ##args); \
|
||||
exit(eval); \
|
||||
} while (0)
|
||||
|
||||
#endif /* _MYERR_H_ */
|
110
netns/netns.c
Normal file
110
netns/netns.c
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* netns.c
|
||||
*
|
||||
* Implements nsfork() and nsexecvp() for forking and executing processes
|
||||
* within a network namespace.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "myerr.h"
|
||||
#include "netns.h"
|
||||
|
||||
#define NSCLONEFLGS \
|
||||
( \
|
||||
SIGCHLD | \
|
||||
CLONE_NEWNS | \
|
||||
CLONE_NEWUTS | \
|
||||
CLONE_NEWIPC | \
|
||||
CLONE_NEWPID | \
|
||||
CLONE_NEWNET \
|
||||
)
|
||||
|
||||
#define MOUNT_SYS_MIN_VERSION "2.6.35"
|
||||
|
||||
static void nssetup(void)
|
||||
{
|
||||
int r;
|
||||
struct utsname uts;
|
||||
|
||||
/* Taken from systemd-nspawn. Not sure why needed, but without this,
|
||||
* the host system goes a bit crazy under systemd. */
|
||||
r = mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL);
|
||||
if (r)
|
||||
WARN("mounting / failed");
|
||||
|
||||
/* mount per-namespace /proc */
|
||||
r = mount(NULL, "/proc", "proc", 0, NULL);
|
||||
if (r)
|
||||
WARN("mounting /proc failed");
|
||||
|
||||
r = uname(&uts);
|
||||
if (r)
|
||||
{
|
||||
WARN("uname() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
r = strncmp(uts.release, MOUNT_SYS_MIN_VERSION,
|
||||
sizeof(MOUNT_SYS_MIN_VERSION) - 1);
|
||||
if (r >= 0)
|
||||
{
|
||||
/* mount per-namespace /sys */
|
||||
r = mount(NULL, "/sys", "sysfs", 0, NULL);
|
||||
if (r)
|
||||
WARN("mounting /sys failed");
|
||||
}
|
||||
}
|
||||
|
||||
pid_t nsfork(int flags)
|
||||
{
|
||||
int pid;
|
||||
|
||||
pid = syscall(SYS_clone, flags | NSCLONEFLGS, NULL, NULL, NULL, NULL);
|
||||
if (pid == 0) /* child */
|
||||
{
|
||||
nssetup();
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
pid_t nsexecvp(char *argv[])
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
pid = nsfork(CLONE_VFORK);
|
||||
switch (pid)
|
||||
{
|
||||
case -1:
|
||||
WARN("nsfork() failed");
|
||||
break;
|
||||
|
||||
case 0:
|
||||
/* child */
|
||||
execvp(argv[0], argv);
|
||||
WARN("execvp() failed for '%s'", argv[0]);
|
||||
_exit(1);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* parent */
|
||||
if (kill(pid, 0))
|
||||
pid = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
20
netns/netns.h
Normal file
20
netns/netns.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* netns.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _FORKNS_H_
|
||||
#define _FORKNS_H_
|
||||
|
||||
#include <linux/sched.h>
|
||||
|
||||
pid_t nsfork(int flags);
|
||||
pid_t nsexecvp(char *argv[]);
|
||||
|
||||
#endif /* _FORKNS_H_ */
|
127
netns/netns_main.c
Normal file
127
netns/netns_main.c
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* netns_main.c
|
||||
*
|
||||
* netns utility program runs the specified program with arguments in a new
|
||||
* namespace.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "version.h"
|
||||
#include "netns.h"
|
||||
#include "myerr.h"
|
||||
|
||||
struct option longopts[] =
|
||||
{
|
||||
{"version", no_argument, NULL, 'V'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static void usage(int status, char *fmt, ...)
|
||||
{
|
||||
extern const char *__progname;
|
||||
va_list ap;
|
||||
FILE *output;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
output = status ? stderr : stdout;
|
||||
fprintf(output, "\n");
|
||||
if (fmt != NULL)
|
||||
{
|
||||
vfprintf(output, fmt, ap);
|
||||
fprintf(output, "\n\n");
|
||||
}
|
||||
fprintf(output,
|
||||
"Usage: %s [-h|-V] [-w] -- command [args...]\n\n"
|
||||
"Run the specified command in a new network namespace.\n\n"
|
||||
"Options:\n"
|
||||
" -h, --help show this help message and exit\n"
|
||||
" -V, --version show version number and exit\n"
|
||||
" -w wait for command to complete "
|
||||
"(useful for interactive commands)\n",
|
||||
__progname);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
exit(status);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
pid_t pid;
|
||||
int waitcmd = 0;
|
||||
int status = 0;
|
||||
extern const char *__progname;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int opt;
|
||||
|
||||
if ((opt = getopt_long(argc, argv, "hwV", longopts, NULL)) == -1)
|
||||
break;
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case 'w':
|
||||
waitcmd++;
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
printf("%s version %s\n", __progname, CORE_VERSION);
|
||||
exit(0);
|
||||
|
||||
case 'h':
|
||||
default:
|
||||
usage(0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (!argc)
|
||||
usage(1, "no command given");
|
||||
|
||||
if (geteuid() != 0)
|
||||
usage(1, "must be suid or run as root");
|
||||
if (setuid(0))
|
||||
ERR(1, "setuid() failed");
|
||||
|
||||
pid = nsexecvp(argv);
|
||||
if (pid < 0)
|
||||
ERR(1, "nsexecvp() failed");
|
||||
|
||||
printf("%d\n", pid);
|
||||
|
||||
if (waitcmd)
|
||||
{
|
||||
if (waitpid(pid, &status, 0) == -1)
|
||||
ERR(1, "waitpid() failed");
|
||||
|
||||
if (WIFEXITED(status))
|
||||
status = WEXITSTATUS(status);
|
||||
else if (WIFSIGNALED(status))
|
||||
{
|
||||
fprintf(stderr, "process terminated by signal %d\n", WTERMSIG(status));
|
||||
status = -1;
|
||||
}
|
||||
}
|
||||
|
||||
exit(status);
|
||||
}
|
146
netns/netnsmodule.c
Normal file
146
netns/netnsmodule.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* netnsmodule.c
|
||||
*
|
||||
* Python module C bindings providing nsfork and nsexecvp methods for
|
||||
* forking a child process into a new namespace, with nsexecvp executing a
|
||||
* new program using the default search path.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "netns.h"
|
||||
|
||||
/* parts taken from python/trunk/Modules/posixmodule.c */
|
||||
|
||||
static void free_string_array(char **array, Py_ssize_t count)
|
||||
{
|
||||
Py_ssize_t i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
PyMem_Free(array[i]);
|
||||
|
||||
PyMem_DEL(array);
|
||||
}
|
||||
|
||||
static PyObject *netns_nsexecvp(PyObject *self, PyObject *args)
|
||||
{
|
||||
pid_t pid;
|
||||
char **argv;
|
||||
Py_ssize_t i, argc;
|
||||
PyObject *(*getitem)(PyObject *, Py_ssize_t);
|
||||
|
||||
/* args should be a list or tuple of strings */
|
||||
|
||||
if (PyList_Check(args))
|
||||
{
|
||||
argc = PyList_Size(args);
|
||||
getitem = PyList_GetItem;
|
||||
}
|
||||
else if (PyTuple_Check(args))
|
||||
{
|
||||
argc = PyTuple_Size(args);
|
||||
getitem = PyTuple_GetItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"netns_nsexecvp() args must be a tuple or list");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
argv = PyMem_NEW(char *, argc + 1);
|
||||
if (argv == NULL)
|
||||
return PyErr_NoMemory();
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
{
|
||||
if (!PyArg_Parse((*getitem)(args, i), "et",
|
||||
Py_FileSystemDefaultEncoding, &argv[i]))
|
||||
{
|
||||
free_string_array(argv, i);
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"netns_nsexecvp() args must contain only strings");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
argv[argc] = NULL;
|
||||
|
||||
pid = nsexecvp(argv);
|
||||
|
||||
free_string_array(argv, argc);
|
||||
|
||||
if (pid < 0)
|
||||
return PyErr_SetFromErrno(PyExc_OSError);
|
||||
else
|
||||
return PyInt_FromLong(pid);
|
||||
}
|
||||
|
||||
static PyObject *netns_nsfork(PyObject *self, PyObject *args)
|
||||
{
|
||||
int flags;
|
||||
pid_t pid;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "i", &flags))
|
||||
return NULL;
|
||||
|
||||
pid = nsfork(flags);
|
||||
if (pid < 0)
|
||||
return PyErr_SetFromErrno(PyExc_OSError);
|
||||
|
||||
if (pid == 0) /* child */
|
||||
PyOS_AfterFork();
|
||||
|
||||
return PyInt_FromLong(pid);
|
||||
}
|
||||
|
||||
static PyMethodDef netns_methods[] = {
|
||||
{"nsfork", netns_nsfork, METH_VARARGS,
|
||||
"nsfork(cloneflags) -> int\n\n"
|
||||
"Fork a child process into a new namespace using the Linux clone()\n"
|
||||
"system call.\n\n"
|
||||
"cloneflags: additional flags passed to clone()"},
|
||||
|
||||
{"nsexecvp", netns_nsexecvp, METH_VARARGS,
|
||||
"nsexecvp(args...) -> int\n\n"
|
||||
"Fork a child process into a new namespace using the Linux clone()\n"
|
||||
"system call and have the child execute a new program using the\n"
|
||||
"default search path.\n\n"
|
||||
"args: the executable file name followed by command arguments"},
|
||||
|
||||
{NULL, NULL, 0, NULL},
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC initnetns(void)
|
||||
{
|
||||
PyObject *m;
|
||||
|
||||
m = Py_InitModule("netns", netns_methods);
|
||||
if (m == NULL)
|
||||
return;
|
||||
|
||||
#define MODADDINT(x) \
|
||||
do { \
|
||||
PyObject *tmp = Py_BuildValue("i", x); \
|
||||
if (tmp) \
|
||||
{ \
|
||||
Py_INCREF(tmp); \
|
||||
PyModule_AddObject(m, #x, tmp); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
MODADDINT(CLONE_VFORK);
|
||||
|
||||
#undef MODADDINT
|
||||
|
||||
return;
|
||||
}
|
42
netns/setup.py
Normal file
42
netns/setup.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
# Copyright (c)2010-2012 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
|
||||
from setuptools import setup, Extension
|
||||
|
||||
netns = Extension(
|
||||
"netns",
|
||||
sources=[
|
||||
"netnsmodule.c",
|
||||
"netns.c"
|
||||
]
|
||||
)
|
||||
|
||||
vcmd = Extension(
|
||||
"vcmd",
|
||||
sources=[
|
||||
"vcmdmodule.c",
|
||||
"vnode_client.c",
|
||||
"vnode_chnl.c",
|
||||
"vnode_io.c",
|
||||
"vnode_msg.c",
|
||||
"vnode_cmd.c",
|
||||
],
|
||||
library_dirs=["build/lib"],
|
||||
libraries=["ev"]
|
||||
)
|
||||
|
||||
setup(
|
||||
name="core-netns",
|
||||
version="1.0",
|
||||
description="Extension modules to support virtual nodes using Linux network namespaces",
|
||||
scripts=["vcmd", "vnoded", "netns"],
|
||||
ext_modules=[
|
||||
netns,
|
||||
vcmd
|
||||
],
|
||||
url="http://www.nrl.navy.mil/itd/ncs/products/core",
|
||||
author="Boeing Research & Technology",
|
||||
author_email="core-dev@nrl.navy.mil",
|
||||
license="BSD",
|
||||
long_description="Extension modules and utilities to support virtual nodes using Linux network namespaces"
|
||||
)
|
439
netns/vcmd_main.c
Normal file
439
netns/vcmd_main.c
Normal file
|
@ -0,0 +1,439 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vcmd_main.c
|
||||
*
|
||||
* vcmd utility program for executing programs in an existing namespace
|
||||
* specified by the given channel.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "version.h"
|
||||
#include "vnode_chnl.h"
|
||||
#include "vnode_cmd.h"
|
||||
#include "vnode_client.h"
|
||||
|
||||
#include "myerr.h"
|
||||
|
||||
#define FORWARD_SIGNALS
|
||||
#define VCMD_DEFAULT_CMD "/bin/bash"
|
||||
|
||||
int verbose;
|
||||
|
||||
typedef struct {
|
||||
vnode_client_t *client;
|
||||
vnode_client_cmdio_t *cmdio;
|
||||
int argc;
|
||||
char **argv;
|
||||
int cmdid;
|
||||
int cmdstatus;
|
||||
ev_io stdin_watcher;
|
||||
int stdin_fwdfd;
|
||||
ev_io ptymaster_watcher;
|
||||
int ptymaster_fwdfd;
|
||||
} vcmd_t;
|
||||
|
||||
static vcmd_t vcmd;
|
||||
|
||||
static struct termios saveattr;
|
||||
static int saveattr_set;
|
||||
|
||||
struct option longopts[] =
|
||||
{
|
||||
{"version", no_argument, NULL, 'V'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
void usage(int status, char *fmt, ...)
|
||||
{
|
||||
extern const char *__progname;
|
||||
va_list ap;
|
||||
FILE *output;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
output = status ? stderr : stdout;
|
||||
fprintf(output, "\n");
|
||||
if (fmt != NULL)
|
||||
{
|
||||
vfprintf(output, fmt, ap);
|
||||
fprintf(output, "\n\n");
|
||||
}
|
||||
fprintf(output,
|
||||
"Usage: %s [-h|-V] [-v] [-q|-i|-I] -c <channel name> -- command args"
|
||||
"...\n\n"
|
||||
"Run the specified command in the Linux namespace container "
|
||||
"specified by the \ncontrol <channel name>, with the specified "
|
||||
"arguments.\n\nOptions:\n"
|
||||
" -h, --help show this help message and exit\n"
|
||||
" -V, --version show version number and exit\n"
|
||||
" -v enable verbose logging\n"
|
||||
" -q run the command quietly, without local input or output\n"
|
||||
" -i run the command interactively (use PTY)\n"
|
||||
" -I run the command non-interactively (without PTY)\n"
|
||||
" -c control channel name (e.g. '/tmp/pycore.45647/n3')\n",
|
||||
__progname);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
exit(status);
|
||||
}
|
||||
|
||||
static void vcmd_rwcb(struct ev_loop *loop, ev_io *w, int revents)
|
||||
{
|
||||
int outfd = *(int *)w->data;
|
||||
char buf[BUFSIZ];
|
||||
ssize_t rcount, wcount;
|
||||
|
||||
rcount = read(w->fd, buf, sizeof(buf));
|
||||
if (rcount <= 0)
|
||||
{
|
||||
ev_io_stop(loop, w);
|
||||
}
|
||||
else
|
||||
{
|
||||
wcount = write(outfd, buf, rcount);
|
||||
if (wcount != rcount)
|
||||
WARN("write() error: wrote %d of %d bytes", wcount, rcount);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void vcmd_cmddonecb(int32_t cmdid, pid_t pid, int status, void *data)
|
||||
{
|
||||
vcmd_t *vcmd = data;
|
||||
|
||||
if (vcmd->cmdio->iotype == VCMD_IO_PTY)
|
||||
{
|
||||
ev_io_stop(vcmd->client->loop, &vcmd->stdin_watcher);
|
||||
ev_io_stop(vcmd->client->loop, &vcmd->ptymaster_watcher);
|
||||
|
||||
/* drain command output */
|
||||
for (;;)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
ssize_t rcount, wcount;
|
||||
|
||||
rcount = read(vcmd->ptymaster_watcher.fd, buf, sizeof(buf));
|
||||
if (rcount <= 0)
|
||||
break;
|
||||
|
||||
wcount = write(STDOUT_FILENO, buf, rcount);
|
||||
if (wcount != rcount)
|
||||
WARN("write() error: %d of %d bytes", wcount, rcount);
|
||||
}
|
||||
}
|
||||
|
||||
vnode_close_clientcmdio(vcmd->cmdio);
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("cmdid %u; pid %d; status: 0x%x", cmdid, pid, status);
|
||||
#endif
|
||||
|
||||
if (WIFEXITED(status))
|
||||
/* normal terminataion */
|
||||
vcmd->cmdstatus = WEXITSTATUS(status);
|
||||
else if (WIFSIGNALED(status))
|
||||
{
|
||||
if (verbose)
|
||||
INFO("command %u terminated by signal: %d", cmdid, WTERMSIG(status));
|
||||
vcmd->cmdstatus = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO("unexpected termination status for command %u: 0x%x", cmdid, status);
|
||||
vcmd->cmdstatus = 255;
|
||||
}
|
||||
|
||||
vcmd->cmdid = -1;
|
||||
|
||||
ev_unloop(vcmd->client->loop, EVUNLOOP_ALL);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void vcmd_cmdreqcb(struct ev_loop *loop, ev_timer *w, int revents)
|
||||
{
|
||||
vcmd_t *vcmd = w->data;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("sending command request: serverfd %d; vcmd %p",
|
||||
vcmd->client->serverfd, vcmd);
|
||||
#endif
|
||||
|
||||
if (vcmd->cmdio->iotype == VCMD_IO_PTY)
|
||||
{
|
||||
/* setup forwarding i/o */
|
||||
|
||||
vcmd->stdin_fwdfd = vcmd->cmdio->stdiopty.masterfd;
|
||||
vcmd->stdin_watcher.data = &vcmd->stdin_fwdfd;
|
||||
ev_io_init(&vcmd->stdin_watcher, vcmd_rwcb, STDIN_FILENO, EV_READ);
|
||||
ev_io_start(loop, &vcmd->stdin_watcher);
|
||||
|
||||
vcmd->ptymaster_fwdfd = STDOUT_FILENO;
|
||||
vcmd->ptymaster_watcher.data = &vcmd->ptymaster_fwdfd;
|
||||
ev_io_init(&vcmd->ptymaster_watcher, vcmd_rwcb,
|
||||
vcmd->cmdio->stdiopty.masterfd, EV_READ);
|
||||
ev_io_start(loop, &vcmd->ptymaster_watcher);
|
||||
}
|
||||
|
||||
vcmd->cmdid = vnode_client_cmdreq(vcmd->client, vcmd->cmdio,
|
||||
vcmd_cmddonecb, vcmd,
|
||||
vcmd->argc, vcmd->argv);
|
||||
if (vcmd->cmdid < 0)
|
||||
{
|
||||
WARNX("vnode_client_cmdreq() failed");
|
||||
vnode_delclient(vcmd->client);
|
||||
vcmd->client = NULL;
|
||||
exit(255);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void vcmd_ioerrorcb(vnode_client_t *client)
|
||||
{
|
||||
vcmd_t *vcmd = client->data;
|
||||
|
||||
WARNX("i/o error");
|
||||
|
||||
vnode_delclient(client);
|
||||
vcmd->client = NULL;
|
||||
|
||||
exit(1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef FORWARD_SIGNALS
|
||||
static void sighandler(int signum)
|
||||
{
|
||||
if (!vcmd.client || vcmd.cmdid < 0)
|
||||
return;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("sending command signal: serverfd %d; cmdid %u; signum: %d",
|
||||
vcmd.client->serverfd, vcmd.cmdid, signum);
|
||||
#endif
|
||||
|
||||
if (vnode_send_cmdsignal(vcmd.client->serverfd, vcmd.cmdid, signum))
|
||||
WARN("vnode_send_cmdsignal() failed");
|
||||
|
||||
return;
|
||||
}
|
||||
#endif /* FORWARD_SIGNALS */
|
||||
|
||||
static void sigwinch_handler(int signum)
|
||||
{
|
||||
struct winsize wsiz;
|
||||
|
||||
if (signum != SIGWINCH)
|
||||
{
|
||||
WARNX("unexpected signal number: %d", signum);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!vcmd.cmdio || vcmd.cmdio->iotype != VCMD_IO_PTY)
|
||||
return;
|
||||
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wsiz))
|
||||
{
|
||||
WARN("ioctl() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ioctl(vcmd.cmdio->stdiopty.masterfd, TIOCSWINSZ, &wsiz))
|
||||
WARN("ioctl() failed");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int termioraw(int fd, struct termios *saveattr)
|
||||
{
|
||||
int err;
|
||||
struct termios raw = {};
|
||||
|
||||
err = tcgetattr(fd, saveattr);
|
||||
if (err)
|
||||
{
|
||||
WARN("tcgetattr() failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
cfmakeraw(&raw);
|
||||
err = tcsetattr(fd, TCSADRAIN, &raw);
|
||||
if (err)
|
||||
{
|
||||
WARN("tcsetattr() failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
if (saveattr_set)
|
||||
if (tcsetattr(STDOUT_FILENO, TCSADRAIN, &saveattr))
|
||||
WARN("tcsetattr() failed");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *ctrlchnlname = NULL;
|
||||
vnode_client_cmdiotype_t iotype = VCMD_IO_FD;
|
||||
ev_timer cmdreq;
|
||||
extern const char *__progname;
|
||||
#ifdef FORWARD_SIGNALS
|
||||
int i;
|
||||
struct sigaction sig_action = {
|
||||
.sa_handler = sighandler,
|
||||
};
|
||||
#endif /* FORWARD_SIGNALS */
|
||||
char *def_argv[2] = { VCMD_DEFAULT_CMD, 0 };
|
||||
|
||||
if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) &&
|
||||
isatty(STDERR_FILENO) && getpgrp() == tcgetpgrp(STDOUT_FILENO))
|
||||
iotype = VCMD_IO_PTY;
|
||||
|
||||
/* Parse command line argument list */
|
||||
for (;;)
|
||||
{
|
||||
int opt;
|
||||
|
||||
if ((opt = getopt_long(argc, argv, "c:hiIqvV", longopts, NULL)) == -1)
|
||||
break;
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case 'c':
|
||||
ctrlchnlname = optarg;
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
iotype = VCMD_IO_PTY;
|
||||
break;
|
||||
|
||||
case 'I':
|
||||
iotype = VCMD_IO_FD;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
iotype = VCMD_IO_NONE;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
printf("%s version %s\n", __progname, CORE_VERSION);
|
||||
exit(0);
|
||||
|
||||
case 'h':
|
||||
/* pass through */
|
||||
default:
|
||||
usage(0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (ctrlchnlname == NULL)
|
||||
usage(1, "no control channel name given");
|
||||
|
||||
if (!argc)
|
||||
{
|
||||
argc = 1;
|
||||
argv = def_argv;
|
||||
}
|
||||
|
||||
if (argc >= VNODE_ARGMAX)
|
||||
usage(1, "too many command arguments");
|
||||
|
||||
if (atexit(cleanup))
|
||||
ERR(1, "atexit() failed");
|
||||
|
||||
#ifdef FORWARD_SIGNALS
|
||||
for (i = 1; i < _NSIG; i++)
|
||||
if (sigaction(i, &sig_action, NULL))
|
||||
if (verbose && i != SIGKILL && i != SIGSTOP)
|
||||
WARN("sigaction() failed for %d", i);
|
||||
#endif /* FORWARD_SIGNALS */
|
||||
|
||||
vcmd.cmdio = vnode_open_clientcmdio(iotype);
|
||||
if (!vcmd.cmdio)
|
||||
ERR(1, "vnode_open_clientcmdio() failed");
|
||||
|
||||
vcmd.argc = argc;
|
||||
vcmd.argv = argv;
|
||||
vcmd.cmdstatus = 255;
|
||||
|
||||
switch (vcmd.cmdio->iotype)
|
||||
{
|
||||
case VCMD_IO_NONE:
|
||||
break;
|
||||
|
||||
case VCMD_IO_FD:
|
||||
SET_STDIOFD(vcmd.cmdio, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO);
|
||||
break;
|
||||
|
||||
case VCMD_IO_PTY:
|
||||
{
|
||||
struct sigaction sigwinch_action = {
|
||||
.sa_handler = sigwinch_handler,
|
||||
};
|
||||
|
||||
if (sigaction(SIGWINCH, &sigwinch_action, NULL))
|
||||
WARN("sigaction() failed for SIGWINCH");
|
||||
|
||||
sigwinch_handler(SIGWINCH);
|
||||
|
||||
if (termioraw(STDOUT_FILENO, &saveattr))
|
||||
WARNX("termioraw() failed");
|
||||
else
|
||||
saveattr_set = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ERR(1, "unsupported i/o type: %u", vcmd.cmdio->iotype);
|
||||
break;
|
||||
}
|
||||
|
||||
vcmd.client = vnode_client(ev_default_loop(0), ctrlchnlname,
|
||||
vcmd_ioerrorcb, &vcmd);
|
||||
if (!vcmd.client)
|
||||
ERR(1, "vnode_client() failed");
|
||||
|
||||
cmdreq.data = &vcmd;
|
||||
ev_timer_init(&cmdreq, vcmd_cmdreqcb, 0, 0);
|
||||
ev_timer_start(vcmd.client->loop, &cmdreq);
|
||||
|
||||
ev_loop(vcmd.client->loop, 0);
|
||||
|
||||
vnode_delclient(vcmd.client);
|
||||
|
||||
exit(vcmd.cmdstatus);
|
||||
}
|
956
netns/vcmdmodule.c
Normal file
956
netns/vcmdmodule.c
Normal file
|
@ -0,0 +1,956 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vcmdmodule.c
|
||||
*
|
||||
* C bindings for the vcmd Python module that allows a Python script to
|
||||
* execute a program within a running namespace given by the specified channel.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
#include <structmember.h>
|
||||
#include <pthread.h>
|
||||
#undef NDEBUG /* XXX force enabling asserts for now */
|
||||
#include <assert.h>
|
||||
|
||||
#include "vnode_client.h"
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
typedef struct vcmdentry {
|
||||
PyObject_HEAD
|
||||
|
||||
vnode_client_t *_client;
|
||||
} VCmd;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
int32_t _cmdid;
|
||||
int _complete;
|
||||
int _status;
|
||||
pthread_mutex_t _mutex;
|
||||
pthread_cond_t _cv;
|
||||
VCmd *_vcmd;
|
||||
} VCmdWait;
|
||||
|
||||
int verbose;
|
||||
|
||||
/* ev_default_loop(0) is not used because it interferes with SIGCHLD */
|
||||
static struct ev_loop *loop;
|
||||
static pthread_t evloopthread;
|
||||
|
||||
static TAILQ_HEAD(asyncreqhead, asyncreq) asyncreqlisthead;
|
||||
static pthread_mutex_t asyncreqlist_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static int asyncpipe[2];
|
||||
static pthread_mutex_t asyncpipe_writemutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static ev_io asyncwatcher;
|
||||
|
||||
typedef void (*asyncfunc_t)(struct ev_loop *loop, void *data);
|
||||
|
||||
typedef struct asyncreq {
|
||||
TAILQ_ENTRY(asyncreq) entries;
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cv;
|
||||
int done;
|
||||
|
||||
asyncfunc_t asyncfunc;
|
||||
void *data;
|
||||
} vcmd_asyncreq_t;
|
||||
|
||||
static void vcmd_asyncreq_cb(struct ev_loop *loop, ev_io *w, int revents)
|
||||
{
|
||||
vcmd_asyncreq_t *asyncreq;
|
||||
|
||||
/* drain the event pipe */
|
||||
for (;;)
|
||||
{
|
||||
ssize_t len;
|
||||
char buf[BUFSIZ];
|
||||
|
||||
len = read(asyncpipe[0], buf, sizeof(buf));
|
||||
if (len <= 0)
|
||||
{
|
||||
if (len == 0)
|
||||
ERR(1, "asynchronous event pipe closed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
pthread_mutex_lock(&asyncreqlist_mutex);
|
||||
asyncreq = TAILQ_FIRST(&asyncreqlisthead);
|
||||
if (asyncreq)
|
||||
TAILQ_REMOVE(&asyncreqlisthead, asyncreq, entries);
|
||||
pthread_mutex_unlock(&asyncreqlist_mutex);
|
||||
|
||||
if (!asyncreq)
|
||||
break;
|
||||
|
||||
assert(asyncreq->asyncfunc);
|
||||
asyncreq->asyncfunc(loop, asyncreq->data);
|
||||
|
||||
pthread_mutex_lock(&asyncreq->mutex);
|
||||
asyncreq->done = 1;
|
||||
pthread_cond_broadcast(&asyncreq->cv);
|
||||
pthread_mutex_unlock(&asyncreq->mutex);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void call_asyncfunc(asyncfunc_t asyncfunc, void *data)
|
||||
{
|
||||
vcmd_asyncreq_t asyncreq = {
|
||||
.asyncfunc = asyncfunc,
|
||||
.data = data,
|
||||
};
|
||||
char zero = 0;
|
||||
ssize_t len;
|
||||
|
||||
pthread_mutex_init(&asyncreq.mutex, NULL);
|
||||
pthread_cond_init(&asyncreq.cv, NULL);
|
||||
|
||||
pthread_mutex_lock(&asyncreqlist_mutex);
|
||||
TAILQ_INSERT_TAIL(&asyncreqlisthead, &asyncreq, entries);
|
||||
pthread_mutex_unlock(&asyncreqlist_mutex);
|
||||
|
||||
pthread_mutex_lock(&asyncpipe_writemutex);
|
||||
len = write(asyncpipe[1], &zero, sizeof(zero));
|
||||
pthread_mutex_unlock(&asyncpipe_writemutex);
|
||||
if (len == -1)
|
||||
ERR(1, "write() failed");
|
||||
if (len != sizeof(zero))
|
||||
WARN("incomplete write: %d of %d", len, sizeof(zero));
|
||||
|
||||
pthread_mutex_lock(&asyncreq.mutex);
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
while (!asyncreq.done)
|
||||
pthread_cond_wait(&asyncreq.cv, &asyncreq.mutex);
|
||||
Py_END_ALLOW_THREADS
|
||||
pthread_mutex_unlock(&asyncreq.mutex);
|
||||
|
||||
pthread_mutex_destroy(&asyncreq.mutex);
|
||||
pthread_cond_destroy(&asyncreq.cv);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void *start_evloop(void *data)
|
||||
{
|
||||
struct ev_loop *loop = data;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("starting event loop: %p", loop);
|
||||
#endif
|
||||
|
||||
ev_loop(loop, 0);
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("event loop done: %p", loop);
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int init_evloop(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
loop = ev_loop_new(0);
|
||||
if (!loop)
|
||||
{
|
||||
WARN("ev_loop_new() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
TAILQ_INIT(&asyncreqlisthead);
|
||||
|
||||
err = pipe(asyncpipe);
|
||||
if (err)
|
||||
{
|
||||
WARN("pipe() failed");
|
||||
return -1;
|
||||
}
|
||||
set_cloexec(asyncpipe[0]);
|
||||
set_cloexec(asyncpipe[1]);
|
||||
set_nonblock(asyncpipe[0]);
|
||||
ev_io_init(&asyncwatcher, vcmd_asyncreq_cb, asyncpipe[0], EV_READ);
|
||||
ev_io_start(loop, &asyncwatcher);
|
||||
|
||||
err = pthread_create(&evloopthread, NULL, start_evloop, loop);
|
||||
if (err)
|
||||
{
|
||||
errno = err;
|
||||
WARN("pthread_create() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *VCmdWait_new(PyTypeObject *type,
|
||||
PyObject *args, PyObject *kwds)
|
||||
{
|
||||
VCmdWait *self;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("enter");
|
||||
#endif
|
||||
|
||||
self = (VCmdWait *)type->tp_alloc(type, 0);
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
self->_cmdid = -1;
|
||||
self->_complete = 0;
|
||||
self->_status = -1;
|
||||
pthread_mutex_init(&self->_mutex, NULL);
|
||||
pthread_cond_init(&self->_cv, NULL);
|
||||
self->_vcmd = NULL;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("%p: exit", self);
|
||||
#endif
|
||||
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
static void VCmdWait_dealloc(VCmdWait *self)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
WARNX("%p: enter", self);
|
||||
#endif
|
||||
|
||||
pthread_mutex_destroy(&self->_mutex);
|
||||
pthread_cond_destroy(&self->_cv);
|
||||
if (self->_vcmd != NULL)
|
||||
Py_DECREF(self->_vcmd);
|
||||
|
||||
self->ob_type->tp_free((PyObject *)self);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static PyObject *VCmdWait_wait(VCmdWait *self)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (self->_vcmd == NULL)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "unstarted command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&self->_mutex);
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("%p: waiting for cmd %d: complete: %d; status: %d",
|
||||
self, self->_cmdid, self->_complete, self->_status);
|
||||
#endif
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
while (!self->_complete)
|
||||
pthread_cond_wait(&self->_cv, &self->_mutex);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
status = self->_status;
|
||||
|
||||
pthread_mutex_unlock(&self->_mutex);
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("%p: done waiting for cmd %d: status: %d",
|
||||
self, self->_cmdid, self->_status);
|
||||
#endif
|
||||
|
||||
return Py_BuildValue("i", status);
|
||||
}
|
||||
|
||||
static PyObject *VCmdWait_complete(VCmdWait *self,
|
||||
PyObject *args, PyObject *kwds)
|
||||
{
|
||||
if (self->_vcmd == NULL)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "unstarted command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self->_complete)
|
||||
Py_RETURN_TRUE;
|
||||
else
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
static PyObject *VCmdWait_status(VCmdWait *self,
|
||||
PyObject *args, PyObject *kwds)
|
||||
{
|
||||
if (self->_vcmd == NULL)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "unstarted command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self->_complete)
|
||||
return Py_BuildValue("i", self->_status);
|
||||
else
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *VCmdWait_kill(VCmdWait *self,
|
||||
PyObject *args, PyObject *kwds)
|
||||
{
|
||||
int sig;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "i", &sig))
|
||||
return NULL;
|
||||
|
||||
if (self->_vcmd == NULL)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "unstarted command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self->_complete)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "command already complete");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (vnode_send_cmdsignal(self->_vcmd->_client->serverfd, self->_cmdid, sig))
|
||||
{
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyMemberDef VCmdWait_members[] = {
|
||||
{"vcmd", T_OBJECT, offsetof(VCmdWait, _vcmd), READONLY,
|
||||
"VCmd instance that created this object"},
|
||||
|
||||
{NULL, 0, 0, 0, NULL},
|
||||
};
|
||||
|
||||
static PyMethodDef VCmdWait_methods[] = {
|
||||
{"wait", (PyCFunction)VCmdWait_wait, METH_NOARGS,
|
||||
"wait() -> int\n\n"
|
||||
"Wait for command to complete and return exit status"},
|
||||
|
||||
{"complete", (PyCFunction)VCmdWait_complete, METH_NOARGS,
|
||||
"complete() -> boolean\n\n"
|
||||
"Return True if command has completed; return False otherwise."},
|
||||
|
||||
{"status", (PyCFunction)VCmdWait_status, METH_NOARGS,
|
||||
"status() -> int\n\n"
|
||||
"Return exit status if command has completed; return None otherwise."},
|
||||
|
||||
{"kill", (PyCFunction)VCmdWait_kill, METH_VARARGS,
|
||||
"kill(signum) -> None\n\n"
|
||||
"Send a signal to command.\n\n"
|
||||
"signum: the signal to send"},
|
||||
|
||||
{NULL, NULL, 0, NULL},
|
||||
};
|
||||
|
||||
static PyTypeObject vcmd_VCmdWaitType = {
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
.tp_name = "vcmd.VCmdWait",
|
||||
.tp_basicsize = sizeof(VCmdWait),
|
||||
.tp_dealloc = (destructor)VCmdWait_dealloc,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
||||
.tp_doc = "VCmdWait objects",
|
||||
.tp_methods = VCmdWait_methods,
|
||||
.tp_members = VCmdWait_members,
|
||||
.tp_new = VCmdWait_new,
|
||||
};
|
||||
|
||||
|
||||
static PyObject *VCmd_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
VCmd *self;
|
||||
|
||||
self = (VCmd *)type->tp_alloc(type, 0);
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
self->_client = NULL;
|
||||
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
static void vcmd_ioerrorcb(vnode_client_t *client)
|
||||
{
|
||||
VCmd *self;
|
||||
PyGILState_STATE gstate = 0;
|
||||
int pythreads;
|
||||
|
||||
pythreads = PyEval_ThreadsInitialized();
|
||||
if (pythreads)
|
||||
gstate = PyGILState_Ensure();
|
||||
|
||||
if (verbose)
|
||||
WARNX("i/o error for client %p", client);
|
||||
|
||||
self = client->data;
|
||||
|
||||
assert(self);
|
||||
assert(self->_client == client);
|
||||
|
||||
if (self->_client)
|
||||
{
|
||||
vnode_delclient(self->_client);
|
||||
self->_client = NULL;
|
||||
}
|
||||
|
||||
if (pythreads)
|
||||
PyGILState_Release(gstate);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
vnode_client_t *client;
|
||||
const char *ctrlchnlname;
|
||||
void *data;
|
||||
} vcmd_newclientreq_t;
|
||||
|
||||
static void async_newclientreq(struct ev_loop *loop, void *data)
|
||||
{
|
||||
vcmd_newclientreq_t *newclreq = data;
|
||||
|
||||
newclreq->client = vnode_client(loop, newclreq->ctrlchnlname,
|
||||
vcmd_ioerrorcb, newclreq->data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
VCmd *vcmd;
|
||||
} vcmd_delclientreq_t;
|
||||
|
||||
static void async_delclientreq(struct ev_loop *loop, void *data)
|
||||
{
|
||||
vcmd_delclientreq_t *delclreq = data;
|
||||
|
||||
if (delclreq->vcmd->_client)
|
||||
{
|
||||
vnode_delclient(delclreq->vcmd->_client);
|
||||
delclreq->vcmd->_client = NULL;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int VCmd_init(VCmd *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
vcmd_newclientreq_t newclreq = {.data = self};
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("%p: enter", self);
|
||||
#endif
|
||||
|
||||
if (!loop)
|
||||
if (init_evloop())
|
||||
return -1;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &newclreq.ctrlchnlname))
|
||||
return -1;
|
||||
|
||||
call_asyncfunc(async_newclientreq, &newclreq);
|
||||
self->_client = newclreq.client;
|
||||
if (!self->_client)
|
||||
{
|
||||
WARN("vnode_client() failed");
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void VCmd_dealloc(VCmd *self)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
WARNX("%p: enter", self);
|
||||
#endif
|
||||
|
||||
if (self->_client)
|
||||
{
|
||||
vcmd_delclientreq_t delclreq = {.vcmd = self};
|
||||
|
||||
call_asyncfunc(async_delclientreq, &delclreq);
|
||||
}
|
||||
|
||||
self->ob_type->tp_free((PyObject *)self);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static PyObject *VCmd_connected(VCmd *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
if (self->_client != NULL)
|
||||
Py_RETURN_TRUE;
|
||||
else
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
static void vcmd_cmddonecb(int32_t cmdid, pid_t pid, int status, void *data)
|
||||
{
|
||||
VCmdWait *cmdwait = data;
|
||||
PyGILState_STATE gstate = 0;
|
||||
int pythreads;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("cmdid %d; pid %d; status: 0x%x", cmdid, pid, status);
|
||||
|
||||
if (WIFEXITED(status))
|
||||
WARNX("command %d terminated normally with status: %d",
|
||||
cmdid, WEXITSTATUS(status));
|
||||
else if (WIFSIGNALED(status))
|
||||
WARNX("command %d terminated by signal: %d", cmdid, WTERMSIG(status));
|
||||
else
|
||||
WARNX("unexpected termination status for command %d: 0x%x", cmdid, status);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("%p: waiting for lock", cmdwait);
|
||||
#endif
|
||||
|
||||
pthread_mutex_lock(&cmdwait->_mutex);
|
||||
|
||||
cmdwait->_status = status;
|
||||
cmdwait->_complete = 1;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("%p: command callback done", cmdwait);
|
||||
#endif
|
||||
|
||||
pthread_cond_broadcast(&cmdwait->_cv);
|
||||
pthread_mutex_unlock(&cmdwait->_mutex);
|
||||
|
||||
pythreads = PyEval_ThreadsInitialized();
|
||||
if (pythreads)
|
||||
gstate = PyGILState_Ensure();
|
||||
|
||||
Py_DECREF(cmdwait);
|
||||
|
||||
if (pythreads)
|
||||
PyGILState_Release(gstate);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int cmdid;
|
||||
vnode_client_t *client;
|
||||
vnode_client_cmdio_t *clientcmdio;
|
||||
void *data;
|
||||
int argc;
|
||||
char **argv;
|
||||
} vcmd_cmdreq_t;
|
||||
|
||||
static void async_cmdreq(struct ev_loop *loop, void *data)
|
||||
{
|
||||
vcmd_cmdreq_t *cmdreq = data;
|
||||
|
||||
cmdreq->cmdid = vnode_client_cmdreq(cmdreq->client, cmdreq->clientcmdio,
|
||||
vcmd_cmddonecb, cmdreq->data,
|
||||
cmdreq->argc, cmdreq->argv);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void free_string_array(char **array, Py_ssize_t count)
|
||||
{
|
||||
Py_ssize_t i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
PyMem_Free(array[i]);
|
||||
|
||||
PyMem_Del(array);
|
||||
}
|
||||
|
||||
static PyObject *_VCmd_cmd(VCmd *self, PyObject *args, PyObject *kwds,
|
||||
vnode_client_cmdiotype_t iotype)
|
||||
{
|
||||
int status, infd, outfd, errfd;
|
||||
PyObject *cmdargs;
|
||||
char **argv = NULL;
|
||||
Py_ssize_t i, argc;
|
||||
PyObject *(*getitem)(PyObject *, Py_ssize_t);
|
||||
VCmdWait *cmdwait;
|
||||
vnode_client_cmdio_t *cmdio;
|
||||
PyObject *pyinfile = NULL, *pyoutfile = NULL, *pyerrfile = NULL;
|
||||
PyObject *pyptyfile = NULL;
|
||||
PyObject *ret;
|
||||
|
||||
if (self->_client == NULL)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "not connected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (iotype == VCMD_IO_FD)
|
||||
{
|
||||
char *kwlist[] = {"infd", "outfd", "errfd", "args", NULL};
|
||||
|
||||
status = PyArg_ParseTupleAndKeywords(args, kwds, "iiiO", kwlist,
|
||||
&infd, &outfd, &errfd, &cmdargs);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *kwlist[] = {"args", NULL};
|
||||
|
||||
status = PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &cmdargs);
|
||||
}
|
||||
|
||||
if (!status)
|
||||
return NULL;
|
||||
|
||||
/* cmdargs must be a list or tuple of strings */
|
||||
if (PyList_Check(cmdargs))
|
||||
{
|
||||
argc = PyList_Size(cmdargs);
|
||||
getitem = PyList_GetItem;
|
||||
}
|
||||
else if (PyTuple_Check(cmdargs))
|
||||
{
|
||||
argc = PyTuple_Size(cmdargs);
|
||||
getitem = PyTuple_GetItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
argc = -1;
|
||||
}
|
||||
|
||||
if (argc <= 0)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"cmd arg must be a nonempty tuple or list");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
argv = PyMem_New(char *, argc + 1);
|
||||
if (argv == NULL)
|
||||
return PyErr_NoMemory();
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
{
|
||||
if (!PyArg_Parse((*getitem)(cmdargs, i), "et",
|
||||
Py_FileSystemDefaultEncoding, &argv[i]))
|
||||
{
|
||||
free_string_array(argv, i);
|
||||
PyErr_SetString(PyExc_TypeError, "cmd arg must contain only strings");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
argv[argc] = NULL;
|
||||
|
||||
cmdwait = (VCmdWait *)VCmdWait_new(&vcmd_VCmdWaitType, NULL, NULL);
|
||||
if (cmdwait == NULL)
|
||||
{
|
||||
free_string_array(argv, i);
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&cmdwait->_mutex);
|
||||
cmdwait->_cmdid = -1;
|
||||
|
||||
cmdio = vnode_open_clientcmdio(iotype);
|
||||
if (cmdio)
|
||||
{
|
||||
int err = 0;
|
||||
vcmd_cmdreq_t cmdreq = {
|
||||
.client = self->_client,
|
||||
.clientcmdio = cmdio,
|
||||
.data = cmdwait,
|
||||
.argc = argc,
|
||||
.argv = argv,
|
||||
};
|
||||
|
||||
#define PYFILE(obj, fd, name, mode) \
|
||||
do { \
|
||||
FILE *tmp; \
|
||||
obj = NULL; \
|
||||
tmp = fdopen(fd, mode); \
|
||||
if (!tmp) \
|
||||
{ \
|
||||
WARN("fdopen() failed for fd %d", fd); \
|
||||
break; \
|
||||
} \
|
||||
obj = PyFile_FromFile(tmp, name, mode, fclose); \
|
||||
if (!obj) \
|
||||
fclose(tmp); \
|
||||
} while(0)
|
||||
|
||||
switch (iotype)
|
||||
{
|
||||
case VCMD_IO_NONE:
|
||||
break;
|
||||
|
||||
case VCMD_IO_FD:
|
||||
SET_STDIOFD(cmdio, infd, outfd, errfd);
|
||||
break;
|
||||
|
||||
case VCMD_IO_PIPE:
|
||||
PYFILE(pyinfile, cmdio->stdiopipe.infd[1], "<pipe>", "wb");
|
||||
if (!pyinfile)
|
||||
{
|
||||
err = 1;
|
||||
break;
|
||||
}
|
||||
PYFILE(pyoutfile, cmdio->stdiopipe.outfd[0], "<pipe>", "rb");
|
||||
if (!pyoutfile)
|
||||
{
|
||||
PyObject_Del(pyinfile);
|
||||
err = 1;
|
||||
break;
|
||||
}
|
||||
PYFILE(pyerrfile, cmdio->stdiopipe.errfd[0], "<pipe>", "rb");
|
||||
if (!pyerrfile)
|
||||
{
|
||||
PyObject_Del(pyoutfile);
|
||||
PyObject_Del(pyinfile);
|
||||
err = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case VCMD_IO_PTY:
|
||||
PYFILE(pyptyfile, cmdio->stdiopty.masterfd, "/dev/ptmx", "r+b");
|
||||
if (!pyptyfile)
|
||||
err = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (verbose)
|
||||
WARNX("invalid iotype: 0x%x", iotype);
|
||||
errno = EINVAL;
|
||||
err = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
#undef PYFILE
|
||||
|
||||
if (!err)
|
||||
{
|
||||
call_asyncfunc(async_cmdreq, &cmdreq);
|
||||
cmdwait->_cmdid = cmdreq.cmdid;
|
||||
}
|
||||
}
|
||||
|
||||
free_string_array(argv, argc);
|
||||
free(cmdio);
|
||||
|
||||
if (cmdwait->_cmdid < 0)
|
||||
{
|
||||
if (pyinfile)
|
||||
PyObject_Del(pyinfile);
|
||||
if (pyoutfile)
|
||||
PyObject_Del(pyoutfile);
|
||||
if (pyerrfile)
|
||||
PyObject_Del(pyerrfile);
|
||||
if (pyptyfile)
|
||||
PyObject_Del(pyptyfile);
|
||||
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
pthread_mutex_unlock(&cmdwait->_mutex);
|
||||
Py_DECREF(cmdwait);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* don't do Py_DECREF(cmdwait) or VCmdWait_dealloc(cmdwait) if
|
||||
* there's an error below since cmddonecb should still get called
|
||||
*/
|
||||
|
||||
Py_INCREF(self);
|
||||
cmdwait->_vcmd = self;
|
||||
|
||||
switch (iotype)
|
||||
{
|
||||
case VCMD_IO_NONE:
|
||||
case VCMD_IO_FD:
|
||||
ret = Py_BuildValue("O", (PyObject *)cmdwait);
|
||||
break;
|
||||
|
||||
case VCMD_IO_PIPE:
|
||||
ret = Py_BuildValue("(OOOO)", (PyObject *)cmdwait,
|
||||
pyinfile, pyoutfile, pyerrfile);
|
||||
break;
|
||||
|
||||
case VCMD_IO_PTY:
|
||||
ret = Py_BuildValue("(OO)", (PyObject *)cmdwait, pyptyfile);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&cmdwait->_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyObject *VCmd_qcmd(VCmd *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
return _VCmd_cmd(self, args, kwds, VCMD_IO_NONE);
|
||||
}
|
||||
|
||||
static PyObject *VCmd_redircmd(VCmd *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
return _VCmd_cmd(self, args, kwds, VCMD_IO_FD);
|
||||
}
|
||||
|
||||
static PyObject *VCmd_popen(VCmd *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
return _VCmd_cmd(self, args, kwds, VCMD_IO_PIPE);
|
||||
}
|
||||
|
||||
static PyObject *VCmd_ptyopen(VCmd *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
return _VCmd_cmd(self, args, kwds, VCMD_IO_PTY);
|
||||
}
|
||||
|
||||
static PyObject *VCmd_kill(VCmd *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
VCmdWait *cmdwait;
|
||||
int sig;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O!i", &vcmd_VCmdWaitType, &cmdwait, &sig))
|
||||
return NULL;
|
||||
|
||||
if (cmdwait->_complete)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "command already complete");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (vnode_send_cmdsignal(self->_client->serverfd, cmdwait->_cmdid, sig))
|
||||
{
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *VCmd_close(VCmd *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
if (self->_client)
|
||||
{
|
||||
vcmd_delclientreq_t delclreq = {.vcmd = self};
|
||||
|
||||
call_asyncfunc(async_delclientreq, &delclreq);
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyMemberDef VCmd_members[] = {
|
||||
{NULL, 0, 0, 0, NULL},
|
||||
};
|
||||
|
||||
static PyMethodDef VCmd_methods[] = {
|
||||
{"connected", (PyCFunction)VCmd_connected, METH_NOARGS,
|
||||
"connected() -> boolean\n\n"
|
||||
"returns True if connected; False otherwise"},
|
||||
|
||||
{"popen", (PyCFunction)VCmd_popen, METH_VARARGS | METH_KEYWORDS,
|
||||
"popen(args...) -> (VCmdWait, cmdin, cmdout, cmderr)\n\n"
|
||||
"Send command request and use pipe I/O.\n\n"
|
||||
"args: executable file name followed by command arguments"},
|
||||
|
||||
{"ptyopen", (PyCFunction)VCmd_ptyopen, METH_VARARGS| METH_KEYWORDS,
|
||||
"ptyopen(args...) -> (VCmdWait, cmdpty)\n\n"
|
||||
"Send command request and use pty I/O.\n\n"
|
||||
"args: executable file name followed by command arguments"},
|
||||
|
||||
{"qcmd", (PyCFunction)VCmd_qcmd, METH_VARARGS | METH_KEYWORDS,
|
||||
"qcmd(args...) -> VCmdWait\n\n"
|
||||
"Send command request without I/O.\n\n"
|
||||
"args: executable file name followed by command arguments"},
|
||||
|
||||
{"redircmd", (PyCFunction)VCmd_redircmd, METH_VARARGS | METH_KEYWORDS,
|
||||
"redircmd(infd, outfd, errfd, args...) -> VCmdWait\n\n"
|
||||
"Send command request with I/O redirected from/to the given fds.\n\n"
|
||||
"infd: file descriptor for command standard input\n"
|
||||
"outfd: file descriptor for command standard output\n"
|
||||
"errfd: file descriptor for command standard error\n"
|
||||
"args: executable file name followed by command arguments"},
|
||||
|
||||
{"kill", (PyCFunction)VCmd_kill, METH_VARARGS,
|
||||
"kill(cmdwait, signum) -> None\n\n"
|
||||
"Send signal to a command.\n\n"
|
||||
"cmdwait: the VCmdWait object from an earlier command request\n"
|
||||
"signum: the signal to send"},
|
||||
|
||||
{"close", (PyCFunction)VCmd_close, METH_NOARGS,
|
||||
"close() -> None"},
|
||||
|
||||
{NULL, NULL, 0, NULL},
|
||||
};
|
||||
|
||||
static PyTypeObject vcmd_VCmdType = {
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
.tp_name = "vcmd.VCmd",
|
||||
.tp_basicsize = sizeof(VCmd),
|
||||
.tp_dealloc = (destructor)VCmd_dealloc,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
||||
.tp_doc = "VCmd objects",
|
||||
.tp_methods = VCmd_methods,
|
||||
.tp_members = VCmd_members,
|
||||
.tp_init = (initproc)VCmd_init,
|
||||
.tp_new = VCmd_new,
|
||||
};
|
||||
|
||||
static PyObject *vcmd_verbose(PyObject *self, PyObject *args)
|
||||
{
|
||||
int oldval = verbose;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|i", &verbose))
|
||||
return NULL;
|
||||
|
||||
return Py_BuildValue("i", oldval);
|
||||
}
|
||||
|
||||
static PyMethodDef vcmd_methods[] = {
|
||||
{"verbose", (PyCFunction)vcmd_verbose, METH_VARARGS,
|
||||
"verbose([newval]) -> int\n\n"
|
||||
"Get the current verbose level and optionally set it to newval."},
|
||||
|
||||
{NULL, NULL, 0, NULL},
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC initvcmd(void)
|
||||
{
|
||||
PyObject *m;
|
||||
|
||||
if (PyType_Ready(&vcmd_VCmdType) < 0)
|
||||
return;
|
||||
|
||||
if (PyType_Ready(&vcmd_VCmdWaitType) < 0)
|
||||
return;
|
||||
|
||||
m = Py_InitModule3("vcmd", vcmd_methods, "vcmd module that does stuff...");
|
||||
if (!m)
|
||||
return;
|
||||
|
||||
Py_INCREF(&vcmd_VCmdType);
|
||||
PyModule_AddObject(m, "VCmd", (PyObject *)&vcmd_VCmdType);
|
||||
|
||||
Py_INCREF(&vcmd_VCmdWaitType);
|
||||
PyModule_AddObject(m, "VCmdWait", (PyObject *)&vcmd_VCmdWaitType);
|
||||
|
||||
return;
|
||||
}
|
16
netns/version.h.in
Normal file
16
netns/version.h.in
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||
*
|
||||
* version.h
|
||||
*
|
||||
*/
|
||||
#ifndef _VERSION_H_
|
||||
#define _VERSION_H_
|
||||
|
||||
#define CORE_VERSION "@CORE_VERSION@"
|
||||
|
||||
#endif /* _VERSION_H_ */
|
117
netns/vnode_chnl.c
Normal file
117
netns/vnode_chnl.c
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnode_chnl.c
|
||||
*
|
||||
* Functions for setting up a local UNIX socket to use as a control channel
|
||||
* for interacting with a network namespace.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "vnode_msg.h"
|
||||
#include "vnode_tlv.h"
|
||||
#include "vnode_chnl.h"
|
||||
#include "vnode_io.h"
|
||||
|
||||
extern int verbose;
|
||||
|
||||
|
||||
int vnode_connect(const char *name)
|
||||
{
|
||||
int fd;
|
||||
struct sockaddr_un addr;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("opening '%s'", name);
|
||||
#endif
|
||||
|
||||
if (strlen(name) > sizeof(addr.sun_path) - 1)
|
||||
{
|
||||
WARNX("name too long: '%s'", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0)
|
||||
{
|
||||
WARN("socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, name);
|
||||
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
WARN("connect() failed for '%s'", name);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_cloexec(fd))
|
||||
WARN("set_cloexec() failed for fd %d", fd);
|
||||
|
||||
if (set_nonblock(fd))
|
||||
WARN("set_nonblock() failed for fd %d", fd);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int vnode_listen(const char *name)
|
||||
{
|
||||
int fd;
|
||||
struct sockaddr_un addr;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("opening '%s'", name);
|
||||
#endif
|
||||
|
||||
if (strlen(name) > sizeof(addr.sun_path) - 1)
|
||||
{
|
||||
WARNX("name too long: '%s'", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0)
|
||||
{
|
||||
WARN("socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unlink(name);
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, name);
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
WARN("bind() failed for '%s'", name);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* to override umask */
|
||||
if (chmod(name, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH))
|
||||
WARN("fchmod() failed for '%s'", name);
|
||||
|
||||
if (listen(fd, 5) < 0)
|
||||
{
|
||||
WARN("listen() failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_nonblock(fd))
|
||||
WARN("set_nonblock() failed for fd %d", fd);
|
||||
|
||||
return fd;
|
||||
}
|
18
netns/vnode_chnl.h
Normal file
18
netns/vnode_chnl.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnode_chnl.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _VNODE_CHNL_H_
|
||||
#define _VNODE_CHNL_H_
|
||||
|
||||
int vnode_connect(const char *name);
|
||||
int vnode_listen(const char *name);
|
||||
|
||||
#endif /* _VNODE_CHNL_H_ */
|
509
netns/vnode_client.c
Normal file
509
netns/vnode_client.c
Normal file
|
@ -0,0 +1,509 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnode_client.c
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "vnode_chnl.h"
|
||||
#include "vnode_client.h"
|
||||
#include "vnode_tlv.h"
|
||||
#include "vnode_io.h"
|
||||
|
||||
|
||||
extern int verbose;
|
||||
|
||||
typedef struct {
|
||||
vnode_client_cmddonecb_t cmddonecb;
|
||||
void *data;
|
||||
} vnode_clientcmd_t;
|
||||
|
||||
vnode_client_cmdio_t *vnode_open_clientcmdio(vnode_client_cmdiotype_t iotype)
|
||||
{
|
||||
int err;
|
||||
vnode_client_cmdio_t *clientcmdio;
|
||||
|
||||
clientcmdio = malloc(sizeof(*clientcmdio));
|
||||
if (!clientcmdio)
|
||||
{
|
||||
WARN("malloc() failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
clientcmdio->iotype = iotype;
|
||||
|
||||
switch (clientcmdio->iotype)
|
||||
{
|
||||
case VCMD_IO_NONE:
|
||||
case VCMD_IO_FD:
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case VCMD_IO_PIPE:
|
||||
err = open_stdio_pipe(&clientcmdio->stdiopipe);
|
||||
break;
|
||||
|
||||
case VCMD_IO_PTY:
|
||||
err = open_stdio_pty(&clientcmdio->stdiopty);
|
||||
break;
|
||||
|
||||
default:
|
||||
WARNX("unknown i/o type: %u", clientcmdio->iotype);
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
free(clientcmdio);
|
||||
clientcmdio = NULL;
|
||||
}
|
||||
|
||||
return clientcmdio;
|
||||
}
|
||||
|
||||
void vnode_close_clientcmdio(vnode_client_cmdio_t *clientcmdio)
|
||||
{
|
||||
switch (clientcmdio->iotype)
|
||||
{
|
||||
case VCMD_IO_NONE:
|
||||
case VCMD_IO_FD:
|
||||
break;
|
||||
|
||||
case VCMD_IO_PIPE:
|
||||
close_stdio_pipe(&clientcmdio->stdiopipe);
|
||||
break;
|
||||
|
||||
case VCMD_IO_PTY:
|
||||
close_stdio_pty(&clientcmdio->stdiopty);
|
||||
break;
|
||||
|
||||
default:
|
||||
WARNX("unknown i/o type: %u", clientcmdio->iotype);
|
||||
break;
|
||||
}
|
||||
|
||||
memset(clientcmdio, 0, sizeof(*clientcmdio));
|
||||
free(clientcmdio);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void vnode_client_cmddone(vnode_cmdentry_t *cmd)
|
||||
{
|
||||
vnode_clientcmd_t *clientcmd = cmd->data;;
|
||||
|
||||
if (clientcmd->cmddonecb)
|
||||
clientcmd->cmddonecb(cmd->cmdid, cmd->pid, cmd->status, clientcmd->data);
|
||||
|
||||
memset(clientcmd, 0, sizeof(*clientcmd));
|
||||
free(clientcmd);
|
||||
|
||||
memset(cmd, 0, sizeof(*cmd));
|
||||
free(cmd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int tlv_cmdreqack_cmdid(vnode_tlv_t *tlv, void *data)
|
||||
{
|
||||
vnode_cmdreqack_t *cmdreqack = data;
|
||||
int tmp;
|
||||
|
||||
assert(tlv->type == VNODE_TLV_CMDID);
|
||||
|
||||
tmp = tlv_int32(&cmdreqack->cmdid, tlv);
|
||||
|
||||
if (tmp == 0 && verbose)
|
||||
INFO("VNODE_TLV_CMDID: %d", cmdreqack->cmdid);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static int tlv_cmdreqack_cmdpid(vnode_tlv_t *tlv, void *data)
|
||||
{
|
||||
vnode_cmdreqack_t *cmdreqack = data;
|
||||
int tmp;
|
||||
|
||||
assert(tlv->type == VNODE_TLV_CMDPID);
|
||||
|
||||
tmp = tlv_int32(&cmdreqack->pid, tlv);
|
||||
|
||||
if (tmp == 0 && verbose)
|
||||
INFO("VNODE_TLV_CMDPID: %d", cmdreqack->pid);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void vnode_clientrecv_cmdreqack(vnode_msgio_t *msgio)
|
||||
{
|
||||
vnode_cmdentry_t *cmd;
|
||||
vnode_client_t *client = msgio->data;
|
||||
vnode_cmdreqack_t cmdreqack = CMDREQACK_INIT;
|
||||
static const vnode_tlvhandler_t tlvhandler[VNODE_TLV_MAX] = {
|
||||
[VNODE_TLV_CMDID] = tlv_cmdreqack_cmdid,
|
||||
[VNODE_TLV_CMDPID] = tlv_cmdreqack_cmdpid,
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("command request ack");
|
||||
#endif
|
||||
|
||||
assert(msgio->msgbuf.msg->hdr.type == VNODE_MSG_CMDREQACK);
|
||||
|
||||
if (vnode_parsemsg(msgio->msgbuf.msg, &cmdreqack, tlvhandler))
|
||||
return;
|
||||
|
||||
TAILQ_FOREACH(cmd, &client->cmdlisthead, entries)
|
||||
if (cmd->cmdid == cmdreqack.cmdid)
|
||||
break;
|
||||
|
||||
if (cmd == NULL)
|
||||
{
|
||||
WARNX("cmdid %d not found in command list", cmdreqack.cmdid);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("cmdid %d found in cmd list", cmdreqack.cmdid);
|
||||
#endif
|
||||
|
||||
cmd->pid = cmdreqack.pid;
|
||||
|
||||
if (cmdreqack.pid == -1)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
WARNX("XXX pid == -1 removing cmd from list");
|
||||
#endif
|
||||
TAILQ_REMOVE(&client->cmdlisthead, cmd, entries);
|
||||
|
||||
cmd->status = -1;
|
||||
vnode_client_cmddone(cmd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int tlv_cmdstatus_cmdid(vnode_tlv_t *tlv, void *data)
|
||||
{
|
||||
vnode_cmdstatus_t *cmdstatus = data;
|
||||
int tmp;
|
||||
|
||||
assert(tlv->type == VNODE_TLV_CMDID);
|
||||
|
||||
tmp = tlv_int32(&cmdstatus->cmdid, tlv);
|
||||
|
||||
if (tmp == 0 && verbose)
|
||||
INFO("VNODE_TLV_CMDID: %d", cmdstatus->cmdid);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static int tlv_cmdstatus_status(vnode_tlv_t *tlv, void *data)
|
||||
{
|
||||
vnode_cmdstatus_t *cmdstatus = data;
|
||||
int tmp;
|
||||
|
||||
assert(tlv->type == VNODE_TLV_CMDSTATUS);
|
||||
|
||||
tmp = tlv_int32(&cmdstatus->status, tlv);
|
||||
|
||||
if (tmp == 0 && verbose)
|
||||
INFO("VNODE_TLV_CMDSTATUS: %d", cmdstatus->status);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void vnode_clientrecv_cmdstatus(vnode_msgio_t *msgio)
|
||||
{
|
||||
vnode_cmdentry_t *cmd;
|
||||
vnode_client_t *client = msgio->data;
|
||||
vnode_cmdstatus_t cmdstatus = CMDSTATUS_INIT;
|
||||
static const vnode_tlvhandler_t tlvhandler[VNODE_TLV_MAX] = {
|
||||
[VNODE_TLV_CMDID] = tlv_cmdstatus_cmdid,
|
||||
[VNODE_TLV_CMDSTATUS] = tlv_cmdstatus_status,
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("command status");
|
||||
#endif
|
||||
|
||||
assert(msgio->msgbuf.msg->hdr.type == VNODE_MSG_CMDSTATUS);
|
||||
|
||||
if (vnode_parsemsg(msgio->msgbuf.msg, &cmdstatus, tlvhandler))
|
||||
return;
|
||||
|
||||
TAILQ_FOREACH(cmd, &client->cmdlisthead, entries)
|
||||
if (cmd->cmdid == cmdstatus.cmdid)
|
||||
break;
|
||||
|
||||
if (cmd == NULL)
|
||||
{
|
||||
WARNX("cmdid %d not found in command list", cmdstatus.cmdid);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("cmdid %d found in cmd list; removing", cmdstatus.cmdid);
|
||||
#endif
|
||||
TAILQ_REMOVE(&client->cmdlisthead, cmd, entries);
|
||||
|
||||
cmd->status = cmdstatus.status;
|
||||
vnode_client_cmddone(cmd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void server_ioerror(vnode_msgio_t *msgio)
|
||||
{
|
||||
vnode_client_t *client = msgio->data;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("i/o error on fd %d; client: %p", msgio->fd, client);
|
||||
#endif
|
||||
|
||||
if (client)
|
||||
{
|
||||
assert(msgio == &client->msgio);
|
||||
if (client->ioerrorcb)
|
||||
client->ioerrorcb(client);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
vnode_client_t *vnode_client(struct ev_loop *loop, const char *ctrlchnlname,
|
||||
vnode_clientcb_t ioerrorcb, void *data)
|
||||
{
|
||||
int fd = -1;
|
||||
vnode_client_t *client;
|
||||
static const vnode_msghandler_t msghandler[VNODE_MSG_MAX] = {
|
||||
[VNODE_MSG_CMDREQACK] = vnode_clientrecv_cmdreqack,
|
||||
[VNODE_MSG_CMDSTATUS] = vnode_clientrecv_cmdstatus,
|
||||
};
|
||||
|
||||
if (!ioerrorcb)
|
||||
{
|
||||
WARNX("no i/o error callback given");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fd = vnode_connect(ctrlchnlname);
|
||||
if (fd < 0)
|
||||
{
|
||||
WARN("vnode_connect() failed for '%s'", ctrlchnlname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((client = calloc(1, sizeof(*client))) == NULL)
|
||||
{
|
||||
WARN("calloc() failed");
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TAILQ_INIT(&client->cmdlisthead);
|
||||
client->loop = loop;
|
||||
client->serverfd = fd;
|
||||
client->ioerrorcb = ioerrorcb;
|
||||
client->data = data;
|
||||
|
||||
if (vnode_msgiostart(&client->msgio, client->loop,
|
||||
client->serverfd, client, server_ioerror, msghandler))
|
||||
{
|
||||
WARNX("vnode_msgiostart() failed");
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("new client connected to %s: %p", ctrlchnlname, client);
|
||||
#endif
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void vnode_delclient(vnode_client_t *client)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
WARNX("deleting client: %p", client);
|
||||
#endif
|
||||
|
||||
vnode_msgiostop(&client->msgio);
|
||||
if (client->serverfd >= 0)
|
||||
{
|
||||
close(client->serverfd);
|
||||
client->serverfd = -1;
|
||||
}
|
||||
|
||||
while (!TAILQ_EMPTY(&client->cmdlisthead))
|
||||
{
|
||||
vnode_cmdentry_t *cmd;
|
||||
|
||||
cmd = TAILQ_FIRST(&client->cmdlisthead);
|
||||
TAILQ_REMOVE(&client->cmdlisthead, cmd, entries);
|
||||
|
||||
cmd->status = -1;
|
||||
vnode_client_cmddone(cmd);
|
||||
}
|
||||
|
||||
/* XXX more stuff ?? */
|
||||
|
||||
memset(client, 0, sizeof(*client));
|
||||
free(client);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int vnode_setcmdio(int *cmdin, int *cmdout, int *cmderr,
|
||||
vnode_client_cmdio_t *clientcmdio)
|
||||
{
|
||||
switch (clientcmdio->iotype)
|
||||
{
|
||||
case VCMD_IO_NONE:
|
||||
*cmdin = -1;
|
||||
*cmdout = -1;
|
||||
*cmderr = -1;
|
||||
break;
|
||||
|
||||
case VCMD_IO_FD:
|
||||
*cmdin = clientcmdio->stdiofd.infd;
|
||||
*cmdout = clientcmdio->stdiofd.outfd;
|
||||
*cmderr = clientcmdio->stdiofd.errfd;
|
||||
break;
|
||||
|
||||
case VCMD_IO_PIPE:
|
||||
*cmdin = clientcmdio->stdiopipe.infd[0];
|
||||
*cmdout = clientcmdio->stdiopipe.outfd[1];
|
||||
*cmderr = clientcmdio->stdiopipe.errfd[1];
|
||||
break;
|
||||
|
||||
case VCMD_IO_PTY:
|
||||
*cmdin = clientcmdio->stdiopty.slavefd;
|
||||
*cmdout = clientcmdio->stdiopty.slavefd;
|
||||
*cmderr = clientcmdio->stdiopty.slavefd;
|
||||
break;
|
||||
|
||||
default:
|
||||
WARNX("unknown i/o type: %u", clientcmdio->iotype);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vnode_cleanupcmdio(vnode_client_cmdio_t *clientcmdio)
|
||||
{
|
||||
#define CLOSE(var) \
|
||||
do { \
|
||||
if (var >= 0) \
|
||||
close(var); \
|
||||
var = -1; \
|
||||
} while (0)
|
||||
|
||||
switch (clientcmdio->iotype)
|
||||
{
|
||||
case VCMD_IO_NONE:
|
||||
case VCMD_IO_FD:
|
||||
break;
|
||||
|
||||
case VCMD_IO_PIPE:
|
||||
CLOSE(clientcmdio->stdiopipe.infd[0]);
|
||||
CLOSE(clientcmdio->stdiopipe.outfd[1]);
|
||||
CLOSE(clientcmdio->stdiopipe.errfd[1]);
|
||||
break;
|
||||
|
||||
case VCMD_IO_PTY:
|
||||
CLOSE(clientcmdio->stdiopty.slavefd);
|
||||
break;
|
||||
|
||||
default:
|
||||
WARNX("unknown i/o type: %u", clientcmdio->iotype);
|
||||
break;
|
||||
}
|
||||
|
||||
#undef CLOSE
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int vnode_client_cmdreq(vnode_client_t *client,
|
||||
vnode_client_cmdio_t *clientcmdio,
|
||||
vnode_client_cmddonecb_t cmddonecb, void *data,
|
||||
int argc, char *argv[])
|
||||
{
|
||||
int cmdin, cmdout, cmderr;
|
||||
vnode_clientcmd_t *clientcmd;
|
||||
vnode_cmdentry_t *cmd;
|
||||
|
||||
if (argc >= VNODE_ARGMAX)
|
||||
{
|
||||
WARNX("too many command arguments");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argv[argc] != NULL)
|
||||
{
|
||||
WARNX("command arguments not null-terminated");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vnode_setcmdio(&cmdin, &cmdout, &cmderr, clientcmdio))
|
||||
{
|
||||
WARNX("vnode_setcmdio() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((clientcmd = malloc(sizeof(*clientcmd))) == NULL)
|
||||
{
|
||||
WARN("malloc() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
clientcmd->cmddonecb = cmddonecb;
|
||||
clientcmd->data = data;
|
||||
|
||||
if ((cmd = malloc(sizeof(*cmd))) == NULL)
|
||||
{
|
||||
WARN("malloc() failed");
|
||||
free(clientcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (client->cmdid < 0)
|
||||
client->cmdid = 0;
|
||||
cmd->cmdid = client->cmdid++;
|
||||
cmd->pid = -1;
|
||||
cmd->status = -1;
|
||||
cmd->data = clientcmd;
|
||||
|
||||
TAILQ_INSERT_TAIL(&client->cmdlisthead, cmd, entries);
|
||||
|
||||
if (vnode_send_cmdreq(client->serverfd, cmd->cmdid,
|
||||
argv, cmdin, cmdout, cmderr))
|
||||
{
|
||||
WARN("vnode_send_cmdreq() failed");
|
||||
TAILQ_REMOVE(&client->cmdlisthead, cmd, entries);
|
||||
free(clientcmd);
|
||||
free(cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
vnode_cleanupcmdio(clientcmdio);
|
||||
|
||||
return cmd->cmdid;
|
||||
}
|
77
netns/vnode_client.h
Normal file
77
netns/vnode_client.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnode_client.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _VNODE_CLIENT_H_
|
||||
#define _VNODE_CLIENT_H_
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "vnode_msg.h"
|
||||
#include "vnode_cmd.h"
|
||||
#include "vnode_io.h"
|
||||
|
||||
struct vnode_client;
|
||||
typedef void (*vnode_clientcb_t)(struct vnode_client *client);
|
||||
|
||||
typedef struct vnode_client {
|
||||
TAILQ_HEAD(cmdlist, cmdentry) cmdlisthead;
|
||||
|
||||
struct ev_loop *loop;
|
||||
int serverfd;
|
||||
struct vnode_msgio msgio;
|
||||
void *data;
|
||||
|
||||
vnode_clientcb_t ioerrorcb;
|
||||
|
||||
int32_t cmdid;
|
||||
} vnode_client_t;
|
||||
|
||||
typedef void (*vnode_client_cmddonecb_t)(int32_t cmdid, pid_t pid,
|
||||
int status, void *data);
|
||||
|
||||
typedef enum {
|
||||
VCMD_IO_NONE = 0,
|
||||
VCMD_IO_FD,
|
||||
VCMD_IO_PIPE,
|
||||
VCMD_IO_PTY,
|
||||
} vnode_client_cmdiotype_t;
|
||||
|
||||
typedef struct {
|
||||
vnode_client_cmdiotype_t iotype;
|
||||
union {
|
||||
stdio_fd_t stdiofd;
|
||||
stdio_pipe_t stdiopipe;
|
||||
stdio_pty_t stdiopty;
|
||||
};
|
||||
} vnode_client_cmdio_t;
|
||||
|
||||
#define SET_STDIOFD(clcmdio, ifd, ofd, efd) \
|
||||
do { \
|
||||
(clcmdio)->iotype = VCMD_IO_FD; \
|
||||
(clcmdio)->stdiofd.infd = (ifd); \
|
||||
(clcmdio)->stdiofd.outfd = (ofd); \
|
||||
(clcmdio)->stdiofd.errfd = (efd); \
|
||||
} while (0)
|
||||
|
||||
vnode_client_t *vnode_client(struct ev_loop *loop, const char *ctrlchnlname,
|
||||
vnode_clientcb_t ioerrorcb, void *data);
|
||||
void vnode_delclient(vnode_client_t *client);
|
||||
|
||||
vnode_client_cmdio_t *vnode_open_clientcmdio(vnode_client_cmdiotype_t iotype);
|
||||
void vnode_close_clientcmdio(vnode_client_cmdio_t *clientcmdio);
|
||||
|
||||
int vnode_client_cmdreq(vnode_client_t *client,
|
||||
vnode_client_cmdio_t *clientcmdio,
|
||||
vnode_client_cmddonecb_t cmddonecb, void *data,
|
||||
int argc, char *argv[]);
|
||||
|
||||
#endif /* _VNODE_CLIENT_H_ */
|
483
netns/vnode_cmd.c
Normal file
483
netns/vnode_cmd.c
Normal file
|
@ -0,0 +1,483 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnode_cmd.c
|
||||
*
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "myerr.h"
|
||||
#include "vnode_msg.h"
|
||||
#include "vnode_server.h"
|
||||
#include "vnode_tlv.h"
|
||||
#include "vnode_io.h"
|
||||
|
||||
#include "vnode_cmd.h"
|
||||
|
||||
extern int verbose;
|
||||
|
||||
static void vnode_process_cmdreq(vnode_cliententry_t *client,
|
||||
vnode_cmdreq_t *cmdreq);
|
||||
|
||||
static int tlv_cmdreq_cmdid(vnode_tlv_t *tlv, void *data)
|
||||
{
|
||||
vnode_cmdreq_t *cmdreq = data;
|
||||
int tmp;
|
||||
|
||||
assert(tlv->type == VNODE_TLV_CMDID);
|
||||
|
||||
tmp = tlv_int32(&cmdreq->cmdid, tlv);
|
||||
|
||||
if (tmp == 0 && verbose)
|
||||
INFO("VNODE_TLV_CMDID: %u", cmdreq->cmdid);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static int tlv_cmdreq_cmdarg(vnode_tlv_t *tlv, void *data)
|
||||
{
|
||||
vnode_cmdreq_t *cmdreq = data;
|
||||
int i, tmp;
|
||||
|
||||
assert(tlv->type == VNODE_TLV_CMDARG);
|
||||
|
||||
#define CMDARGMAX (sizeof(cmdreq->cmdarg) / sizeof(cmdreq->cmdarg[0]))
|
||||
|
||||
for (i = 0; i < CMDARGMAX; i++)
|
||||
if (cmdreq->cmdarg[i] == NULL)
|
||||
break;
|
||||
|
||||
if (i == CMDARGMAX)
|
||||
{
|
||||
WARNX("too many command arguments");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef CMDARGMAX
|
||||
|
||||
tmp = tlv_string(&cmdreq->cmdarg[i], tlv);
|
||||
|
||||
if (tmp == 0 && verbose)
|
||||
INFO("VNODE_TLV_CMDARG: '%s'", cmdreq->cmdarg[i]);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void vnode_recv_cmdreq(vnode_msgio_t *msgio)
|
||||
{
|
||||
vnode_cliententry_t *client = msgio->data;
|
||||
vnode_cmdreq_t cmdreq = CMDREQ_INIT;
|
||||
static vnode_tlvhandler_t cmdreq_tlvhandler[VNODE_TLV_MAX] = {
|
||||
[VNODE_TLV_CMDID] = tlv_cmdreq_cmdid,
|
||||
[VNODE_TLV_CMDARG] = tlv_cmdreq_cmdarg,
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("command request");
|
||||
#endif
|
||||
|
||||
assert(msgio->msgbuf.msg->hdr.type == VNODE_MSG_CMDREQ);
|
||||
|
||||
if (vnode_parsemsg(msgio->msgbuf.msg, &cmdreq, cmdreq_tlvhandler))
|
||||
return;
|
||||
|
||||
cmdreq.cmdio.infd = msgio->msgbuf.infd;
|
||||
cmdreq.cmdio.outfd = msgio->msgbuf.outfd;
|
||||
cmdreq.cmdio.errfd = msgio->msgbuf.errfd;
|
||||
|
||||
vnode_process_cmdreq(client, &cmdreq);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int vnode_send_cmdreq(int fd, int32_t cmdid, char *argv[],
|
||||
int infd, int outfd, int errfd)
|
||||
{
|
||||
size_t offset = 0;
|
||||
vnode_msgbuf_t msgbuf;
|
||||
char **cmdarg;
|
||||
int tmp;
|
||||
|
||||
if (vnode_initmsgbuf(&msgbuf))
|
||||
return -1;
|
||||
|
||||
#define ADDTLV(t, l, vp) \
|
||||
do { \
|
||||
ssize_t tlvlen; \
|
||||
tlvlen = vnode_addtlv(&msgbuf, offset, t, l, vp); \
|
||||
if (tlvlen < 0) \
|
||||
{ \
|
||||
WARNX("vnode_addtlv() failed"); \
|
||||
FREE_MSGBUF(&msgbuf); \
|
||||
return -1; \
|
||||
} \
|
||||
offset += tlvlen; \
|
||||
} while (0)
|
||||
|
||||
ADDTLV(VNODE_TLV_CMDID, sizeof(cmdid), &cmdid);
|
||||
|
||||
for (cmdarg = argv; *cmdarg; cmdarg++)
|
||||
ADDTLV(VNODE_TLV_CMDARG, strlen(*cmdarg) + 1, *cmdarg);
|
||||
|
||||
#undef ADDTLV
|
||||
|
||||
msgbuf.infd = infd;
|
||||
msgbuf.outfd = outfd;
|
||||
msgbuf.errfd = errfd;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("sending cmd req on fd %d: cmd '%s'", fd, argv[0]);
|
||||
#endif
|
||||
|
||||
msgbuf.msg->hdr.type = VNODE_MSG_CMDREQ;
|
||||
msgbuf.msg->hdr.datalen = offset;
|
||||
if (vnode_sendmsg(fd, &msgbuf) == vnode_msglen(&msgbuf))
|
||||
tmp = 0;
|
||||
else
|
||||
tmp = -1;
|
||||
|
||||
FREE_MSGBUF(&msgbuf);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
int vnode_send_cmdreqack(int fd, int32_t cmdid, int32_t pid)
|
||||
{
|
||||
ssize_t tmp = -1;
|
||||
size_t offset = 0;
|
||||
vnode_msgbuf_t msgbuf;
|
||||
|
||||
if (vnode_initmsgbuf(&msgbuf))
|
||||
return -1;
|
||||
|
||||
#define ADDTLV(t, l, vp) \
|
||||
do { \
|
||||
ssize_t tlvlen; \
|
||||
tlvlen = vnode_addtlv(&msgbuf, offset, t, l, vp); \
|
||||
if (tlvlen < 0) \
|
||||
{ \
|
||||
WARNX("vnode_addtlv() failed"); \
|
||||
FREE_MSGBUF(&msgbuf); \
|
||||
return -1; \
|
||||
} \
|
||||
offset += tlvlen; \
|
||||
} while (0)
|
||||
|
||||
ADDTLV(VNODE_TLV_CMDID, sizeof(cmdid), &cmdid);
|
||||
ADDTLV(VNODE_TLV_CMDPID, sizeof(pid), &pid);
|
||||
|
||||
#undef ADDTLV
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("sending cmd req ack on fd %d: cmdid %d; pid %d", fd, cmdid, pid);
|
||||
#endif
|
||||
|
||||
msgbuf.msg->hdr.type = VNODE_MSG_CMDREQACK;
|
||||
msgbuf.msg->hdr.datalen = offset;
|
||||
if (vnode_sendmsg(fd, &msgbuf) == vnode_msglen(&msgbuf))
|
||||
tmp = 0;
|
||||
|
||||
FREE_MSGBUF(&msgbuf);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
int vnode_send_cmdstatus(int fd, int32_t cmdid, int32_t status)
|
||||
{
|
||||
int tmp;
|
||||
size_t offset = 0;
|
||||
vnode_msgbuf_t msgbuf;
|
||||
|
||||
if (vnode_initmsgbuf(&msgbuf))
|
||||
return -1;
|
||||
|
||||
#define ADDTLV(t, l, vp) \
|
||||
do { \
|
||||
ssize_t tlvlen; \
|
||||
tlvlen = vnode_addtlv(&msgbuf, offset, t, l, vp); \
|
||||
if (tlvlen < 0) \
|
||||
{ \
|
||||
WARNX("vnode_addtlv() failed"); \
|
||||
FREE_MSGBUF(&msgbuf); \
|
||||
return -1; \
|
||||
} \
|
||||
offset += tlvlen; \
|
||||
} while (0)
|
||||
|
||||
ADDTLV(VNODE_TLV_CMDID, sizeof(cmdid), &cmdid);
|
||||
ADDTLV(VNODE_TLV_CMDSTATUS, sizeof(status), &status);
|
||||
|
||||
#undef ADDTLV
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("sending cmd status on fd %d: cmdid %d; status %d",
|
||||
fd, cmdid, status);
|
||||
#endif
|
||||
|
||||
msgbuf.msg->hdr.type = VNODE_MSG_CMDSTATUS;
|
||||
msgbuf.msg->hdr.datalen = offset;
|
||||
if (vnode_sendmsg(fd, &msgbuf) == vnode_msglen(&msgbuf))
|
||||
tmp = 0;
|
||||
else
|
||||
tmp = -1;
|
||||
|
||||
FREE_MSGBUF(&msgbuf);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
int vnode_send_cmdsignal(int fd, int32_t cmdid, int32_t signum)
|
||||
{
|
||||
ssize_t tmp;
|
||||
size_t offset = 0;
|
||||
vnode_msgbuf_t msgbuf;
|
||||
|
||||
if (vnode_initmsgbuf(&msgbuf))
|
||||
return -1;
|
||||
|
||||
#define ADDTLV(t, l, vp) \
|
||||
do { \
|
||||
ssize_t tlvlen; \
|
||||
tlvlen = vnode_addtlv(&msgbuf, offset, t, l, vp); \
|
||||
if (tlvlen < 0) \
|
||||
{ \
|
||||
WARNX("vnode_addtlv() failed"); \
|
||||
FREE_MSGBUF(&msgbuf); \
|
||||
return -1; \
|
||||
} \
|
||||
offset += tlvlen; \
|
||||
} while (0)
|
||||
|
||||
ADDTLV(VNODE_TLV_CMDID, sizeof(cmdid), &cmdid);
|
||||
ADDTLV(VNODE_TLV_SIGNUM, sizeof(signum), &signum);
|
||||
|
||||
#undef ADDTLV
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("sending cmd signal on fd %d: cmdid %d; signum %d",
|
||||
fd, cmdid, signum);
|
||||
#endif
|
||||
|
||||
msgbuf.msg->hdr.type = VNODE_MSG_CMDSIGNAL;
|
||||
msgbuf.msg->hdr.datalen = offset;
|
||||
if (vnode_sendmsg(fd, &msgbuf) == vnode_msglen(&msgbuf))
|
||||
tmp = 0;
|
||||
else
|
||||
tmp = -1;
|
||||
|
||||
FREE_MSGBUF(&msgbuf);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static int tlv_cmdsignal_cmdid(vnode_tlv_t *tlv, void *data)
|
||||
{
|
||||
vnode_cmdsignal_t *cmdsignal = data;
|
||||
int tmp;
|
||||
|
||||
assert(tlv->type == VNODE_TLV_CMDID);
|
||||
|
||||
tmp = tlv_int32(&cmdsignal->cmdid, tlv);
|
||||
|
||||
if (tmp == 0 && verbose)
|
||||
INFO("VNODE_TLV_CMDID: %d", cmdsignal->cmdid);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static int tlv_cmdsignal_signum(vnode_tlv_t *tlv, void *data)
|
||||
{
|
||||
vnode_cmdsignal_t *cmdsignal = data;
|
||||
int tmp;
|
||||
|
||||
assert(tlv->type == VNODE_TLV_SIGNUM);
|
||||
|
||||
tmp = tlv_int32(&cmdsignal->signum, tlv);
|
||||
|
||||
if (tmp == 0 && verbose)
|
||||
INFO("VNODE_TLV_SIGNUM: %d", cmdsignal->signum);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void vnode_recv_cmdsignal(vnode_msgio_t *msgio)
|
||||
{
|
||||
vnode_cliententry_t *client = msgio->data;
|
||||
vnode_cmdsignal_t cmdsignal = CMDSIGNAL_INIT;
|
||||
static vnode_tlvhandler_t cmdsignal_tlvhandler[VNODE_TLV_MAX] = {
|
||||
[VNODE_TLV_CMDID] = tlv_cmdsignal_cmdid,
|
||||
[VNODE_TLV_SIGNUM] = tlv_cmdsignal_signum,
|
||||
};
|
||||
vnode_cmdentry_t *cmd;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("command signal");
|
||||
#endif
|
||||
|
||||
assert(msgio->msgbuf.msg->hdr.type == VNODE_MSG_CMDSIGNAL);
|
||||
|
||||
if (vnode_parsemsg(msgio->msgbuf.msg, &cmdsignal, cmdsignal_tlvhandler))
|
||||
return;
|
||||
|
||||
|
||||
TAILQ_FOREACH(cmd, &client->server->cmdlisthead, entries)
|
||||
{
|
||||
if (cmd->cmdid == cmdsignal.cmdid && cmd->data == client)
|
||||
{
|
||||
if (verbose)
|
||||
INFO("sending pid %u signal %u", cmd->pid, cmdsignal.signum);
|
||||
|
||||
if (kill(cmd->pid, cmdsignal.signum))
|
||||
WARN("kill() failed");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd == NULL)
|
||||
WARNX("cmdid %d not found for client %p", cmdsignal.cmdid, client);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static pid_t forkexec(vnode_cmdreq_t *cmdreq)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
if (verbose)
|
||||
INFO("spawning '%s'", cmdreq->cmdarg[0]);
|
||||
|
||||
pid = fork();
|
||||
switch (pid)
|
||||
{
|
||||
case -1:
|
||||
WARN("fork() failed");
|
||||
break;
|
||||
|
||||
case 0:
|
||||
/* child */
|
||||
if (setsid() == -1)
|
||||
WARN("setsid() failed");
|
||||
|
||||
#define DUP2(oldfd, newfd) \
|
||||
do { \
|
||||
if (oldfd >= 0) \
|
||||
if (dup2(oldfd, newfd) < 0) \
|
||||
{ \
|
||||
WARN("dup2() failed for " #newfd \
|
||||
": oldfd: %d; newfd: %d", \
|
||||
oldfd, newfd); \
|
||||
_exit(1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
DUP2(cmdreq->cmdio.infd, STDIN_FILENO);
|
||||
DUP2(cmdreq->cmdio.outfd, STDOUT_FILENO);
|
||||
DUP2(cmdreq->cmdio.errfd, STDERR_FILENO);
|
||||
|
||||
#undef DUP2
|
||||
|
||||
#define CLOSE_IF_NOT(fd, notfd) \
|
||||
do { \
|
||||
if (fd >= 0 && fd != notfd) \
|
||||
close(fd); \
|
||||
} while (0)
|
||||
|
||||
CLOSE_IF_NOT(cmdreq->cmdio.infd, STDIN_FILENO);
|
||||
CLOSE_IF_NOT(cmdreq->cmdio.outfd, STDOUT_FILENO);
|
||||
CLOSE_IF_NOT(cmdreq->cmdio.errfd, STDERR_FILENO);
|
||||
|
||||
#undef CLOSE_IF_NOT
|
||||
|
||||
if (clear_nonblock(STDIN_FILENO))
|
||||
WARN("clear_nonblock() failed");
|
||||
if (clear_nonblock(STDOUT_FILENO))
|
||||
WARN("clear_nonblock() failed");
|
||||
if (clear_nonblock(STDERR_FILENO))
|
||||
WARN("clear_nonblock() failed");
|
||||
|
||||
/* try to get a controlling terminal (don't steal a terminal and
|
||||
ignore errors) */
|
||||
if (isatty(STDIN_FILENO))
|
||||
ioctl(STDIN_FILENO, TIOCSCTTY, 0);
|
||||
else if (isatty(STDOUT_FILENO))
|
||||
ioctl(STDOUT_FILENO, TIOCSCTTY, 0);
|
||||
|
||||
execvp(cmdreq->cmdarg[0], cmdreq->cmdarg);
|
||||
WARN("execvp() failed for '%s'", cmdreq->cmdarg[0]);
|
||||
_exit(1);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* parent */
|
||||
break;
|
||||
}
|
||||
|
||||
#define CLOSE(fd) \
|
||||
do { \
|
||||
if (fd >= 0) \
|
||||
close(fd); \
|
||||
} while (0)
|
||||
|
||||
CLOSE(cmdreq->cmdio.infd);
|
||||
CLOSE(cmdreq->cmdio.outfd);
|
||||
CLOSE(cmdreq->cmdio.errfd);
|
||||
|
||||
#undef CLOSE
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
static void vnode_process_cmdreq(vnode_cliententry_t *client,
|
||||
vnode_cmdreq_t *cmdreq)
|
||||
{
|
||||
vnode_cmdentry_t *cmd = NULL;
|
||||
|
||||
if ((cmd = malloc(sizeof(*cmd))) == NULL)
|
||||
{
|
||||
WARN("malloc() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
cmd->cmdid = cmdreq->cmdid;
|
||||
cmd->pid = -1;
|
||||
cmd->status = -1;
|
||||
cmd->data = client;
|
||||
cmd->pid = forkexec(cmdreq);
|
||||
|
||||
if (verbose)
|
||||
INFO("cmd: '%s'; pid: %d; cmdid: %d; "
|
||||
"infd: %d; outfd: %d; errfd: %d",
|
||||
cmdreq->cmdarg[0], cmd->pid, cmd->cmdid,
|
||||
cmdreq->cmdio.infd, cmdreq->cmdio.outfd, cmdreq->cmdio.errfd);
|
||||
|
||||
if (vnode_send_cmdreqack(client->clientfd, cmd->cmdid, cmd->pid))
|
||||
{
|
||||
WARNX("vnode_send_cmdreqack() failed");
|
||||
// XXX if (cmd->pid != -1) kill(cmd->pid, SIGKILL); ?
|
||||
free(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd->pid == -1)
|
||||
free(cmd);
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG
|
||||
WARNX("adding pid %d to cmd list", cmd->pid);
|
||||
#endif
|
||||
TAILQ_INSERT_TAIL(&client->server->cmdlisthead, cmd, entries);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
71
netns/vnode_cmd.h
Normal file
71
netns/vnode_cmd.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnode_cmd.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _VNODE_CMD_H_
|
||||
#define _VNODE_CMD_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include "vnode_msg.h"
|
||||
|
||||
typedef struct {
|
||||
int infd;
|
||||
int outfd;
|
||||
int errfd;
|
||||
} vnode_cmdio_t;
|
||||
|
||||
typedef struct {
|
||||
int32_t cmdid;
|
||||
vnode_cmdio_t cmdio;
|
||||
char *cmdarg[VNODE_ARGMAX];
|
||||
} vnode_cmdreq_t;
|
||||
|
||||
#define CMDREQ_INIT {}
|
||||
|
||||
typedef struct {
|
||||
int32_t cmdid;
|
||||
int32_t pid;
|
||||
} vnode_cmdreqack_t;
|
||||
|
||||
#define CMDREQACK_INIT {.cmdid = 0, .pid = -1}
|
||||
|
||||
typedef struct {
|
||||
int32_t cmdid;
|
||||
int32_t status;
|
||||
} vnode_cmdstatus_t;
|
||||
|
||||
#define CMDSTATUS_INIT {.cmdid = 0, .status = -1}
|
||||
|
||||
typedef struct {
|
||||
int32_t cmdid;
|
||||
int32_t signum;
|
||||
} vnode_cmdsignal_t;
|
||||
|
||||
#define CMDSIGNAL_INIT {.cmdid = 0, .signum = 0}
|
||||
|
||||
typedef struct cmdentry {
|
||||
TAILQ_ENTRY(cmdentry) entries;
|
||||
|
||||
int32_t cmdid;
|
||||
pid_t pid;
|
||||
int status;
|
||||
void *data;
|
||||
} vnode_cmdentry_t;
|
||||
|
||||
void vnode_recv_cmdreq(vnode_msgio_t *msgio);
|
||||
int vnode_send_cmdreq(int fd, int32_t cmdid, char *argv[],
|
||||
int infd, int outfd, int errfd);
|
||||
int vnode_send_cmdstatus(int fd, int32_t cmdid, int32_t status);
|
||||
int vnode_send_cmdsignal(int fd, int32_t cmdid, int32_t signum);
|
||||
void vnode_recv_cmdsignal(vnode_msgio_t *msgio);
|
||||
|
||||
#endif /* _VNODE_CMD_H_ */
|
185
netns/vnode_io.c
Normal file
185
netns/vnode_io.c
Normal file
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnode_io.c
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
/* #define _XOPEN_SOURCE */
|
||||
#ifndef __USE_XOPEN
|
||||
#define __USE_XOPEN
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "myerr.h"
|
||||
#include "vnode_chnl.h"
|
||||
#include "vnode_io.h"
|
||||
|
||||
|
||||
int set_nonblock(int fd)
|
||||
{
|
||||
int fl, r = 0;
|
||||
|
||||
if ((fl = fcntl(fd, F_GETFL)) == -1)
|
||||
{
|
||||
fl = 0;
|
||||
r = -1;
|
||||
}
|
||||
if (fcntl(fd, F_SETFL, fl | O_NONBLOCK))
|
||||
r = -1;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int clear_nonblock(int fd)
|
||||
{
|
||||
int fl, r = 0;
|
||||
|
||||
if ((fl = fcntl(fd, F_GETFL)) == -1)
|
||||
{
|
||||
fl = 0;
|
||||
r = -1;
|
||||
}
|
||||
if (fcntl(fd, F_SETFL, fl & ~O_NONBLOCK))
|
||||
r = -1;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int set_cloexec(int fd)
|
||||
{
|
||||
int fdflags;
|
||||
|
||||
if ((fdflags = fcntl(fd, F_GETFD)) == -1)
|
||||
fdflags = 0;
|
||||
if (fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int open_stdio_pty(stdio_pty_t *stdiopty)
|
||||
{
|
||||
int masterfd, slavefd;
|
||||
|
||||
INIT_STDIO_PTY(stdiopty);
|
||||
|
||||
if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) < 0)
|
||||
{
|
||||
WARN("posix_openpt() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (grantpt(masterfd))
|
||||
{
|
||||
WARN("grantpt() failed");
|
||||
close(masterfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (unlockpt(masterfd))
|
||||
{
|
||||
WARN("unlockpt() failed");
|
||||
close(masterfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((slavefd = open(ptsname(masterfd), O_RDWR | O_NOCTTY)) < 0)
|
||||
{
|
||||
WARN("open() failed");
|
||||
close(masterfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stdiopty->masterfd = masterfd;
|
||||
stdiopty->slavefd = slavefd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void close_stdio_pty(stdio_pty_t *stdiopty)
|
||||
{
|
||||
if (stdiopty->masterfd >= 0)
|
||||
close(stdiopty->masterfd);
|
||||
if (stdiopty->slavefd >= 0)
|
||||
close(stdiopty->slavefd);
|
||||
|
||||
INIT_STDIO_PTY(stdiopty);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int open_stdio_pipe(stdio_pipe_t *stdiopipe)
|
||||
{
|
||||
int infd[2], outfd[2], errfd[2];
|
||||
|
||||
INIT_STDIO_PIPE(stdiopipe);
|
||||
|
||||
if (pipe(infd) < 0)
|
||||
{
|
||||
WARN("pipe() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pipe(outfd) < 0)
|
||||
{
|
||||
WARN("pipe() failed");
|
||||
close(infd[0]);
|
||||
close(infd[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pipe(errfd) < 0)
|
||||
{
|
||||
WARN("pipe() failed");
|
||||
close(infd[0]);
|
||||
close(infd[1]);
|
||||
close(outfd[0]);
|
||||
close(outfd[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stdiopipe->infd[0] = infd[0];
|
||||
stdiopipe->infd[1] = infd[1];
|
||||
|
||||
stdiopipe->outfd[0] = outfd[0];
|
||||
stdiopipe->outfd[1] = outfd[1];
|
||||
|
||||
stdiopipe->errfd[0] = errfd[0];
|
||||
stdiopipe->errfd[1] = errfd[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void close_stdio_pipe(stdio_pipe_t *stdiopipe)
|
||||
{
|
||||
if (stdiopipe->infd[0] >= 0)
|
||||
close(stdiopipe->infd[0]);
|
||||
if (stdiopipe->infd[1] >= 0)
|
||||
close(stdiopipe->infd[1]);
|
||||
|
||||
if (stdiopipe->outfd[0] >= 0)
|
||||
close(stdiopipe->outfd[0]);
|
||||
if (stdiopipe->outfd[1] >= 0)
|
||||
close(stdiopipe->outfd[1]);
|
||||
|
||||
if (stdiopipe->errfd[0] >= 0)
|
||||
close(stdiopipe->errfd[0]);
|
||||
if (stdiopipe->errfd[1] >= 0)
|
||||
close(stdiopipe->errfd[1]);
|
||||
|
||||
INIT_STDIO_PIPE(stdiopipe);
|
||||
|
||||
return;
|
||||
}
|
63
netns/vnode_io.h
Normal file
63
netns/vnode_io.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnode_io.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _VNODE_IO_H_
|
||||
#define _VNODE_IO_H_
|
||||
|
||||
typedef struct {
|
||||
int infd;
|
||||
int outfd;
|
||||
int errfd;
|
||||
} stdio_fd_t;
|
||||
|
||||
#define INIT_STDIO_FD(s) \
|
||||
do { \
|
||||
(s)->infd = -1; \
|
||||
(s)->outfd = -1; \
|
||||
(s)->errfd = -1; \
|
||||
} while (0)
|
||||
|
||||
typedef struct {
|
||||
int masterfd;
|
||||
int slavefd;
|
||||
} stdio_pty_t;
|
||||
|
||||
#define INIT_STDIO_PTY(s) \
|
||||
do { \
|
||||
(s)->masterfd = -1; \
|
||||
(s)->slavefd = -1; \
|
||||
} while (0)
|
||||
|
||||
typedef struct {
|
||||
int infd[2];
|
||||
int outfd[2];
|
||||
int errfd[2];
|
||||
} stdio_pipe_t;
|
||||
|
||||
#define INIT_STDIO_PIPE(s) \
|
||||
do { \
|
||||
(s)->infd[0] = (s)->infd[1] = -1; \
|
||||
(s)->outfd[0] = (s)->outfd[1] = -1; \
|
||||
(s)->errfd[0] = (s)->errfd[1] = -1; \
|
||||
} while (0)
|
||||
|
||||
int set_nonblock(int fd);
|
||||
int clear_nonblock(int fd);
|
||||
|
||||
int set_cloexec(int fd);
|
||||
|
||||
int open_stdio_pty(stdio_pty_t *stdiopty);
|
||||
void close_stdio_pty(stdio_pty_t *stdiopty);
|
||||
|
||||
int open_stdio_pipe(stdio_pipe_t *stdiopipe);
|
||||
void close_stdio_pipe(stdio_pipe_t *stdiopipe);
|
||||
|
||||
#endif /* _VNODE_IO_H_ */
|
262
netns/vnode_msg.c
Normal file
262
netns/vnode_msg.c
Normal file
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnode_msg.c
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "myerr.h"
|
||||
|
||||
#include "vnode_msg.h"
|
||||
|
||||
|
||||
static void vnode_msg_cb(struct ev_loop *loop, ev_io *w, int revents)
|
||||
{
|
||||
vnode_msgio_t *msgio = w->data;
|
||||
ssize_t tmp;
|
||||
vnode_msghandler_t msghandlefn;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("new message on fd %d", msgio->fd);
|
||||
#endif
|
||||
|
||||
assert(msgio);
|
||||
|
||||
tmp = vnode_recvmsg(msgio);
|
||||
if (tmp == 0)
|
||||
return;
|
||||
else if (tmp < 0)
|
||||
{
|
||||
ev_io_stop(loop, w);
|
||||
if (msgio->ioerror)
|
||||
msgio->ioerror(msgio);
|
||||
return;
|
||||
}
|
||||
|
||||
msghandlefn = msgio->msghandler[msgio->msgbuf.msg->hdr.type];
|
||||
if (!msghandlefn)
|
||||
{
|
||||
WARNX("no handler found for msg type %u from fd %d",
|
||||
msgio->msgbuf.msg->hdr.type, msgio->fd);
|
||||
return;
|
||||
}
|
||||
|
||||
msghandlefn(msgio);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t vnode_sendmsg(int fd, vnode_msgbuf_t *msgbuf)
|
||||
{
|
||||
struct msghdr msg = {};
|
||||
struct iovec iov[1];
|
||||
char buf[CMSG_SPACE(3 * sizeof(int))];
|
||||
|
||||
iov[0].iov_base = msgbuf->msg;
|
||||
iov[0].iov_len = vnode_msglen(msgbuf);
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
if (msgbuf->infd >= 0)
|
||||
{
|
||||
struct cmsghdr *cmsg;
|
||||
int *fdptr;
|
||||
|
||||
assert(msgbuf->outfd >= 0);
|
||||
assert(msgbuf->errfd >= 0);
|
||||
|
||||
msg.msg_control = buf;
|
||||
msg.msg_controllen = sizeof(buf);
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int));
|
||||
|
||||
fdptr = (int *)CMSG_DATA(cmsg);
|
||||
fdptr[0] = msgbuf->infd;
|
||||
fdptr[1] = msgbuf->outfd;
|
||||
fdptr[2] = msgbuf->errfd;
|
||||
|
||||
msg.msg_controllen = cmsg->cmsg_len;
|
||||
}
|
||||
|
||||
return sendmsg(fd, &msg, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* return the number of bytes received
|
||||
* return 0 if the message should be ignored
|
||||
* return a negative value if i/o should stop
|
||||
*/
|
||||
ssize_t vnode_recvmsg(vnode_msgio_t *msgio)
|
||||
{
|
||||
ssize_t recvlen;
|
||||
struct msghdr msg = {};
|
||||
struct iovec iov[1];
|
||||
char buf[CMSG_SPACE(3 * sizeof(int))];
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
if (msgio->msgbuf.msgbufsize < VNODE_MSGSIZMAX)
|
||||
{
|
||||
if (vnode_resizemsgbuf(&msgio->msgbuf, VNODE_MSGSIZMAX))
|
||||
return -1;
|
||||
}
|
||||
|
||||
msgio->msgbuf.infd = msgio->msgbuf.outfd = msgio->msgbuf.errfd = -1;
|
||||
|
||||
iov[0].iov_base = msgio->msgbuf.msg;
|
||||
iov[0].iov_len = msgio->msgbuf.msgbufsize;
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = buf;
|
||||
msg.msg_controllen = sizeof(buf);
|
||||
|
||||
recvlen = recvmsg(msgio->fd, &msg, 0);
|
||||
if (recvlen == 0)
|
||||
return -1;
|
||||
else if (recvlen < 0)
|
||||
{
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
WARN("recvmsg() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (cmsg != NULL && cmsg->cmsg_type == SCM_RIGHTS)
|
||||
{
|
||||
int *fdptr;
|
||||
|
||||
fdptr = (int *)CMSG_DATA(cmsg);
|
||||
msgio->msgbuf.infd = fdptr[0];
|
||||
msgio->msgbuf.outfd = fdptr[1];
|
||||
msgio->msgbuf.errfd = fdptr[2];
|
||||
}
|
||||
|
||||
if (recvlen < sizeof(msgio->msgbuf.msg->hdr))
|
||||
{
|
||||
WARNX("message header truncated: received %d of %d bytes",
|
||||
recvlen, sizeof(msgio->msgbuf.msg->hdr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (msgio->msgbuf.msg->hdr.type == VNODE_MSG_NONE ||
|
||||
msgio->msgbuf.msg->hdr.type >= VNODE_MSG_MAX)
|
||||
{
|
||||
WARNX("invalid message type: %u", msgio->msgbuf.msg->hdr.type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (recvlen - sizeof(msgio->msgbuf.msg->hdr) !=
|
||||
msgio->msgbuf.msg->hdr.datalen)
|
||||
{
|
||||
WARNX("message length mismatch: received %d bytes; expected %d bytes",
|
||||
recvlen - sizeof(msgio->msgbuf.msg->hdr),
|
||||
msgio->msgbuf.msg->hdr.datalen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return recvlen;
|
||||
}
|
||||
|
||||
int vnode_msgiostart(vnode_msgio_t *msgio, struct ev_loop *loop,
|
||||
int fd, void *data, vnode_msghandler_t ioerror,
|
||||
const vnode_msghandler_t msghandler[VNODE_MSG_MAX])
|
||||
{
|
||||
#ifdef DEBUG
|
||||
WARNX("starting message i/o for fd %d", fd);
|
||||
#endif
|
||||
|
||||
if (vnode_initmsgbuf(&msgio->msgbuf))
|
||||
return -1;
|
||||
|
||||
msgio->loop = loop;
|
||||
msgio->fd = fd;
|
||||
msgio->fdwatcher.data = msgio;
|
||||
ev_io_init(&msgio->fdwatcher, vnode_msg_cb, fd, EV_READ);
|
||||
msgio->data = data;
|
||||
msgio->ioerror = ioerror;
|
||||
memcpy(msgio->msghandler, msghandler, sizeof(msgio->msghandler));
|
||||
|
||||
ev_io_start(msgio->loop, &msgio->fdwatcher);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vnode_msgiostop(vnode_msgio_t *msgio)
|
||||
{
|
||||
ev_io_stop(msgio->loop, &msgio->fdwatcher);
|
||||
FREE_MSGBUF(&msgio->msgbuf);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int vnode_parsemsg(vnode_msg_t *msg, void *data,
|
||||
const vnode_tlvhandler_t tlvhandler[VNODE_TLV_MAX])
|
||||
{
|
||||
size_t offset = 0;
|
||||
vnode_tlv_t *tlv;
|
||||
vnode_tlvhandler_t tlvhandlefn;
|
||||
int tmp = -1;
|
||||
|
||||
while (offset < msg->hdr.datalen)
|
||||
{
|
||||
tlv = (void *)msg->data + offset;
|
||||
|
||||
offset += sizeof(*tlv) + tlv->vallen;
|
||||
|
||||
if (tlv->vallen == 0 || offset > msg->hdr.datalen)
|
||||
{
|
||||
WARNX("invalid value length: %u", tlv->vallen);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tlvhandlefn = tlvhandler[tlv->type]) == NULL)
|
||||
{
|
||||
WARNX("unknown tlv type: %u", tlv->type);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tmp = tlvhandlefn(tlv, data)))
|
||||
break;
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
ssize_t vnode_addtlv(vnode_msgbuf_t *msgbuf, size_t offset,
|
||||
uint32_t type, uint32_t vallen, const void *valp)
|
||||
{
|
||||
vnode_tlv_t *tlv;
|
||||
size_t msglen, tlvlen;
|
||||
|
||||
tlv = (void *)msgbuf->msg->data + offset;
|
||||
msglen = (void *)tlv - (void *)msgbuf->msg;
|
||||
tlvlen = sizeof(*tlv) + vallen;
|
||||
|
||||
if (msglen + tlvlen > msgbuf->msgbufsize)
|
||||
{
|
||||
if (vnode_resizemsgbuf(msgbuf, msgbuf->msgbufsize + tlvlen))
|
||||
return -1;
|
||||
else
|
||||
tlv = (void *)msgbuf->msg->data + offset;
|
||||
}
|
||||
|
||||
tlv->type = type;
|
||||
tlv->vallen = vallen;
|
||||
memcpy(tlv->val, valp, vallen);
|
||||
|
||||
return tlvlen;
|
||||
}
|
148
netns/vnode_msg.h
Normal file
148
netns/vnode_msg.h
Normal file
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnode_msg.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _VNODE_MSG_H_
|
||||
#define _VNODE_MSG_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <ev.h>
|
||||
|
||||
#include "myerr.h"
|
||||
|
||||
typedef struct __attribute__ ((__packed__)) {
|
||||
uint32_t type;
|
||||
uint32_t vallen;
|
||||
uint8_t val[];
|
||||
} vnode_tlv_t;
|
||||
|
||||
typedef struct __attribute__ ((__packed__)) {
|
||||
uint32_t type;
|
||||
uint32_t datalen;
|
||||
} vnode_msghdr_t;
|
||||
|
||||
typedef struct __attribute__ ((__packed__)) {
|
||||
vnode_msghdr_t hdr;
|
||||
uint8_t data[];
|
||||
} vnode_msg_t;
|
||||
|
||||
typedef enum {
|
||||
VNODE_MSG_NONE = 0,
|
||||
VNODE_MSG_CMDREQ,
|
||||
VNODE_MSG_CMDREQACK,
|
||||
VNODE_MSG_CMDSTATUS,
|
||||
VNODE_MSG_CMDSIGNAL,
|
||||
VNODE_MSG_MAX,
|
||||
} vnode_msgtype_t;
|
||||
|
||||
typedef enum {
|
||||
VNODE_TLV_NONE = 0,
|
||||
VNODE_TLV_CMDID,
|
||||
VNODE_TLV_STDIN,
|
||||
VNODE_TLV_STDOUT,
|
||||
VNODE_TLV_STDERR,
|
||||
VNODE_TLV_CMDARG,
|
||||
VNODE_TLV_CMDPID,
|
||||
VNODE_TLV_CMDSTATUS,
|
||||
VNODE_TLV_SIGNUM,
|
||||
VNODE_TLV_MAX,
|
||||
} vnode_tlvtype_t;
|
||||
|
||||
enum {
|
||||
VNODE_ARGMAX = 1024,
|
||||
VNODE_MSGSIZMAX = 65535,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
vnode_msg_t *msg;
|
||||
size_t msgbufsize;
|
||||
int infd;
|
||||
int outfd;
|
||||
int errfd;
|
||||
} vnode_msgbuf_t;
|
||||
|
||||
#define INIT_MSGBUF(msgbuf) \
|
||||
do { \
|
||||
(msgbuf)->msg = NULL; \
|
||||
(msgbuf)->msgbufsize = 0; \
|
||||
(msgbuf)->infd = -1; \
|
||||
(msgbuf)->outfd = -1; \
|
||||
(msgbuf)->errfd = -1; \
|
||||
} while (0)
|
||||
|
||||
#define FREE_MSGBUF(msgbuf) \
|
||||
do { \
|
||||
if ((msgbuf)->msg) \
|
||||
free((msgbuf)->msg); \
|
||||
INIT_MSGBUF(msgbuf); \
|
||||
} while (0)
|
||||
|
||||
struct vnode_msgio;
|
||||
typedef void (*vnode_msghandler_t)(struct vnode_msgio *msgio);
|
||||
|
||||
typedef struct vnode_msgio {
|
||||
struct ev_loop *loop;
|
||||
int fd;
|
||||
ev_io fdwatcher;
|
||||
vnode_msgbuf_t msgbuf;
|
||||
void *data;
|
||||
vnode_msghandler_t ioerror;
|
||||
vnode_msghandler_t msghandler[VNODE_MSG_MAX];
|
||||
} vnode_msgio_t;
|
||||
|
||||
typedef int (*vnode_tlvhandler_t)(vnode_tlv_t *tlv, void *data);
|
||||
|
||||
|
||||
static inline void vnode_msgiohandler(vnode_msgio_t *msgio,
|
||||
vnode_msgtype_t msgtype,
|
||||
vnode_msghandler_t msghandlefn)
|
||||
{
|
||||
msgio->msghandler[msgtype] = msghandlefn;
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int vnode_resizemsgbuf(vnode_msgbuf_t *msgbuf, size_t size)
|
||||
{
|
||||
void *newbuf;
|
||||
if ((newbuf = realloc(msgbuf->msg, size)) == NULL)
|
||||
{
|
||||
WARN("realloc() failed for size %u", size);
|
||||
return -1;
|
||||
}
|
||||
msgbuf->msg = newbuf;
|
||||
msgbuf->msgbufsize = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int vnode_initmsgbuf(vnode_msgbuf_t *msgbuf)
|
||||
{
|
||||
INIT_MSGBUF(msgbuf);
|
||||
return vnode_resizemsgbuf(msgbuf, VNODE_MSGSIZMAX);
|
||||
}
|
||||
|
||||
#define vnode_msglen(msgbuf) \
|
||||
(sizeof(*(msgbuf)->msg) + (msgbuf)->msg->hdr.datalen)
|
||||
|
||||
ssize_t vnode_sendmsg(int fd, vnode_msgbuf_t *msgbuf);
|
||||
ssize_t vnode_recvmsg(vnode_msgio_t *msgio);
|
||||
|
||||
int vnode_msgiostart(vnode_msgio_t *msgio, struct ev_loop *loop,
|
||||
int fd, void *data, vnode_msghandler_t ioerror,
|
||||
const vnode_msghandler_t msghandler[VNODE_MSG_MAX]);
|
||||
void vnode_msgiostop(vnode_msgio_t *msgio);
|
||||
|
||||
int vnode_parsemsg(vnode_msg_t *msg, void *data,
|
||||
const vnode_tlvhandler_t tlvhandler[VNODE_TLV_MAX]);
|
||||
ssize_t vnode_addtlv(vnode_msgbuf_t *msgbuf, size_t offset,
|
||||
uint32_t type, uint32_t vallen, const void *valp);
|
||||
|
||||
#endif /* _VNODE_MSG_H_ */
|
378
netns/vnode_server.c
Normal file
378
netns/vnode_server.c
Normal file
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnode_server.c
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "netns.h"
|
||||
#include "myerr.h"
|
||||
#include "vnode_msg.h"
|
||||
#include "vnode_chnl.h"
|
||||
#include "vnode_cmd.h"
|
||||
#include "vnode_io.h"
|
||||
|
||||
#include "vnode_server.h"
|
||||
|
||||
extern int verbose;
|
||||
|
||||
static vnode_cliententry_t *vnode_server_newclient(vnode_server_t *server,
|
||||
int fd);
|
||||
static void vnode_server_delclient(vnode_cliententry_t *client);
|
||||
|
||||
static void client_ioerror(vnode_msgio_t *msgio)
|
||||
{
|
||||
vnode_cliententry_t *client = msgio->data;
|
||||
|
||||
if (verbose)
|
||||
INFO("i/o error for client fd %d; deleting client", client->msgio.fd);
|
||||
|
||||
vnode_server_delclient(client);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static vnode_cliententry_t *vnode_server_newclient(vnode_server_t *server,
|
||||
int fd)
|
||||
{
|
||||
vnode_cliententry_t *client;
|
||||
vnode_msghandler_t msghandler[VNODE_MSG_MAX] = {
|
||||
[VNODE_MSG_CMDREQ] = vnode_recv_cmdreq,
|
||||
[VNODE_MSG_CMDSIGNAL] = vnode_recv_cmdsignal,
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("new client on fd %d", fd);
|
||||
#endif
|
||||
|
||||
set_cloexec(fd);
|
||||
|
||||
if ((client = malloc(sizeof(*client))) == NULL)
|
||||
{
|
||||
WARN("malloc() failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
client->server = server;
|
||||
client->clientfd = fd;
|
||||
|
||||
TAILQ_INSERT_TAIL(&server->clientlisthead, client, entries);
|
||||
|
||||
if (vnode_msgiostart(&client->msgio, server->loop,
|
||||
client->clientfd, client, client_ioerror, msghandler))
|
||||
{
|
||||
WARNX("vnode_msgiostart() failed");
|
||||
free(client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
static void vnode_server_delclient(vnode_cliententry_t *client)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
WARNX("deleting client for fds %d %d", client->clientfd, client->msgio.fd);
|
||||
#endif
|
||||
|
||||
TAILQ_REMOVE(&client->server->clientlisthead, client, entries);
|
||||
vnode_msgiostop(&client->msgio);
|
||||
close(client->clientfd);
|
||||
memset(client, 0, sizeof(*client));
|
||||
free(client);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* XXX put this in vnode_cmd.c ?? */
|
||||
static void vnode_child_cb(struct ev_loop *loop, ev_child *w, int revents)
|
||||
{
|
||||
vnode_server_t *server = w->data;
|
||||
vnode_cmdentry_t *cmd;
|
||||
char *how;
|
||||
int status;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("child process %d exited with status 0x%x", w->rpid, w->rstatus);
|
||||
if (WIFEXITED(w->rstatus))
|
||||
WARNX("normal terminataion status: %d", WEXITSTATUS(w->rstatus));
|
||||
else if (WIFSIGNALED(w->rstatus))
|
||||
WARNX("terminated by signal: %d", WTERMSIG(w->rstatus));
|
||||
else
|
||||
WARNX("unexpected status: %d", w->rstatus);
|
||||
#endif
|
||||
|
||||
if (WIFEXITED(w->rstatus))
|
||||
{
|
||||
how = "normally";
|
||||
status = WEXITSTATUS(w->rstatus);
|
||||
}
|
||||
else if (WIFSIGNALED(w->rstatus))
|
||||
{
|
||||
how = "due to signal";
|
||||
status = WTERMSIG(w->rstatus);
|
||||
}
|
||||
else
|
||||
{
|
||||
how = "for unknown reason";
|
||||
status = w->rstatus;
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(cmd, &server->cmdlisthead, entries)
|
||||
{
|
||||
if (cmd->pid == w->rpid)
|
||||
{
|
||||
vnode_cliententry_t *client = cmd->data;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("pid %d found in cmd list; removing", w->rpid);
|
||||
#endif
|
||||
|
||||
TAILQ_REMOVE(&server->cmdlisthead, cmd, entries);
|
||||
|
||||
if (verbose)
|
||||
INFO("cmd completed %s: pid: %d; cmdid: %d; status %d",
|
||||
how, w->rpid, cmd->cmdid, status);
|
||||
|
||||
if (vnode_send_cmdstatus(client->clientfd, cmd->cmdid, w->rstatus))
|
||||
WARNX("vnode_send_cmdstatus() failed");
|
||||
|
||||
free(cmd);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WARNX("pid %d not found in client command list: "
|
||||
"completed %s with status %d", w->rpid, how, status);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void vnode_server_cb(struct ev_loop *loop, ev_io *w, int revents)
|
||||
{
|
||||
vnode_server_t *server = w->data;
|
||||
int fd;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
fd = accept(server->serverfd, NULL, NULL);
|
||||
if (fd < 0)
|
||||
{
|
||||
if (errno != EAGAIN)
|
||||
WARN("accept() failed");
|
||||
break;
|
||||
}
|
||||
|
||||
if (vnode_server_newclient(server, fd) == NULL)
|
||||
{
|
||||
WARN("vnode_server_newclient() failed");
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static vnode_server_t *vnode_newserver(struct ev_loop *loop,
|
||||
int ctrlfd, const char *ctrlchnlname)
|
||||
{
|
||||
vnode_server_t *server;
|
||||
|
||||
if ((server = malloc(sizeof(*server))) == NULL)
|
||||
{
|
||||
WARN("malloc() failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TAILQ_INIT(&server->clientlisthead);
|
||||
TAILQ_INIT(&server->cmdlisthead);
|
||||
server->loop = loop;
|
||||
|
||||
strncpy(server->ctrlchnlname, ctrlchnlname, sizeof(server->ctrlchnlname));
|
||||
server->ctrlchnlname[sizeof(server->ctrlchnlname) -1] = '\0';
|
||||
memset(server->pidfilename, 0, sizeof(server->pidfilename));
|
||||
server->serverfd = ctrlfd;
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("adding vnode_child_cb for pid 0");
|
||||
#endif
|
||||
|
||||
server->childwatcher.data = server;
|
||||
ev_child_init(&server->childwatcher, vnode_child_cb, 0, 0);
|
||||
ev_child_start(server->loop, &server->childwatcher);
|
||||
|
||||
#ifdef DEBUG
|
||||
WARNX("adding vnode_server_cb for fd %d", server->serverfd);
|
||||
#endif
|
||||
|
||||
server->fdwatcher.data = server;
|
||||
ev_io_init(&server->fdwatcher, vnode_server_cb, server->serverfd, EV_READ);
|
||||
ev_io_start(server->loop, &server->fdwatcher);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
void vnode_delserver(vnode_server_t *server)
|
||||
{
|
||||
unlink(server->ctrlchnlname);
|
||||
if (server->pidfilename[0] != '\0')
|
||||
{
|
||||
unlink(server->pidfilename);
|
||||
}
|
||||
ev_io_stop(server->loop, &server->fdwatcher);
|
||||
close(server->serverfd);
|
||||
|
||||
ev_child_stop(server->loop, &server->childwatcher);
|
||||
|
||||
while (!TAILQ_EMPTY(&server->clientlisthead))
|
||||
{
|
||||
vnode_cliententry_t *client;
|
||||
|
||||
client = TAILQ_FIRST(&server->clientlisthead);
|
||||
TAILQ_REMOVE(&server->clientlisthead, client, entries);
|
||||
vnode_server_delclient(client);
|
||||
}
|
||||
|
||||
while (!TAILQ_EMPTY(&server->cmdlisthead))
|
||||
{
|
||||
vnode_cmdentry_t *cmd;
|
||||
|
||||
cmd = TAILQ_FIRST(&server->cmdlisthead);
|
||||
TAILQ_REMOVE(&server->cmdlisthead, cmd, entries);
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
memset(server, 0, sizeof(*server));
|
||||
free(server);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
vnode_server_t *vnoded(int newnetns, const char *ctrlchnlname,
|
||||
const char *logfilename, const char *pidfilename,
|
||||
const char *chdirname)
|
||||
{
|
||||
int ctrlfd;
|
||||
unsigned int i;
|
||||
long openmax;
|
||||
vnode_server_t *server;
|
||||
pid_t pid;
|
||||
|
||||
setsid();
|
||||
|
||||
if ((ctrlfd = vnode_listen(ctrlchnlname)) < 0)
|
||||
{
|
||||
WARNX("vnode_listen() failed for '%s'", ctrlchnlname);
|
||||
return NULL;
|
||||
}
|
||||
set_cloexec(ctrlfd);
|
||||
|
||||
if (newnetns)
|
||||
{
|
||||
pid = nsfork(0);
|
||||
if (pid == -1)
|
||||
{
|
||||
WARN("nsfork() failed");
|
||||
close(ctrlfd);
|
||||
unlink(ctrlchnlname);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pid = getpid();
|
||||
}
|
||||
|
||||
if (pid)
|
||||
{
|
||||
printf("%u\n", pid);
|
||||
fflush(stdout);
|
||||
|
||||
if (pidfilename)
|
||||
{
|
||||
FILE *pidfile;
|
||||
|
||||
pidfile = fopen(pidfilename, "w");
|
||||
if (pidfile != NULL)
|
||||
{
|
||||
fprintf(pidfile, "%u\n", pid);
|
||||
fclose(pidfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN("fopen() failed for '%s'", pidfilename);
|
||||
}
|
||||
}
|
||||
|
||||
if (newnetns)
|
||||
_exit(0); /* nothing else for the parent to do */
|
||||
}
|
||||
|
||||
/* try to close any open files */
|
||||
if ((openmax = sysconf(_SC_OPEN_MAX)) < 0)
|
||||
openmax = 1024;
|
||||
assert(openmax >= _POSIX_OPEN_MAX);
|
||||
for (i = 3; i < openmax; i++)
|
||||
if (i != ctrlfd)
|
||||
close(i);
|
||||
|
||||
if (!logfilename)
|
||||
logfilename = "/dev/null";
|
||||
|
||||
#define DUPFILE(filename, mode, fileno) \
|
||||
do { \
|
||||
int fd; \
|
||||
if ((fd = open(filename, mode, 0644)) == -1) \
|
||||
WARN("open() failed for '%s'", filename); \
|
||||
else \
|
||||
{ \
|
||||
if (dup2(fd, fileno) == -1) \
|
||||
WARN("dup2() failed for " #fileno); \
|
||||
close(fd); \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
DUPFILE("/dev/null", O_RDONLY, STDIN_FILENO);
|
||||
DUPFILE(logfilename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, STDOUT_FILENO);
|
||||
DUPFILE(logfilename,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, STDERR_FILENO);
|
||||
|
||||
#undef DUPFILE
|
||||
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
setvbuf(stderr, NULL, _IOLBF, 0);
|
||||
|
||||
if (chdirname && chdir(chdirname))
|
||||
WARN("chdir() failed");
|
||||
|
||||
server = vnode_newserver(ev_default_loop(0), ctrlfd, ctrlchnlname);
|
||||
if (!server)
|
||||
{
|
||||
close(ctrlfd);
|
||||
unlink(ctrlchnlname);
|
||||
}
|
||||
if (pidfilename)
|
||||
{
|
||||
strncpy(server->pidfilename, pidfilename, sizeof(server->pidfilename));
|
||||
server->pidfilename[sizeof(server->pidfilename) -1] = '\0';
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
45
netns/vnode_server.h
Normal file
45
netns/vnode_server.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnode_server.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _VNODE_SERVER_H_
|
||||
#define _VNODE_SERVER_H_
|
||||
|
||||
#include <limits.h>
|
||||
#include <ev.h>
|
||||
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include "vnode_msg.h"
|
||||
|
||||
typedef struct {
|
||||
TAILQ_HEAD(clientlist, cliententry) clientlisthead;
|
||||
TAILQ_HEAD(cmdlist, cmdentry) cmdlisthead;
|
||||
struct ev_loop *loop;
|
||||
char ctrlchnlname[PATH_MAX];
|
||||
char pidfilename[PATH_MAX];
|
||||
int serverfd;
|
||||
ev_io fdwatcher;
|
||||
ev_child childwatcher;
|
||||
} vnode_server_t;
|
||||
|
||||
typedef struct cliententry {
|
||||
TAILQ_ENTRY(cliententry) entries;
|
||||
|
||||
vnode_server_t *server;
|
||||
int clientfd;
|
||||
vnode_msgio_t msgio;
|
||||
} vnode_cliententry_t;
|
||||
|
||||
vnode_server_t *vnoded(int newnetns, const char *ctrlchnlname,
|
||||
const char *logfilename, const char *pidfilename,
|
||||
const char *chdirname);
|
||||
void vnode_delserver(vnode_server_t *server);
|
||||
#endif /* _VNODE_SERVER_H_ */
|
41
netns/vnode_tlv.h
Normal file
41
netns/vnode_tlv.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnode_tlv.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _VNODE_TLV_H_
|
||||
#define _VNODE_TLV_H_
|
||||
|
||||
static inline int tlv_string(char **var, vnode_tlv_t *tlv)
|
||||
{
|
||||
if (tlv->val[tlv->vallen - 1] != '\0')
|
||||
{
|
||||
WARNX("string not null-terminated");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*var = (char *)tlv->val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int tlv_int32(int32_t *var, vnode_tlv_t *tlv)
|
||||
{
|
||||
if (tlv->vallen != sizeof(int32_t))
|
||||
{
|
||||
WARNX("invalid value length for int32: %u", tlv->vallen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*var = *(int32_t *)tlv->val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _VNODE_TLV_H_ */
|
227
netns/vnoded_main.c
Normal file
227
netns/vnoded_main.c
Normal file
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* CORE
|
||||
* Copyright (c)2010-2012 the Boeing Company.
|
||||
* See the LICENSE file included in this distribution.
|
||||
*
|
||||
* author: Tom Goff <thomas.goff@boeing.com>
|
||||
*
|
||||
* vnoded_main.c
|
||||
*
|
||||
* vnoded daemon runs as PID 1 in the Linux namespace container and receives
|
||||
* and executes commands via a control channel.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "version.h"
|
||||
#include "vnode_server.h"
|
||||
#include "myerr.h"
|
||||
|
||||
int verbose;
|
||||
|
||||
static vnode_server_t *vnodeserver;
|
||||
|
||||
struct option longopts[] =
|
||||
{
|
||||
{"version", no_argument, NULL, 'V'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static void usage(int status, char *fmt, ...)
|
||||
{
|
||||
extern const char *__progname;
|
||||
va_list ap;
|
||||
FILE *output;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
output = status ? stderr : stdout;
|
||||
fprintf(output, "\n");
|
||||
if (fmt != NULL)
|
||||
{
|
||||
vfprintf(output, fmt, ap);
|
||||
fprintf(output, "\n\n");
|
||||
}
|
||||
fprintf(output,
|
||||
"Usage: %s [-h|-V] [-v] [-n] [-C <chdir>] [-l <logfile>] "
|
||||
"[-p <pidfile>] -c <control channel>\n\n"
|
||||
"Linux namespace container server daemon runs as PID 1 in the "
|
||||
"container. \nNormally this process is launched automatically by the "
|
||||
"CORE daemon.\n\nOptions:\n"
|
||||
" -h, --help show this help message and exit\n"
|
||||
" -V, --version show version number and exit\n"
|
||||
" -v enable verbose logging\n"
|
||||
" -n do not create and run daemon within a new network namespace "
|
||||
"(for debug)\n"
|
||||
" -C change to the specified <chdir> directory\n"
|
||||
" -l log output to the specified <logfile> file\n"
|
||||
" -p write process id to the specified <pidfile> file\n"
|
||||
" -c establish the specified <control channel> for receiving "
|
||||
"control commands\n",
|
||||
__progname);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void sigexit(int signum)
|
||||
{
|
||||
WARNX("exiting due to signal: %d", signum);
|
||||
exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
static void cleanup_sigchld(int signum)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void cleanup()
|
||||
{
|
||||
static int incleanup = 0;
|
||||
|
||||
if (incleanup)
|
||||
return;
|
||||
incleanup = 1;
|
||||
|
||||
if (vnodeserver)
|
||||
{
|
||||
struct ev_loop *loop = vnodeserver->loop;
|
||||
vnode_delserver(vnodeserver);
|
||||
if (loop)
|
||||
ev_unloop(loop, EVUNLOOP_ALL);
|
||||
}
|
||||
|
||||
/* don't use SIG_IGN here because receiving SIGCHLD is needed to
|
||||
* interrupt the sleep below in order to avoid long delays
|
||||
*/
|
||||
if (signal(SIGCHLD, cleanup_sigchld) == SIG_ERR)
|
||||
WARN("signal() failed");
|
||||
|
||||
if (getpid() == 1)
|
||||
{
|
||||
struct timespec delay = {
|
||||
.tv_sec = 2,
|
||||
.tv_nsec = 0,
|
||||
};
|
||||
|
||||
/* try to gracefully terminate all processes in this namespace
|
||||
* first
|
||||
*/
|
||||
kill(-1, SIGTERM);
|
||||
/* wait for child processes to terminate */
|
||||
for (;;)
|
||||
{
|
||||
pid_t pid;
|
||||
int err;
|
||||
struct timespec rem;
|
||||
|
||||
pid = waitpid(-1, NULL, WNOHANG);
|
||||
if (pid == -1)
|
||||
break; /* an error occurred */
|
||||
if (pid != 0)
|
||||
continue; /* a child was reaped */
|
||||
|
||||
err = nanosleep(&delay, &rem);
|
||||
if (err == -1 && errno == EINTR)
|
||||
{
|
||||
delay = rem;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* force termination after delay */
|
||||
kill(-1, SIGKILL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int newnetns = 1;
|
||||
char *ctrlchnlname = NULL, *logfilename = NULL, *chdirname = NULL;
|
||||
char *pidfilename = NULL;
|
||||
extern const char *__progname;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int opt;
|
||||
|
||||
if ((opt = getopt_long(argc, argv, "c:C:l:nvVhp:", longopts, NULL)) == -1)
|
||||
break;
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case 'c':
|
||||
ctrlchnlname = optarg;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
chdirname = optarg;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
logfilename = optarg;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
newnetns = 0;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
pidfilename = optarg;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
printf("%s version %s\n", __progname, CORE_VERSION);
|
||||
exit(0);
|
||||
|
||||
case 'h':
|
||||
/* pass through */
|
||||
default:
|
||||
usage(0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (ctrlchnlname == NULL)
|
||||
usage(1, "no control channel given");
|
||||
|
||||
for (; argc; argc--, argv++)
|
||||
WARNX("ignoring command line argument: '%s'", *argv);
|
||||
|
||||
if (atexit(cleanup))
|
||||
ERR(1, "atexit() failed");
|
||||
|
||||
if (signal(SIGTERM, sigexit) == SIG_ERR)
|
||||
ERR(1, "signal() failed");
|
||||
if (signal(SIGINT, sigexit) == SIG_ERR)
|
||||
ERR(1, "signal() failed");
|
||||
/* XXX others? */
|
||||
|
||||
vnodeserver = vnoded(newnetns, ctrlchnlname, logfilename, pidfilename,
|
||||
chdirname);
|
||||
if (vnodeserver == NULL)
|
||||
ERRX(1, "vnoded() failed");
|
||||
|
||||
ev_loop(vnodeserver->loop, 0);
|
||||
|
||||
exit(0);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue