Break up the old chbind into ncontext, nattribute, and naddress.
authorDaniel Hokka Zakrisson <daniel@hozac.com>
Sun, 19 Nov 2006 17:25:11 +0000 (17:25 +0000)
committerDaniel Hokka Zakrisson <daniel@hozac.com>
Sun, 19 Nov 2006 17:25:11 +0000 (17:25 +0000)
This is done mostly to add support for network flags and caps, but also to get the same level of flexibility that the security contexts have.
In addition, a vc_nidopt2nid was created to correctly map --nid self to the current process's nid.

git-svn-id: http://svn.linux-vserver.org/svn/util-vserver/trunk@2384 94cd875c-1c1d-0410-91d2-eb244daf1a30

13 files changed:
contrib/manifest.dat.pathsubst
ensc_wrappers/wrappers-vserver.hc
lib/Makefile-files
lib/nidopt2nid.c [new file with mode: 0644]
lib/vserver.h
scripts/Makefile-files
scripts/chbind [new file with mode: 0755]
scripts/util-vserver-vars.pathsubst
src/Makefile-files
src/chbind.c
src/naddress.c [new file with mode: 0644]
src/nattribute.c [new file with mode: 0644]
src/ncontext.c [new file with mode: 0644]

index 0fe2a58..0fbdc08 100644 (file)
@@ -46,6 +46,7 @@ build  @PKGLIBDIR@/defaults/debootstrap.uri
 base   @PKGLIBDIR@/capchroot
 base   @PKGLIBDIR@/chain-echo
 core   @PKGLIBDIR@/chcontext-compat
+core   @PKGLIBDIR@/chbind-compat
 base   @PKGLIBDIR@/check-unixfile
 base   @PKGLIBDIR@/chroot-sh
 base   @PKGLIBDIR@/exec-ulimit
@@ -83,6 +84,9 @@ core   @SBINDIR@/chcontext
 core   @SBINDIR@/chxid
 base   @SBINDIR@/exec-cd
 core   @SBINDIR@/lsxid
+core   @SBINDIR@/naddress
+core   @SBINDIR@/nattribute
+core   @SBINDIR@/ncontext
 core   @SBINDIR@/reducecap
 core   @SBINDIR@/setattr
 core   @SBINDIR@/showattr
index 0c59a8c..488d57d 100644 (file)
@@ -40,6 +40,14 @@ Evc_get_task_xid(pid_t pid)
   return res;
 }
 
+inline static WRAPPER_DECL nid_t
+Evc_get_task_nid(pid_t pid)
+{
+  register nid_t       res = vc_get_task_nid(pid);
+  FatalErrnoError(res==VC_NOCTX, "vc_get_task_nid()");
+  return res;
+}
+
 inline static WRAPPER_DECL xid_t
 Evc_ctx_create(xid_t xid)
 {
@@ -48,6 +56,14 @@ Evc_ctx_create(xid_t xid)
   return res;
 }
 
+inline static WRAPPER_DECL nid_t
+Evc_net_create(nid_t nid)
+{
+  register nid_t       res = vc_net_create(nid);
+  FatalErrnoError(res==VC_NOCTX, "vc_net_create()");
+  return res;
+}
+
 inline static WRAPPER_DECL void
 Evc_ctx_migrate(xid_t xid)
 {
@@ -55,6 +71,12 @@ Evc_ctx_migrate(xid_t xid)
 }
 
 inline static WRAPPER_DECL void
+Evc_net_migrate(nid_t nid)
+{
+  FatalErrnoError(vc_net_migrate(nid)==-1, "vc_net_migrate()");
+}
+
+inline static WRAPPER_DECL void
 Evc_get_cflags(xid_t xid, struct vc_ctx_flags *flags)
 {
   FatalErrnoError(vc_get_cflags(xid, flags)==-1, "vc_get_cflags()");
@@ -67,6 +89,18 @@ Evc_set_cflags(xid_t xid, struct vc_ctx_flags const *flags)
 }
 
 inline static WRAPPER_DECL void
+Evc_get_nflags(nid_t nid, struct vc_net_flags *flags)
+{
+  FatalErrnoError(vc_get_nflags(nid, flags)==-1, "vc_get_nflags()");
+}
+
+inline static WRAPPER_DECL void
+Evc_set_nflags(nid_t nid, struct vc_net_flags const *flags)
+{
+  FatalErrnoError(vc_set_nflags(nid, flags)==-1, "vc_set_nflags()");
+}
+
+inline static WRAPPER_DECL void
 Evc_set_vhi_name(xid_t xid, vc_uts_type type,
                 char const *val, size_t len)
 {
@@ -86,6 +120,18 @@ Evc_set_ccaps(xid_t xid, struct vc_ctx_caps const *caps)
 }
 
 inline static WRAPPER_DECL void
+Evc_get_ncaps(nid_t nid, struct vc_net_caps *caps)
+{
+  FatalErrnoError(vc_get_ncaps(nid, caps)==-1, "vc_get_ncaps()");
+}
+
+inline static WRAPPER_DECL void
+Evc_set_ncaps(nid_t nid, struct vc_net_caps const *caps)
+{
+  FatalErrnoError(vc_set_ncaps(nid, caps)==-1, "vc_set_ncaps()");
+}
+
+inline static WRAPPER_DECL void
 Evc_set_namespace()
 {
   FatalErrnoError(vc_set_namespace()==-1, "vc_set_namespace()");
@@ -122,3 +168,15 @@ Evc_xidopt2xid(char const *id, bool honor_static)
 
   return rc;
 }
+
+inline static WRAPPER_DECL nid_t
+Evc_nidopt2nid(char const *id, bool honor_static)
+{
+  char const * err;
+  nid_t                rc = vc_nidopt2nid(id, honor_static, &err);
+  if (__builtin_expect(rc==VC_NOCTX,0)) {
+    ENSC_DETAIL1(msg, "vc_nidopt2nid", id, 1);
+    FatalErrnoErrorFail(msg);
+  }
+  return rc;
+}
index 736d7f1..7d2c3bf 100644 (file)
@@ -33,7 +33,8 @@ lib_management_SRCS =         lib/comparevserverbyid.c \
                                lib/getvserverctx.c \
                                lib/getvservername.c \
                                lib/getvservervdir.c \
