138494Sobrien/*
2310490Scy * Copyright (c) 1997-2014 Erez Zadok
338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1990 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
19310490Scy * 3. Neither the name of the University nor the names of its contributors
2038494Sobrien *    may be used to endorse or promote products derived from this software
2138494Sobrien *    without specific prior written permission.
2238494Sobrien *
2338494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2438494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2538494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2638494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2738494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2838494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2938494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3038494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3138494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3238494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3338494Sobrien * SUCH DAMAGE.
3438494Sobrien *
3538494Sobrien *
36174294Sobrien * File: am-utils/amd/sched.c
3738494Sobrien *
3838494Sobrien */
3938494Sobrien
4038494Sobrien/*
4138494Sobrien * Process scheduler
4238494Sobrien */
4338494Sobrien
4438494Sobrien#ifdef HAVE_CONFIG_H
4538494Sobrien# include <config.h>
4638494Sobrien#endif /* HAVE_CONFIG_H */
4738494Sobrien#include <am_defs.h>
4838494Sobrien#include <amd.h>
4938494Sobrien
5038494Sobrien
5138494Sobrientypedef struct pjob pjob;
5238494Sobrien
5338494Sobrienstruct pjob {
54174294Sobrien  qelem hdr;		/* Linked list */
55174294Sobrien  int pid;		/* Process ID of job */
56174294Sobrien  cb_fun *cb_fun;	/* Callback function */
57174294Sobrien  opaque_t cb_arg;	/* Argument for callback */
5838494Sobrien  int w;		/* everyone these days uses int, not a "union wait" */
59174294Sobrien  wchan_t wchan;	/* Wait channel */
6038494Sobrien};
6138494Sobrien
6238494Sobrien/* globals */
6338494Sobrienqelem proc_list_head = {&proc_list_head, &proc_list_head};
6438494Sobrienqelem proc_wait_list = {&proc_wait_list, &proc_wait_list};
6538494Sobrienint task_notify_todo;
6638494Sobrien
6738494Sobrien
6838494Sobrienvoid
6938494Sobrienins_que(qelem *elem, qelem *pred)
7038494Sobrien{
7138494Sobrien  qelem *p = pred->q_forw;
7238494Sobrien
7338494Sobrien  elem->q_back = pred;
7438494Sobrien  elem->q_forw = p;
7538494Sobrien  pred->q_forw = elem;
7638494Sobrien  p->q_back = elem;
7738494Sobrien}
7838494Sobrien
7938494Sobrien
8038494Sobrienvoid
8138494Sobrienrem_que(qelem *elem)
8238494Sobrien{
8338494Sobrien  qelem *p = elem->q_forw;
8438494Sobrien  qelem *p2 = elem->q_back;
8538494Sobrien
8638494Sobrien  p2->q_forw = p;
8738494Sobrien  p->q_back = p2;
8838494Sobrien}
8938494Sobrien
9038494Sobrien
9138494Sobrienstatic pjob *
92174294Sobriensched_job(cb_fun *cf, opaque_t ca)
9338494Sobrien{
9438494Sobrien  pjob *p = ALLOC(struct pjob);
9538494Sobrien
9638494Sobrien  p->cb_fun = cf;
97174294Sobrien  p->cb_arg = ca;
9838494Sobrien
9938494Sobrien  /*
10038494Sobrien   * Now place on wait queue
10138494Sobrien   */
10238494Sobrien  ins_que(&p->hdr, &proc_wait_list);
10338494Sobrien
10438494Sobrien  return p;
10538494Sobrien}
10638494Sobrien
10738494Sobrien
10838494Sobrien/*
10938494Sobrien * tf: The task to execute (ta is its arguments)
11038494Sobrien * cf: Continuation function (ca is its arguments)
11138494Sobrien */
11238494Sobrienvoid
113174294Sobrienrun_task(task_fun *tf, opaque_t ta, cb_fun *cf, opaque_t ca)
11438494Sobrien{
11538494Sobrien  pjob *p = sched_job(cf, ca);
11638494Sobrien#ifdef HAVE_SIGACTION
11738494Sobrien  sigset_t new, mask;
11838494Sobrien#else /* not HAVE_SIGACTION */
11938494Sobrien  int mask;
12038494Sobrien#endif /* not HAVE_SIGACTION */
12138494Sobrien
122174294Sobrien  p->wchan = (wchan_t) p;
12338494Sobrien
12438494Sobrien#ifdef HAVE_SIGACTION
12542629Sobrien  sigemptyset(&new);		/* initialize signal set we wish to block */
12638494Sobrien  sigaddset(&new, SIGCHLD);	/* only block on SIGCHLD */
12738494Sobrien  sigprocmask(SIG_BLOCK, &new, &mask);
12838494Sobrien#else /* not HAVE_SIGACTION */
12938494Sobrien  mask = sigblock(sigmask(SIGCHLD));
13038494Sobrien#endif /* not HAVE_SIGACTION */
13138494Sobrien
13238494Sobrien  if ((p->pid = background())) {
13338494Sobrien#ifdef HAVE_SIGACTION
13438494Sobrien    sigprocmask(SIG_SETMASK, &mask, NULL);
13538494Sobrien#else /* not HAVE_SIGACTION */
13638494Sobrien    sigsetmask(mask);
13738494Sobrien#endif /* not HAVE_SIGACTION */
13838494Sobrien    return;
13938494Sobrien  }
14038494Sobrien
141174294Sobrien  /* child code runs here, parent has returned to caller */
14238494Sobrien
14338494Sobrien  exit((*tf) (ta));
14438494Sobrien  /* firewall... */
14538494Sobrien  abort();
14638494Sobrien}
14738494Sobrien
14838494Sobrien
14938494Sobrien/*
15038494Sobrien * Schedule a task to be run when woken up
15138494Sobrien */
15238494Sobrienvoid
153174294Sobriensched_task(cb_fun *cf, opaque_t ca, wchan_t wchan)
15438494Sobrien{
15538494Sobrien  /*
15638494Sobrien   * Allocate a new task
15738494Sobrien   */
15838494Sobrien  pjob *p = sched_job(cf, ca);
15938494Sobrien
160174294Sobrien  dlog("SLEEP on %p", wchan);
16138494Sobrien  p->wchan = wchan;
16238494Sobrien  p->pid = 0;
163174294Sobrien  p->w = 0;			/* was memset (when ->w was union) */
16438494Sobrien}
16538494Sobrien
16638494Sobrien
16738494Sobrienstatic void
16838494Sobrienwakeupjob(pjob *p)
16938494Sobrien{
17038494Sobrien  rem_que(&p->hdr);
17138494Sobrien  ins_que(&p->hdr, &proc_list_head);
17238494Sobrien  task_notify_todo++;
17338494Sobrien}
17438494Sobrien
17538494Sobrien
17638494Sobrienvoid
177174294Sobrienwakeup(wchan_t wchan)
17838494Sobrien{
17938494Sobrien  pjob *p, *p2;
18038494Sobrien
18138494Sobrien  if (!foreground)
18238494Sobrien    return;
18338494Sobrien
18438494Sobrien  /*
185174294Sobrien   * Can't use ITER() here because
18638494Sobrien   * wakeupjob() juggles the list.
18738494Sobrien   */
18838494Sobrien  for (p = AM_FIRST(pjob, &proc_wait_list);
18938494Sobrien       p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
19038494Sobrien       p = p2) {
19138494Sobrien    if (p->wchan == wchan) {
19238494Sobrien      wakeupjob(p);
19338494Sobrien    }
19438494Sobrien  }
19538494Sobrien}
19638494Sobrien
19738494Sobrien
19838494Sobrienvoid
199174294Sobrienwakeup_task(int rc, int term, wchan_t wchan)
20038494Sobrien{
201174294Sobrien  wakeup(wchan);
20238494Sobrien}
20338494Sobrien
20438494Sobrien
205174294Sobrienwchan_t
206174294Sobrienget_mntfs_wchan(mntfs *mf)
207174294Sobrien{
208174294Sobrien  if (mf &&
209174294Sobrien      mf->mf_ops &&
210174294Sobrien      mf->mf_ops->get_wchan)
211174294Sobrien    return mf->mf_ops->get_wchan(mf);
212174294Sobrien  return mf;
213174294Sobrien}
214174294Sobrien
215174294Sobrien
21638494Sobrien/*
21738494Sobrien * Run any pending tasks.
21838494Sobrien * This must be called with SIGCHLD disabled
21938494Sobrien */
22038494Sobrienvoid
22138494Sobriendo_task_notify(void)
22238494Sobrien{
22338494Sobrien  /*
22438494Sobrien   * Keep taking the first item off the list and processing it.
22538494Sobrien   *
22682794Sobrien   * Done this way because the callback can, quite reasonably,
22738494Sobrien   * queue a new task, so no local reference into the list can be
22838494Sobrien   * held here.
22938494Sobrien   */
23038494Sobrien  while (AM_FIRST(pjob, &proc_list_head) != HEAD(pjob, &proc_list_head)) {
23138494Sobrien    pjob *p = AM_FIRST(pjob, &proc_list_head);
23238494Sobrien    rem_que(&p->hdr);
23338494Sobrien    /*
23438494Sobrien     * This job has completed
23538494Sobrien     */
23638494Sobrien    --task_notify_todo;
23738494Sobrien
23838494Sobrien    /*
23938494Sobrien     * Do callback if it exists
24038494Sobrien     */
24138494Sobrien    if (p->cb_fun) {
24242629Sobrien      /* these two trigraphs will ensure compatibility with strict POSIX.1 */
243174294Sobrien      p->cb_fun(WIFEXITED(p->w)   ? WEXITSTATUS(p->w) : 0,
244174294Sobrien		WIFSIGNALED(p->w) ? WTERMSIG(p->w)    : 0,
245174294Sobrien		p->cb_arg);
24638494Sobrien    }
24738494Sobrien    XFREE(p);
24838494Sobrien  }
24938494Sobrien}
25038494Sobrien
25138494Sobrien
25238494SobrienRETSIGTYPE
25338494Sobriensigchld(int sig)
25438494Sobrien{
25538494Sobrien  int w;	/* everyone these days uses int, not a "union wait" */
25638494Sobrien  int pid;
25738494Sobrien
25838494Sobrien#ifdef HAVE_WAITPID
25938494Sobrien  while ((pid = waitpid((pid_t) -1,  &w, WNOHANG)) > 0) {
26038494Sobrien#else /* not HAVE_WAITPID */
261310490Scy  while ((pid = wait3( &w, WNOHANG, (struct rusage *) NULL)) > 0) {
26238494Sobrien#endif /* not HAVE_WAITPID */
26338494Sobrien    pjob *p, *p2;
26438494Sobrien
26538494Sobrien    if (WIFSIGNALED(w))
26638494Sobrien      plog(XLOG_ERROR, "Process %d exited with signal %d",
26738494Sobrien	   pid, WTERMSIG(w));
26838494Sobrien    else
26938494Sobrien      dlog("Process %d exited with status %d",
27038494Sobrien	   pid, WEXITSTATUS(w));
27138494Sobrien
27238494Sobrien    for (p = AM_FIRST(pjob, &proc_wait_list);
27338494Sobrien	 p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
27438494Sobrien	 p = p2) {
27538494Sobrien      if (p->pid == pid) {
27638494Sobrien	p->w = w;
27738494Sobrien	wakeupjob(p);
27838494Sobrien	break;
27938494Sobrien      }
28038494Sobrien    } /* end of for loop */
28138494Sobrien
282174294Sobrien    if (p == HEAD(pjob, &proc_wait_list))
28338494Sobrien      dlog("can't locate task block for pid %d", pid);
28438494Sobrien
28538494Sobrien    /*
28638494Sobrien     * Must count down children inside the while loop, otherwise we won't
287174294Sobrien     * count them all, and NumChildren (and later backoff) will be set
28838494Sobrien     * incorrectly. SH/RUNIT 940519.
28938494Sobrien     */
290174294Sobrien    if (--NumChildren < 0)
291174294Sobrien      NumChildren = 0;
29238494Sobrien  } /* end of "while wait..." loop */
29338494Sobrien
29438494Sobrien#ifdef REINSTALL_SIGNAL_HANDLER
29538494Sobrien  signal(sig, sigchld);
29638494Sobrien#endif /* REINSTALL_SIGNAL_HANDLER */
29738494Sobrien
29838494Sobrien  if (select_intr_valid)
29938494Sobrien    longjmp(select_intr, sig);
30038494Sobrien}
301