1/*
2 * Copyright (c) 1997-2014 Erez Zadok
3 * Copyright (c) 1990 Jan-Simon Pendry
4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 *
36 * File: am-utils/amd/sched.c
37 *
38 */
39
40/*
41 * Process scheduler
42 */
43
44#ifdef HAVE_CONFIG_H
45# include <config.h>
46#endif /* HAVE_CONFIG_H */
47#include <am_defs.h>
48#include <amd.h>
49
50
51typedef struct pjob pjob;
52
53struct pjob {
54  qelem hdr;		/* Linked list */
55  int pid;		/* Process ID of job */
56  cb_fun *cb_fun;	/* Callback function */
57  opaque_t cb_arg;	/* Argument for callback */
58  int w;		/* everyone these days uses int, not a "union wait" */
59  wchan_t wchan;	/* Wait channel */
60};
61
62/* globals */
63qelem proc_list_head = {&proc_list_head, &proc_list_head};
64qelem proc_wait_list = {&proc_wait_list, &proc_wait_list};
65int task_notify_todo;
66
67
68void
69ins_que(qelem *elem, qelem *pred)
70{
71  qelem *p = pred->q_forw;
72
73  elem->q_back = pred;
74  elem->q_forw = p;
75  pred->q_forw = elem;
76  p->q_back = elem;
77}
78
79
80void
81rem_que(qelem *elem)
82{
83  qelem *p = elem->q_forw;
84  qelem *p2 = elem->q_back;
85
86  p2->q_forw = p;
87  p->q_back = p2;
88}
89
90
91static pjob *
92sched_job(cb_fun *cf, opaque_t ca)
93{
94  pjob *p = ALLOC(struct pjob);
95
96  p->cb_fun = cf;
97  p->cb_arg = ca;
98
99  /*
100   * Now place on wait queue
101   */
102  ins_que(&p->hdr, &proc_wait_list);
103
104  return p;
105}
106
107
108/*
109 * tf: The task to execute (ta is its arguments)
110 * cf: Continuation function (ca is its arguments)
111 */
112void
113run_task(task_fun *tf, opaque_t ta, cb_fun *cf, opaque_t ca)
114{
115  pjob *p = sched_job(cf, ca);
116#ifdef HAVE_SIGACTION
117  sigset_t new, mask;
118#else /* not HAVE_SIGACTION */
119  int mask;
120#endif /* not HAVE_SIGACTION */
121
122  p->wchan = (wchan_t) p;
123
124#ifdef HAVE_SIGACTION
125  sigemptyset(&new);		/* initialize signal set we wish to block */
126  sigaddset(&new, SIGCHLD);	/* only block on SIGCHLD */
127  sigprocmask(SIG_BLOCK, &new, &mask);
128#else /* not HAVE_SIGACTION */
129  mask = sigblock(sigmask(SIGCHLD));
130#endif /* not HAVE_SIGACTION */
131
132  if ((p->pid = background())) {
133#ifdef HAVE_SIGACTION
134    sigprocmask(SIG_SETMASK, &mask, NULL);
135#else /* not HAVE_SIGACTION */
136    sigsetmask(mask);
137#endif /* not HAVE_SIGACTION */
138    return;
139  }
140
141  /* child code runs here, parent has returned to caller */
142
143  exit((*tf) (ta));
144  /* firewall... */
145  abort();
146}
147
148
149/*
150 * Schedule a task to be run when woken up
151 */
152void
153sched_task(cb_fun *cf, opaque_t ca, wchan_t wchan)
154{
155  /*
156   * Allocate a new task
157   */
158  pjob *p = sched_job(cf, ca);
159
160  dlog("SLEEP on %p", wchan);
161  p->wchan = wchan;
162  p->pid = 0;
163  p->w = 0;			/* was memset (when ->w was union) */
164}
165
166
167static void
168wakeupjob(pjob *p)
169{
170  rem_que(&p->hdr);
171  ins_que(&p->hdr, &proc_list_head);
172  task_notify_todo++;
173}
174
175
176void
177wakeup(wchan_t wchan)
178{
179  pjob *p, *p2;
180
181  if (!foreground)
182    return;
183
184  /*
185   * Can't use ITER() here because
186   * wakeupjob() juggles the list.
187   */
188  for (p = AM_FIRST(pjob, &proc_wait_list);
189       p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
190       p = p2) {
191    if (p->wchan == wchan) {
192      wakeupjob(p);
193    }
194  }
195}
196
197
198void
199wakeup_task(int rc, int term, wchan_t wchan)
200{
201  wakeup(wchan);
202}
203
204
205wchan_t
206get_mntfs_wchan(mntfs *mf)
207{
208  if (mf &&
209      mf->mf_ops &&
210      mf->mf_ops->get_wchan)
211    return mf->mf_ops->get_wchan(mf);
212  return mf;
213}
214
215
216/*
217 * Run any pending tasks.
218 * This must be called with SIGCHLD disabled
219 */
220void
221do_task_notify(void)
222{
223  /*
224   * Keep taking the first item off the list and processing it.
225   *
226   * Done this way because the callback can, quite reasonably,
227   * queue a new task, so no local reference into the list can be
228   * held here.
229   */
230  while (AM_FIRST(pjob, &proc_list_head) != HEAD(pjob, &proc_list_head)) {
231    pjob *p = AM_FIRST(pjob, &proc_list_head);
232    rem_que(&p->hdr);
233    /*
234     * This job has completed
235     */
236    --task_notify_todo;
237
238    /*
239     * Do callback if it exists
240     */
241    if (p->cb_fun) {
242      /* these two trigraphs will ensure compatibility with strict POSIX.1 */
243      p->cb_fun(WIFEXITED(p->w)   ? WEXITSTATUS(p->w) : 0,
244		WIFSIGNALED(p->w) ? WTERMSIG(p->w)    : 0,
245		p->cb_arg);
246    }
247    XFREE(p);
248  }
249}
250
251
252RETSIGTYPE
253sigchld(int sig)
254{
255  int w;	/* everyone these days uses int, not a "union wait" */
256  int pid;
257
258#ifdef HAVE_WAITPID
259  while ((pid = waitpid((pid_t) -1,  &w, WNOHANG)) > 0) {
260#else /* not HAVE_WAITPID */
261  while ((pid = wait3( &w, WNOHANG, (struct rusage *) NULL)) > 0) {
262#endif /* not HAVE_WAITPID */
263    pjob *p, *p2;
264
265    if (WIFSIGNALED(w))
266      plog(XLOG_ERROR, "Process %d exited with signal %d",
267	   pid, WTERMSIG(w));
268    else
269      dlog("Process %d exited with status %d",
270	   pid, WEXITSTATUS(w));
271
272    for (p = AM_FIRST(pjob, &proc_wait_list);
273	 p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
274	 p = p2) {
275      if (p->pid == pid) {
276	p->w = w;
277	wakeupjob(p);
278	break;
279      }
280    } /* end of for loop */
281
282    if (p == HEAD(pjob, &proc_wait_list))
283      dlog("can't locate task block for pid %d", pid);
284
285    /*
286     * Must count down children inside the while loop, otherwise we won't
287     * count them all, and NumChildren (and later backoff) will be set
288     * incorrectly. SH/RUNIT 940519.
289     */
290    if (--NumChildren < 0)
291      NumChildren = 0;
292  } /* end of "while wait..." loop */
293
294#ifdef REINSTALL_SIGNAL_HANDLER
295  signal(sig, sigchld);
296#endif /* REINSTALL_SIGNAL_HANDLER */
297
298  if (select_intr_valid)
299    longjmp(select_intr, sig);
300}
301