-                               lib/xidopt2xid.c
+                               lib/xidopt2xid.c \
+                               lib/nidopt2nid.c
 lib_v11_SRCS =                 lib/syscall_rlimit.c \
                                lib/syscall_rlimit-v11.hc \
                                lib/syscall_kill.c  \
diff --git a/lib/nidopt2nid.c b/lib/nidopt2nid.c
new file mode 100644 (file)
index 0000000..d3fd8c6
--- /dev/null
@@ -0,0 +1,40 @@
+// $Id$    --*- c -*--
+
+// Copyright (C) 2004 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+//  
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//  
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//  
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "vserver.h"
+#include <string.h>
+#include <stdlib.h>
+
+nid_t
+vc_nidopt2nid(char const *str, bool honor_static, char const **err_info)
+{
+  char const *         err;
+  nid_t                        res = VC_NOCTX;
+
+  err = "vc_get_task_nid()";
+  if (strcmp(str,"self")==0) res = vc_get_task_nid(0);
+  else                       res = vc_xidopt2xid(str, honor_static, &err);
+
+  if (res==VC_NOCTX && err_info) *err_info = err;
+
+  return res;
+}
index 41479fb..a265d30 100644 (file)
@@ -855,6 +855,8 @@ extern "C" {
 
   /** Maps an xid given at '--xid' options to an xid_t */
   xid_t                vc_xidopt2xid(char const *, bool honor_static, char const **err_info);
+  /** Maps a  nid given at '--nid' options to a  nid_t */
+  nid_t                vc_nidopt2nid(char const *, bool honor_static, char const **err_info);
 
   vcCfgStyle   vc_getVserverCfgStyle(char const *id);
   
index 1dca1da..a4ddccb 100644 (file)
@@ -82,7 +82,8 @@ scripts_legacy_src_SCRPTS =   scripts/legacy/save_s_context \
 scripts_legacy_gen_SCRPTS =    scripts/legacy/vps
 scripts_legacy_src_PRGS =      scripts/legacy/vserver-copy
 
-scripts_sbin_src_PRGS =                scripts/chcontext \
+scripts_sbin_src_PRGS =                scripts/chbind \
+                               scripts/chcontext \
                                scripts/vapt-get \
                                scripts/vdispatch-conf \
                                scripts/vemerge \
diff --git a/scripts/chbind b/scripts/chbind
new file mode 100755 (executable)
index 0000000..75aa2a4
--- /dev/null
@@ -0,0 +1,130 @@
+#! /bin/bash
+# $Id$
+
+# Copyright (C) 2004 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+# Copyright (C) 2006 Daniel Hokka Zakrisson
+#  
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#  
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#  
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+: ${UTIL_VSERVER_VARS:=/usr/lib/util-vserver/util-vserver-vars}
+test -e "$UTIL_VSERVER_VARS" || {
+    echo $"Can not find util-vserver installation (the file '$UTIL_VSERVER_VARS' would be expected); aborting..." >&2
+    exit 1
+}
+. "$UTIL_VSERVER_VARS"
+. "$_LIB_FUNCTIONS"
+
+function showHelp()
+{
+    echo \
+$"Usage: $1  [--silent] [--nid <nid>] [--ip <ip_num>[/<mask>]]
+             [--bcast <broadcast>] [--] <commands> <args>*
+
+--silent
+    Do not print the addresses assigned.
+--nid <nid>
+    Network context id to use.
+--ip <ip_num>[/<mask>]
+    IP address to bind to.
+--bcast <broadcast>
+    Broadcast address for the network context.
+
+Report bugs to <$PACKAGE_BUGREPORT>."
+    exit $2
+}
+
+function showVersion()
+{
+    echo \
+$"chbind $PACKAGE_VERSION -- bind to IP addresses and execute a program
+This program is part of $PACKAGE_STRING
+
+Copyright (C) 2004 Enrico Scholz
+Copyright (C) 2006 Daniel Hokka Zakrisson
+This program is free software; you may redistribute it under the terms of
+the GNU General Public License.  This program has absolutely no warranty."
+    exit $1
+}
+
+$_VSERVER_INFO - FEATURE vnet || exec $_CHBIND_COMPAT "$@"
+
+tmp=$(getopt -o + --long ncap:,nid:,ip:,bcast:,disconnect,flag:,secure,silent,help,version -n "$0" -- "$@") || exit 1
+eval set -- "$tmp"
+
+OPT_CAPS=()
+OPT_NID=
+OPT_DISCONNECT=
+OPT_FLAGS=()
+OPT_SECURE=
+OPT_SILENT=
+OPT_BCAST=
+OPT_IPS=()
+
+while true; do
+    case "$1" in
+       --help)         showHelp $0 0;;
+       --version)      showVersion 0;;
+       --ncap)         OPT_CAPS=( "${OPT_CAPS[@]}" "$2" ); shift;;
+       --nid)          OPT_NID=$2; shift;;
+       --disconnect)   OPT_DISCONNECT=1;;
+       --flag)         OPT_FLAGS=( "${OPT_FLAGS[@]}" "$2" ); shift;;
+       --secure)       OPT_SECURE=1;;
+       --silent)       OPT_SILENT=1;;
+       --ip)           OPT_IPS=( "${OPT_IPS[@]}" "$2" ); shift;;
+       --bcast)        OPT_BCAST=$2; shift;;
+       --)             shift; break;;
+       *)              echo $"chbind: internal error; arg=='$1'" >&2; exit 1;;
+    esac
+    shift
+done
+
+create_cmd=( $_NCONTEXT --create --silentexist
+            ${OPT_SILENT:+--silent}
+            ${OPT_NID:+--nid "$OPT_NID"} )
+
+chain_cmd=()
+
+old_IFS=$IFS
+IFS=,$IFS
+
+chain_cmd=( "${chain_cmd[@]}"
+               --
+               $_NATTRIBUTE --set
+               ${OPT_SECURE:+--secure}
+               ${OPT_CAPS:+--ncap "${OPT_CAPS[*]}"}
+               ${OPT_FLAGS:+--flag "${OPT_FLAGS[*]}"}
+               --
+               $_NADDRESS --add
+               ${OPT_SILENT:+--silent}
+               ${OPT_BCAST:+--bcast "$OPT_BCAST"} )
+
+for ip in "${OPT_IPS[@]}"; do
+    chain_cmd=( "${chain_cmd[@]}" --ip "$ip" )
+done
+               
+migrate_cmd=( $_NCONTEXT
+             ${OPT_SILENT:+--silent}
+             ${OPT_DISCONNECT:+--disconnect} )
+
+IFS=$old_IFS
+
+if test -z "$OPT_NID" || $_VSERVER_INFO -q "$OPT_NID" XIDTYPE static; then
+    "${create_cmd[@]}" "${chain_cmd[@]}" -- "$@"
+    rc=$?
+else
+    rc=254
+fi
+
+test "$rc" -ne 254 || exec "${migrate_cmd[@]}" --nid "$OPT_NID" --migrate -- "$@"
+exit $rc
index ee7662f..4548503 100644 (file)
@@ -31,6 +31,7 @@ __DEFAULT_VSERVERPKGDIR='@VSERVERPKGDIR@'
 _CAPCHROOT="$__PKGLIBDIR/capchroot"
 _CHAINECHO="$__PKGLIBDIR/chain-echo"
 _CHBIND="$__SBINDIR/chbind"
