16 static int want_debug = 0;
18 static char *command = NULL;
19 static int command_buflen = 0;
20 static int command_length = 0;
21 static int current_request = 0;
23 static char *program_name = NULL;
24 static int arg_count = 0;
25 static char **arg_list = NULL;
27 #define COMMAND_TERMINATOR '\0'
29 static struct SignalTable {
58 { "VTALRM", SIGVTALRM },
60 { "WINCH", SIGWINCH },
65 void append_command_char(int ch) {
66 if (command_length >= command_buflen) {
68 command = realloc(command, command_buflen + delta);
69 command_buflen += delta;
72 command[command_length++] = ch;
75 int read_command(void) {
80 if ((ch == EOF) || (ch == COMMAND_TERMINATOR)) {
83 append_command_char(ch);
85 append_command_char(0);
86 command_length--; /* don't count the trailing nul */
87 return command_length;
90 void write_response(char *code, char *arg) {
91 if (want_debug) fprintf(stderr, "response: %d:%s,%s\n", current_request, code, arg);
92 fprintf(stdout, "%d:%s,%s", current_request, code, arg);
93 fputc(COMMAND_TERMINATOR, stdout);
97 char *num_buf(int n) {
99 sprintf(buf, "%d", n);
103 void errno_response() {
105 write_response("errno", num_buf(e));
108 void handle_program(char *cmd, char *arg) {
109 if (program_name != NULL) {
112 program_name = strdup(arg);
113 write_response("ok", arg);
116 void handle_argc(char *cmd, char *arg) {
119 for (i = 0; i < arg_count; i++) {
120 if (arg_list[i] != NULL) {
127 arg_count = (int) strtoul(arg, NULL, 10);
129 /* Allocate arg_count+2 slots because execv wants a
130 NULL-terminated array and we copy the program name into the 0th
131 arg slot before execing */
132 arg_list = calloc(arg_count + 2, sizeof(char *));
133 write_response("ok", arg);
136 void handle_arg(char *cmd, char *arg) {
137 char *val = strchr(arg, ',');
145 index = (int) strtoul(arg, NULL, 10);
146 if (index < 0 || index >= arg_count) {
147 write_response("bad", arg);
149 if (arg_list[index + 1] != NULL) {
150 free(arg_list[index + 1]);
152 arg_list[index + 1] = strdup(val);
153 write_response("ok", arg);
157 void handle_execv(char *cmd, char *arg) {
159 write_response("bad", "already");
163 if (command_length == 0) {
164 write_response("bad", "command");
168 if (arg_list == NULL) {
169 write_response("bad", "args");
173 arg_list[0] = strdup(program_name);
177 int nullfd = open("/dev/null", O_RDWR, 0777);
178 close(0); dup2(nullfd, 0);
179 close(1); dup2(nullfd, 1);
180 close(2); dup2(nullfd, 2);
182 if (execv(program_name, arg_list) == -1) {
186 write_response("pid", num_buf((int) kid));
190 int lookup_signal(char *name) {
191 struct SignalTable *entry;
192 for (entry = &signal_table[0]; entry->sig_name != NULL; entry++) {
193 if (!strcasecmp(name, entry->sig_name)) {
194 return entry->sig_num;
200 void handle_sendsig(char *cmd, char *arg) {
202 if (isdigit(arg[0])) {
203 signum = (int) strtoul(arg, NULL, 10);
205 signum = lookup_signal(arg);
209 write_response("bad", "invalid");
214 write_response("bad", "not_running");
218 if (kill(kid, signum) == 0) {
219 write_response("ok","");
225 struct CommandTableEntry {
227 void (*handler)(char *cmd, char *arg);
229 {"program", handle_program},
230 {"argc", handle_argc},
232 {"execv", handle_execv},
233 {"sendsig", handle_sendsig},
237 void eval_command(void) {
238 char *reqnumstr, *cmd, *arg;
239 struct CommandTableEntry *cte;
242 cmd = strchr(command, ':');
250 current_request = (int) strtoul(reqnumstr, NULL, 10);
251 if (current_request == 0) {
255 arg = strchr(cmd, ',');
263 if (want_debug) fprintf(stderr, "command: %d:%s,%s\n", current_request, cmd, arg);
265 for (cte = &cmd_table[0]; cte->name != NULL; cte++) {
266 if (!strcmp(cte->name, cmd)) {
267 cte->handler(cmd, arg);
273 write_response("bad_command", cmd);
277 int main(int argc, char *argv[]) {
281 want_debug = (argc > 1) && (!strcmp(argv[1], "-debug"));
283 if (want_debug) fprintf(stderr, "execdaemon starting.\n");
286 struct timeval timeout;
288 timeout.tv_usec = 100000; /* 100 ms */
293 switch (select(1, &fds, NULL, NULL, &timeout)) {
295 if (!read_command()) {
313 pid_t result = waitpid(kid, &pidstatus, WNOHANG);
315 if (WIFEXITED(pidstatus)) {
316 write_response("exit", num_buf(WEXITSTATUS(pidstatus)));
317 } else if (WIFSIGNALED(pidstatus)) {
318 write_response("signal", num_buf(WTERMSIG(pidstatus)));
320 write_response("died", "");
327 if (want_debug) fprintf(stderr, "execdaemon exiting.\n");