merge with 0.30.213
[util-vserver.git] / src / ncontext.c
1 // $Id: ncontext.c 2414 2006-12-08 13:20:10Z dhozac $    --*- c -*--
2
3 // Copyright (C) 2004-2006 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 // Copyright (C) 2006 Daniel Hokka Zakrisson <daniel@hozac.com>
5 //  
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; version 2 of the License.
9 //  
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //  
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif
23
24 #include "util.h"
25 #include "lib/internal.h"
26 #include "lib_internal/util.h"
27 #include "lib_internal/jail.h"
28
29 #include <vserver.h>
30 #include <getopt.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <sys/socket.h>
34 #include <sys/un.h>
35 #include <assert.h>
36 #include <signal.h>
37 #include <sys/types.h>
38
39
40 #define ENSC_WRAPPERS_PREFIX    "ncontext: "
41 #define ENSC_WRAPPERS_UNISTD    1
42 #define ENSC_WRAPPERS_VSERVER   1
43 #define ENSC_WRAPPERS_FCNTL     1
44 #define ENSC_WRAPPERS_SOCKET    1
45 #define ENSC_WRAPPERS_IOSOCK    1
46 #include <wrappers.h>
47
48 #define CMD_HELP                0x1000
49 #define CMD_VERSION             0x1001
50 #define CMD_NID                 0x4000
51 #define CMD_CREATE              0x4001
52 #define CMD_MIGRATE             0x4002
53 #define CMD_DISCONNECT          0x4003
54 #define CMD_SILENT              0x4004
55 #define CMD_SYNCSOCK            0x4005
56 #define CMD_SYNCMSG             0x4006
57 #define CMD_MIGRATESELF         0x4007
58 #define CMD_SILENTEXIST         0x4008
59
60
61 struct option const
62 CMDLINE_OPTIONS[] = {
63   { "help",       no_argument,       0, CMD_HELP },
64   { "version",    no_argument,       0, CMD_VERSION },
65   { "nid",        required_argument, 0, CMD_NID },
66   { "create",     no_argument,       0, CMD_CREATE },
67   { "migrate",    no_argument,       0, CMD_MIGRATE },
68   { "migrate-self", no_argument,        0, CMD_MIGRATESELF },
69   { "disconnect",   no_argument,        0, CMD_DISCONNECT },
70   { "silent",       no_argument,        0, CMD_SILENT },
71   { "silentexist",  no_argument,        0, CMD_SILENTEXIST },
72   { "syncsock",     required_argument,  0, CMD_SYNCSOCK },
73   { "syncmsg",      required_argument,  0, CMD_SYNCMSG },
74   { 0,0,0,0 },
75 };
76
77 struct Arguments {
78     bool                do_create;
79     bool                do_migrate;
80     bool                do_migrateself;
81     bool                do_disconnect;
82     bool                is_silentexist;
83     int                 verbosity;
84     nid_t               nid;
85     char const *        sync_sock;
86     char const *        sync_msg;
87 };
88
89 int             wrapper_exit_code = 255;
90
91 static void
92 showHelp(int fd, char const *cmd, int res)
93 {
94   WRITE_MSG(fd, "Usage:\n    ");
95   WRITE_STR(fd, cmd);
96   WRITE_MSG(fd,
97             " --create [--nid <nid>] <opts>* [--] <program> <args>*\n    ");
98   WRITE_STR(fd, cmd);
99   WRITE_MSG(fd,
100             " [(--migrate --nid <nid>)|--migrate-self]  <opts>* [--] <program> <args>*\n"
101             "\n"
102             "<opts> can be:\n"
103             "    --disconnect    ...  start program in background\n"
104             "    --silent        ...  be silent\n"
105             "    --silentexist   ...  be silent when context exists already; useful\n"
106             "                         for '--create' only\n"
107             "    --syncsock <filename>\n"
108             "                    ...  before executing the program, send a message\n"
109             "                         to the socket and wait until it closes.\n"
110             "                         <filename> must be a SOCK_STREAM unix socket\n"
111             "    --syncmsg <message>\n"
112             "                    ...  use <message> as synchronization message; by\n"
113             "                         default, 'ok' will be used\n"
114             "\n"
115             "'ncontext --create' exits with code 254 iff the context exists already.\n"
116             "\n"
117             "Please report bugs to " PACKAGE_BUGREPORT "\n");
118
119   exit(res);
120 }
121
122 static void
123 showVersion()
124 {
125   WRITE_MSG(1,
126             "ncontext " VERSION " -- manages the creation of network contexts\n"
127             "This program is part of " PACKAGE_STRING "\n\n"
128             "Copyright (C) 2004-2006 Enrico Scholz\n"
129             "Copyright (C) 2006 Daniel Hokka Zakrisson\n"
130             VERSION_COPYRIGHT_DISCLAIMER);
131   exit(0);
132 }
133
134 #include "context-sync.hc"
135
136 static inline ALWAYSINLINE void
137 tellContext(nid_t nid, bool do_it)
138 {
139   char          buf[sizeof(nid_t)*3+2];
140   size_t        l;
141
142   if (!do_it) return;
143
144   l = utilvserver_fmt_long(buf,nid);
145
146   WRITE_MSG(1, "New network context is ");
147   Vwrite   (1, buf, l);
148   WRITE_MSG(1, "\n");
149 }
150
151 static int
152 connectExternalSync(char const *filename)
153 {
154   int                   fd;
155   struct sockaddr_un    addr;
156   
157   if (filename==0) return -1;
158
159   ENSC_INIT_UNIX_SOCK(addr, filename);
160
161   fd = Esocket(PF_UNIX, SOCK_STREAM, 0);
162   Econnect(fd, &addr, sizeof(addr));
163
164   return fd;
165 }
166
167 static void
168 doExternalSync(int fd, char const *msg)
169 {
170   char          c;
171   
172   if (fd==-1) return;
173
174   if (msg) EsendAll(fd, msg, strlen(msg));
175   Eshutdown(fd, SHUT_WR);
176
177   if (TEMP_FAILURE_RETRY(recv(fd, &c, 1, MSG_NOSIGNAL))!=0) {
178     WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "unexpected external synchronization event\n");
179     exit(wrapper_exit_code);
180   }
181
182   Eclose(fd);
183 }
184
185 static inline ALWAYSINLINE int
186 doit(struct Arguments const *args, char *argv[])
187 {
188   int                   p[2][2];
189   pid_t                 pid = initSync(p, args->do_disconnect);
190   
191   if (pid==0) {
192     nid_t                       nid;
193     int                         ext_sync_fd = connectExternalSync(args->sync_sock);
194
195     doSyncStage0(p, args->do_disconnect);  
196     
197     if (args->do_create) {
198       nid = vc_net_create(args->nid);
199       if (nid==VC_NOCTX) {
200         switch (errno) {
201           case EEXIST   :
202             if (!args->is_silentexist)
203               perror(ENSC_WRAPPERS_PREFIX "vc_net_create()");
204             return 254;
205           default       :
206             perror(ENSC_WRAPPERS_PREFIX "vc_net_create()");
207             return wrapper_exit_code;
208         }
209       }
210       tellContext(nid, args->verbosity>=1);
211     }
212     else
213       nid = args->nid;
214
215     if (args->do_migrate && !args->do_migrateself)
216       Evc_net_migrate(nid);
217
218     doExternalSync(ext_sync_fd, args->sync_msg);
219     doSyncStage1(p, args->do_disconnect);
220     DPRINTF("doit: pid=%u, ppid=%u\n", getpid(), getppid());
221     execvp (argv[optind],argv+optind);
222     doSyncStage2(p, args->do_disconnect);
223
224     PERROR_Q(ENSC_WRAPPERS_PREFIX "execvp", argv[optind]);
225     exit(wrapper_exit_code);
226   }
227
228   assert(args->do_disconnect);
229     
230   waitOnSync(pid, p, args->nid!=VC_DYNAMIC_XID && args->do_migrate);
231   return EXIT_SUCCESS;
232 }
233
234 int main (int argc, char *argv[])
235 {
236   struct Arguments              args = {
237     .nid               = VC_DYNAMIC_XID,
238     .do_create         = false,
239     .do_migrate        = false,
240     .do_migrateself    = false,
241     .do_disconnect     = false,
242     .is_silentexist    = false,
243     .verbosity         = 1,
244     .sync_msg          = "ok",
245   };
246   
247   while (1) {
248     int         c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
249     if (c==-1) break;
250     
251     switch (c) {
252       case CMD_HELP             :  showHelp(1, argv[0], 0);
253       case CMD_VERSION          :  showVersion();
254       case CMD_CREATE           :  args.do_create      = true;   break;
255       case CMD_MIGRATE          :  args.do_migrate     = true;   break;
256       case CMD_DISCONNECT       :  args.do_disconnect  = true;   break;
257       case CMD_SILENTEXIST      :  args.is_silentexist = true;   break;
258       case CMD_SYNCSOCK         :  args.sync_sock      = optarg; break;
259       case CMD_SYNCMSG          :  args.sync_msg       = optarg; break;
260       case CMD_NID              :  args.nid = Evc_nidopt2nid(optarg,true); break;
261       case CMD_SILENT           :  --args.verbosity; break;
262       case CMD_MIGRATESELF      :
263         args.do_migrate     = true;
264         args.do_migrateself = true;
265         break;
266
267       default           :
268         WRITE_MSG(2, "Try '");
269         WRITE_STR(2, argv[0]);
270         WRITE_MSG(2, " --help' for more information.\n");
271         return wrapper_exit_code;
272         break;
273     }
274   }
275
276   signal(SIGCHLD, SIG_DFL);
277   
278   if (args.do_migrateself)
279     args.nid = Evc_get_task_nid(0);
280   
281   if (!args.do_create && !args.do_migrate)
282     WRITE_MSG(2, "Neither '--create' nor '--migrate' specified; try '--help' for more information\n");
283   else if (args.do_create && args.do_migrate)
284     WRITE_MSG(2, "Can not specify '--create' and '--migrate' at the same time; try '--help' for more information\n");
285   else if (!args.do_create && args.nid==VC_DYNAMIC_XID)
286     WRITE_MSG(2, "Can not migrate to an unknown context\n");
287   else if (optind>=argc)
288     WRITE_MSG(2, "No command given; use '--help' for more information.\n");
289   else
290     return doit(&args, argv);
291
292   return wrapper_exit_code;
293 }