summaryrefslogtreecommitdiff
path: root/proc/mgt.c
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1994-03-22 18:36:52 +0000
committerMichael I. Bushnell <mib@gnu.org>1994-03-22 18:36:52 +0000
commita2e14dc11cdd72f366a0e0058375ab5a34bc15af (patch)
treea6e9e84032a8d25715c1981f5baa32b291939247 /proc/mgt.c
parente6516d3d37826282d434bfd455722c507e7cd417 (diff)
Initial revision
Diffstat (limited to 'proc/mgt.c')
-rw-r--r--proc/mgt.c573
1 files changed, 573 insertions, 0 deletions
diff --git a/proc/mgt.c b/proc/mgt.c
new file mode 100644
index 00000000..bbfba744
--- /dev/null
+++ b/proc/mgt.c
@@ -0,0 +1,573 @@
+/* Process management
+ Copyright (C) 1992, 1993, 1994 Free Software Foundation
+
+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 <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 "proc_S.h"
+#include "msg.h"
+
+/* Create a new id structure with the given genuine uids and gids. */
+static inline struct ids *
+make_ids (uid_t *uids, int nuids, uid_t *gids, int 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;
+
+ bcopy (i->i_uids, uids, sizeof (uid_t) * nuids);
+ bcopy (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. */
+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)
+ return 1;
+ return 0;
+}
+
+
+/* Implement proc_reathenticate as described in <hurd/proc.defs>. */
+error_t
+S_proc_reauthenticate (struct proc *p, int id)
+{
+ 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;
+
+ 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, p->p_reqport,
+ MACH_MSG_TYPE_COPY_SEND, id,
+ 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;
+
+ 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)
+ vm_deallocate (mach_task_self (), (u_int) gen_uids,
+ ngen_uids * sizeof (uid_t));
+ if (aux_uids != aubuf)
+ vm_deallocate (mach_task_self (), (u_int) aux_uids,
+ naux_uids * sizeof (uid_t));
+ if (gen_gids != ggbuf)
+ vm_deallocate (mach_task_self (), (u_int) gen_gids,
+ ngen_gids * sizeof (uid_t));
+ if (aux_gids != agbuf)
+ vm_deallocate (mach_task_self (), (u_int) aux_gids,
+ naux_gids * sizeof (uid_t));
+
+ return 0;
+}
+
+/* Implement proc_child as described in <hurd/proc.defs>. */
+error_t
+S_proc_child (struct proc *parentp,
+ task_t childt)
+{
+ struct proc *childp = task_find (childt);
+
+ if (!childp)
+ return ESRCH;
+
+ if (childp->p_parentset)
+ return EBUSY;
+
+ /* 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);
+ *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. */
+ leave_pgrp (childp);
+ childp->p_pgrp = parentp->p_pgrp;
+ join_pgrp (childp);
+
+ inherit_process_collections (childp);
+
+ childp->p_parentset = 1;
+ if (childp->p_msgport)
+ nowait_proc_newids (childp->p_msgport, childp->p_task,
+ childp->p_parent->p_pid, childp->p_pgrp->pg_pgid,
+ !childp->p_pgrp->pg_orphcnt);
+ return 0;
+}
+
+/* Implement proc_reassign as described in <hurd/proc.defs>. */
+error_t
+S_proc_reassign (struct proc *p,
+ task_t newt)
+{
+ struct proc *stubp = task_find (newt);
+ mach_port_t foo;
+
+ if (!stubp)
+ return ESRCH;
+
+ remove_proc_from_hash (p);
+
+ task_terminate (p->p_task);
+ mach_port_deallocate (mach_task_self (), p->p_task);
+ p->p_task = newt;
+ mach_port_request_notification (mach_task_self (), p->p_task,
+ MACH_NOTIFY_DEAD_NAME, 1, p->p_reqport,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo);
+ if (foo)
+ mach_port_deallocate (mach_task_self (), foo);
+
+ /* For security, we need to get a new request port */
+ mach_port_mod_refs (mach_task_self (), p->p_reqport,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &p->p_reqport);
+ mach_port_move_member (mach_task_self (), p->p_reqport,
+ request_portset);
+
+ /* 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 = 0; /* block deallocation */
+ process_has_exited (stubp);
+
+ add_proc_to_hash (p);
+
+ return 0;
+}
+
+/* Implement proc_setowner as described in <hurd/proc.defs>. */
+error_t
+S_proc_setowner (struct proc *p,
+ uid_t owner)
+{
+ 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>. */
+error_t
+S_proc_getpids (struct proc *p,
+ pid_t *pid,
+ pid_t *ppid,
+ int *orphaned)
+{
+ *pid = p->p_pid;
+ *ppid = p->p_parent->p_pid;
+ *orphaned = !p->p_pgrp->pg_orphcnt;
+ return 0;
+}
+
+/* Implement proc_setprocargs as described in <hurd/proc.defs>. */
+error_t
+S_proc_setprocargs (struct proc *p,
+ int argv,
+ int envp)
+{
+ p->p_argv = argv;
+ p->p_envp = envp;
+ return 0;
+}
+
+/* Implement proc_dostop as described in <hurd/proc.defs>. */
+error_t
+S_proc_dostop (struct proc *p,
+ thread_t contthread)
+{
+ thread_t *threads;
+ int i;
+ u_int nthreads;
+
+ task_suspend (p->p_task);
+ task_threads (p->p_task, &threads, &nthreads);
+ for (i = 0; i < nthreads; i++)
+ if (threads[i] != contthread)
+ {
+ thread_suspend (threads[i]);
+ mach_port_deallocate (mach_task_self (), threads[i]);
+ }
+ vm_deallocate (mach_task_self (), (u_int) threads,
+ nthreads * sizeof (thread_t));
+ task_resume (p->p_task);
+ return 0;
+}
+
+/* Implement proc_getallpids as described in <hurd/proc.defs>. */
+error_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;
+ }
+
+ add_tasks (0);
+
+ nprocs = 0;
+ prociterate (count_up, &nprocs);
+
+ if (nprocs > *pidslen)
+ vm_allocate (mach_task_self (), (vm_address_t *) *pids,
+ nprocs * sizeof (pid_t), 1);
+
+ loc = *pids;
+ prociterate (store_pid, &loc);
+
+ *pidslen = nprocs;
+ return 0;
+}
+
+/* Create a process for TASK, which is not otherwise known to us.
+ The task will be placed as a child of init and in init's pgrp. */
+struct proc *
+new_proc (task_t task)
+{
+ struct proc *p;
+ mach_port_t foo;
+
+ /* 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>");
+ }
+
+ /* 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). */
+
+ p = malloc (sizeof (struct proc));
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &p->p_reqport);
+ mach_port_move_member (mach_task_self (), p->p_reqport,
+ request_portset);
+
+ p->p_pid = genpid ();
+ p->p_task = task;
+ mach_port_request_notification (mach_task_self (), p->p_task,
+ MACH_NOTIFY_DEAD_NAME, 1, p->p_reqport,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo);
+ if (foo)
+ mach_port_deallocate (mach_task_self (), foo);
+
+
+ switch (p->p_pid)
+ {
+ case 0:
+ p->p_login = malloc (sizeof (struct login) + 5);
+ p->p_login->l_refcnt = 1;
+ strcpy (p->p_login->l_name, "root");
+ break;
+
+ case 1:
+ p->p_login = self_proc->p_login;
+ p->p_login->l_refcnt++;
+ break;
+
+ default:
+ p->p_login = nulllogin;
+ p->p_login->l_refcnt++;
+ }
+
+ p->p_owner = 0;
+
+ if (p->p_pid < 2)
+ {
+ uid_t foo = 0;
+ p->p_id = make_ids (&foo, 1, &foo, 1);
+ p->p_parent = 0;
+ p->p_sib = 0;
+ }
+
+ else
+ {
+ p->p_id = &nullids;
+ p->p_id->i_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_ochild = 0;
+
+ if (p->p_pid < 2)
+ boot_setsid (p);
+ else
+ p->p_pgrp = startup_proc->p_pgrp;
+
+ p->p_colls = 0; /* should add to allcolls XXX */
+
+ p->p_msgport = MACH_PORT_NULL;
+
+ p->p_argv = p->p_envp = p->p_status = 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_parentset = 0;
+ p->p_deadmsg = 0;
+ p->p_checkmsghangs = 0;
+ p->p_msgportwait = 0;
+ p->p_noowner = 1;
+
+ if (p->p_pid > 1)
+ {
+ add_proc_to_hash (p);
+ join_pgrp (p);
+ }
+
+ return p;
+}
+
+/* The task associated with process P has died. Free everything, and
+ record our presence in the zombie table, then return wait if necessary. */
+void
+process_has_exited (struct proc *p)
+{
+ alert_parent (p);
+
+ leave_all_process_collections (p);
+
+ if (p->p_checkmsghangs)
+ prociterate ((void (*) (struct proc *, void *))check_message_dying, p);
+
+ mach_port_mod_refs (mach_task_self (), p->p_reqport,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+
+ remove_proc_from_hash (p);
+
+ mach_port_deallocate (mach_task_self (), p->p_task);
+
+ 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 */
+
+ /* first tell them their parent is changing */
+ for (tp = p->p_ochild; tp->p_sib; tp = tp->p_sib)
+ nowait_proc_newids (tp->p_msgport, tp->p_task, 1, tp->p_pgrp->pg_pgid,
+ !tp->p_pgrp->pg_orphcnt);
+ nowait_proc_newids (tp->p_msgport, tp->p_task, 1, tp->p_pgrp->pg_pgid,
+ !tp->p_pgrp->pg_orphcnt);
+
+ /* And now nappend the list. */
+ 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;
+ }
+
+ reparent_zombies (p);
+
+ /* Remove us from our parent's list of children. */
+ *p->p_prevsib = p->p_sib;
+
+ leave_pgrp (p);
+
+ mach_port_deallocate (mach_task_self (), p->p_msgport);
+
+ if (p->p_waiting)
+ mach_port_deallocate (mach_task_self (),
+ p->p_continuation.wait_c.reply_port);
+ if (p->p_msgportwait)
+ mach_port_deallocate (mach_task_self (),
+ p->p_continuation.getmsgport_c.reply_port);
+
+ free (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++)
+ {
+ if (!foundp)
+ {
+ struct proc *p = task_find_nocreate (tasks[j]);
+ if (!p)
+ p = new_proc (tasks[j]);
+ if (!foundp && tasks[j] == task)
+ foundp = p;
+ }
+ mach_port_deallocate (mach_task_self (), tasks[j]);
+ }
+ vm_deallocate (mach_task_self (), (vm_address_t) tasks,
+ ntasks * sizeof (task_t));
+ mach_port_deallocate (mach_task_self (), psetpriv);
+ }
+ mach_port_deallocate (mach_task_self (), psets[i]);
+ }
+ vm_deallocate (mach_host_self (), (vm_address_t) psets,
+ npsets * sizeof (mach_port_t));
+ return foundp;
+}
+
+/* Allocate a new pid. The first two times this is called it must return
+ 0 and 1 in order; after that it must simply return an unused pid.
+ (Unused means it is neither the pid nor pgrp of any relevant data.) */
+int
+genpid ()
+{
+ static int nextpid = 0;
+
+#define WRAP_AROUND 30000
+#define START_OVER 100
+
+ while (!pidfree (nextpid)
+ && ((++nextpid > WRAP_AROUND) || (nextpid = START_OVER)))
+ ;
+ return nextpid++;
+}