moved netns code from daemon/src to netns at the top level, updated files to account for location change

This commit is contained in:
Blake J. Harnden 2018-01-04 12:03:14 -08:00
parent e4a0069bc3
commit d799390c4a
32 changed files with 14 additions and 14 deletions

View file

@ -11,7 +11,7 @@ SETUPPY = setup.py
SETUPPYFLAGS = -v
if WANT_NETNS
SUBDIRS = src ns3
SUBDIRS = ns3
endif
SBIN_FILES = \

View file

@ -1,7 +1,4 @@
enum34==1.1.6
logzero==1.3.0
grpcio==1.0.0
grpcio-tools==1.0.0
mock==1.3.0
pycco==0.5.1
pytest==3.0.7

View file

@ -1,8 +0,0 @@
*.o
Makefile
Makefile.in
build
netns
vcmd
version.h
vnoded

View file

@ -1,2 +0,0 @@
include *.h
include sbin/*

View file

@ -1,73 +0,0 @@
# 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

View file

@ -1,80 +0,0 @@
/*
* 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_ */

View file

@ -1,110 +0,0 @@
/*
* 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;
}

View file

@ -1,20 +0,0 @@
/*
* 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_ */

View file

@ -1,127 +0,0 @@
/*
* 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);
}

View file

@ -1,146 +0,0 @@
/*
* 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;
}

View file

@ -1,42 +0,0 @@
# 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"
)

View file

@ -1,439 +0,0 @@
/*
* 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);
}

View file

@ -1,956 +0,0 @@
/*
* 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;
}

View file

@ -1,16 +0,0 @@
/*
* 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_ */

View file

@ -1,117 +0,0 @@
/*
* 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;
}

View file

@ -1,18 +0,0 @@
/*
* 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_ */

View file

@ -1,509 +0,0 @@
/*
* 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;
}

View file

@ -1,77 +0,0 @@
/*
* 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_ */

View file

@ -1,483 +0,0 @@
/*
* 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;
}

View file

@ -1,71 +0,0 @@
/*
* 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_ */

View file

@ -1,185 +0,0 @@
/*
* 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;
}

View file

@ -1,63 +0,0 @@
/*
* 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_ */

View file

@ -1,262 +0,0 @@
/*
* 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;
}

View file

@ -1,148 +0,0 @@
/*
* 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_ */

View file

@ -1,378 +0,0 @@
/*
* 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;
}

View file

@ -1,45 +0,0 @@
/*
* 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_ */

View file

@ -1,41 +0,0 @@
/*
* 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_ */

View file

@ -1,227 +0,0 @@
/*
* 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);
}