aboutsummaryrefslogtreecommitdiff
path: root/proc/mgt.c
diff options
context:
space:
mode:
Diffstat (limited to 'proc/mgt.c')
-rw-r--r--proc/mgt.c809
1 files changed, 809 insertions, 0 deletions
diff --git a/proc/mgt.c b/proc/mgt.c
new file mode 100644
index 00000000..551885de
--- /dev/null
+++ b/proc/mgt.c
@@ -0,0 +1,809 @@
+/* Process management
+ Copyright (C) 1992,93,94,95,96,99 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd 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 General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <mach.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <hurd/hurd_types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mach/notify.h>
+#include <sys/wait.h>
+#include <mach/mig_errors.h>
+#include <sys/resource.h>
+#include <hurd/auth.h>
+#include <assert.h>
+
+#include "proc.h"
+#include "process_S.h"
+#include "mutated_ourmsg_U.h"
+#include "proc_exc_S.h"
+#include "proc_exc_U.h"
+#include <hurd/signal.h>
+
+/* Create a new id structure with the given genuine uids and gids. */
+static inline struct ids *
+make_ids (const uid_t *uids, size_t nuids, const uid_t *gids, size_t ngids)
+{
+ struct ids *i;
+
+ i = malloc (sizeof (struct ids));
+ i->i_nuids = nuids;
+ i->i_ngids = ngids;
+ i->i_uids = malloc (sizeof (uid_t) * nuids);
+ i->i_gids = malloc (sizeof (uid_t) * ngids);
+ i->i_refcnt = 1;
+
+ memcpy (i->i_uids, uids, sizeof (uid_t) * nuids);
+ memcpy (i->i_gids, gids, sizeof (uid_t) * ngids);
+ return i;
+}
+
+/* Free an id structure. */
+static inline void
+free_ids (struct ids *i)
+{
+ free (i->i_uids);
+ free (i->i_gids);
+ free (i);
+}
+
+/* Tell if process P has uid UID, or has root. */
+int
+check_uid (struct proc *p, uid_t uid)
+{
+ int i;
+ for (i = 0; i < p->p_id->i_nuids; i++)
+ if (p->p_id->i_uids[i] == uid || p->p_id->i_uids[i] == 0)
+ return 1;
+ return 0;
+}
+
+
+/* Implement proc_reathenticate as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_reauthenticate (struct proc *p, mach_port_t rendport)
+{
+ error_t err;
+ uid_t gubuf[50], aubuf[50], ggbuf[50], agbuf[50];
+ uid_t *gen_uids, *aux_uids, *gen_gids, *aux_gids;
+ u_int ngen_uids, naux_uids, ngen_gids, naux_gids;
+
+ if (!p)
+ return EOPNOTSUPP;
+
+ gen_uids = gubuf;
+ aux_uids = aubuf;
+ gen_gids = ggbuf;
+ aux_gids = agbuf;
+
+ ngen_uids = naux_uids = 50;
+ ngen_gids = naux_gids = 50;
+
+
+ err = auth_server_authenticate (authserver,
+ rendport, MACH_MSG_TYPE_COPY_SEND,
+ MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND,
+ &gen_uids, &ngen_uids,
+ &aux_uids, &naux_uids,
+ &gen_gids, &ngen_gids,
+ &aux_gids, &naux_gids);
+ if (err)
+ return err;
+ mach_port_deallocate (mach_task_self (), rendport);
+
+ if (!--p->p_id->i_refcnt)
+ free_ids (p->p_id);
+ p->p_id = make_ids (gen_uids, ngen_uids, gen_gids, ngen_gids);
+
+ if (gen_uids != gubuf)
+ munmap (gen_uids, ngen_uids * sizeof (uid_t));
+ if (aux_uids != aubuf)
+ munmap (aux_uids, naux_uids * sizeof (uid_t));
+ if (gen_gids != ggbuf)
+ munmap (gen_gids, ngen_gids * sizeof (uid_t));
+ if (aux_gids != agbuf)
+ munmap (aux_gids, naux_gids * sizeof (uid_t));
+
+ return 0;
+}
+
+/* Implement proc_child as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_child (struct proc *parentp,
+ task_t childt)
+{
+ struct proc *childp = task_find (childt);
+
+ if (!parentp)
+ return EOPNOTSUPP;
+
+ if (!childp)
+ return ESRCH;
+
+ if (childp->p_parentset)
+ return EBUSY;
+
+ mach_port_deallocate (mach_task_self (), childt);
+
+ /* Process identification.
+ Leave p_task and p_pid alone; all the rest comes from the
+ new parent. */
+
+ if (!--childp->p_login->l_refcnt)
+ free (childp->p_login);
+ childp->p_login = parentp->p_login;
+ childp->p_login->l_refcnt++;
+
+ childp->p_owner = parentp->p_owner;
+ childp->p_noowner = parentp->p_noowner;
+
+ if (!--childp->p_id->i_refcnt)
+ free_ids (childp->p_id);
+ childp->p_id = parentp->p_id;
+ childp->p_id->i_refcnt++;
+
+ /* Process hierarchy. Remove from our current location
+ and place us under our new parent. Sanity check to make sure
+ parent is currently init. */
+ assert (childp->p_parent == startup_proc);
+ if (childp->p_sib)
+ childp->p_sib->p_prevsib = childp->p_prevsib;
+ *childp->p_prevsib = childp->p_sib;
+
+ childp->p_parent = parentp;
+ childp->p_sib = parentp->p_ochild;
+ childp->p_prevsib = &parentp->p_ochild;
+ if (parentp->p_ochild)
+ parentp->p_ochild->p_prevsib = &childp->p_sib;
+ parentp->p_ochild = childp;
+
+ /* Process group structure. */
+ if (childp->p_pgrp != parentp->p_pgrp)
+ {
+ leave_pgrp (childp);
+ childp->p_pgrp = parentp->p_pgrp;
+ join_pgrp (childp);
+ /* Not necessary to call newids ourself because join_pgrp does
+ it for us. */
+ }
+ else if (childp->p_msgport != MACH_PORT_NULL)
+ nowait_msg_proc_newids (childp->p_msgport, childp->p_task,
+ childp->p_parent->p_pid, childp->p_pgrp->pg_pgid,
+ !childp->p_pgrp->pg_orphcnt);
+ childp->p_parentset = 1;
+ return 0;
+}
+
+/* Implement proc_reassign as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_reassign (struct proc *p,
+ task_t newt)
+{
+ struct proc *stubp = task_find (newt);
+
+ if (!p)
+ return EOPNOTSUPP;
+
+ if (!stubp)
+ return ESRCH;
+
+ if (stubp == p)
+ return EINVAL;
+
+ mach_port_deallocate (mach_task_self (), newt);
+
+ remove_proc_from_hash (p);
+
+ task_terminate (p->p_task);
+ mach_port_destroy (mach_task_self (), p->p_task);
+ p->p_task = stubp->p_task;
+
+ /* For security, we need use the request port from STUBP */
+ ports_transfer_right (p, stubp);
+
+ /* Enqueued messages might refer to the old task port, so
+ destroy them. */
+ if (p->p_msgport != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), p->p_msgport);
+ p->p_msgport = MACH_PORT_NULL;
+ p->p_deadmsg = 1;
+ }
+
+ /* These two are image dependent. */
+ p->p_argv = stubp->p_argv;
+ p->p_envp = stubp->p_envp;
+
+ /* Destroy stubp */
+ stubp->p_task = MACH_PORT_NULL;/* block deallocation */
+ process_has_exited (stubp);
+ stubp->p_waited = 1; /* fake out complete_exit */
+ complete_exit (stubp);
+
+ add_proc_to_hash (p);
+
+ return 0;
+}
+
+/* Implement proc_setowner as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_setowner (struct proc *p,
+ uid_t owner,
+ int clear)
+{
+ if (!p)
+ return EOPNOTSUPP;
+
+ if (clear)
+ p->p_noowner = 1;
+ else
+ {
+ if (! check_uid (p, owner))
+ return EPERM;
+
+ p->p_owner = owner;
+ p->p_noowner = 0;
+ }
+
+ return 0;
+}
+
+/* Implement proc_getpids as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_getpids (struct proc *p,
+ pid_t *pid,
+ pid_t *ppid,
+ int *orphaned)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ *pid = p->p_pid;
+ *ppid = p->p_parent->p_pid;
+ *orphaned = !p->p_pgrp->pg_orphcnt;
+ return 0;
+}
+
+/* Implement proc_set_arg_locations as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_set_arg_locations (struct proc *p,
+ vm_address_t argv,
+ vm_address_t envp)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ p->p_argv = argv;
+ p->p_envp = envp;
+ return 0;
+}
+
+/* Implement proc_get_arg_locations as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_get_arg_locations (struct proc *p,
+ vm_address_t *argv,
+ vm_address_t *envp)
+{
+ *argv = p->p_argv;
+ *envp = p->p_envp;
+ return 0;
+}
+
+/* Implement proc_dostop as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_dostop (struct proc *p,
+ thread_t contthread)
+{
+ thread_t threadbuf[2], *threads = threadbuf;
+ unsigned int nthreads = 2, i;
+ error_t err;
+
+ if (!p)
+ return EOPNOTSUPP;
+
+ err = task_suspend (p->p_task);
+ if (err)
+ return err;
+ err = task_threads (p->p_task, &threads, &nthreads);
+ if (err)
+ return err;
+ for (i = 0; i < nthreads; i++)
+ {
+ if (threads[i] != contthread)
+ err = thread_suspend (threads[i]);
+ mach_port_deallocate (mach_task_self (), threads[i]);
+ }
+ if (threads != threadbuf)
+ munmap (threads, nthreads * sizeof (thread_t));
+ err = task_resume (p->p_task);
+ if (err)
+ return err;
+
+ mach_port_deallocate (mach_task_self (), contthread);
+ return 0;
+}
+
+/* Clean state of E before it is deallocated */
+void
+exc_clean (void *arg)
+{
+ struct exc *e = arg;
+ mach_port_deallocate (mach_task_self (), e->forwardport);
+}
+
+/* Implement proc_handle_exceptions as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_handle_exceptions (struct proc *p,
+ mach_port_t msgport,
+ mach_port_t forwardport,
+ int flavor,
+ thread_state_t new_state,
+ mach_msg_type_number_t statecnt)
+{
+ struct exc *e;
+ error_t err;
+
+ /* No need to check P here; we don't use it. */
+
+ err = ports_import_port (exc_class, proc_bucket, msgport,
+ (sizeof (struct exc)
+ + (statecnt * sizeof (natural_t))), &e);
+ if (err)
+ return err;
+
+ e->forwardport = forwardport;
+ e->flavor = flavor;
+ e->statecnt = statecnt;
+ bcopy (new_state, e->thread_state, statecnt * sizeof (natural_t));
+ ports_port_deref (e);
+ return 0;
+}
+
+/* Called on exception ports provided to proc_handle_exceptions. Do
+ the thread_set_state requested by proc_handle_exceptions and then
+ send an exception_raise message as requested. */
+kern_return_t
+S_proc_exception_raise (mach_port_t excport,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t thread,
+ mach_port_t task,
+ int exception,
+ int code,
+ int subcode)
+{
+ error_t err;
+ struct proc *p;
+ struct exc *e = ports_lookup_port (proc_bucket, excport, exc_class);
+ if (!e)
+ return EOPNOTSUPP;
+
+ p = task_find (task);
+ if (! p)
+ {
+ /* Bogus RPC. */
+ ports_port_deref (e);
+ return EINVAL;
+ }
+
+ /* Try to forward the message. */
+ err = proc_exception_raise (e->forwardport,
+ reply, reply_type, MACH_SEND_NOTIFY,
+ thread, task, exception, code, subcode);
+ mach_port_deallocate (mach_task_self (), thread);
+ mach_port_deallocate (mach_task_self (), task);
+
+ switch (err)
+ {
+ struct hurd_signal_detail hsd;
+ int signo;
+
+ case 0:
+ /* We have successfully forwarded the exception message. Now reset
+ the faulting thread's state to run its recovery code, which should
+ dequeue that message. */
+ err = thread_set_state (thread, e->flavor, e->thread_state, e->statecnt);
+ ports_port_deref (e);
+ return MIG_NO_REPLY;
+
+ default:
+ /* Some unexpected error in forwarding the message. */
+ /* FALLTHROUGH */
+
+ case MACH_SEND_INVALID_NOTIFY:
+ /* The port's queue is full, meaning the thread didn't receive
+ the exception message we forwarded last time it faulted.
+ Declare that signal thread hopeless and the task crashed. */
+
+ /* Translate the exception code into a signal number
+ and mark the process has dying that way. */
+ hsd.exc = exception;
+ hsd.exc_code = code;
+ hsd.exc_subcode = subcode;
+ _hurd_exception2signal (&hsd, &signo);
+ p->p_exiting = 1;
+ p->p_status = W_EXITCODE (0, signo);
+ p->p_sigcode = hsd.code;
+
+ /* Nuke the task; we will get a notification message and report it
+ died with SIGNO. */
+ task_terminate (task);
+ ports_port_deref (e);
+ return 0;
+ }
+
+}
+
+/* Implement proc_getallpids as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_getallpids (struct proc *p,
+ pid_t **pids,
+ u_int *pidslen)
+{
+ int nprocs;
+ pid_t *loc;
+
+ void count_up (struct proc *p, void *counter)
+ {
+ ++*(int *)counter;
+ }
+ void store_pid (struct proc *p, void *loc)
+ {
+ *(*(pid_t **)loc)++ = p->p_pid;
+ }
+
+ /* No need to check P here; we don't use it. */
+
+ add_tasks (0);
+
+ nprocs = 0;
+ prociterate (count_up, &nprocs);
+
+ if (nprocs > *pidslen)
+ *pids = mmap (0, nprocs * sizeof (pid_t), PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+
+ loc = *pids;
+ prociterate (store_pid, &loc);
+
+ *pidslen = nprocs;
+ return 0;
+}
+
+/* Create a process for TASK, which is not otherwise known to us.
+ The PID/parentage/job-control fields are not yet filled in,
+ and the proc is not entered into any hash table. */
+struct proc *
+allocate_proc (task_t task)
+{
+ struct proc *p;
+ mach_port_t foo;
+
+ /* Pid 0 is us; pid 1 is init. We handle those here specially;
+ all other processes inherit from init here (though proc_child
+ will move them to their actual parent usually). */
+
+ ports_create_port (proc_class, proc_bucket, sizeof (struct proc), &p);
+
+ p->p_task = task;
+
+ mach_port_request_notification (mach_task_self (), p->p_task,
+ MACH_NOTIFY_DEAD_NAME, 1, p->p_pi.port_right,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo);
+ if (foo != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), foo);
+
+ p->p_msgport = MACH_PORT_NULL;
+
+ condition_init (&p->p_wakeup);
+
+ p->p_argv = p->p_envp = p->p_status = 0;
+
+ p->p_owner = 0;
+ p->p_exec = 0;
+ p->p_stopped = 0;
+ p->p_waited = 0;
+ p->p_exiting = 0;
+ p->p_waiting = 0;
+ p->p_traced = 0;
+ p->p_nostopcld = 0;
+ p->p_deadmsg = 0;
+ p->p_checkmsghangs = 0;
+ p->p_msgportwait = 0;
+ p->p_dead = 0;
+
+ return p;
+}
+
+/* Allocate and initialize the proc structure for init (PID 1),
+ the original parent of all other procs. */
+struct proc *
+create_startup_proc (void)
+{
+ static const uid_t zero;
+ struct proc *p;
+
+ p = allocate_proc (MACH_PORT_NULL);
+
+ p->p_pid = 1;
+
+ p->p_parent = p;
+ p->p_sib = 0;
+ p->p_prevsib = &p->p_ochild;
+ p->p_ochild = p;
+ p->p_parentset = 1;
+
+ p->p_deadmsg = 1; /* Force initial "re-"fetch of msgport. */
+
+ p->p_noowner = 0;
+ p->p_id = make_ids (&zero, 1, &zero, 1);
+
+ p->p_loginleader = 1;
+ p->p_login = malloc (sizeof (struct login) + 5);
+ p->p_login->l_refcnt = 1;
+ strcpy (p->p_login->l_name, "root");
+
+ boot_setsid (p);
+
+ return p;
+}
+
+/* Complete a new process that has been allocated but not entirely initialized.
+ This gets called for every process except startup_proc (PID 1). */
+void
+complete_proc (struct proc *p, pid_t pid)
+{
+ /* Because these have a reference count of one before starting,
+ they can never be freed, so we're safe. */
+ static struct login *nulllogin;
+ static struct ids nullids = {0, 0, 0, 0, 1};
+
+ if (!nulllogin)
+ {
+ nulllogin = malloc (sizeof (struct login) + 7);
+ nulllogin->l_refcnt = 1;
+ strcpy (nulllogin->l_name, "<none>");
+ }
+
+ p->p_pid = pid;
+
+ p->p_id = &nullids;
+ p->p_id->i_refcnt++;
+
+ p->p_login = nulllogin;
+ p->p_login->l_refcnt++;
+
+ /* Our parent is init for now. */
+ p->p_parent = startup_proc;
+
+ p->p_sib = startup_proc->p_ochild;
+ p->p_prevsib = &startup_proc->p_ochild;
+ if (p->p_sib)
+ p->p_sib->p_prevsib = &p->p_sib;
+ startup_proc->p_ochild = p;
+ p->p_loginleader = 0;
+ p->p_ochild = 0;
+ p->p_parentset = 0;
+
+ p->p_noowner = 1;
+
+ p->p_pgrp = startup_proc->p_pgrp;
+
+ add_proc_to_hash (p);
+ join_pgrp (p);
+}
+
+
+/* Create a process for TASK, which is not otherwise known to us
+ and initialize it in the usual ways. */
+static struct proc *
+new_proc (task_t task)
+{
+ struct proc *p = allocate_proc (task);
+ complete_proc (p, genpid ());
+ return p;
+}
+
+/* The task associated with process P has died. Drop most state,
+ and then record us as dead. Our parent will eventually complete the
+ deallocation. */
+void
+process_has_exited (struct proc *p)
+{
+ /* We have already died; this can happen since both proc_reassign
+ and dead-name notifications could result in two calls to this
+ routine for the same process. */
+ if (p->p_dead)
+ return;
+
+ p->p_waited = 0;
+ if (p->p_task != MACH_PORT_NULL)
+ alert_parent (p);
+
+ if (p->p_msgport)
+ mach_port_deallocate (mach_task_self (), p->p_msgport);
+ p->p_msgport = MACH_PORT_NULL;
+
+ prociterate ((void (*) (struct proc *, void *))check_message_dying, p);
+
+ /* Nuke external send rights and the (possible) associated reference */
+ ports_destroy_right (p);
+
+ if (!--p->p_login->l_refcnt)
+ free (p->p_login);
+
+ if (!--p->p_id->i_refcnt)
+ free_ids (p->p_id);
+
+ /* Reparent our children to init by attaching the head and tail
+ of our list onto init's. */
+ if (p->p_ochild)
+ {
+ struct proc *tp; /* will point to the last one */
+ int isdead = 0;
+
+ /* first tell them their parent is changing */
+ for (tp = p->p_ochild; tp->p_sib; tp = tp->p_sib)
+ {
+ if (tp->p_msgport != MACH_PORT_NULL)
+ nowait_msg_proc_newids (tp->p_msgport, tp->p_task,
+ 1, tp->p_pgrp->pg_pgid,
+ !tp->p_pgrp->pg_orphcnt);
+ tp->p_parent = startup_proc;
+ if (tp->p_dead)
+ isdead = 1;
+ }
+ if (tp->p_msgport != MACH_PORT_NULL)
+ nowait_msg_proc_newids (tp->p_msgport, tp->p_task,
+ 1, tp->p_pgrp->pg_pgid,
+ !tp->p_pgrp->pg_orphcnt);
+ tp->p_parent = startup_proc;
+
+ /* And now nappend the lists. */
+ tp->p_sib = startup_proc->p_ochild;
+ if (tp->p_sib)
+ tp->p_sib->p_prevsib = &tp->p_sib;
+ startup_proc->p_ochild = p->p_ochild;
+ p->p_ochild->p_prevsib = &startup_proc->p_ochild;
+
+ if (isdead)
+ alert_parent (startup_proc);
+ }
+
+ /* If an operation is in progress for this process, cause it
+ to wakeup and return now. */
+ if (p->p_waiting || p->p_msgportwait)
+ condition_broadcast (&p->p_wakeup);
+
+ p->p_dead = 1;
+}
+
+void
+complete_exit (struct proc *p)
+{
+ assert (p->p_dead);
+ assert (p->p_waited);
+
+ remove_proc_from_hash (p);
+ if (p->p_task != MACH_PORT_NULL)
+ mach_port_destroy (mach_task_self (), p->p_task);
+
+ /* Remove us from our parent's list of children. */
+ if (p->p_sib)
+ p->p_sib->p_prevsib = p->p_prevsib;
+ *p->p_prevsib = p->p_sib;
+
+ leave_pgrp (p);
+
+ /* Drop the reference we created long ago in new_proc. The only
+ other references that ever show up are those for RPC args, which
+ will shortly vanish (because we are p_dead, those routines do
+ nothing). */
+ ports_port_deref (p);
+}
+
+/* Get the list of all tasks from the kernel and start adding them.
+ If we encounter TASK, then don't do any more and return its proc.
+ If TASK is null or we never find it, then return 0. */
+struct proc *
+add_tasks (task_t task)
+{
+ mach_port_t *psets;
+ u_int npsets;
+ int i;
+ struct proc *foundp = 0;
+
+ host_processor_sets (mach_host_self (), &psets, &npsets);
+ for (i = 0; i < npsets; i++)
+ {
+ mach_port_t psetpriv;
+ mach_port_t *tasks;
+ u_int ntasks;
+ int j;
+
+ if (!foundp)
+ {
+ host_processor_set_priv (master_host_port, psets[i], &psetpriv);
+ processor_set_tasks (psetpriv, &tasks, &ntasks);
+ for (j = 0; j < ntasks; j++)
+ {
+ int set = 0;
+
+ /* The kernel can deliver us an array with null slots in the
+ middle, e.g. if a task died during the call. */
+ if (! MACH_PORT_VALID (tasks[j]))
+ continue;
+
+ if (!foundp)
+ {
+ struct proc *p = task_find_nocreate (tasks[j]);
+ if (!p)
+ {
+ p = new_proc (tasks[j]);
+ set = 1;
+ }
+ if (!foundp && tasks[j] == task)
+ foundp = p;
+ }
+ if (!set)
+ mach_port_deallocate (mach_task_self (), tasks[j]);
+ }
+ munmap (tasks, ntasks * sizeof (task_t));
+ mach_port_deallocate (mach_task_self (), psetpriv);
+ }
+ mach_port_deallocate (mach_task_self (), psets[i]);
+ }
+ munmap (psets, npsets * sizeof (mach_port_t));
+ return foundp;
+}
+
+/* Allocate a new unused PID.
+ (Unused means it is neither the pid nor pgrp of any relevant data.) */
+int
+genpid ()
+{
+#define WRAP_AROUND 30000
+#define START_OVER 100
+ static int nextpid = 0;
+ static int wrap = WRAP_AROUND;
+
+ int wrapped = 0;
+
+ while (!pidfree (nextpid))
+ {
+ ++nextpid;
+ if (nextpid > wrap)
+ {
+ if (wrapped)
+ {
+ wrap *= 2;
+ wrapped = 0;
+ }
+ else
+ {
+ nextpid = START_OVER;
+ wrapped = 1;
+ }
+ }
+ }
+
+ return nextpid++;
+}