/*
 * 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>

int r1;
int r2;

#define CHILD_READY(pid) ((long)rendezvous(&r1, (void*)(~(pid))))
//#define C2P_READY(pid) ((long)rendezvous(&r2, (void*)(~(pid))))

int *__target_pid;

static void
forwarding_note_handler(void *ureg, char *note)
{
	postnote(PNPROC, *__target_pid, note);
	noted(NDFLT);
}

static void
donothing(void)
{
	int i = 0;
	while(++i < 100)
		sleep(3000);
	exits(nil);
}

static int
crazy_fork(void)
{
	int father = getpid();
	int p2c;
	long c2p = -1, child = -1;

	switch(p2c = rfork(RFPROC|RFMEM)){
	case -1:
		return -1;
	case 0:
		switch(c2p = rfork(RFPROC|RFMEM)){
		case -1:
			exits("rfork (c2p)");
		case 0:
			switch(child = fork()){
			case -1:
				exits("rfork (child)");
			case 0:
				return 0;
			default:
				while(CHILD_READY(child) == -1)
					;
				*__target_pid = father;
				notify(forwarding_note_handler);
				donothing();
			}
		default:
			while((child = CHILD_READY(-3)) == -1)
				;
			child = ~child;
			*__target_pid = child;
			notify(forwarding_note_handler);
			donothing();
		}
	default:
		break;
	}

	return p2c;
}

int
pass_on_die(void *v, char *s)
{
	if(strncmp(s, "die", 4) == 0){
		print("PASS\n");
		exits("PASS");
	}
	return 0;
}

void
main(void)
{
	int c, target_pid = 0;
	__target_pid = &target_pid;
	
	if (!atnotify(pass_on_die, 1)){
		fprint(2, "%r\n");
		exits("atnotify fails");
	}

	c = crazy_fork();
	switch(c){
	case -1:
		print("FAIL: fork\n");
		exits("FAIL");
	case 0:
		print("child is %d; child's parent is %d\n", getpid(), getppid());
		*__target_pid = getppid();
		notify(forwarding_note_handler);
		/* wait to be killed */
		donothing();
		break;
	default:
		print("father is %d; forked child is %d\n", getpid(), c);
		sleep(1000); /* give children time to notify() */
		print("starting note chain\n");
		postnote(PNPROC, c, "die");
		/* wait to be killed by the chain */
		sleep(30000);
	}
	print("FAIL\n");
	exits("FAIL");
}