File Coverage

SocketAlarm_action.c
Criterion Covered Total %
statement 202 283 71.3
branch 98 184 53.2
condition n/a
subroutine n/a
pod n/a
total 300 467 64.2


line stmt bran cond sub pod time code
1 52           bool parse_actions(SV **spec, int n_spec, struct action *actions, size_t *n_actions, char *aux_buf, size_t *aux_len) {
2             bool success;
3 52           size_t action_pos= 0;
4 52           size_t aux_pos= 0;
5             int spec_i;
6              
7             // Default is one SIGALRM to self
8 52 100         if (!spec || n_spec == 0) {
    100          
9 10 100         if (*n_actions) {
10 5           actions[0].op= ACT_KILL;
11 5           actions[0].orig_idx= 0;
12 5           actions[0].act.kill.signal= SIGALRM;
13 5           actions[0].act.kill.pid= getpid();
14             }
15 10           action_pos++;
16             }
17 84 100         else for (spec_i= 0; spec_i < n_spec; spec_i++) {
18             AV *action_spec;
19 42           const char *act_name= NULL;
20 42           STRLEN act_namelen= 0;
21             SV **el;
22             size_t n_el;
23             pid_t common_pid;
24             int common_op, common_signal;
25              
26             // Get the arrayref for the next action
27 42 50         if (!(spec[spec_i] && SvROK(spec[spec_i]) && SvTYPE(SvRV(spec[spec_i])) == SVt_PVAV))
    50          
    50          
28 0           croak("Actions must be arrayrefs");
29              
30             // Get the 'command' name of the action
31 42           action_spec= (AV*) SvRV(spec[spec_i]);
32 42           n_el= av_count(action_spec);
33 42 50         if (n_el < 1 || !(el= av_fetch(action_spec, 0, 0)) || !SvPOK(*el))
    50          
    50          
34 0           croak("First element of action must be a string");
35 42           act_name= SvPV(*el, act_namelen);
36              
37             // Dispatch based on the command
38 42           switch (act_namelen) {
39 4           case 3:
40 4 100         if (strcmp(act_name, "sig") == 0) {
41 2 50         if (n_el > 2)
42 0           croak("Too many parameters for 'sig' action");
43 2 50         common_signal= (n_el == 2 && (el= av_fetch(action_spec, 1, 0)) != NULL && SvOK(*el))?
    50          
44 4 50         parse_signal(*el) : SIGALRM;
45 2           common_pid= getpid();
46 2           goto parse_kill_common;
47             }
48 2 50         if (strcmp(act_name, "run") == 0) {
49 2           common_op= ACT_RUN;
50 2           goto parse_run_common;
51             }
52             case 4:
53 4 100         if (strcmp(act_name, "kill") == 0) {
54 2 50         if (n_el != 3)
55 0           croak("Expected 2 parameters for 'kill' action");
56 2           el= av_fetch(action_spec, 1, 0);
57 2 50         if (!el || !SvOK(*el))
    50          
58 0           croak("Expected Signal as first parameter to 'kill'");
59 2           common_signal= parse_signal(*el);
60 2           el= av_fetch(action_spec, 2, 0);
61 2 50         if (!el || !SvIOK(*el))
    50          
62 0           croak("Expected PID as second parameter to 'kill'");
63 2           common_pid= SvIV(*el);
64 2           goto parse_kill_common;
65             }
66 2 50         if (strcmp(act_name, "exec") == 0) {
67 2           common_op= ACT_EXEC;
68 2           goto parse_run_common;
69             }
70             case 5:
71 8 100         if (strcmp(act_name, "sleep") == 0) {
72 2 50         if (n_el != 2)
73 0           croak("Expected 1 parameter to 'sleep' action");
74 2           el= av_fetch(action_spec, 1, 0);
75 2 50         if (!el || !SvOK(*el) || !looks_like_number(*el))
    50          
    50          
76 0           croak("Expected number of seconds in 'sleep' action");
77 2 100         if (action_pos < *n_actions) {
78 1           actions[action_pos].op= ACT_SLEEP;
79 1           actions[action_pos].orig_idx= spec_i;
80 1           actions[action_pos].act.slp.seconds= SvNV(*el);
81             }
82 2           ++action_pos;
83 2           continue;
84             }
85 6 50         if (strcmp(act_name, "close") == 0) {
86 6           common_op= ACT_x_CLOSE;
87 6           goto parse_close_common;
88             }
89             case 6:
90             // The repeat feature opens the possibility of infinite busy-loops,
91             // and probably creates more problems than it solves.
92             //if (strcmp(act_name, "repeat") == 0) {
93             // int act_count= spec_i;
94             // if (!act_count)
95             // croak("'repeat' cannot be the first action");
96             // if (n_el != 1) { // default is to repeat all, via act_count=i above
97             // if (n_el != 2)
98             // croak("Expected 0 or 1 parameters to 'repeat'");
99             // el= av_fetch(action_spec, 1, 0);
100             // if (!el || !SvOK(*el) || !looks_like_number(*el) || SvIV(*el) <= 0)
101             // croak("Expected positive integer of actions to repeat");
102             // act_count= SvIV(*el);
103             // }
104             // if (action_pos < *n_actions) {
105             // int dest_act_idx, dest_spec_idx= spec_i - act_count;
106             // // Locate the first action record with orig_idx == dest_spec_idx;
107             // for (dest_act_idx= 0; dest_act_idx < spec_i; dest_act_idx++)
108             // if (actions[dest_act_idx].orig_idx == dest_spec_idx)
109             // break;
110             // actions[action_pos].op= ACT_JUMP;
111             // actions[action_pos].orig_idx= spec_i;
112             // actions[action_pos].act.jmp.idx= dest_act_idx;
113             // }
114             // ++action_pos;
115             // continue;
116             //}
117 24 100         if (strcmp(act_name, "shut_r") == 0) {
118 4           common_op= ACT_x_SHUT_R;
119 4           goto parse_close_common;
120             }
121 20 50         if (strcmp(act_name, "shut_w") == 0) {
122 20           common_op= ACT_x_SHUT_W;
123 20           goto parse_close_common;
124             }
125             case 7:
126 2 50         if (strcmp(act_name, "shut_rw") == 0) {
127 2           common_op= ACT_x_SHUT_RW;
128 2           goto parse_close_common;
129             }
130             default:
131 0           croak("Unknown command '%s' in action list", act_name);
132             }
133 4           if (0) parse_kill_common: { // arrive from 'kill' and 'sig'
134             // common_signal, common_pid will be set.
135             // Is there an available action?
136 4 100         if (action_pos < *n_actions) {
137 2           actions[action_pos].op= ACT_KILL;
138 2           actions[action_pos].orig_idx= spec_i;
139 2           actions[action_pos].act.kill.signal= common_signal;
140 2           actions[action_pos].act.kill.pid= common_pid;
141             }
142 4           ++action_pos;
143             }
144 4           if (0) parse_run_common: { // arrive from 'run' and 'exec'
145 4           char **argv= NULL, *str;
146             STRLEN len;
147 4           int j, argc= n_el-1;
148             // common_op will be set.
149 4 50         if (n_el < 2)
150 0           croak("Expected at least one parameter for '%s'", act_name);
151             // Align to pointer boundary within aux_buf
152 4           aux_pos += sizeof(void*) - 1;
153 4           aux_pos &= ~(sizeof(void*) - 1);
154             // allocate an array of char* within aux_buf
155             // argv remains NULL if there isn't room for it
156 4 100         if (aux_pos + sizeof(void*) * n_el <= *aux_len)
157 2           argv= (char**)(aux_buf + aux_pos);
158 4           aux_pos += sizeof(void*) * (argc+1);
159             // size up each of the strings, and copy them to the buffer if space available
160 12 100         for (j= 0; j < argc; j++) {
161 8           el= av_fetch(action_spec, j+1, 0);
162 8 50         if (!el || !*el || !SvOK(*el))
    50          
    50          
163 0           croak("Found undef element in arguments for '%s'", act_name);
164 8           str= SvPV(*el, len);
165 8 100         if (argv && aux_pos + len + 1 <= *aux_len) {
    50          
166 4           argv[j]= aux_buf + aux_pos;
167 4           memcpy(argv[j], str, len+1);
168             }
169 8           aux_pos += len+1;
170             }
171             // argv lists must end with NULL
172 4 100         if (argv)
173 2           argv[argc]= NULL;
174             // store in an action if space remaining.
175 4 100         if (action_pos < *n_actions) {
176 2           actions[action_pos].op= common_op;
177 2           actions[action_pos].orig_idx= spec_i;
178 2           actions[action_pos].act.run.argc= argc;
179 2           actions[action_pos].act.run.argv= argv;
180             }
181 4           ++action_pos;
182             }
183 32           if (0) parse_close_common: { // arrive from 'close', 'shut_r', 'shut_w', 'shut_rw'
184             int j;
185             const char *str;
186             STRLEN len;
187             // common_op will be set, and can be ORed with the variant
188 32 50         if (n_el < 2)
189 0           croak("Expected 1 or more parameters to '%s'", act_name);
190             // Each parameter is another action_fd or action_sockname action
191 72 100         for (j= 1; j < n_el; j++) {
192 40           el= av_fetch(action_spec, j, 0);
193 40 50         if (!el || !*el || !SvOK(*el))
    50          
    50          
194 0           croak("'%s' parameter %d is undefined", act_name, j-1);
195             // If not a ref...
196 40 100         if (!SvROK(*el)) {
197             // Is it a file descriptor integer?
198 22 100         if (looks_like_number(*el)) {
199 20           IV fd= SvIV(*el);
200 20 50         if (fd >= 0 && fd < 0x10000) {
    50          
201 20 100         if (action_pos < *n_actions) {
202 10           actions[action_pos].op= common_op | ACT_FD_x;
203 10           actions[action_pos].orig_idx= spec_i;
204 10           actions[action_pos].act.fd.fd= fd;
205             }
206 20           ++action_pos;
207 20           continue;
208             }
209             }
210 2           str= SvPV(*el, len);
211             // Is the length one of struct sockaddr_in, sockaddr_un, or sockaddr?
212 2 50         if (len == sizeof(struct sockaddr)
213 0 0         || len == sizeof(struct sockaddr_in)
214 0 0         || len == sizeof(struct sockaddr_un)
215             ) {
216 2 100         if (action_pos < *n_actions) {
217 1           actions[action_pos].op= common_op | ACT_PNAME_x;
218 1           actions[action_pos].orig_idx= spec_i;
219 1           actions[action_pos].act.nam.addr_len= len;
220 1           actions[action_pos].act.nam.addr= (struct sockaddr*) (aux_buf+aux_pos);
221             }
222 2 100         if (aux_pos + len <= *aux_len)
223 1           memcpy((aux_buf+aux_pos), str, len);
224 2           aux_pos += len;
225 2           action_pos++;
226 2           continue;
227             }
228             // TODO: parse host:port strings
229             }
230             else {
231             // Try getting a file descriptor from the ref
232 18           int fd= fileno_from_sv(*el);
233 18 50         if (fd >= 0) {
234 18 100         if (action_pos < *n_actions) {
235 9           actions[action_pos].op= common_op | ACT_FD_x;
236 9           actions[action_pos].orig_idx= spec_i;
237 9           actions[action_pos].act.fd.fd= fd;
238             }
239 18           ++action_pos;
240 18           continue;
241             }
242             // TODO: allow notation for socket self-name like { sockname => $x }
243             // and maybe allow a full pair of { sockname => $x, peername => $y }
244             // or even partial matches like { port => $num }
245             }
246 0           str= SvPV(*el, len);
247 0           croak("Invalid parameter to '%s': '%s'; must be integer (fileno), file handle, or socket name like from getpeername", act_name, str);
248             }
249             }
250             }
251 52 100         success= (action_pos <= *n_actions) && (aux_pos <= *aux_len);
    50          
252 52           *n_actions= action_pos;
253 52           *aux_len= aux_pos;
254 52           return success;
255             }
256              
257 12           bool execute_action(struct action *act, bool resume, struct timespec *now_ts, struct socketalarm *parent) {
258 12           int low= act->op & 0xF;
259 12           int high= act->op & ~0xF;
260             int how;
261             char msgbuf[128];
262              
263 12           switch (high) {
264 3           case ACT_KILL:
265 3 50         if (kill(act->act.kill.pid, act->act.kill.signal) != 0)
266 0           perror("kill");
267 3           return true; // move to next action
268 0           case ACT_SLEEP: {
269 0           lazy_build_now_ts(now_ts);
270             // On initial entry to this action, use current time to calculate the wake time
271 0 0         if (!resume) {
272             double t_seconds;
273 0           t_seconds= (double) now_ts->tv_sec + .000000001 * now_ts->tv_nsec;
274             // Add seconds to this time
275 0           t_seconds += act->act.slp.seconds;
276 0           parent->wake_ts.tv_sec= (time_t) t_seconds;
277 0           parent->wake_ts.tv_nsec= (t_seconds - (long) t_seconds) * 1000000000;
278 0 0         if (!parent->wake_ts.tv_nsec)
279 0           parent->wake_ts.tv_nsec= 1; // because using tv_nsec as a defined-test
280 0           return false; // come back later
281             }
282             // Else see whether we have reached that time yet
283 0 0         if (now_ts->tv_sec > parent->wake_ts.tv_sec
284 0 0         || (now_ts->tv_sec == parent->wake_ts.tv_sec && now_ts->tv_nsec >= parent->wake_ts.tv_nsec))
    0          
285 0           return true; // reached end_ts
286 0           return false; // still waiting
287             }
288             //case ACT_JUMP:
289             // parent->cur_action= act->act.jmp.idx - 1; // parent will ++ after we return true
290             // return true;
291 9           case ACT_FD_x:
292 9           switch (low) {
293 0           case ACT_x_SHUT_R: how= SHUT_RD; if (0)
294 9           case ACT_x_SHUT_W: how= SHUT_WR; if (0)
295 0           case ACT_x_SHUT_RW: how= SHUT_RDWR;
296 9 50         if (shutdown(act->act.fd.fd, how) < 0) perror("shutdown");
297 9           break;
298 0           default:
299 0 0         if (close(act->act.fd.fd) < 0) perror("close");
300             }
301 9           return true;
302 0           case ACT_PNAME_x:
303             case ACT_SNAME_x: {
304             struct sockaddr_storage addr;
305             socklen_t len;
306             int ret, i;
307 0 0         for (i= 0; i < 1024; i++) {
308 0           len= sizeof(addr);
309 0           ret= high == ACT_PNAME_x? getpeername(i, (struct sockaddr*)&addr, &len)
310 0 0         : getsockname(i, (struct sockaddr*)&addr, &len);
311 0 0         if (ret == 0 && len == act->act.nam.addr_len
    0          
312 0 0         && memcmp(act->act.nam.addr, &addr, len) == 0
313             ) {
314 0           switch (low) {
315 0           case ACT_x_SHUT_R: how= SHUT_RD; if (0)
316 0           case ACT_x_SHUT_W: how= SHUT_WR; if (0)
317 0           case ACT_x_SHUT_RW: how= SHUT_RDWR;
318 0 0         if (shutdown(i, how) < 0) perror("shutdown");
319 0           break;
320 0           default:
321 0 0         if (close(i) < 0) perror("close");
322             }
323             }
324             }
325 0           return true;
326             }
327 0           case ACT_EXEC: {
328 0           char **argv= act->act.run.argv;
329 0 0         if (act->op == ACT_RUN) {
330             // double-fork, so that parent can reap child, and grandchild gets cleaned up by init()
331             pid_t child, gchild;
332 0 0         if ((child= fork()) < 0) { // fork failure
333 0           perror("fork");
334 0           return true;
335             }
336 0 0         else if (child > 0) { // parent - wait for immediate child to return
337 0           int status= -1;
338 0           waitpid(child, &status, 0);
339 0 0         if (status != 0)
340 0           perror("waitpid"); // not accurate, but probably not going to happen unless child fails to fork
341 0           return true;
342             }
343 0 0         else if ((gchild= fork()) != 0) { // second fork
344 0 0         if (gchild < 0) perror("fork");
345 0           _exit(gchild < 0? 1 : 0); // immediately exit
346             }
347             // else we are the grandchild now
348             }
349 0           close(0);
350 0           open("/dev/null", O_RDONLY);
351 0           execvp(argv[0], argv);
352 0           perror("exec"); // if we got here, it failed. Log the error.
353 0           _exit(1); // make sure we don't continue this process.
354             }
355 0           default: {
356 0           int unused= write(2, msgbuf, snprintf(msgbuf, sizeof(msgbuf), "BUG: No such action code %d", act->op));
357             (void) unused;
358 0           return true; // pretend success; false would cause it to come back to this action later
359             }}
360             }
361              
362 11           const char *act_fd_variant_name(int variant) {
363 11           switch (variant) {
364 5           case ACT_x_CLOSE: return "close";
365 4           case ACT_x_SHUT_R: return "shut_r";
366 1           case ACT_x_SHUT_W: return "shut_w";
367 1           case ACT_x_SHUT_RW: return "shut_rw";
368 0           default: return "BUG";
369             }
370             }
371              
372 11           const char *act_fd_variant_description(int variant) {
373 11           switch (variant) {
374 5           case ACT_x_CLOSE: return "close";
375 4           case ACT_x_SHUT_R: return "shutdown SHUT_RD";
376 1           case ACT_x_SHUT_W: return "shutdown SHUT_WR";
377 1           case ACT_x_SHUT_RW: return "shutdown SHUT_RDWR";
378 0           default: return "BUG";
379             }
380             }
381              
382 18           static void inflate_action(struct action *act, AV *dest) {
383 18           int low= act->op & 0xF;
384 18           int high= act->op & ~0xF;
385             int i;
386 18           switch (high) {
387 4           case ACT_KILL: av_extend(dest, 2);
388 4           av_push(dest, newSVpvs("kill"));
389 4           av_push(dest, newSViv(act->act.kill.signal));
390 4           av_push(dest, newSViv(act->act.kill.pid));
391 4           return;
392 1           case ACT_SLEEP: av_extend(dest, 1);
393 1           av_push(dest, newSVpvs("sleep"));
394 1           av_push(dest, newSVnv(act->act.slp.seconds));
395 1           return;
396             //case ACT_JUMP: return snprintf(buffer, buflen, "goto %d", (int)act->act.jmp.idx);
397 10           case ACT_FD_x: av_extend(dest, 1);
398 10           av_push(dest, newSVpv(act_fd_variant_name(low), 0));
399 10           av_push(dest, newSViv(act->act.fd.fd));
400 10           return;
401 1           case ACT_PNAME_x: av_extend(dest, 1);
402 1           av_push(dest, newSVpv(act_fd_variant_name(low), 0));
403 1           av_push(dest, newSVpvn((char*)act->act.nam.addr, act->act.nam.addr_len));
404 1           return;
405 2           case ACT_EXEC: av_extend(dest, act->act.run.argc);
406 2 100         av_push(dest, newSVpv(act->op == ACT_RUN? "run":"exec", 0));
407 6 100         for (i= 0; i < act->act.run.argc; i++)
408 4           av_push(dest, newSVpv(act->act.run.argv[i], 0));
409 2           return;
410 0           default:
411 0           croak("BUG: action code %d", act->op);
412             }
413             }
414              
415 18           int snprint_action(char *buffer, size_t buflen, struct action *act) {
416 18           int low= act->op & 0xF;
417 18           int high= act->op & ~0xF;
418 18           switch (high) {
419 4           case ACT_KILL: return snprintf(buffer, buflen, "kill sig=%d pid=%d", (int)act->act.kill.signal, (int) act->act.kill.pid);
420 1           case ACT_SLEEP: return snprintf(buffer, buflen, "sleep %.3lfs", (double)act->act.slp.seconds);
421             //case ACT_JUMP: return snprintf(buffer, buflen, "goto %d", (int)act->act.jmp.idx);
422 10           case ACT_FD_x: return snprintf(buffer, buflen, "%s %d", act_fd_variant_description(low), act->act.fd.fd);
423 1           case ACT_PNAME_x:
424             case ACT_SNAME_x: {
425 1 50         int pos= snprintf(buffer, buflen, "%s %s ",
426             act_fd_variant_description(low),
427             high == ACT_PNAME_x? "peername":"sockname"
428             );
429 1 50         return pos + snprint_sockaddr(buffer+pos, buflen > pos? buflen-pos : 0, act->act.nam.addr);
430             }
431 2           case ACT_EXEC: {
432 2 100         int i, pos= snprintf(buffer, buflen, "%sexec(", act->op == ACT_RUN? "fork,fork," : "");
433 6 100         for (i= 0; i < act->act.run.argc; i++) {
434 4 50         pos += snprintf(buffer+pos, buflen > pos? buflen-pos : 0, "'%s',", act->act.run.argv[i]);
435             }
436             // if still in bounds, overwrite final character with ')'
437 2 50         if (pos < buflen)
438 2           buffer[pos-1]= ')';
439 2           return pos;
440             }
441 0           default:
442 0           return snprintf(buffer, buflen, "BUG: action code %d", act->op);
443             }
444             }