12 #include <sys/select.h>
14 #define CHILD_SCRIPT 0
15 #define CHILD_CHECKED 1
27 void (*handle
)(struct child
*, fd_set
*);
30 #define fail_fprintf(f, fmt, args...) \
31 fprintf(f, "%s failed: " fmt, scriptname , ##args)
33 #define fail_perror(str) \
34 fail_fprintf(stderr, "%s: %s\n", str, strerror(errno))
36 static const char *scriptname
;
37 static volatile int sigexit
;
38 static time_t termload_start
, sigexit_start
= 0;
39 static sigset_t sigchld_mask
;
40 static struct child
*first_child
;
41 static char default_loadcmd
[] = "dohell 900";
42 static char *loadcmd
= default_loadcmd
;
44 static struct child script
, load
;
46 void handle_checked_child(struct child
*child
, fd_set
*fds
);
47 void handle_script_child(struct child
*child
, fd_set
*fds
);
48 void handle_load_child(struct child
*child
, fd_set
*fds
);
50 int child_initv(struct child
*child
, int type
, char *argv
[])
57 if (pipe(pipe_out
) < 0)
60 /* Set the CLOEXEC flag so that we do not leak file
61 descriptors in our children. */
62 fcntl(pipe_out
[0], F_SETFD
,
63 fcntl(pipe_out
[0], F_GETFD
) | FD_CLOEXEC
);
64 fcntl(pipe_out
[1], F_SETFD
,
65 fcntl(pipe_out
[0], F_GETFD
) | FD_CLOEXEC
);
67 if (type
== CHILD_SCRIPT
) {
68 if (pipe(pipe_in
) < 0)
69 goto err_close_pipe_out
;
71 /* Set the CLOEXEC flag so that we do not leak file
72 descriptors in our children. */
73 fcntl(pipe_in
[0], F_SETFD
,
74 fcntl(pipe_out
[0], F_GETFD
) | FD_CLOEXEC
);
75 fcntl(pipe_in
[1], F_SETFD
,
76 fcntl(pipe_out
[0], F_GETFD
) | FD_CLOEXEC
);
80 sigprocmask(SIG_BLOCK
, &sigchld_mask
, NULL
);
83 sigprocmask(SIG_UNBLOCK
, &sigchld_mask
, NULL
);
89 sigprocmask(SIG_UNBLOCK
, &sigchld_mask
, NULL
);
94 if (dup2(pipe_out
[1], STDOUT_FILENO
) < 0) {
95 fail_perror("dup2(pipe_out)");
98 if (dup2(pipe_out
[1], STDERR_FILENO
) < 0) {
99 fail_perror("dup2(pipe_err)");
102 /* Detach child from terminal,
103 to avoid child catching SIGINT */
109 if (dup2(pipe_in
[0], 1022) < 0) {
110 fail_perror("dup2(pipe_in)");
113 if (dup2(pipe_out
[1], 1023) < 0) {
114 fail_perror("dup2(pipe_out)");
120 err
= execvp(argv
[0], argv
);
122 fail_fprintf(stderr
, "execvp(%s): %m", argv
[0]);
130 child
->next
= first_child
;
132 sigprocmask(SIG_UNBLOCK
, &sigchld_mask
, NULL
);
134 fprintf(stderr
, "Started child %d:", pid
);
135 for (i
= 0; argv
[i
]; i
++)
136 fprintf(stderr
, " %s", argv
[i
]);
140 fcntl(pipe_out
[0], F_SETFL
,
141 fcntl(pipe_out
[0], F_GETFL
) | O_NONBLOCK
);
142 child
->out
= pipe_out
[0];
143 FD_SET(child
->out
, &inputs
);
145 if (type
== CHILD_SCRIPT
) {
147 child
->in
= pipe_in
[1];
150 time(&child
->timeout
);
151 child
->timeout
+= 300;
155 child
->handle
= handle_checked_child
;
158 child
->handle
= handle_script_child
;
161 child
->handle
= handle_load_child
;
168 if (type
== CHILD_SCRIPT
) {
178 int child_init(struct child
*child
, int type
, char *cmdline
)
180 char **argv
= malloc(sizeof(*argv
));
188 char **new_argv
= realloc(argv
, sizeof(*argv
) * (argc
+ 2));
195 argv
[argc
++] = cmdline
;
196 cmdline
= strpbrk(cmdline
, " \t");
200 } while (isspace(*cmdline
));
201 } while (cmdline
&& *cmdline
);
204 rc
= child_initv(child
, type
, argv
);
211 void child_cleanup(struct child
*child
)
215 if (child
== first_child
)
216 first_child
= child
->next
;
218 for (prev
= first_child
; prev
; prev
= prev
->next
)
219 if (prev
->next
== child
) {
220 prev
->next
= child
->next
;
224 FD_CLR(child
->out
, &inputs
);
226 if (child
->type
== CHILD_SCRIPT
)
230 struct child
*child_search_pid(pid_t pid
)
234 for (child
= first_child
; child
; child
= child
->next
)
235 if (child
->pid
== pid
)
241 struct child
*child_search_type(int type
)
245 for (child
= first_child
; child
; child
= child
->next
)
246 if (child
->type
== type
)
252 int children_done_p(int type
)
256 for (child
= first_child
; child
; child
= child
->next
)
257 if ((type
== CHILD_ANY
|| type
== child
->type
) && !child
->dead
)
263 int children_kill(int type
, int sig
)
268 if (children_done_p(type
))
271 for (child
= first_child
; child
; child
= child
->next
)
272 if ((type
== CHILD_ANY
|| child
->type
== type
)
274 kill(child
->pid
, sig
);
276 return children_done_p(type
);
279 void sigchld_handler(int sig
)
285 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0) {
286 child
= child_search_pid(pid
);
288 fail_fprintf(stderr
, "dead child %d not found!\n", pid
);
292 child
->exit_status
= status
;
299 children_kill(CHILD_ANY
, SIGKILL
);
302 void termsig(int sig
)
305 sigexit_start
= time(NULL
);
306 children_kill(CHILD_ANY
, SIGTERM
);
307 signal(sig
, SIG_DFL
);
310 void copy(int from
, int to
)
316 ssize_t written
, wsz
;
317 sz
= read(from
, buffer
, sizeof(buffer
));
325 for (written
= 0; written
< sz
;
326 written
+= (wsz
> 0 ? wsz
: 0)) {
327 wsz
= write(to
, buffer
+ written
, sz
- written
);
329 fail_perror("write");
336 void handle_checked_child(struct child
*child
, fd_set
*fds
)
338 time_t now
= time(NULL
);
340 if (FD_ISSET(child
->out
, fds
)) {
341 copy(child
->out
, STDOUT_FILENO
);
342 child
->timeout
= now
+ 300;
346 int status
= child
->exit_status
;
348 /* A checked child died, this may be abnormal if no
349 termination signal was sent. */
350 if (WIFEXITED(status
)) {
351 if (sigexit
|| termload_start
)
354 "child %d exited with status %d\n",
355 child
->pid
, WEXITSTATUS(status
));
358 if (WIFSIGNALED(status
)) {
359 if (sigexit
|| termload_start
) {
361 child_cleanup(child
);
365 fail_fprintf(stderr
, "child %d exited with signal %d\n",
366 child
->pid
, WTERMSIG(status
));
367 if (WCOREDUMP(status
))
368 fprintf(stderr
, "(core dumped)\n");
375 if (now
> child
->timeout
) {
376 fail_fprintf(stderr
, "child %d produced no output for 5 minutes.\n",
383 void handle_script_child(struct child
*child
, fd_set
*fds
)
385 static char buffer
[4096];
392 child_cleanup(child
);
396 if (!FD_ISSET(child
->out
, fds
))
399 sz
= read(child
->out
, buffer
+ pos
, sizeof(buffer
) - pos
);
400 for (l
= buffer
; (eol
= strchr(l
, '\n')); l
= eol
+ 1) {
404 if (!memcmp(l
, "check_alive ", 12)) {
405 struct child
*new_child
;
407 new_child
= malloc(sizeof(*new_child
));
409 fail_fprintf(stderr
, "allocation failed\n");
413 rc
= child_init(new_child
, CHILD_CHECKED
, l
+ 12);
415 fail_perror("child_init");
418 } else if (!memcmp(l
, "start_load", 10)) {
420 fail_fprintf(stderr
, "start_load run while load"
421 " script is already running.\n");
424 rc
= child_init(&load
, CHILD_LOAD
, loadcmd
);
426 fail_perror("child_init");
430 fprintf(stderr
, "Invalid command %s\n", l
);
436 memmove(buffer
, l
, pos
+ 1);
440 void handle_load_child(struct child
*child
, fd_set
*fds
)
444 if (FD_ISSET(child
->out
, fds
))
445 copy(child
->out
, STDOUT_FILENO
);
448 time_t now
= time(NULL
);
450 if (!termload_start
) {
452 child_cleanup(child
);
456 fprintf(stderr
, "Load script terminated, "
457 "terminating checked scripts\n");
459 children_kill(CHILD_CHECKED
, SIGTERM
);
460 termload_start
= now
;
462 if (child_search_type(CHILD_CHECKED
)
463 && now
< termload_start
+ 30)
466 if (now
>= termload_start
+ 30) {
467 fail_fprintf(stderr
, "timeout waiting for "
469 "sending SIGKILL\n");
470 children_kill(CHILD_ANY
, SIGKILL
);
473 child_cleanup(child
);
477 write(script
.in
, "0\n", 2);
483 void usage(const char *progname
)
485 fprintf(stderr
, "%s [-l \"load command\"] script arguments...\n"
486 "Run \"script\" with \"arguments\" in a shell supplemented with"
487 " a few commands\nsuitable for running Xenomai tests.\n"
488 "\"load command\" is a command line to be run in order to"
489 " generate load\nwhile running tests.\n", progname
);
497 path_len
= strlen(getenv("PATH") ?: "") + strlen(TESTDIR
) + 2;
498 path
= malloc(path_len
);
504 snprintf(path
, path_len
, TESTDIR
":%s", getenv("PATH"));
506 snprintf(path
, path_len
, TESTDIR
);
508 setenv("PATH", path
, 1);
511 int main(int argc
, char *argv
[])
513 struct sigaction action
;
523 if (!strcmp(argv
[1], "-h")
524 || !strcmp(argv
[1], "--help")) {
530 if (!strcmp(argv
[2], "-l")) {
542 scriptname
= argv
[1];
546 action
.sa_handler
= termsig
;
547 sigemptyset(&action
.sa_mask
);
548 action
.sa_flags
= SA_RESTART
;
549 if (sigaction(SIGTERM
, &action
, NULL
) < 0) {
550 fail_perror("sigaction(SIGTERM)");
553 if (sigaction(SIGINT
, &action
, NULL
) < 0) {
554 fail_perror("sigaction(SIGTERM)");
558 action
.sa_flags
|= SA_NOCLDSTOP
;
559 action
.sa_handler
= sigchld_handler
;
560 if (sigaction(SIGCHLD
, &action
, NULL
) < 0) {
561 fail_perror("sigaction(SIGCHLD)");
569 new_argv
= malloc(sizeof(*new_argv
) * (argc
+ 2));
571 fail_fprintf(stderr
, "memory allocation failed\n");
574 new_argv
[0] = getenv("SHELL") ?: "/bin/bash";
575 new_argv
[1] = TESTDIR
"/xeno-test-run-wrapper";
576 for (i
= 1; i
< argc
+ 1; i
++)
577 new_argv
[i
+ 1] = argv
[i
];
579 rc
= child_initv(&script
, CHILD_SCRIPT
, new_argv
);
581 fail_fprintf(stderr
, "script creation failed: %s\n", strerror(-rc
));
586 while (first_child
) {
587 struct child
*child
, *next
;
596 rc
= select(maxfd
+ 1, &in
, NULL
, NULL
, &tv
);
601 fail_perror("select");
606 for (child
= first_child
; child
; child
= next
) {
609 if (child
->out
> maxfd
)
612 child
->handle(child
, &in
);
615 if (sigexit_start
&& time(NULL
) >= sigexit_start
+ 30) {
616 fail_fprintf(stderr
, "timeout waiting for all "
617 "children, sending SIGKILL\n");
618 children_kill(CHILD_ANY
, SIGKILL
);
624 signal(sigexit
, SIG_DFL
);