+_CHBIND_COMPAT="$__PKGLIBDIR/chbind-compat"
 _CHCONTEXT="$__SBINDIR/chcontext"
 _CHCONTEXT_COMPAT="$__PKGLIBDIR/chcontext-compat"
 _CHECK_UNIXFILE="$__PKGLIBDIR/check-unixfile"
@@ -53,6 +54,9 @@ _LIB_VSERVER_BUILD_FUNCTIONS_PKGMGMT="$__PKGLIBDIR/vserver-build.functions.pkgmg
 _LISTDEVIP="$__LEGACYDIR/listdevip"
 _LOCKFILE="$__PKGLIBDIR/lockfile"
 _MASK2PREFIX="$__PKGLIBDIR/mask2prefix"
+_NADDRESS="$__SBINDIR/naddress"
+_NATTRIBUTE="$__SBINDIR/nattribute"
+_NCONTEXT="$__SBINDIR/ncontext"
 _PARSERPMDUMP="$__LEGACYDIR/parserpmdump"
 _PKGMGMT="$__PKGLIBDIR/pkgmgmt"
 _READLINK="$__PKGLIBDIR/readlink"
index f19fa08..01f8af2 100644 (file)
@@ -46,7 +46,7 @@ DIETPROGS +=          src/chcontext-compat \
                        src/secure-mount \
                        src/save_ctxinfo \
                        src/mask2prefix \
-                       src/chbind \
+                       src/chbind-compat \
                        src/exec-cd \
                        src/fakerunlevel \
                        src/keep-ctx-alive \
@@ -74,7 +74,10 @@ DIETPROGS +=         src/chcontext-compat \
                        src/vserver-info \
                        src/vdlimit \
                        src/vdu \
-                       src/vwait
+                       src/vwait \
+                       src/ncontext \
+                       src/nattribute \
+                       src/naddress
 
 if ENSC_CAN_BEECRYPT_WITH_DIETLIBC
 DIETPROGS +=           src/vhashify
@@ -110,6 +113,7 @@ pkglib_PROGRAMS +=  src/capchroot \
                        src/rpm-fake-resolver \
                        src/vshelper-sync \
                        src/sigexec \
+                       src/chbind-compat \
                        $(src_pkglib_C99_X_PROGS) \
                        $(src_pkglib_CXX_X_PROGS)
 
@@ -128,8 +132,7 @@ legacy_PROGRAMS +=  src/ifspec \
 
 pkglib_LTLIBRARIES +=  src/rpm-fake.la
 
-sbin_PROGRAMS +=       src/chbind \
-                       src/exec-cd \
+sbin_PROGRAMS +=       src/exec-cd \
                        src/lsxid \
                        src/chxid \
                        src/vps \
@@ -148,6 +151,9 @@ sbin_PROGRAMS +=    src/chbind \
                        src/vserver-info \
                        src/vuname \
                        src/vwait \
+                       src/ncontext \
+                       src/nattribute \
+                       src/naddress \
                        $(src_sbin_CXX_X_PROGS)
 
 if ENSC_HAVE_C99_COMPILER
@@ -159,9 +165,9 @@ src_capchroot_SOURCES =             src/capchroot.c
 src_capchroot_LDADD =          $(VSERVER_LDADDS)
 src_capchroot_LDFLAGS =                $(VSERVER_LDFLGS)
 
-src_chbind_SOURCES =           src/chbind.c
-src_chbind_LDADD =             $(VSERVER_LDADDS)
-src_chbind_LDFLAGS =           $(VSERVER_LDFLGS)
+src_chbind_compat_SOURCES =    src/chbind.c
+src_chbind_compat_LDADD =      $(VSERVER_LDADDS)
+src_chbind_compat_LDFLAGS =    $(VSERVER_LDFLGS)
 
 src_chcontext_compat_SOURCES = src/chcontext.c
 src_chcontext_compat_LDADD =   $(VSERVER_LDADDS) $(LIBINTERNAL)
@@ -301,6 +307,18 @@ src_vwait_SOURCES          = src/vwait.c
 src_vwait_LDADD                        = $(VSERVER_LDADDS)
 src_vwait_LDFLAGS              = $(VSERVER_LDFLGS)
 
