#include <errno.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>

#define NODEFINE
#include <u.h>
#include <libc.h>
#include "sig.h"

typedef struct Waitevent Waitevent;
struct Waitevent {
	Waitevent *next;
	char msg[256];
	int iserror;
};

static ILock waitilock;
static QLock dataready;
static Waitevent *head, *tail;

int 
pm_await(char *s, int n)
{
	char *e;
	Waitevent *w;

	while(!pm_canqlock(&dataready))
		if(pm_checkkids() < 0)
			return -1;

	pm_ilock(&waitilock);
	w = head;
	pm_assert(w != nil);
	head = w->next;
	if(head == nil)
		tail = nil;
	else
		pm_qunlock(&dataready);
	pm_iunlock(&waitilock);
	
	if(pm_debug & PmDebugWait)
		pm_fprint(2, "w %p: %s%s (head=%p)\n", w, w->iserror?" (err)":"", w->msg, head);

	if(w->iserror){
		pm_werrstr("%s", w->msg);
		pm_free(w);
		return -1;
	}else{
		e = pm_utfecpy(s, s+n, w->msg);
		pm_free(w);
		return e-s;
	}
}

void
pm_waitinit(void)
{
	pm_qlock(&dataready);
}

int
pm_checkkids(void)
{
	pid_t pidt;
	ulong usertime, systemtime, realtime, pid;
	int status;
	char stbuf[128];
	struct rusage rusage;
	Waitevent *w;

	while((pidt = wait4(-1, &status, WNOHANG, &rusage)) != 0){
		if(pidt < 0){	/* no kids */
			pm_oserror();
			return -1;
		}
		if(WIFSTOPPED(status)){
			if(pm_debug & PmDebugWait)
				pm_fprint(2, "pid %d: wait4: process stopped\n", getpid());
			continue;
		}
	
		pid = pidt;
		usertime = rusage.ru_utime.tv_sec*1000 + rusage.ru_utime.tv_usec/1000;
		systemtime = rusage.ru_stime.tv_sec*1000 + rusage.ru_stime.tv_usec/1000;
		realtime = 0;
	
		strcpy(stbuf, "cannot happen in pm_await");
		if(WIFEXITED(status)){
			if(WEXITSTATUS(status) == 0)
				stbuf[0] = '\0';
			else
				pm_snprint(stbuf, sizeof stbuf, "%d", status);
		}
	
		if(WIFSIGNALED(status)){
			if(0 <= WTERMSIG(status) && WTERMSIG(status) < pm_nsigtab)
				pm_strcpy(stbuf, pm_sigtab[WTERMSIG(status)].desc);
			else
				pm_snprint(stbuf, sizeof stbuf, "signal %d", WTERMSIG(status));
			if(WCOREDUMP(status))
				pm_strcat(stbuf, " (core dumped)");
		}
		pm_quotefmtinstall();
		w = pm_malloc(sizeof(*w));
		w->iserror = 0;
		pm_snprint(w->msg, sizeof w->msg, "%lud %lud %lud %lud %q",
			pid, usertime, systemtime, realtime, stbuf);
		w->next = nil;
		if(pm_debug & PmDebugWait)
			pm_fprint(2, "pid %d: checkkids %p: %s\n", getpid(), w, w->msg);
		pm_ilock(&waitilock);
		if(tail){
			tail->next = w;
			tail = w;
		}else{
			head = tail = w;
			pm_qunlock(&dataready);
		}
		pm_iunlock(&waitilock);
	}
	return 0;
}
