jehanne/sys/src/lib/posix/sigchlds.c

202 lines
4.9 KiB
C

/*
* This file is part of Jehanne.
*
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <lib9.h>
#include <posix.h>
#include "internal.h"
/* pointer to the pid to forward notes to */
extern ChildList **__libposix_child_list;
extern WaitList **__libposix_wait_list;
extern SignalConf *__libposix_signals;
extern int *__libposix_devsignal;
static void
open_sighelper_nanny(void)
{
int mypid;
if(*__libposix_devsignal >= 0)
close(*__libposix_devsignal);
mypid = *__libposix_pid;
*__libposix_devsignal = create("/dev/posix/nanny", OWRITE|OCEXEC, mypid);
if(*__libposix_devsignal < 0)
sysfatal("cannot create /dev/posix/nanny: %r");
}
static void
exit_on_SA_NOCLDWAIT(char *msg){
/* we share the father's memory, we can inspect its configuration */
if(__libposix_signals[PosixSIGCHLD-1].sa_nochildwait){
/* the parent does not care about us*/
rfork(RFNOWAIT);
exits(msg);
}
}
static void
forward_wait_msg(int father, int child)
{
int n, mypid;
PosixSignalInfo si;
char buf[512], err[ERRMAX], note[512], *fld[5], *tmp, *name;
mypid = *__libposix_pid;
name = smprint("signal proxy %d <> %d", father, child);
snprint(buf, sizeof(buf), "/proc/%d/args", mypid);
n = open(buf, OWRITE);
write(n, name, strlen(name)+1);
close(n);
rfork(RFCNAMEG|RFCENVG|RFNOMNT);
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);
exit_on_SA_NOCLDWAIT(note);
postnote(PNPROC, father, note);
__libposix_sighelper_cmd(PHProcessExited, child);
exits(note);
}
buf[n] = '\0';
if(jehanne_tokenize(buf, fld, nelem(fld)) != nelem(fld)){
snprint(note, sizeof(note), "%s: can not parse wait msg: %s", name, buf);
exit_on_SA_NOCLDWAIT(note);
postnote(PNPROC, father, note);
exits(note);
}
memset(&si, 0, sizeof(PosixSignalInfo));
si.si_pid = mypid;
si.si_signo = PosixSIGCHLD;
si.si_code = PosixSIChildExited;
si.si_uid = POSIX_getuid(&n);
tmp = fld[4];
if(tmp == nil || tmp[0] == '\0')
n = 0;
else if(strcmp(__POSIX_EXIT_PREFIX, tmp) == 0)
n = atoi(tmp + (sizeof(__POSIX_EXIT_PREFIX)/sizeof(char) - 1));
else if(strcmp(__POSIX_EXIT_SIGNAL_PREFIX, tmp) == 0){
n = atoi(tmp + (sizeof(__POSIX_EXIT_SIGNAL_PREFIX)/sizeof(char) - 1));
if(n == PosixSIGTRAP)
si.si_code = PosixSIChildTrapped;
else
si.si_code = PosixSIChildKilled;
} else
n = 127;
si.si_status = n;
exit_on_SA_NOCLDWAIT("SIGCHLD explicity ignored by parent");
__libposix_notify_signal_to_process(father, &si);
__libposix_sighelper_cmd(PHProcessExited, child);
if(n == 0)
exits(nil);
exits(fld[4]);
}
static int
fork_with_sigchld(int *errnop)
{
int proxy, father, child = -1;
ChildList *c;
uint64_t rend;
char *buf;
/* Father here:
* - create proxy
* - wait for child to be ready
* - register proxy in children list
* - return proxy pid
*/
father = getpid();
switch(proxy = rfork(RFPROC|RFMEM|RFFDG)){
case -1:
return -1;
case 0:
/* proxy here:
* - create child
* - start waiting
*/
*__libposix_pid = getpid();
open_sighelper_nanny();
switch(child = rfork(RFPROC|RFFDG)){
case -1:
rend = *__libposix_pid;
while(rendezvous((void*)rend, "e") == (void*)-1)
sleep(100);
rfork(RFNOWAIT);
exits("rfork (child)");
case 0:
/* Beloved child here
*/
__libposix_setup_new_process();
rend = *__libposix_pid;
while(rendezvous((void*)rend, "d") == (void*)-1)
sleep(100);
rfork(RFREND);
return 0;
default:
rend = child;
while((buf = rendezvous((void*)rend, "")) == (void*)-1)
sleep(100);
rend = *__libposix_pid;
while(rendezvous((void*)rend, "d") == (void*)-1)
sleep(100);
/* we share the memory of the parent but we do
* not need these that fortunately are on the private stack
*/
*__libposix_wait_list = nil;
*__libposix_child_list = nil;
forward_wait_msg(father, child);
}
default:
rend = proxy;
while((buf = rendezvous((void*)rend, "")) == (void*)-1)
sleep(100);
if(buf[0] == 'e')
return -1;
break;
}
/* no need to lock: the child list is private */
c = malloc(sizeof(ChildList));
c->pid = proxy;
c->next = *__libposix_child_list;
*__libposix_child_list = c;
return proxy;
}
int
libposix_emulate_SIGCHLD(void)
{
extern int (*__libposix_fork)(int *errnop);
__libposix_fork = fork_with_sigchld;
return 1;
}