+src_ncontext_SOURCES           = src/ncontext.c
+src_ncontext_LDADD             = $(VSERVER_LDADDS) $(LIBINTERNAL)
+src_ncontext_LDFLAGS           = $(VSERVER_LDFLGS)
+
+src_nattribute_SOURCES         = src/nattribute.c
+src_nattribute_LDADD           = $(VSERVER_LDADDS)
+src_nattribute_LDFLAGS         = $(VSERVER_LDFLGS)
+
+src_naddress_SOURCES           = src/naddress.c
+src_naddress_LDADD             = $(VSERVER_LDADDS)
+src_naddress_LDFLAGS           = $(VSERVER_LDFLGS)
+
 EXTRA_PROGRAMS +=              $(src_sbin_CXX_PROGS) $(src_pkglib_CXX_PROGS)
 
 TESTS_ENVIRONMENT +=           srctestsuitedir=$(top_builddir)/src/testsuite
index 4fb628e..f017c33 100644 (file)
@@ -316,7 +316,7 @@ int main (int argc, char *argv[])
       case CMD_SILENT          :  is_silent = true; break;
       case CMD_BCAST           :  readBcast(optarg, &bcast); break;
 #if defined(VC_ENABLE_API_NET)
-      case CMD_NID             :  nid = Evc_xidopt2xid(optarg,true); break;
+      case CMD_NID             :  nid = Evc_nidopt2nid(optarg,true); break;
 #else
       case CMD_NID             :  WRITE_MSG(2, "WARNING: --nid is not supported by this version\n"); break;
 #endif
