generalized building with dietlibc
[util-vserver.git] / util-vserver / src / ctx-kill.c
1 // $Id$    --*- c++ -*--
2
3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 //  
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; version 2 of the License.
8 //  
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //  
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22 #include "compat.h"
23
24 #include "wrappers.h"
25 #include "wrappers-vserver.h"
26 #include "util.h"
27 #include "stack-start.h"
28
29 #include <time.h>
30 #include <dirent.h>
31 #include <getopt.h>
32 #include <signal.h>
33 #include <sched.h>
34 #include <assert.h>
35
36 #ifndef CLONE_NEWNS
37 #  define CLONE_NEWNS 0x00020000
38 #endif
39
40 #ifndef MNT_DETACH
41 #  define MNT_DETACH    0x000000002
42 #endif
43
44 #if 0
45 #  include <stdio.h>
46 #  define DPRINTF(...)  printf(__VA_ARGS__)
47 #else
48 #  define DPRINTF(...)  (void)0
49 #endif
50
51 struct ArgInfo {
52     enum { tpUNSET, tpCTX, tpPID }      type;
53     ctx_t               ctx;
54     pid_t               pid;
55     unsigned int        interval;
56     bool                shutdown;
57     bool                omit_init;
58     size_t              argc;
59     char * const *      argv;
60 };
61
62 static struct option const
63 CMDLINE_OPTIONS[] = {
64   { "help",     no_argument,  0, 'h' },
65   { "version",  no_argument,  0, 'v' },
66   { "all",      no_argument,       0, 'a' },
67   { "ctx",      required_argument, 0, 'c' },
68   { "pid",      required_argument, 0, 'p' },
69   { "interval", required_argument, 0, 'i' },
70   { "shutdown", no_argument,       0, 's' },
71   { 0,0,0,0 }
72 };
73
74 int     wrapper_exit_code = 1;
75
76 static void
77 showHelp(int fd, char const *cmd, int res)
78 {
79   WRITE_MSG(fd, "Usage:  ");
80   WRITE_STR(fd, cmd);
81   WRITE_MSG(fd,
82             " (-c|--ctx <ctx>)|(-p|--pid <pid>) [-i|--interval <msec>] [-a|--all]\n"
83             "                [-s|--shutdown] [--] <signal> [<sec> <signal>]*\n\n"
84             "Please report bugs to " PACKAGE_BUGREPORT "\n");
85   exit(res);
86 }
87
88 static void
89 showVersion()
90 {
91   WRITE_MSG(1,
92             "ctx-kill " VERSION " -- kills processes in foreign contexts\n"
93             "This program is part of " PACKAGE_STRING "\n\n"
94             "Copyright (C) 2003 Enrico Scholz\n"
95             VERSION_COPYRIGHT_DISCLAIMER);
96   exit(0);
97 }
98
99 static void
100 iterateThroughProc(void (*fn)(pid_t pid, void *data),
101                    struct ArgInfo const * const args,
102                    void *data)
103 {
104   int           grp  = getpgrp();
105   DIR           *dir;
106   struct dirent *d;
107
108   kill(-1, SIGSTOP);
109   dir = opendir("/proc");
110   if (dir==0) {
111     perror("opendir()");
112     kill(-1, SIGCONT);
113     exit(1);
114   }
115     
116   kill(-1, SIGSTOP);
117   while ((d=readdir(dir))!=0) {
118     pid_t       pid = atoi(d->d_name);
119     if (pid==0 ||                       // not a /proc/<pid>/ entry
120         (args->omit_init && pid==1) ||
121         getpgid(pid)==grp)              // our own process-group
122       continue;
123
124     (*fn)(pid,data);
125   }
126   kill(-1, SIGCONT);
127
128   closedir(dir);
129 }
130
131 static void
132 doKillAllSingle(pid_t pid, void *sig_ptr_v)
133 {
134   register int const * const    sig_ptr = sig_ptr_v;
135   
136   DPRINTF("sending signal '%u' to process '%u'\n",
137           *sig_ptr, pid);
138   kill(pid, *sig_ptr);
139 }
140
141 static void
142 getRemainingProcCountProc(pid_t UNUSED pid, void *cnt_v)
143 {
144   register size_t * const       cnt = cnt_v;
145   
146   ++*cnt;
147 }
148
149 static size_t
150 getRemainingProcCount(struct ArgInfo const * const args)
151 {
152   size_t        cnt = 0;
153   iterateThroughProc(&getRemainingProcCountProc, args, &cnt);
154
155   return cnt;
156 }
157
158 static void
159 doKillAll(struct ArgInfo const * const args)
160 {
161   size_t        i=0;
162
163   signal(SIGSTOP, SIG_IGN);
164
165   while (i<args->argc) {
166     int         sig       = atoi(args->argv[i]);
167     time_t      next_time = time(0);
168
169     ++i;
170     if (i<args->argc)
171       next_time += atoi(args->argv[i]);
172     
173     iterateThroughProc(&doKillAllSingle, args, &sig);
174     while (time(0)<next_time && getRemainingProcCount(args)>0)
175       usleep(args->interval * 1000);
176
177     ++i;
178   }
179
180   if (args->shutdown && getRemainingProcCount(args)>0) {
181     WRITE_MSG(2, "There are processes which could not be killed\n");
182     exit(1);
183   }
184
185 }
186
187 static void
188 doKillSingle(struct ArgInfo const * const args)
189 {
190   size_t        i=0;
191
192   while (i<args->argc) {
193     int         sig       = atoi(args->argv[i]);
194     time_t      next_time = time(0);
195
196     ++i;
197     if (i<args->argc)
198       next_time += atoi(args->argv[i]);
199
200     DPRINTF("sending signal '%u' to process '%u'\n",
201             sig, args->pid);
202     kill(args->pid, sig);
203     while (time(0)<next_time &&
204            (getpgid(args->pid)!=-1 || errno!=ESRCH))
205       
206       usleep(args->interval * 1000);
207
208     ++i;
209   }
210 }
211
212 static int
213 childFunc(void *args_v)
214 {
215   struct ArgInfo *      args = args_v;
216
217   Emount("none", "/mnt", "tmpfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, "size=4k");
218   Echdir("/mnt");
219   Emkdir("proc", 0600);
220   Emkdir("old",  0600);
221   Epivot_root(".", "old");
222   Echroot(".");
223   Eumount2("old", MNT_DETACH);
224   Emkdir("old/foo",0600);
225   Emount("none", "/proc", "proc", 0, 0);
226
227     // TODO: it may be be better for ctx-shutdown to:
228     // * generate ctx with S_CTX_INFO_INIT
229     // * send kill to -1
230   Evc_new_s_context(args->ctx, ~0, S_CTX_INFO_LOCK|S_CTX_INFO_SCHED);
231
232   switch (args->type) {
233     case tpCTX  :  doKillAll(args); break;
234     case tpPID  :  doKillSingle(args); break;
235     default     :  assert(false); return 1;
236   }
237
238   return 0;
239 }
240   
241 static ctx_t
242 determineContext(pid_t pid)
243 {
244   int           fd[2];
245   pid_t         chld;
246   ctx_t         res;
247   
248   Epipe(fd);
249   chld = Efork();
250
251   if (chld==0) {
252     Eclose(fd[0]);
253     Evc_new_s_context(1, ~0, S_CTX_INFO_LOCK|S_CTX_INFO_SCHED);
254     res = vc_X_getctx(pid);
255     Ewrite(fd[1], &res, sizeof res);
256     Eclose(fd[1]);
257     _exit(0);
258   }
259   
260   Eclose(fd[1]);
261   if (Eread(fd[0], &res, sizeof res)!=sizeof(res)) {
262     WRITE_MSG(2, "internal communication error while determining ctx\n");
263     exit(1);
264   }
265   Eclose(fd[0]);
266   if (res==VC_NOCTX) {
267     WRITE_MSG(2, "can not determine contex for given pid\n");
268     exit(1);
269   }
270
271   return res;
272 }
273
274 int main(int argc, char *argv[])
275 {
276   pid_t                 pid, p;
277   char                  buf[0x8000];
278   int                   status;
279   struct ArgInfo        args = {
280     .type      = tpUNSET,
281     .interval  = 500,
282     .shutdown  = false,
283     .omit_init = true };
284
285   while (1) {
286     int         c = getopt_long(argc, argv, "hvsac:p:i:", CMDLINE_OPTIONS, 0);
287     if (c==-1) break;
288
289     switch (c) {
290       case 'h'          :  showHelp(1, argv[0], 0);
291       case 'v'          :  showVersion();
292       case 'c'          :
293         args.type = tpCTX;
294         args.ctx  = atoi(optarg);
295         break;
296       case 'p'          :
297         args.type = tpPID;
298         args.pid  = atoi(optarg);
299         break;
300       case 'i'          :  args.interval  = atoi(optarg); break;
301       case 's'          :  args.shutdown  = true; break;
302       case 'a'          :  args.omit_init = false; break;
303       default           :
304         WRITE_MSG(2, "Try '");
305         WRITE_STR(2, argv[0]);
306         WRITE_MSG(2, " --help\" for more information.\n");
307         return EXIT_FAILURE;
308         break;
309     }
310   }
311
312   if (args.type==tpUNSET){
313     WRITE_MSG(2, "Please specify a context or a pid\n");
314     return EXIT_FAILURE;
315   }
316
317   if (optind==argc) {
318     WRITE_MSG(2, "No signal specified\n");
319     return EXIT_FAILURE;
320   }
321
322   if (args.type==tpPID)
323     args.ctx = determineContext(args.pid);
324   
325   args.argc = argc-optind;
326   args.argv = argv+optind;
327
328     // TODO: args.argv verification (only integers)
329
330   if (args.argc%2 != 1) {
331     WRITE_MSG(2, "Wrong count of '<signal> [<pause> <signal>]*' arguments specified.\n");
332     return EXIT_FAILURE;
333   }
334
335   pid = Eclone(childFunc, STACK_START(buf), CLONE_NEWNS|SIGCHLD, &args);
336   p   = Ewait4(pid, &status, 0,0);
337
338   if (WIFEXITED(status))   return WEXITSTATUS(status);
339   if (WIFSIGNALED(status)) return kill(getpid(), WTERMSIG(status));
340   return EXIT_FAILURE;
341 }