initial checkin
[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   pid_t         init_pid = vc_X_getinitpid(0);
163
164   signal(SIGSTOP, SIG_IGN);
165
166   while (i<args->argc) {
167     int         sig       = atoi(args->argv[i]);
168     time_t      next_time = time(0);
169
170     ++i;
171     if (i<args->argc)
172       next_time += atoi(args->argv[i]);
173     
174     iterateThroughProc(&doKillAllSingle, args, &sig);
175     if (init_pid!=-1 && init_pid>1 && init_pid!=getpid())
176       kill(init_pid, sig);
177
178     while (time(0)<next_time && getRemainingProcCount(args)>0)
179       usleep(args->interval * 1000);
180
181     ++i;
182   }
183
184   if (args->shutdown && getRemainingProcCount(args)>0) {
185     WRITE_MSG(2, "There are processes which could not be killed\n");
186     exit(1);
187   }
188
189 }
190
191 static void
192 doKillSingle(struct ArgInfo const * const args)
193 {
194   size_t        i=0;
195
196   while (i<args->argc) {
197     int         sig       = atoi(args->argv[i]);
198     time_t      next_time = time(0);
199
200     ++i;
201     if (i<args->argc)
202       next_time += atoi(args->argv[i]);
203
204     DPRINTF("sending signal '%u' to process '%u'\n",
205             sig, args->pid);
206     kill(args->pid, sig);
207     while (time(0)<next_time &&
208            (getpgid(args->pid)!=-1 || errno!=ESRCH))
209       
210       usleep(args->interval * 1000);
211
212     ++i;
213   }
214 }
215
216 static int
217 childFunc(void *args_v)
218 {
219   struct ArgInfo *      args = args_v;
220
221   Emount("none", "/mnt", "tmpfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, "size=4k");
222   Echdir("/mnt");
223   Emkdir("proc", 0600);
224   Emkdir("old",  0600);
225   Epivot_root(".", "old");
226   Echroot(".");
227   Eumount2("old", MNT_DETACH);
228   Emkdir("old/foo",0600);
229   Emount("none", "/proc", "proc", 0, 0);
230
231     // TODO: drop additional capabilities?
232   
233     // TODO: it may be be better for ctx-shutdown to:
234     // * generate ctx with S_CTX_INFO_INIT
235     // * send kill to -1
236   Evc_new_s_context(args->ctx, ~0, S_CTX_INFO_LOCK|S_CTX_INFO_SCHED);
237
238   switch (args->type) {
239     case tpCTX  :  doKillAll(args); break;
240     case tpPID  :  doKillSingle(args); break;
241     default     :  assert(false); return 1;
242   }
243
244   return 0;
245 }
246   
247 static ctx_t
248 determineContext(pid_t pid)
249 {
250   int           fd[2];
251   pid_t         chld;
252   ctx_t         res;
253   
254   Epipe(fd);
255   chld = Efork();
256
257   if (chld==0) {
258     Eclose(fd[0]);
259     Evc_new_s_context(1, ~0, S_CTX_INFO_LOCK|S_CTX_INFO_SCHED);
260     res = vc_X_getctx(pid);
261     Ewrite(fd[1], &res, sizeof res);
262     Eclose(fd[1]);
263     _exit(0);
264   }
265   
266   Eclose(fd[1]);
267   if (Eread(fd[0], &res, sizeof res)!=sizeof(res)) {
268     WRITE_MSG(2, "internal communication error while determining ctx\n");
269     exit(1);
270   }
271   Eclose(fd[0]);
272   if (res==VC_NOCTX) {
273     WRITE_MSG(2, "can not determine contex for given pid\n");
274     exit(1);
275   }
276
277   return res;
278 }
279
280 int main(int argc, char *argv[])
281 {
282   pid_t                 pid, p;
283   char                  buf[0x8000];
284   int                   status;
285   struct ArgInfo        args = {
286     .type      = tpUNSET,
287     .interval  = 500,
288     .shutdown  = false,
289     .omit_init = true };
290
291   while (1) {
292     int         c = getopt_long(argc, argv, "hvsac:p:i:", CMDLINE_OPTIONS, 0);
293     if (c==-1) break;
294
295     switch (c) {
296       case 'h'          :  showHelp(1, argv[0], 0);
297       case 'v'          :  showVersion();
298       case 'c'          :
299         args.type = tpCTX;
300         args.ctx  = atoi(optarg);
301         break;
302       case 'p'          :
303         args.type = tpPID;
304         args.pid  = atoi(optarg);
305         break;
306       case 'i'          :  args.interval  = atoi(optarg); break;
307       case 's'          :  args.shutdown  = true; break;
308       case 'a'          :  args.omit_init = false; break;
309       default           :
310         WRITE_MSG(2, "Try '");
311         WRITE_STR(2, argv[0]);
312         WRITE_MSG(2, " --help\" for more information.\n");
313         return EXIT_FAILURE;
314         break;
315     }
316   }
317
318   if (args.type==tpUNSET){
319     WRITE_MSG(2, "Please specify a context or a pid\n");
320     return EXIT_FAILURE;
321   }
322
323   if (optind==argc) {
324     WRITE_MSG(2, "No signal specified\n");
325     return EXIT_FAILURE;
326   }
327
328   if (args.type==tpPID)
329     args.ctx = determineContext(args.pid);
330
331   if (args.ctx==0) {
332     WRITE_MSG(2, "Can not operate in ctx 0\n");
333     return EXIT_FAILURE;
334   }
335   
336   args.argc = argc-optind;
337   args.argv = argv+optind;
338
339     // TODO: args.argv verification (only integers)
340
341   if (args.argc%2 != 1) {
342     WRITE_MSG(2, "Wrong count of '<signal> [<pause> <signal>]*' arguments specified.\n");
343     return EXIT_FAILURE;
344   }
345
346   pid = Eclone(childFunc, STACK_START(buf), CLONE_NEWNS|SIGCHLD, &args);
347   p   = Ewait4(pid, &status, 0,0);
348
349   if (WIFEXITED(status))   return WEXITSTATUS(status);
350   if (WIFSIGNALED(status)) return kill(getpid(), WTERMSIG(status));
351   return EXIT_FAILURE;
352 }