diff --git a/src/naddress.c b/src/naddress.c
new file mode 100644 (file)
index 0000000..0d1efc1
--- /dev/null
@@ -0,0 +1,416 @@
+// $Id$
+
+// Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+// Copyright (C) 2006 Daniel Hokka Zakrisson <daniel@hozac.com>
+// based on chbind.cc by Jacques Gelinas
+//  
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2, or (at your option)
+// any later version.
+//  
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//  
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "vserver.h"
+#include "util.h"
+
+#include <lib/internal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define ENSC_WRAPPERS_PREFIX   "naddress: "
+#define ENSC_WRAPPERS_IO       1
+#define ENSC_WRAPPERS_UNISTD   1
+#define ENSC_WRAPPERS_VSERVER  1
+#include "wrappers.h"
+
+#define CMD_HELP       0x1000
+#define CMD_VERSION    0x1001
+
+#define CMD_SILENT     0x2000
+#define CMD_NID                0x2001
+#define CMD_ADD                0x2002
+#define CMD_REMOVE     0x2003
+#define CMD_IP         0x2010
+#define CMD_BCAST      0x2011
+
+int wrapper_exit_code = 255;
+
+
+static struct option const
+CMDLINE_OPTIONS[] = {
+  { "help",     no_argument,  0, CMD_HELP },
+  { "version",  no_argument,  0, CMD_VERSION },
+  { "silent",   no_argument,  0, CMD_SILENT },
+  { "add",      no_argument,  0, CMD_ADD },
+  { "remove",   no_argument,  0, CMD_REMOVE },
+  { "nid",      required_argument, 0, CMD_NID },
+  { "ip",       required_argument, 0, CMD_IP },
+  { "bcast",    required_argument, 0, CMD_BCAST },
+  { 0,0,0,0 }
+};
+
+struct vc_ips {
+  struct vc_net_nx a;
+  struct vc_ips *next;
+};
+
+struct Arguments {
+  nid_t                nid;
+  struct vc_ips        head;
+  bool         is_silent;
+  bool         do_add;
+  bool         do_remove;
+};
+
+static void
+showHelp(int fd, char const *cmd, int res)
+{
+  WRITE_MSG(fd, "Usage:\n  ");
+  WRITE_STR(fd, cmd);
+  WRITE_MSG(fd,
+           " [--silent] [--nid <nid>] [--ip <ip_num>[/<mask>]] [--bcast <broadcast>] [--] <commands> <args>*\n\n"
+           "Please report bugs to " PACKAGE_BUGREPORT "\n");
+
+  exit(res);
+}
+
+static void
+showVersion()
+{
+  WRITE_MSG(1,
+           "naddress " VERSION " -- bind to an ip and execute a program\n"
+           "This program is part of " PACKAGE_STRING "\n\n"
+           "Copyright (C) 2003,2004 Enrico Scholz\n"
+           "Copyright (C) 2006 Daniel Hokka Zakrisson\n"
+           VERSION_COPYRIGHT_DISCLAIMER);
+  exit(0);
+}
+
+/*
+       Check if a network device exist in /proc/net/dev.
+       This is used because ifconfig_ioctl triggers modprobe if requesting
+       information about non existant devices.
+
+       Return != 0 if the device exist.
+*/
+static bool
+existsDevice(char const *dev_raw)
+{
+  size_t       buf_size=8192;
+  char         dev[strlen(dev_raw)+2];
+
+  strcpy(dev, dev_raw);
+  strcat(dev, ":");
+  for (;;) {
+    char       buf[buf_size];
+    char *     pos;
+    bool       too_small;
+    int                fd=open("/proc/net/dev", O_RDONLY);
+    
+    if (fd==-1) return false;
+    too_small = EreadAll(fd, buf, buf_size);
+    close(fd);
+
+    if (too_small) {
+      buf_size *= 2;
+      continue;
+    }
+
+    pos = strstr(buf, dev);
+    return (pos && (pos==buf || pos[-1]==' ' || pos[-1]=='\n'));
+  }
+}
+
+static int ifconfig_ioctl(
+       int fd,
+       const char *ifname,
+       int cmd,
+       struct ifreq *ifr)
+{
+       strcpy(ifr->ifr_name, ifname);
+       return ioctl(fd, cmd, ifr);
+}
+
+/*
+       Fetch the IP number of an interface from the kernel.
+       Assume the device is already available in the kernel
+       Return -1 if any error.
+*/
+int ifconfig_getaddr (
+       const char *ifname,
+       uint32_t *addr,
+       uint32_t *mask,
+       uint32_t *bcast)
+{
+       int ret = -1;
+       if (existsDevice(ifname)){
+               int skfd = socket(AF_INET, SOCK_DGRAM, 0);
+               if (skfd != -1){
+                       struct ifreq ifr;
+                       if (addr != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFADDR, &ifr) >= 0){
+                               struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
+                               *addr = sin->sin_addr.s_addr;
+                               ret = 0;
+                       }
+                       if (mask != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFNETMASK, &ifr) >= 0){
+                               struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
+                               *mask = sin->sin_addr.s_addr;
+                               ret = 0;
+                       }
+                       if (bcast != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFBRDADDR, &ifr) >= 0){
+                               struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
+                               *bcast = sin->sin_addr.s_addr;
+                               ret = 0;
+                       }
+                       close (skfd);
+               }
+       }
+       return ret;
+}
+
+static int
+convertAddress(const char *str, vc_net_nx_type *type, void *dst)
+{
+  int  ret;
+  if (type) *type = vcNET_IPV4;
+  ret = inet_pton(AF_INET, str, dst);
+  if (ret==0) {
+    if (type) *type = vcNET_IPV6;
+    ret = inet_pton(AF_INET6, str, dst);
+  }
+  return ret > 0 ? 0 : -1;
+}
+
+static void
+readIP(char const *str, struct vc_ips **ips)
+{
+  if (ifconfig_getaddr(str, &(*ips)->a.ip[0], &(*ips)->a.mask[0], NULL)==-1) {
+    char               *pt;
+    char               tmpopt[strlen(str)+1];
+    uint32_t           *mask = (*ips)->a.mask;
+
+    strcpy(tmpopt,str);
+    pt = strchr(tmpopt,'/');
+    if (pt)
+      *pt++ = '\0';
+
+    if (convertAddress(tmpopt, &(*ips)->a.type, (*ips)->a.ip) == -1) {
+      WRITE_MSG(2, "Invalid IP number '");
+      WRITE_STR(2, tmpopt);
+      WRITE_MSG(2, "'\n");
+      exit(wrapper_exit_code);
+    }
+
+    if (pt==0) {
+      switch ((*ips)->a.type) {
+       case vcNET_IPV4:
+         mask[0] = htonl(0xffffff00);
+         break;
+       case vcNET_IPV6:
+         mask[0] = 64;
+         break;
+       default: break;
+      }
+    }
+    else {
+      // Ok, we have a network size, not a netmask
+      if (strchr(pt,'.')==0 && strchr(pt,':')==0) {
+       int             sz = atoi(pt);
+       switch ((*ips)->a.type) {
+         case vcNET_IPV4:
+           mask[0] = htonl((1 << sz) - 1);
+           break;
+         case vcNET_IPV6:
+           mask[0] = sz;
+           break;
+         default: break;
+       }
+      }
+      else { 
+       if (convertAddress(pt, NULL, &(*ips)->a.mask) == -1) {
+         WRITE_MSG(2, "Invalid netmask '");
+         WRITE_STR(2, pt);
+         WRITE_MSG(2, "'\n");
+         exit(wrapper_exit_code);
+       }
+      }
+    }
+  }
+  else
+    (*ips)->a.type = vcNET_IPV4;
+
+  (*ips)->a.count = 1;
+  (*ips)->next = calloc(1, sizeof(struct vc_ips));
+  *ips = (*ips)->next;
+}
+
+static void
+readBcast(char const *str, struct vc_ips **ips)
+{
+  uint32_t bcast;
+  if (ifconfig_getaddr(str, NULL, NULL, &bcast)==-1){
+    if (convertAddress(str, NULL, &bcast) == -1) {
+      WRITE_MSG(2, "Invalid broadcast number '");
+      WRITE_STR(2, optarg);
+      WRITE_MSG(2, "'\n");
+      exit(wrapper_exit_code);
+    }
+  }
+  (*ips)->a.ip[0] = bcast;
+  (*ips)->a.count = 1;
+  (*ips)->a.type = vcNET_IPV4B;
+  (*ips)->next = calloc(1, sizeof(struct vc_ips));
+  *ips = (*ips)->next;
+}
+
+static void
+tellAddress(struct vc_net_nx *addr, bool silent)
+{
+  char buf[41];
+  if (silent)
+    return;
+  if (inet_ntop(addr->type == vcNET_IPV6 ? AF_INET6 : AF_INET,
+               &addr->ip, buf, sizeof(buf)) == NULL) {
+    WRITE_MSG(1, " <conversion failed>");
+    return;
+  }
+  WRITE_MSG(1, " ");
+  WRITE_STR(1, buf);
+}
+
+static inline void
+doit(struct Arguments *args)
+{
+  struct vc_ips *ips;
+  if (args->do_add) {
+    if (!args->is_silent)
+      WRITE_MSG(1, "Adding");
+    for (ips = &args->head; ips->next; ips = ips->next) {
+      tellAddress(&ips->a, args->is_silent);
+      if (vc_net_add(args->nid, &ips->a) != (int)ips->a.count) {
+       perror(ENSC_WRAPPERS_PREFIX "vc_net_add()");
+       exit(wrapper_exit_code);
+      }
+    }
+    if (!args->is_silent)
+      WRITE_MSG(1, "\n");
+  }
+  else if (args->do_remove) {
+    if (!args->is_silent)
+      WRITE_MSG(1, "Removing");
+    for (ips = &args->head; ips->next; ips = ips->next) {
+      tellAddress(&ips->a, args->is_silent);
+      if (vc_net_remove(args->nid, &ips->a) != (int)ips->a.count) {
+       perror(ENSC_WRAPPERS_PREFIX "vc_net_remove()");
+       exit(wrapper_exit_code);
+      }
+    }
+    if (!args->is_silent)
+      WRITE_MSG(1, "\n");
+  }
+}
+
+int main (int argc, char *argv[])
+{
+  struct Arguments args = {
+    .nid       = VC_NOCTX,
+    .is_silent = false,
+    .do_add    = false,
+    .do_remove = false,
+    .head      = { .next = NULL },
+  };
+  struct vc_ips *ips = &args.head;
+  
+  while (1) {
+    int                c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
+    if (c==-1) break;
+
+    switch (c) {
+      case CMD_HELP            :  showHelp(1, argv[0], 0);
+      case CMD_VERSION         :  showVersion();
+      case CMD_SILENT          :  args.is_silent = true; break;
+      case CMD_BCAST           :  readBcast(optarg, &ips); break;
+      case CMD_NID             :  args.nid = Evc_nidopt2nid(optarg,true); break;
+      case CMD_ADD             :  args.do_add = true; break;
+      case CMD_REMOVE          :  args.do_remove = true; break;
+      case CMD_IP              :  readIP(optarg, &ips); break;
+      default          :
+       WRITE_MSG(2, "Try '");
+       WRITE_STR(2, argv[0]);
+       WRITE_MSG(2, " --help\" for more information.\n");
+       exit(wrapper_exit_code);
+       break;
+    }
+  }
+
+  if (args.nid == VC_NOCTX) args.nid = Evc_get_task_nid(0);
+
+  if (!args.do_add && !args.do_remove) {
+    WRITE_MSG(2, "No operation specified; try '--help' for more information\n");
+    exit(wrapper_exit_code);
+  }
+  else if (args.do_add && args.do_remove) {
+    WRITE_MSG(2, "Multiple operations specified; try '--help' for more information\n");
+    exit(wrapper_exit_code);
+  }
+
+  doit(&args);
+
+  if (optind != argc)
+    Eexecvp (argv[optind],argv+optind);
+  return EXIT_SUCCESS;
+}
+
+#ifdef ENSC_TESTSUITE
+#include <assert.h>
+
+void test()
+{
+  struct vc_ip_mask_pair       ip;
+  uint32_t                     bcast;
+
+  bcast = 0;
+  readIP("1.2.3.4", &ip, &bcast);
+  assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffffff00) && bcast==0);
+
+  readIP("1.2.3.4/8", &ip, &bcast);
+  assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xff000000) && bcast==0);
+
+  readIP("1.2.3.4/255.255.0.0", &ip, &bcast);
+  assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffff0000) && bcast==0);
+
+  readIP("localhost", &ip, &bcast);
+  assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xffffff00) && bcast==0);
+
+#if 0
+  if (ifconfig_getaddr("lo", &tmp, &tmp, &tmp)!=-1) {
+    readIP("lo", &ip, &bcast);
+    assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xff000000) && bcast==ntohl(0x7fffffff));
+  }
+#endif
+}
+#endif
diff --git a/src/nattribute.c b/src/nattribute.c
new file mode 100644 (file)
index 0000000..5199525
--- /dev/null
@@ -0,0 +1,179 @@
+// $Id$    --*- c -*--
+
+// Copyright (C) 2004 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+// Copyright (C) 2006 Daniel Hokka Zakrisson
+//  
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//  
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//  
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "util.h"
+#include <lib/vserver.h>
+
+#include <getopt.h>
+#include <stdint.h>
+#include <errno.h>
+
+#define ENSC_WRAPPERS_PREFIX   "nattribute: "
+#define ENSC_WRAPPERS_VSERVER  1
+#define ENSC_WRAPPERS_UNISTD   1
+#include <wrappers.h>
+
+#define CMD_HELP               0x1000
+#define CMD_VERSION            0x1001
+#define CMD_NID                        0x2000
+#define CMD_SET                        0x2001
+#define CMD_CAP                        0x2002
+#define CMD_FLAG               0x2003
+#define CMD_SECURE             0x2004
+
+int                    wrapper_exit_code = 1;
+
+struct option const
+CMDLINE_OPTIONS[] = {
+  { "help",       no_argument,       0, CMD_HELP },
+  { "version",    no_argument,       0, CMD_VERSION },
+  { "nid",        required_argument, 0, CMD_NID },
+  { "set",        no_argument,       0, CMD_SET },
+  { "ncap",       required_argument, 0, CMD_CAP },
+  { "flag",       required_argument, 0, CMD_FLAG },
+  { "secure",     no_argument,       0, CMD_SECURE },
+  {0,0,0,0}
+};
+
+struct Arguments {
+    nid_t              nid;
+    struct vc_net_flags flags;
+    struct vc_net_caps  caps;
+};
+
+static void
+showHelp(int fd, char const *cmd, int res)
+{
+  WRITE_MSG(fd, "Usage:\n    ");
+  WRITE_STR(fd, cmd);
+  WRITE_MSG(fd,
+           " --set [--nid <nid>] [--ncap [~!]<ncap>] [--flag [~!]<flag>] [--secure] -- [<program> <args>*]\n"
+           "\n"
+           " --ncap <cap>   ...  network capability to be added\n"
+           " --flag <flag>  ...  network flag to be added\n"
+           "\n"
+           "Please report bugs to " PACKAGE_BUGREPORT "\n");
+
+  exit(res);
+}
+
+static void
+showVersion()
+{
+  WRITE_MSG(1,
+           "nattribute " VERSION " -- sets attributes of network contexts\n"
+           "This program is part of " PACKAGE_STRING "\n\n"
+           "Copyright (C) 2004 Enrico Scholz\n"
+           "Copyright (C) 2006 Daniel Hokka Zakrisson\n"
+           VERSION_COPYRIGHT_DISCLAIMER);
+  exit(0);
+}
+
+static void
+parseFlags(char const *str, struct vc_net_flags *flags)
+{
+  struct vc_err_listparser     err;
+  int                          rc;
+
+  rc = vc_list2nflag(str, 0, &err, flags);
+  
+  if (rc==-1) {
+    WRITE_MSG(2, "Unknown flag '");
+    Vwrite(2, err.ptr, err.len);
+    WRITE_MSG(2, "'\n");
+    exit(wrapper_exit_code);
+  }
+}
+
+static void
+parseNCaps(char const *str, struct vc_net_caps *caps)
+{
+  struct vc_err_listparser     err;
+  int                          rc;
+
+  rc = vc_list2ncap(str,0, &err, caps);
+  
+  if (rc==-1) {
+    WRITE_MSG(2, "Unknown ncap '");
+    Vwrite(2, err.ptr, err.len);
+    WRITE_MSG(2, "'\n");
+    exit(wrapper_exit_code);
+  }
+}
+
+static void
+parseSecure(struct vc_net_flags * flags,
+           struct vc_net_caps  * caps)
+{
+    // TODO: generalize this
+  caps->ncaps = 0ull;
+  caps->cmask = 0ull;
+
+  flags->flagword = 0ull;
+  flags->mask     = 0ull;
+}
+
+int main(int argc, char *argv[])
+{
+  struct Arguments             args = {
+    .nid   = VC_NOCTX,
+    .flags = { .flagword = 0, .mask = 0 },
+    .caps  = { .ncaps = 0, .cmask = 0 },
+  };
+  
+  while (1) {
+    int                c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
+    if (c==-1) break;
+
+    switch (c) {
+      case CMD_HELP    :  showHelp(1, argv[0], 0);
+      case CMD_VERSION :  showVersion();
+      case CMD_SET     :  break; // default op currently
+      case CMD_NID     :  args.nid = Evc_nidopt2nid(optarg,true); break;
+      case CMD_FLAG    :  parseFlags(optarg, &args.flags);        break;
+      case CMD_CAP     :  parseNCaps(optarg, &args.caps);         break;
+      case CMD_SECURE  :  parseSecure(&args.flags, &args.caps);   break;
+      default          :
+       WRITE_MSG(2, "Try '");
+       WRITE_STR(2, argv[0]);
+       WRITE_MSG(2, " --help\" for more information.\n");
+       return 255;
+       break;
+    }
+  }
+
+  if (args.nid==VC_NOCTX) args.nid = Evc_get_task_nid(0);
+
+  if (args.caps.cmask &&
+      vc_set_ncaps(args.nid, &args.caps)==-1)
+    perror(ENSC_WRAPPERS_PREFIX "vc_set_ncaps()");
+  else if (args.flags.mask &&
+          vc_set_nflags(args.nid, &args.flags)==-1)
+    perror(ENSC_WRAPPERS_PREFIX "vc_set_nflags()");
+  else if (optind<argc)
+    EexecvpD(argv[optind], argv+optind);
+  else
+    return EXIT_SUCCESS;
+
+  return EXIT_FAILURE;
+}
diff --git a/src/ncontext.c b/src/ncontext.c
new file mode 100644 (file)
index 0000000..158732d
--- /dev/null
@@ -0,0 +1,293 @@
+// $Id$    --*- c -*--
+
+// Copyright (C) 2004-2006 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+// Copyright (C) 2006 Daniel Hokka Zakrisson <daniel@hozac.com>
+//  
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//  
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//  
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "util.h"
+#include "lib/internal.h"
+#include "lib_internal/util.h"
+#include "lib_internal/jail.h"
+
+#include <vserver.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/types.h>
+
+
+#define ENSC_WRAPPERS_PREFIX   "ncontext: "
+#define ENSC_WRAPPERS_UNISTD   1
+#define ENSC_WRAPPERS_VSERVER  1
+#define ENSC_WRAPPERS_FCNTL    1
+#define ENSC_WRAPPERS_SOCKET   1
+#define ENSC_WRAPPERS_IOSOCK   1
+#include <wrappers.h>
+
+#define CMD_HELP               0x1000
+#define CMD_VERSION            0x1001
+#define CMD_NID                        0x4000
+#define CMD_CREATE             0x4001
+#define CMD_MIGRATE            0x4002
+#define CMD_DISCONNECT         0x4003
+#define CMD_SILENT             0x4004
+#define CMD_SYNCSOCK           0x4005
+#define CMD_SYNCMSG            0x4006
+#define CMD_MIGRATESELF                0x4007
+#define CMD_SILENTEXIST                0x4008
+
+
+struct option const
+CMDLINE_OPTIONS[] = {
+  { "help",       no_argument,       0, CMD_HELP },
+  { "version",    no_argument,       0, CMD_VERSION },
+  { "nid",        required_argument, 0, CMD_NID },
+  { "create",     no_argument,       0, CMD_CREATE },
+  { "migrate",    no_argument,       0, CMD_MIGRATE },
+  { "migrate-self", no_argument,               0, CMD_MIGRATESELF },
+  { "disconnect",   no_argument,       0, CMD_DISCONNECT },
+  { "silent",       no_argument,               0, CMD_SILENT },
+  { "silentexist",  no_argument,               0, CMD_SILENTEXIST },
+  { "syncsock",     required_argument,         0, CMD_SYNCSOCK },
+  { "syncmsg",      required_argument,         0, CMD_SYNCMSG },
+  { 0,0,0,0 },
+};
+
+struct Arguments {
+    bool               do_create;
+    bool               do_migrate;
+    bool               do_migrateself;
+    bool               do_disconnect;
+    bool               is_silentexist;
+    int                        verbosity;
+    nid_t              nid;
+    char const *       sync_sock;
+    char const *       sync_msg;
+};
+
+int            wrapper_exit_code = 255;
+
+static void
+showHelp(int fd, char const *cmd, int res)
+{
+  WRITE_MSG(fd, "Usage:\n    ");
+  WRITE_STR(fd, cmd);
+  WRITE_MSG(fd,
+           " --create [--nid <nid>] <opts>* [--] <program> <args>*\n    ");
+  WRITE_STR(fd, cmd);
+  WRITE_MSG(fd,
+           " [(--migrate --nid <nid>)|--migrate-self]  <opts>* [--] <program> <args>*\n"
+           "\n"
+           "<opts> can be:\n"
+           "    --disconnect    ...  start program in background\n"
+           "    --silent        ...  be silent\n"
+           "    --silentexist   ...  be silent when context exists already; useful\n"
+           "                         for '--create' only\n"
+           "    --syncsock <filename>\n"
+           "                    ...  before executing the program, send a message\n"
+           "                         to the socket and wait until it closes.\n"
+           "                         <filename> must be a SOCK_STREAM unix socket\n"
+           "    --syncmsg <message>\n"
+           "                    ...  use <message> as synchronization message; by\n"
+           "                         default, 'ok' will be used\n"
+           "\n"
+           "'ncontext --create' exits with code 254 iff the context exists already.\n"
+           "\n"
+           "Please report bugs to " PACKAGE_BUGREPORT "\n");
+
+  exit(res);
+}
+
+static void
+showVersion()
+{
+  WRITE_MSG(1,
+           "ncontext " VERSION " -- manages the creation of network contexts\n"
+           "This program is part of " PACKAGE_STRING "\n\n"
+           "Copyright (C) 2004-2006 Enrico Scholz\n"
+           "Copyright (C) 2006 Daniel Hokka Zakrisson\n"
+           VERSION_COPYRIGHT_DISCLAIMER);
+  exit(0);
+}
+
+#include "context-sync.hc"
+
+static inline ALWAYSINLINE void
+tellContext(nid_t nid, bool do_it)
+{
+  char         buf[sizeof(nid_t)*3+2];
+  size_t       l;
+
+  if (!do_it) return;
+
+  l = utilvserver_fmt_long(buf,nid);
+
+  WRITE_MSG(1, "New network context is ");
+  Vwrite   (1, buf, l);
+  WRITE_MSG(1, "\n");
+}
+
+static int
+connectExternalSync(char const *filename)
+{
+  int                  fd;
+  struct sockaddr_un   addr;
+  
+  if (filename==0) return -1;
+
+  ENSC_INIT_UNIX_SOCK(addr, filename);
+
+  fd = Esocket(PF_UNIX, SOCK_STREAM, 0);
+  Econnect(fd, &addr, sizeof(addr));
+
+  return fd;
+}
+
+static void
+doExternalSync(int fd, char const *msg)
+{
+  char         c;
+  
+  if (fd==-1) return;
+
+  if (msg) EsendAll(fd, msg, strlen(msg));
+  Eshutdown(fd, SHUT_WR);
+
+  if (TEMP_FAILURE_RETRY(recv(fd, &c, 1, MSG_NOSIGNAL))!=0) {
+    WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "unexpected external synchronization event\n");
+    exit(wrapper_exit_code);
+  }
+
+  Eclose(fd);
+}
+
+static inline ALWAYSINLINE int
+doit(struct Arguments const *args, int argc, char *argv[])
+{
+  int                  p[2][2];
+  pid_t                        pid = initSync(p, args->do_disconnect);
+  
+  if (pid==0) {
+    nid_t                      nid;
+    int                                ext_sync_fd = connectExternalSync(args->sync_sock);
+
+    doSyncStage0(p, args->do_disconnect);  
+    
+    if (args->do_create) {
+      nid = vc_net_create(args->nid);
+      if (nid==VC_NOCTX) {
+       switch (errno) {
+         case EEXIST   :
+           if (!args->is_silentexist)
+             perror(ENSC_WRAPPERS_PREFIX "vc_net_create()");
+           return 254;
+         default       :
+           perror(ENSC_WRAPPERS_PREFIX "vc_net_create()");
+           return wrapper_exit_code;
+       }
+      }
+      tellContext(nid, args->verbosity>=1);
+    }
+    else
+      nid = args->nid;
+
+    if (args->do_migrate && !args->do_migrateself)
+      Evc_net_migrate(nid);
+
+    doExternalSync(ext_sync_fd, args->sync_msg);
+    doSyncStage1(p, args->do_disconnect);
+    DPRINTF("doit: pid=%u, ppid=%u\n", getpid(), getppid());
+    execvp (argv[optind],argv+optind);
+    doSyncStage2(p, args->do_disconnect);
+
+    PERROR_Q(ENSC_WRAPPERS_PREFIX "execvp", argv[optind]);
+    exit(wrapper_exit_code);
+  }
+
+  assert(args->do_disconnect);
+    
+  waitOnSync(pid, p, args->nid!=VC_DYNAMIC_XID && args->do_migrate);
+  return EXIT_SUCCESS;
+}
+
+int main (int argc, char *argv[])
+{
+  struct Arguments             args = {
+    .nid               = VC_DYNAMIC_XID,
+    .do_create         = false,
+    .do_migrate        = false,
+    .do_migrateself    = false,
+    .do_disconnect     = false,
+    .is_silentexist    = false,
+    .verbosity         = 1,
+    .sync_msg          = "ok",
+  };
+  
+  while (1) {
+    int                c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
+    if (c==-1) break;
+    
+    switch (c) {
+      case CMD_HELP            :  showHelp(1, argv[0], 0);
+      case CMD_VERSION         :  showVersion();
+      case CMD_CREATE          :  args.do_create      = true;   break;
+      case CMD_MIGRATE         :  args.do_migrate     = true;   break;
+      case CMD_DISCONNECT      :  args.do_disconnect  = true;   break;
+      case CMD_SILENTEXIST     :  args.is_silentexist = true;   break;
+      case CMD_SYNCSOCK                :  args.sync_sock      = optarg; break;
+      case CMD_SYNCMSG         :  args.sync_msg       = optarg; break;
+      case CMD_NID             :  args.nid = Evc_nidopt2nid(optarg,true); break;
+      case CMD_SILENT          :  --args.verbosity; break;
+      case CMD_MIGRATESELF     :
+       args.do_migrate     = true;
+       args.do_migrateself = true;
+       break;
+
+      default          :
+       WRITE_MSG(2, "Try '");
+       WRITE_STR(2, argv[0]);
+       WRITE_MSG(2, " --help' for more information.\n");
+       return wrapper_exit_code;
+       break;
+    }
+  }
+
+  signal(SIGCHLD, SIG_DFL);
+  
+  if (args.do_migrateself)
+    args.nid = Evc_get_task_nid(0);
+  
+  if (!args.do_create && !args.do_migrate)
+    WRITE_MSG(2, "Neither '--create' nor '--migrate specified; try '--help' for more information\n");
+  else if (args.do_create && args.do_migrate)
+    WRITE_MSG(2, "Can not specify '--create' and '--migrate' at the same time; try '--help' for more information\n");
+  else if (!args.do_create && args.nid==VC_DYNAMIC_XID)
+    WRITE_MSG(2, "Can not migrate to an unknown context\n");
+  else if (optind>=argc)
+    WRITE_MSG(2, "No command given; use '--help' for more information.\n");
+  else
+    return doit(&args, argc, argv);
+
+  return wrapper_exit_code;
+}