|
tonyg@24
|
1 |
#include <stdlib.h> |
|
tonyg@24
|
2 |
#include <string.h> |
|
tonyg@24
|
3 |
#include <stdio.h> |
|
tonyg@24
|
4 |
#include <errno.h> |
|
tonyg@24
|
5 |
#include <signal.h> |
|
tonyg@27
|
6 |
#include <fcntl.h> |
|
tonyg@35
|
7 |
#include <ctype.h> |
|
tonyg@24
|
8 |
|
|
tonyg@24
|
9 |
#include <sys/types.h> |
|
tonyg@24
|
10 |
#include <sys/wait.h> |
|
tonyg@24
|
11 |
#include <sys/time.h> |
|
tonyg@24
|
12 |
#include <unistd.h> |
|
tonyg@24
|
13 |
|
|
tonyg@24
|
14 |
static pid_t kid = 0; |
|
tonyg@24
|
15 |
|
|
tonyg@24
|
16 |
static int want_debug = 0; |
|
tonyg@24
|
17 |
|
|
tonyg@24
|
18 |
static char *command = NULL; |
|
tonyg@24
|
19 |
static int command_buflen = 0; |
|
tonyg@24
|
20 |
static int command_length = 0; |
|
tonyg@24
|
21 |
static int current_request = 0; |
|
tonyg@24
|
22 |
|
|
tonyg@24
|
23 |
static char *program_name = NULL; |
|
tonyg@24
|
24 |
static int arg_count = 0; |
|
tonyg@24
|
25 |
static char **arg_list = NULL; |
|
tonyg@24
|
26 |
|
|
tonyg@24
|
27 |
#define COMMAND_TERMINATOR '\0' |
|
tonyg@24
|
28 |
|
|
tonyg@35
|
29 |
static struct SignalTable { |
|
tonyg@35
|
30 |
char *sig_name; |
|
tonyg@35
|
31 |
int sig_num; |
|
tonyg@35
|
32 |
} signal_table[] = { |
|
tonyg@35
|
33 |
{ "HUP", SIGHUP }, |
|
tonyg@35
|
34 |
{ "INT", SIGINT }, |
|
tonyg@35
|
35 |
{ "QUIT", SIGQUIT }, |
|
tonyg@35
|
36 |
{ "ILL", SIGILL }, |
|
tonyg@35
|
37 |
{ "TRAP", SIGTRAP }, |
|
tonyg@35
|
38 |
{ "ABRT", SIGABRT }, |
|
tonyg@35
|
39 |
{ "IOT", SIGIOT }, |
|
tonyg@35
|
40 |
{ "BUS", SIGBUS }, |
|
tonyg@35
|
41 |
{ "FPE", SIGFPE }, |
|
tonyg@35
|
42 |
{ "KILL", SIGKILL }, |
|
tonyg@35
|
43 |
{ "USR1", SIGUSR1 }, |
|
tonyg@35
|
44 |
{ "SEGV", SIGSEGV }, |
|
tonyg@35
|
45 |
{ "USR2", SIGUSR2 }, |
|
tonyg@35
|
46 |
{ "PIPE", SIGPIPE }, |
|
tonyg@35
|
47 |
{ "ALRM", SIGALRM }, |
|
tonyg@35
|
48 |
{ "TERM", SIGTERM }, |
|
tonyg@35
|
49 |
{ "CHLD", SIGCHLD }, |
|
tonyg@35
|
50 |
{ "CONT", SIGCONT }, |
|
tonyg@35
|
51 |
{ "STOP", SIGSTOP }, |
|
tonyg@35
|
52 |
{ "TSTP", SIGTSTP }, |
|
tonyg@35
|
53 |
{ "TTIN", SIGTTIN }, |
|
tonyg@35
|
54 |
{ "TTOU", SIGTTOU }, |
|
tonyg@35
|
55 |
{ "URG", SIGURG }, |
|
tonyg@35
|
56 |
{ "XCPU", SIGXCPU }, |
|
tonyg@35
|
57 |
{ "XFSZ", SIGXFSZ }, |
|
tonyg@35
|
58 |
{ "VTALRM", SIGVTALRM }, |
|
tonyg@35
|
59 |
{ "PROF", SIGPROF }, |
|
tonyg@35
|
60 |
{ "WINCH", SIGWINCH }, |
|
tonyg@35
|
61 |
{ "IO", SIGIO }, |
|
tonyg@35
|
62 |
{ NULL, 0 } |
|
tonyg@35
|
63 |
}; |
|
tonyg@35
|
64 |
|
|
tonyg@24
|
65 |
void append_command_char(int ch) { |
|
tonyg@24
|
66 |
if (command_length >= command_buflen) { |
|
tonyg@24
|
67 |
int delta = 1024; |
|
tonyg@24
|
68 |
command = realloc(command, command_buflen + delta); |
|
tonyg@24
|
69 |
command_buflen += delta; |
|
tonyg@24
|
70 |
} |
|
tonyg@24
|
71 |
|
|
tonyg@24
|
72 |
command[command_length++] = ch; |
|
tonyg@24
|
73 |
} |
|
tonyg@24
|
74 |
|
|
tonyg@24
|
75 |
int read_command(void) { |
|
tonyg@24
|
76 |
int ch; |
|
tonyg@24
|
77 |
command_length = 0; |
|
tonyg@24
|
78 |
while (1) { |
|
tonyg@24
|
79 |
ch = fgetc(stdin); |
|
tonyg@24
|
80 |
if ((ch == EOF) || (ch == COMMAND_TERMINATOR)) { |
|
tonyg@24
|
81 |
break; |
|
tonyg@24
|
82 |
} |
|
tonyg@24
|
83 |
append_command_char(ch); |
|
tonyg@24
|
84 |
} |
|
tonyg@24
|
85 |
append_command_char(0); |
|
tonyg@24
|
86 |
command_length--; /* don't count the trailing nul */ |
|
tonyg@24
|
87 |
return command_length; |
|
tonyg@24
|
88 |
} |
|
tonyg@24
|
89 |
|
|
tonyg@24
|
90 |
void write_response(char *code, char *arg) { |
|
tonyg@24
|
91 |
if (want_debug) fprintf(stderr, "response: %d:%s,%s\n", current_request, code, arg); |
|
tonyg@24
|
92 |
fprintf(stdout, "%d:%s,%s", current_request, code, arg); |
|
tonyg@24
|
93 |
fputc(COMMAND_TERMINATOR, stdout); |
|
tonyg@24
|
94 |
fflush(stdout); |
|
tonyg@24
|
95 |
} |
|
tonyg@24
|
96 |
|
|
tonyg@24
|
97 |
char *num_buf(int n) { |
|
tonyg@24
|
98 |
static char buf[32]; |
|
tonyg@24
|
99 |
sprintf(buf, "%d", n); |
|
tonyg@24
|
100 |
return buf; |
|
tonyg@24
|
101 |
} |
|
tonyg@24
|
102 |
|
|
tonyg@24
|
103 |
void errno_response() { |
|
tonyg@24
|
104 |
int e = errno; |
|
tonyg@24
|
105 |
write_response("errno", num_buf(e)); |
|
tonyg@24
|
106 |
} |
|
tonyg@24
|
107 |
|
|
tonyg@24
|
108 |
void handle_program(char *cmd, char *arg) { |
|
tonyg@24
|
109 |
if (program_name != NULL) { |
|
tonyg@24
|
110 |
free(program_name); |
|
tonyg@24
|
111 |
} |
|
tonyg@24
|
112 |
program_name = strdup(arg); |
|
tonyg@24
|
113 |
write_response("ok", arg); |
|
tonyg@24
|
114 |
} |
|
tonyg@24
|
115 |
|
|
tonyg@24
|
116 |
void handle_argc(char *cmd, char *arg) { |
|
tonyg@24
|
117 |
int i; |
|
tonyg@24
|
118 |
if (arg_count > 0) { |
|
tonyg@24
|
119 |
for (i = 0; i < arg_count; i++) { |
|
tonyg@24
|
120 |
if (arg_list[i] != NULL) { |
|
tonyg@24
|
121 |
free(arg_list[i]); |
|
tonyg@24
|
122 |
} |
|
tonyg@24
|
123 |
} |
|
tonyg@24
|
124 |
free(arg_list); |
|
tonyg@24
|
125 |
} |
|
tonyg@24
|
126 |
|
|
tonyg@24
|
127 |
arg_count = (int) strtoul(arg, NULL, 10); |
|
tonyg@24
|
128 |
|
|
tonyg@24
|
129 |
/* Allocate arg_count+2 slots because execv wants a |
|
tonyg@24
|
130 |
NULL-terminated array and we copy the program name into the 0th |
|
tonyg@24
|
131 |
arg slot before execing */ |
|
tonyg@24
|
132 |
arg_list = calloc(arg_count + 2, sizeof(char *)); |
|
tonyg@24
|
133 |
write_response("ok", arg); |
|
tonyg@24
|
134 |
} |
|
tonyg@24
|
135 |
|
|
tonyg@24
|
136 |
void handle_arg(char *cmd, char *arg) { |
|
tonyg@24
|
137 |
char *val = strchr(arg, ','); |
|
tonyg@24
|
138 |
int index; |
|
tonyg@24
|
139 |
if (arg == NULL) { |
|
tonyg@24
|
140 |
val = ""; |
|
tonyg@24
|
141 |
} else { |
|
tonyg@24
|
142 |
*val = '\0'; |
|
tonyg@24
|
143 |
val++; |
|
tonyg@24
|
144 |
} |
|
tonyg@24
|
145 |
index = (int) strtoul(arg, NULL, 10); |
|
tonyg@24
|
146 |
if (index < 0 || index >= arg_count) { |
|
tonyg@24
|
147 |
write_response("bad", arg); |
|
tonyg@24
|
148 |
} else { |
|
tonyg@24
|
149 |
if (arg_list[index + 1] != NULL) { |
|
tonyg@24
|
150 |
free(arg_list[index + 1]); |
|
tonyg@24
|
151 |
} |
|
tonyg@24
|
152 |
arg_list[index + 1] = strdup(val); |
|
tonyg@24
|
153 |
write_response("ok", arg); |
|
tonyg@24
|
154 |
} |
|
tonyg@24
|
155 |
} |
|
tonyg@24
|
156 |
|
|
tonyg@24
|
157 |
void handle_execv(char *cmd, char *arg) { |
|
tonyg@24
|
158 |
if (kid != 0) { |
|
tonyg@24
|
159 |
write_response("bad", "already"); |
|
tonyg@24
|
160 |
return; |
|
tonyg@24
|
161 |
} |
|
tonyg@24
|
162 |
|
|
tonyg@24
|
163 |
if (command_length == 0) { |
|
tonyg@24
|
164 |
write_response("bad", "command"); |
|
tonyg@24
|
165 |
return; |
|
tonyg@24
|
166 |
} |
|
tonyg@24
|
167 |
|
|
tonyg@24
|
168 |
if (arg_list == NULL) { |
|
tonyg@24
|
169 |
write_response("bad", "args"); |
|
tonyg@24
|
170 |
return; |
|
tonyg@24
|
171 |
} |
|
tonyg@24
|
172 |
|
|
tonyg@24
|
173 |
arg_list[0] = strdup(program_name); |
|
tonyg@24
|
174 |
|
|
tonyg@24
|
175 |
kid = fork(); |
|
tonyg@24
|
176 |
if (kid == 0) { |
|
tonyg@27
|
177 |
int nullfd = open("/dev/null", O_RDWR, 0777); |
|
tonyg@27
|
178 |
close(0); dup2(nullfd, 0); |
|
tonyg@27
|
179 |
close(1); dup2(nullfd, 1); |
|
tonyg@27
|
180 |
close(2); dup2(nullfd, 2); |
|
tonyg@27
|
181 |
close(nullfd); |
|
tonyg@24
|
182 |
if (execv(program_name, arg_list) == -1) { |
|
tonyg@24
|
183 |
exit(1); |
|
tonyg@24
|
184 |
} |
|
tonyg@24
|
185 |
} else { |
|
tonyg@24
|
186 |
write_response("pid", num_buf((int) kid)); |
|
tonyg@24
|
187 |
} |
|
tonyg@24
|
188 |
} |
|
tonyg@24
|
189 |
|
|
tonyg@35
|
190 |
int lookup_signal(char *name) { |
|
tonyg@35
|
191 |
struct SignalTable *entry; |
|
tonyg@35
|
192 |
for (entry = &signal_table[0]; entry->sig_name != NULL; entry++) { |
|
tonyg@35
|
193 |
if (!strcasecmp(name, entry->sig_name)) { |
|
tonyg@35
|
194 |
return entry->sig_num; |
|
tonyg@35
|
195 |
} |
|
tonyg@35
|
196 |
} |
|
tonyg@35
|
197 |
return 0; |
|
tonyg@35
|
198 |
} |
|
tonyg@35
|
199 |
|
|
tonyg@24
|
200 |
void handle_sendsig(char *cmd, char *arg) { |
|
tonyg@35
|
201 |
int signum; |
|
tonyg@35
|
202 |
if (isdigit(arg[0])) { |
|
tonyg@35
|
203 |
signum = (int) strtoul(arg, NULL, 10); |
|
tonyg@35
|
204 |
} else { |
|
tonyg@35
|
205 |
signum = lookup_signal(arg); |
|
tonyg@35
|
206 |
} |
|
tonyg@35
|
207 |
|
|
tonyg@24
|
208 |
if (signum == 0) { |
|
tonyg@24
|
209 |
write_response("bad", "invalid"); |
|
tonyg@24
|
210 |
return; |
|
tonyg@24
|
211 |
} |
|
tonyg@24
|
212 |
|
|
tonyg@24
|
213 |
if (kid == 0) { |
|
tonyg@24
|
214 |
write_response("bad", "not_running"); |
|
tonyg@24
|
215 |
return; |
|
tonyg@24
|
216 |
} |
|
tonyg@24
|
217 |
|
|
tonyg@24
|
218 |
if (kill(kid, signum) == 0) { |
|
tonyg@24
|
219 |
write_response("ok",""); |
|
tonyg@24
|
220 |
} else { |
|
tonyg@24
|
221 |
errno_response(); |
|
tonyg@24
|
222 |
} |
|
tonyg@24
|
223 |
} |
|
tonyg@24
|
224 |
|
|
tonyg@24
|
225 |
struct CommandTableEntry { |
|
tonyg@24
|
226 |
char *name; |
|
tonyg@24
|
227 |
void (*handler)(char *cmd, char *arg); |
|
tonyg@24
|
228 |
} cmd_table[] = { |
|
tonyg@24
|
229 |
{"program", handle_program}, |
|
tonyg@24
|
230 |
{"argc", handle_argc}, |
|
tonyg@24
|
231 |
{"arg", handle_arg}, |
|
tonyg@24
|
232 |
{"execv", handle_execv}, |
|
tonyg@24
|
233 |
{"sendsig", handle_sendsig}, |
|
tonyg@24
|
234 |
{NULL, NULL} |
|
tonyg@24
|
235 |
}; |
|
tonyg@24
|
236 |
|
|
tonyg@24
|
237 |
void eval_command(void) { |
|
tonyg@24
|
238 |
char *reqnumstr, *cmd, *arg; |
|
tonyg@24
|
239 |
struct CommandTableEntry *cte; |
|
tonyg@24
|
240 |
|
|
tonyg@24
|
241 |
reqnumstr = command; |
|
tonyg@24
|
242 |
cmd = strchr(command, ':'); |
|
tonyg@24
|
243 |
if (cmd == NULL) { |
|
tonyg@24
|
244 |
exit(1); |
|
tonyg@24
|
245 |
} else { |
|
tonyg@24
|
246 |
*cmd = '\0'; |
|
tonyg@24
|
247 |
cmd++; |
|
tonyg@24
|
248 |
} |
|
tonyg@24
|
249 |
|
|
tonyg@24
|
250 |
current_request = (int) strtoul(reqnumstr, NULL, 10); |
|
tonyg@24
|
251 |
if (current_request == 0) { |
|
tonyg@24
|
252 |
exit(1); |
|
tonyg@24
|
253 |
} |
|
tonyg@24
|
254 |
|
|
tonyg@24
|
255 |
arg = strchr(cmd, ','); |
|
tonyg@24
|
256 |
if (arg == NULL) { |
|
tonyg@24
|
257 |
arg = ""; |
|
tonyg@24
|
258 |
} else { |
|
tonyg@24
|
259 |
*arg = '\0'; |
|
tonyg@24
|
260 |
arg++; |
|
tonyg@24
|
261 |
} |
|
tonyg@24
|
262 |
|
|
tonyg@24
|
263 |
if (want_debug) fprintf(stderr, "command: %d:%s,%s\n", current_request, cmd, arg); |
|
tonyg@24
|
264 |
|
|
tonyg@24
|
265 |
for (cte = &cmd_table[0]; cte->name != NULL; cte++) { |
|
tonyg@24
|
266 |
if (!strcmp(cte->name, cmd)) { |
|
tonyg@24
|
267 |
cte->handler(cmd, arg); |
|
tonyg@24
|
268 |
current_request = 0; |
|
tonyg@24
|
269 |
return; |
|
tonyg@24
|
270 |
} |
|
tonyg@24
|
271 |
} |
|
tonyg@24
|
272 |
|
|
tonyg@24
|
273 |
write_response("bad_command", cmd); |
|
tonyg@24
|
274 |
current_request = 0; |
|
tonyg@24
|
275 |
} |
|
tonyg@24
|
276 |
|
|
tonyg@24
|
277 |
int main(int argc, char *argv[]) { |
|
tonyg@24
|
278 |
fd_set fds; |
|
tonyg@24
|
279 |
int keep_going = 1; |
|
tonyg@24
|
280 |
|
|
tonyg@24
|
281 |
want_debug = (argc > 1) && (!strcmp(argv[1], "-debug")); |
|
tonyg@24
|
282 |
|
|
tonyg@24
|
283 |
if (want_debug) fprintf(stderr, "execdaemon starting.\n"); |
|
tonyg@24
|
284 |
|
|
tonyg@24
|
285 |
while (keep_going) { |
|
tonyg@24
|
286 |
struct timeval timeout; |
|
tonyg@24
|
287 |
timeout.tv_sec = 0; |
|
tonyg@24
|
288 |
timeout.tv_usec = 100000; /* 100 ms */ |
|
tonyg@24
|
289 |
|
|
tonyg@24
|
290 |
FD_ZERO(&fds); |
|
tonyg@24
|
291 |
FD_SET(0, &fds); |
|
tonyg@24
|
292 |
|
|
tonyg@24
|
293 |
switch (select(1, &fds, NULL, NULL, &timeout)) { |
|
tonyg@24
|
294 |
case 1: { |
|
tonyg@24
|
295 |
if (!read_command()) { |
|
tonyg@24
|
296 |
keep_going = 0; |
|
tonyg@24
|
297 |
} else { |
|
tonyg@24
|
298 |
eval_command(); |
|
tonyg@24
|
299 |
} |
|
tonyg@24
|
300 |
break; |
|
tonyg@24
|
301 |
} |
|
tonyg@24
|
302 |
|
|
tonyg@24
|
303 |
case 0: |
|
tonyg@24
|
304 |
break; |
|
tonyg@24
|
305 |
|
|
tonyg@24
|
306 |
case -1: |
|
tonyg@24
|
307 |
perror("select"); |
|
tonyg@24
|
308 |
exit(1); |
|
tonyg@24
|
309 |
} |
|
tonyg@24
|
310 |
|
|
tonyg@24
|
311 |
if (kid != 0) { |
|
tonyg@24
|
312 |
int pidstatus; |
|
tonyg@24
|
313 |
pid_t result = waitpid(kid, &pidstatus, WNOHANG); |
|
tonyg@24
|
314 |
if (result != 0) { |
|
tonyg@24
|
315 |
if (WIFEXITED(pidstatus)) { |
|
tonyg@24
|
316 |
write_response("exit", num_buf(WEXITSTATUS(pidstatus))); |
|
tonyg@24
|
317 |
} else if (WIFSIGNALED(pidstatus)) { |
|
tonyg@24
|
318 |
write_response("signal", num_buf(WTERMSIG(pidstatus))); |
|
tonyg@24
|
319 |
} else { |
|
tonyg@24
|
320 |
write_response("died", ""); |
|
tonyg@24
|
321 |
} |
|
tonyg@24
|
322 |
kid = 0; |
|
tonyg@24
|
323 |
} |
|
tonyg@24
|
324 |
} |
|
tonyg@24
|
325 |
} |
|
tonyg@24
|
326 |
|
|
tonyg@24
|
327 |
if (want_debug) fprintf(stderr, "execdaemon exiting.\n"); |
|
tonyg@24
|
328 |
return 0; |
|
tonyg@24
|
329 |
} |