summaryrefslogtreecommitdiff
path: root/wait.c
blob: d799494ebd8338b88e2840f63eff543d4f9d334e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include "rc.h"

#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/wait.h>

#include "jbwrap.h"

bool forked = FALSE;

static pid_t rc_wait(int *);

typedef struct Pid Pid;

static struct Pid {
	pid_t pid;
	int stat;
	bool alive;
	Pid *n;
} *plist = NULL;

extern pid_t rc_fork() {
	Pid *new;
	pid_t pid = fork();
	switch (pid) {
	case -1:
		uerror("fork");
		rc_error(NULL);
		/* NOTREACHED */
	case 0:
		forked = TRUE;
		sigchk();
		return 0;
	default:
		new = enew(Pid);
		new->pid = pid;
		new->alive = TRUE;
		new->n = plist;
		plist = new;
		return pid;
	}
}

extern pid_t rc_wait4(pid_t pid, int *stat, bool nointr) {
	Pid *r, *prev;

	/* Find the child on the list. */
	for (r = plist, prev = NULL; r != NULL; prev = r, r = r->n)
		if (r->pid == pid)
			break;

	/* Uh-oh, not there. */
	if (r == NULL) {
		errno = ECHILD; /* no children */
		uerror("wait");
		*stat = 0x100; /* exit(1) */
		return -1;
	}

	/* If it's still alive, wait() for it. */
	while (r->alive) {
		int ret;
		Pid *q;

		ret = rc_wait(stat);

		if (ret < 0) {
			if (errno == ECHILD)
				panic("lost child");
			if (!nointr)
				return ret;
		}

		for (q = plist; q != NULL; q = q->n)
			if (q->pid == ret) {
				q->alive = FALSE;
				q->stat = *stat;
				break;
			}
	}
	*stat = r->stat;
	if (prev == NULL)
		plist = r->n; /* remove element from head of list */
	else
		prev->n = r->n;
	efree(r);
	return pid;
}

extern List *sgetapids() {
	List *r;
	Pid *p;
	for (r = NULL, p = plist; p != NULL; p = p->n) {
		List *q;
		if (!p->alive)
			continue;
		q = nnew(List);
		q->w = nprint("%d", p->pid);
		q->m = NULL;
		q->n = r;
		r = q;
	}
	return r;
}

extern void waitforall() {
	int stat;
	while (plist != NULL) {
		int pid = rc_wait4(plist->pid, &stat, FALSE);
		if (pid > 0)
			setstatus(pid, stat);
		else
			set(FALSE);
		sigchk();
	}
}

static int gotint;
void gotint_handler(int s) {
	gotint = 1;
	signal(s, gotint_handler);
}

/*
   rc_wait: a wait() wrapper that interfaces wait() w/rc signals.
   Note that the signal queue is not checked in this fn; someone
   may want to resume the wait() without delivering any signals.
*/

static int r;
static pid_t rc_wait(int *stat) {
	void (*old)(int);

	interrupt_happened = FALSE;
	r = -1;

	gotint = 0;
	old = signal(SIGINT, gotint_handler);
	if (!sigsetjmp(slowbuf.j, 1)) {
		slow = TRUE;
		if (!interrupt_happened)
			r = wait(stat);
	}
	slow = FALSE;
	signal(SIGINT, old);
	if (gotint && old != SIG_DFL && old != SIG_IGN)
		(*old)(SIGINT);

	return r;
}