- made the alarm() code working with glibc also; glibc restarts
[util-vserver.git] / util-vserver / src / vwait.c
1 // $Id$    --*- c -*--
2
3 // Copyright (C) 2005 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
23 #include "lib/vserver.h"
24 #include "lib/internal.h"
25 #include "linuxvirtual.h"
26 #include "util.h"
27
28 #include <getopt.h>
29 #include <signal.h>
30 #include <time.h>
31 #include <errno.h>
32 #include <libgen.h>
33 #include <assert.h>
34
35 #define ENSC_WRAPPERS_PREFIX    "vwait: "
36 #define ENSC_WRAPPERS_STDLIB    1
37 #define ENSC_WRAPPERS_UNISTD    1
38 #define ENSC_WRAPPERS_VSERVER   1
39 #include <wrappers.h>
40
41 #define CMD_HELP        0x8000
42 #define CMD_VERSION     0x8001
43
44 #define CMD_TIMEOUT     0x4000
45 #define CMD_TERMINATE   0x4001
46 #define CMD_STATUS_FD   0x4002
47
48 static struct option const
49 CMDLINE_OPTIONS[] = {
50   { "help",       no_argument,        0, CMD_HELP },
51   { "version",    no_argument,        0, CMD_VERSION },
52   { "timeout",    required_argument,  0, CMD_TIMEOUT },
53   { "terminate",  no_argument,        0, CMD_TERMINATE },
54   { "status-fd",  required_argument,  0, CMD_STATUS_FD },
55   { 0,0,0,0 }
56 };
57
58 int                     wrapper_exit_code = 1;
59 static sig_atomic_t     aborted = 0;
60
61 struct StatusType {
62     enum {stERROR, stFINISHED, stKILLED,
63           stTIMEOUT}                            status;
64     int                                         rc;
65 };
66
67 struct Arguments
68 {
69     xid_t               xid;
70     int                 timeout;
71     int                 status_fd;
72     bool                do_terminate;
73 };
74
75 static void
76 showHelp(char const *cmd)
77 {
78   VSERVER_DECLARE_CMD(cmd);
79
80   WRITE_MSG(1, "Usage:  ");
81   WRITE_STR(1, cmd);
82   WRITE_STR(1,
83             " [--timeout <timeout>] [--terminate] [--status-fd <fd>] [--] <xid>\n"
84             "\n"
85             "Please report bugs to " PACKAGE_BUGREPORT "\n");
86   exit(0);
87 }
88
89 static void
90 showVersion()
91 {
92   WRITE_MSG(1,
93             "vwait " VERSION " -- waits for a context to finish\n"
94             "This program is part of " PACKAGE_STRING "\n\n"
95             "Copyright (C) 2005 Enrico Scholz\n"
96             VERSION_COPYRIGHT_DISCLAIMER);
97   exit(0);
98 }
99
100 static void
101 handler(int UNUSED num)
102 {
103   aborted = 1;
104 }
105
106 static struct StatusType
107 doit(struct Arguments const *args)
108 {
109   time_t                        end_time = 0, now = 0;
110   struct StatusType             res;
111   
112   if (args->timeout>0) {
113     end_time = time(0) + args->timeout;
114     siginterrupt(SIGALRM, 1);
115     signal(SIGALRM, handler);
116     alarm(args->timeout);
117   }
118
119   for (;;) {
120     res.rc = vc_wait_exit(args->xid);
121     
122     if      (res.rc==-1 && errno!=EAGAIN && errno!=EINTR) {
123         // the error-case
124       res.rc     = errno;
125       res.status = stERROR;
126       perror(ENSC_WRAPPERS_PREFIX "vc_wait_exit()");
127     }
128     else if (res.rc==-1 && args->timeout>0 && (now=time(0))>=end_time) {
129         // an EINTR or EAGAIN signal was delivered, a timeout was set and
130         // reached
131       if (!args->do_terminate)
132         res.status = stTIMEOUT;
133       else {
134         vc_ctx_kill(args->xid, 1, 9);
135         vc_ctx_kill(args->xid, 0, 9);
136         res.status = stKILLED;
137       }
138     }
139     else if (res.rc==-1) {
140         // an EINTR or EAGAIN signal was delivered but the timeout not set or
141         // not reached yet
142
143         // we are here, when args->timeout==0 or 'now' was initialized (and
144         // compared with 'end_time'). So, 'now' can be used below.
145       assert(args->timeout<=0 || (now < end_time));
146
147       if (args->timeout>0)      // (re)set the alarm-clock
148         alarm(end_time-now);
149
150       continue;
151     }
152     else
153         // vc_wait_exit(2) finished successfully
154       res.status = stFINISHED;
155
156     break;
157   }
158
159   alarm(0);
160   return res;
161 }
162
163 static void
164 writeStatus(int fd, char const *str, int const *rc, int exit_code)
165 {
166   if (fd==-1) exit(exit_code);
167
168   WRITE_STR(fd, str);
169   if (rc) {
170     char                buf[sizeof(*rc)*3 + 2];
171     size_t              len = utilvserver_fmt_long(buf, *rc);
172     WRITE_MSG(fd, " ");
173     Vwrite   (fd, buf, len);
174   }
175   WRITE_MSG(fd, "\n");
176   
177   exit(exit_code);
178 }
179
180 int main(int argc, char *argv[])
181 {
182   struct StatusType     res;
183   struct Arguments      args = {
184     .xid          = VC_NOCTX,
185     .timeout      = -1,
186     .status_fd    = -1,
187     .do_terminate = false,
188   };
189
190   while (1) {
191     int         c = getopt_long(argc, argv, "c:", CMDLINE_OPTIONS, 0);
192     if (c==-1) break;
193
194     switch (c) {
195       case CMD_HELP             :  showHelp(argv[0]);
196       case CMD_VERSION          :  showVersion();
197       case CMD_TERMINATE        :  args.do_terminate = true;         break;
198       case CMD_TIMEOUT          :  args.timeout      = atoi(optarg); break;
199       case CMD_STATUS_FD        :  args.status_fd    = atoi(optarg); break;
200       default           :
201         WRITE_MSG(2, "Try '");
202         WRITE_STR(2, argv[0]);
203         WRITE_MSG(2, " --help\" for more information.\n");
204         return EXIT_FAILURE;
205         break;
206     }
207   }
208
209   if (optind+1 > argc) {
210     WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "no context specified; try '--help' for more information\n");
211     exit(wrapper_exit_code);
212   }
213
214   if (optind+1 < argc) {
215     WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "can not wait for more than one context; try '--help' for more information\n");
216     exit(wrapper_exit_code);
217   }
218   
219   args.xid = Evc_xidopt2xid(argv[optind], true);
220
221   if (args.xid==VC_NOCTX) {
222     WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "invalid context specified; try '--help' for more information\n");
223     exit(wrapper_exit_code);
224   }
225
226   res = doit(&args);
227
228   switch (res.status) {
229     case stERROR        :  writeStatus(args.status_fd, "ERROR",    &res.rc, 127);
230     case stFINISHED     :  writeStatus(args.status_fd, "FINISHED", &res.rc,   0);
231     case stKILLED       :  writeStatus(args.status_fd, "KILLED",         0,   1);
232     case stTIMEOUT      :  writeStatus(args.status_fd, "TIMEOUT",        0,   2);
233     default             :  writeStatus(args.status_fd, "???",      &res.rc, 126);
234   }
235 }