diff --git a/qa/kern/build.json b/qa/kern/build.json index 08ee7e2..3bc6203 100644 --- a/qa/kern/build.json +++ b/qa/kern/build.json @@ -18,6 +18,7 @@ "execl.c", "float.c", "fork.c", + "fork_chain.c", "frexp.c", "getmainpid.c", "getpid.c", diff --git a/qa/kern/fork_chain.c b/qa/kern/fork_chain.c new file mode 100644 index 0000000..90ba6a3 --- /dev/null +++ b/qa/kern/fork_chain.c @@ -0,0 +1,216 @@ +/* + * This file is part of Jehanne. + * + * Copyright (C) 2017 Giacomo Tesio + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3 of the License. + * + * Jehanne is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Jehanne. If not, see . + */ +#include +#include + +int r1; +int r2; + +#define CHILD_READY(pid) ((long)rendezvous(&r1, (void*)(~(pid)))) +#define C2P_READY(pid) ((long)rendezvous(&r2, (void*)(~(pid)))) + +int *__libposix_sigchld_target_pid; + +static void +release_inherited_resources(void) +{ + notify(nil); + rfork(RFCNAMEG|RFCENVG|RFNOTEG|RFCFDG); + bind("#p", "/proc", MREPL); + rfork(RFNOMNT); +} + +static void +forwarding_note_handler(void *ureg, char *note) +{ + postnote(PNPROC, *__libposix_sigchld_target_pid, note); + noted(NCONT); +} + +static void +forward_wait_msg(int sigchld_receiver, char *name) +{ + int n; + char buf[512], err[ERRMAX], note[512], *fld[5]; + + snprint(buf, sizeof(buf), "/proc/%d/args", getpid()); + n = open(buf, OWRITE); + write(n, name, strlen(name)+1); + close(n); + + n = 0; +WaitInterrupted: + n = await(buf, sizeof buf-1); + if(n < 0){ + rerrstr(err, ERRMAX); + if(strstr(err, "no living children") == nil) + goto WaitInterrupted; + snprint(note, sizeof(note), "%s: %r", name); + if(sigchld_receiver) + postnote(PNPROC, sigchld_receiver, note); + exits(note); + } + buf[n] = '\0'; + if(jehanne_tokenize(buf, fld, nelem(fld)) != nelem(fld)){ + snprint(note, sizeof(note), "%s: couldn't parse wait message", name); + if(sigchld_receiver) + postnote(PNPROC, sigchld_receiver, note); + exits(note); + } + snprint(note, sizeof(note), "posix: 20"); + if(sigchld_receiver){ + postnote(PNPROC, sigchld_receiver, note); + } + exits(fld[4]); +} + + +static int +fork_with_sigchld(void) +{ + int father = getpid(); + int p2c; + long c2p = -1, child = -1; + char proxy_name[256]; + + /* Father here: + * - create P2C + * - wait for C2P to be ready + * - register P2C in children list + * - return P2C pid + */ + switch(p2c = rfork(RFPROC|RFMEM)){ + case -1: + return -1; + case 0: + /* P2C here: + * - create C2P + * - wait for the child pid + * - release all inherited resources + * - install forwarding_note_handler + * - send to father the C2P pid + * - start waiting for the child + */ + switch(c2p = rfork(RFPROC|RFMEM)){ + case -1: + while(C2P_READY(-2) == -1) + ; + exits("rfork (c2p)"); + case 0: + /* C2P here: + * - create child + * - wait for it to get a copy of everything + * - release all inherited resources + * - install forwarding_note_handler + * - send to P2C the child pid + * - start forwarding notes to the father + */ + switch(child = fork()){ + case -1: + while(CHILD_READY(-2) == -1) + ; + exits("rfork (child)"); + case 0: + /* Beloved child here + */ +// __libposix_setup_new_process(); + return 0; + default: + release_inherited_resources(); + *__libposix_sigchld_target_pid = father; + notify(forwarding_note_handler); + snprint(proxy_name, sizeof(proxy_name), "libposix signal proxy %d < %d", father, child); + while(CHILD_READY(child) == -1) + ; + forward_wait_msg(father, proxy_name); + } + default: + while((child = CHILD_READY(-3)) == -1) + ; + child = ~child; + if(child < 0){ + while(C2P_READY(-2) == -1) + ; + waitpid(); + exits("rfork (child)"); + } + release_inherited_resources(); + *__libposix_sigchld_target_pid = child; + notify(forwarding_note_handler); + snprint(proxy_name, sizeof(proxy_name), "libposix signal proxy %d > %d", father, child); + while(C2P_READY(c2p) == -1) + ; + forward_wait_msg(0, proxy_name); + } + default: + while((c2p = C2P_READY(-3)) == -1) + ; + c2p = ~c2p; + if(c2p < 0){ + waitpid(); + return -1; + } + break; + } + + print("f %d; p2c %d, c2p %d\n", father, p2c, c2p); + + return p2c; +} + +int +passOnSIGCHLD(void *v, char *s) +{ + if(strncmp(s, "posix: 20", 4) == 0){ + print("PASS\n"); + exits("PASS"); + } + return 0; +} + +void +main(void) +{ + int c, target_pid = 0; + __libposix_sigchld_target_pid = &target_pid; + + if (!atnotify(passOnSIGCHLD, 1)){ + fprint(2, "%r\n"); + exits("atnotify fails"); + } + + c = fork_with_sigchld(); + switch(c){ + case -1: + print("FAIL: fork\n"); + exits("FAIL"); + case 0: + /* wait to be killed */ + print("child is %d\n", getpid()); +// for(;;); + sleep(30000); + break; + default: + print("father is %d\n", getpid()); + postnote(PNPROC, c, "die"); + /* wait to be killed */ + sleep(30000); + } + print("FAIL\n"); + exits("FAIL"); +}