aboutsummaryrefslogtreecommitdiff
path: root/proc/info.c
diff options
context:
space:
mode:
Diffstat (limited to 'proc/info.c')
-rw-r--r--proc/info.c694
1 files changed, 694 insertions, 0 deletions
diff --git a/proc/info.c b/proc/info.c
new file mode 100644
index 00000000..2855adef
--- /dev/null
+++ b/proc/info.c
@@ -0,0 +1,694 @@
+/* Process information queries
+ Copyright (C) 1992,93,94,95,96,99,2000 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 <hurd/hurd_types.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <assert.h>
+#include <hurd/msg.h>
+
+#include "proc.h"
+#include "process_S.h"
+
+/* Implement S_proc_pid2task as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_pid2task (struct proc *callerp,
+ pid_t pid,
+ task_t *t)
+{
+ struct proc *p = pid_find_allow_zombie (pid);
+
+ if (!callerp)
+ return EOPNOTSUPP;
+
+ if (!p)
+ return ESRCH;
+
+ if (p->p_dead)
+ {
+ *t = MACH_PORT_NULL;
+ return 0;
+ }
+
+ if (! check_owner (callerp, p))
+ return EPERM;
+
+ assert (MACH_PORT_VALID (p->p_task));
+ *t = p->p_task;
+
+ return 0;
+}
+
+/* Implement proc_task2pid as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_task2pid (struct proc *callerp,
+ task_t t,
+ pid_t *pid)
+{
+ struct proc *p = task_find (t);
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!p)
+ return ESRCH;
+
+ *pid = p->p_pid;
+ mach_port_deallocate (mach_task_self (), t);
+ return 0;
+}
+
+/* Implement proc_task2proc as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_task2proc (struct proc *callerp,
+ task_t t,
+ mach_port_t *outproc)
+{
+ struct proc *p = task_find (t);
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!p)
+ return ESRCH;
+
+ *outproc = ports_get_right (p);
+ mach_port_deallocate (mach_task_self (), t);
+ return 0;
+}
+
+/* Implement proc_proc2task as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_proc2task (struct proc *p,
+ task_t *t)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ *t = p->p_task;
+ return 0;
+}
+
+/* Implement proc_pid2proc as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_pid2proc (struct proc *callerp,
+ pid_t pid,
+ mach_port_t *outproc)
+{
+ struct proc *p = pid_find_allow_zombie (pid);
+
+ if (!callerp)
+ return EOPNOTSUPP;
+
+ if (!p)
+ return ESRCH;
+
+ if (p->p_dead)
+ {
+ *outproc = MACH_PORT_NULL;
+ return 0;
+ }
+
+ if (! check_owner (callerp, p))
+ return EPERM;
+
+ *outproc = ports_get_right (p);
+ return 0;
+}
+
+
+/* Read a string starting at address ADDR in task T; set *STR to point at
+ newly malloced storage holding it, and *LEN to its length with null. */
+static error_t
+get_string (task_t t,
+ vm_address_t addr,
+ char **str, size_t *len)
+{
+ /* This version assumes that a string is never more than one
+ page in length. */
+
+ vm_address_t readaddr;
+ vm_address_t data;
+ u_int readlen;
+ error_t err;
+ char *c;
+
+ readaddr = trunc_page (addr);
+ err = vm_read (t, readaddr, vm_page_size * 2, &data, &readlen);
+ if (err == KERN_INVALID_ADDRESS)
+ err = vm_read (t, readaddr, vm_page_size, &data, &readlen);
+ if (err == MACH_SEND_INVALID_DEST)
+ err = ESRCH;
+ if (err)
+ return err;
+
+ /* Scan for a null. */
+ c = memchr ((char *) (data + (addr - readaddr)), '\0',
+ readlen - (addr - readaddr));
+ if (c == NULL)
+ err = KERN_INVALID_ADDRESS;
+ else
+ {
+ c++; /* Include the null. */
+ *len = c - (char *) (data + (addr - readaddr));
+ *str = malloc (*len);
+ if (*str == NULL)
+ err = ENOMEM;
+ else
+ memcpy (*str, (char *) data + (addr - readaddr), *len);
+ }
+
+ munmap ((caddr_t) data, readlen);
+ return err;
+}
+
+/* Read a vector of addresses (stored as are argv and envp) from task TASK
+ found at address ADDR. Set *VEC to point to newly malloced storage holding
+ the addresses. */
+static error_t
+get_vector (task_t task,
+ vm_address_t addr,
+ int **vec)
+{
+ vm_address_t readaddr;
+ vm_size_t readsize;
+ vm_address_t scanned;
+ error_t err;
+
+ *vec = NULL;
+ readaddr = trunc_page (addr);
+ readsize = 0;
+ scanned = addr;
+ do
+ {
+ vm_address_t data;
+ mach_msg_type_number_t readlen = 0;
+ vm_address_t *t;
+
+ readsize += vm_page_size;
+ err = vm_read (task, readaddr, readsize, &data, &readlen);
+ if (err == MACH_SEND_INVALID_DEST)
+ err = ESRCH;
+ if (err)
+ return err;
+
+ /* XXX fault bad here */
+
+ /* Scan for a null. */
+ for (t = (vm_address_t *) (data + (scanned - readaddr));
+ t < (vm_address_t *) (data + readlen);
+ ++t)
+ if (*t == 0)
+ {
+ ++t; /* Include the null. */
+ *vec = malloc ((char *)t - (char *)(data + (addr - readaddr)));
+ if (*vec == NULL)
+ err = ENOMEM;
+ else
+ bcopy ((char *)(data + (addr - readaddr)), *vec,
+ (char *)t - (char *)(data + (addr - readaddr)));
+ break;
+ }
+
+ /* If we didn't find the null terminator, then we will loop
+ to read an additional page. */
+ scanned = data + readlen;
+ munmap ((caddr_t) data, readlen);
+ } while (!err && *vec == NULL);
+
+ return err;
+}
+
+/* Fetch an array of strings at address LOC in task T into
+ BUF of size BUFLEN. */
+static error_t
+get_string_array (task_t t,
+ vm_address_t loc,
+ vm_address_t *buf,
+ u_int *buflen)
+{
+ char *bp;
+ int *vector, *vp;
+ error_t err;
+ vm_address_t origbuf = *buf;
+
+ err = get_vector (t, loc, &vector);
+ if (err)
+ return err;
+
+ bp = (char *) *buf;
+ for (vp = vector; *vp; ++vp)
+ {
+ char *string;
+ size_t len;
+
+ err = get_string (t, *vp, &string, &len);
+ if (err)
+ {
+ free (vector);
+ if (*buf != origbuf)
+ munmap ((caddr_t) *buf, *buflen);
+ return err;
+ }
+
+ if (len > (char *) *buf + *buflen - bp)
+ {
+ char *newbuf;
+ vm_size_t prev_len = bp - (char *) *buf;
+ vm_size_t newsize = *buflen * 2;
+
+ if (newsize < prev_len + len)
+ /* Since we will mmap whole pages anyway,
+ notice how much space we really have. */
+ newsize = round_page (prev_len + len);
+
+ newbuf = mmap (0, newsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (newbuf == MAP_FAILED)
+ {
+ err = errno;
+ free (string);
+ free (vector);
+ if (*buf != origbuf)
+ munmap ((caddr_t) *buf, *buflen);
+ return err;
+ }
+
+ memcpy (newbuf, (char *) *buf, prev_len);
+ bp = newbuf + prev_len;
+ if (*buf != origbuf)
+ munmap ((caddr_t) *buf, *buflen);
+
+ *buf = (vm_address_t) newbuf;
+ *buflen = newsize;
+ }
+
+ memcpy (bp, string, len);
+ bp += len;
+ free (string);
+ }
+
+ free (vector);
+ *buflen = bp - (char *) *buf;
+ return 0;
+}
+
+
+/* Implement proc_getprocargs as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_getprocargs (struct proc *callerp,
+ pid_t pid,
+ char **buf,
+ u_int *buflen)
+{
+ struct proc *p = pid_find (pid);
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!p)
+ return ESRCH;
+
+ return get_string_array (p->p_task, p->p_argv, (vm_address_t *) buf, buflen);
+}
+
+/* Implement proc_getprocenv as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_getprocenv (struct proc *callerp,
+ pid_t pid,
+ char **buf,
+ u_int *buflen)
+{
+ struct proc *p = pid_find (pid);
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!p)
+ return ESRCH;
+
+ return get_string_array (p->p_task, p->p_envp, (vm_address_t *)buf, buflen);
+}
+
+/* Handy abbreviation for all the various thread details. */
+#define PI_FETCH_THREAD_DETAILS \
+ (PI_FETCH_THREAD_SCHED | PI_FETCH_THREAD_BASIC | PI_FETCH_THREAD_WAITS)
+
+/* Implement proc_getprocinfo as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_getprocinfo (struct proc *callerp,
+ pid_t pid,
+ int *flags,
+ int **piarray,
+ u_int *piarraylen,
+ char **waits, mach_msg_type_number_t *waits_len)
+{
+ struct proc *p = pid_find (pid);
+ struct procinfo *pi;
+ int nthreads;
+ thread_t *thds;
+ error_t err = 0;
+ size_t structsize;
+ int i;
+ int pi_alloced = 0, waits_alloced = 0;
+ /* The amount of WAITS we've filled in so far. */
+ mach_msg_type_number_t waits_used = 0;
+ u_int tkcount, thcount;
+ struct proc *tp;
+ task_t task; /* P's task port. */
+ mach_port_t msgport; /* P's msgport, or MACH_PORT_NULL if none. */
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!p)
+ return ESRCH;
+
+ task = p->p_task;
+
+ check_msgport_death (p);
+ msgport = p->p_msgport;
+
+ if (*flags & PI_FETCH_THREAD_DETAILS)
+ *flags |= PI_FETCH_THREADS;
+
+ if (*flags & PI_FETCH_THREADS)
+ {
+ err = task_threads (p->p_task, &thds, &nthreads);
+ if (err == MACH_SEND_INVALID_DEST)
+ err = ESRCH;
+ if (err)
+ return err;
+ }
+ else
+ nthreads = 0;
+
+ structsize = sizeof (struct procinfo);
+ if (*flags & PI_FETCH_THREAD_DETAILS)
+ structsize += nthreads * sizeof (pi->threadinfos[0]);
+
+ if (structsize / sizeof (int) > *piarraylen)
+ {
+ *piarray = mmap (0, structsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ pi_alloced = 1;
+ }
+ *piarraylen = structsize / sizeof (int);
+ pi = (struct procinfo *) *piarray;
+
+ pi->state =
+ ((p->p_stopped ? PI_STOPPED : 0)
+ | (p->p_exec ? PI_EXECED : 0)
+ | (p->p_waiting ? PI_WAITING : 0)
+ | (!p->p_pgrp->pg_orphcnt ? PI_ORPHAN : 0)
+ | (p->p_msgport == MACH_PORT_NULL ? PI_NOMSG : 0)
+ | (p->p_pgrp->pg_session->s_sid == p->p_pid ? PI_SESSLD : 0)
+ | (p->p_noowner ? PI_NOTOWNED : 0)
+ | (!p->p_parentset ? PI_NOPARENT : 0)
+ | (p->p_traced ? PI_TRACED : 0)
+ | (p->p_msgportwait ? PI_GETMSG : 0)
+ | (p->p_loginleader ? PI_LOGINLD : 0));
+ pi->owner = p->p_owner;
+ pi->ppid = p->p_parent->p_pid;
+ pi->pgrp = p->p_pgrp->pg_pgid;
+ pi->session = p->p_pgrp->pg_session->s_sid;
+ for (tp = p; !tp->p_loginleader; tp = tp->p_parent)
+ assert (tp);
+ pi->logincollection = tp->p_pid;
+ if (p->p_dead || p->p_stopped)
+ {
+ pi->exitstatus = p->p_status;
+ pi->sigcode = p->p_sigcode;
+ }
+ else
+ pi->exitstatus = pi->sigcode = 0;
+
+ pi->nthreads = nthreads;
+
+ /* Release GLOBAL_LOCK around time consuming bits, and more importatantly,
+ potential calls to P's msgport, which can block. */
+ mutex_unlock (&global_lock);
+
+ if (*flags & PI_FETCH_TASKINFO)
+ {
+ tkcount = TASK_BASIC_INFO_COUNT;
+ err = task_info (task, TASK_BASIC_INFO, (int *)&pi->taskinfo, &tkcount);
+ if (err == MACH_SEND_INVALID_DEST)
+ err = ESRCH;
+ }
+
+ for (i = 0; i < nthreads; i++)
+ {
+ if (*flags & PI_FETCH_THREAD_DETAILS)
+ pi->threadinfos[i].died = 0;
+ if (*flags & PI_FETCH_THREAD_BASIC)
+ {
+ thcount = THREAD_BASIC_INFO_COUNT;
+ err = thread_info (thds[i], THREAD_BASIC_INFO,
+ (int *)&pi->threadinfos[i].pis_bi,
+ &thcount);
+ if (err == MACH_SEND_INVALID_DEST)
+ {
+ pi->threadinfos[i].died = 1;
+ err = 0;
+ continue;
+ }
+ else if (err)
+ /* Something screwy, give up on this bit of info. */
+ {
+ *flags &= ~PI_FETCH_THREAD_BASIC;
+ err = 0;
+ }
+ }
+
+ if (*flags & PI_FETCH_THREAD_SCHED)
+ {
+ thcount = THREAD_SCHED_INFO_COUNT;
+ err = thread_info (thds[i], THREAD_SCHED_INFO,
+ (int *)&pi->threadinfos[i].pis_si,
+ &thcount);
+ if (err == MACH_SEND_INVALID_DEST)
+ {
+ pi->threadinfos[i].died = 1;
+ err = 0;
+ continue;
+ }
+ if (err)
+ /* Something screwy, give up o nthis bit of info. */
+ {
+ *flags &= ~PI_FETCH_THREAD_SCHED;
+ err = 0;
+ }
+ }
+
+ /* Note that there are thread wait entries only for threads not marked
+ dead. */
+
+ if (*flags & PI_FETCH_THREAD_WAITS)
+ {
+ /* See what thread I is waiting on. */
+ if (msgport == MACH_PORT_NULL)
+ *flags &= ~PI_FETCH_THREAD_WAITS; /* Can't return much... */
+ else
+ {
+ string_t desc;
+ size_t desc_len;
+
+ if (msg_report_wait (msgport, thds[i],
+ desc, &pi->threadinfos[i].rpc_block))
+ desc[0] = '\0'; /* Don't know. */
+
+ /* See how long DESC is, being sure not to barf if it's
+ unterminated (string_t's are fixed length). */
+ desc_len = strnlen (desc, sizeof desc);
+
+ if (waits_used + desc_len + 1 > *waits_len)
+ /* Not enough room in WAITS, we must allocate more. */
+ {
+ char *new_waits = 0;
+ mach_msg_type_number_t new_len =
+ round_page (waits_used + desc_len + 1);
+
+ new_waits = mmap (0, new_len, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ err = (new_waits == (char *) -1) ? errno : 0;
+ if (err)
+ /* Just don't return any more waits information. */
+ *flags &= ~PI_FETCH_THREAD_WAITS;
+ else
+ {
+ if (waits_used > 0)
+ bcopy (*waits, new_waits, waits_used);
+ if (*waits_len > 0 && waits_alloced)
+ munmap (*waits, *waits_len);
+ *waits = new_waits;
+ *waits_len = new_len;
+ waits_alloced = 1;
+ }
+ }
+
+ if (waits_used + desc_len + 1 <= *waits_len)
+ /* Append DESC to WAITS. */
+ {
+ bcopy (desc, *waits + waits_used, desc_len);
+ waits_used += desc_len;
+ (*waits)[waits_used++] = '\0';
+ }
+ }
+ }
+
+ mach_port_deallocate (mach_task_self (), thds[i]);
+ }
+
+ if (*flags & PI_FETCH_THREADS)
+ munmap (thds, nthreads * sizeof (thread_t));
+ if (err && pi_alloced)
+ munmap (*piarray, structsize);
+ if (err && waits_alloced)
+ munmap (*waits, *waits_len);
+ else
+ *waits_len = waits_used;
+
+ /* Reacquire GLOBAL_LOCK to make the central locking code happy. */
+ mutex_lock (&global_lock);
+
+ return err;
+}
+
+/* Implement proc_make_login_coll as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_make_login_coll (struct proc *p)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ p->p_loginleader = 1;
+ return 0;
+}
+
+/* Implement proc_getloginid as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getloginid (struct proc *callerp,
+ pid_t pid,
+ pid_t *leader)
+{
+ struct proc *proc = pid_find (pid);
+ struct proc *p;
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!proc)
+ return ESRCH;
+
+ for (p = proc; !p->p_loginleader; p = p->p_parent)
+ assert (p);
+
+ *leader = p->p_pid;
+ return 0;
+}
+
+/* Implement proc_getloginpids as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getloginpids (struct proc *callerp,
+ pid_t id,
+ pid_t **pids,
+ u_int *npids)
+{
+ struct proc *l = pid_find (id);
+ struct proc *p;
+ struct proc **tail, **new, **parray;
+ int parraysize;
+ int i;
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!l || !l->p_loginleader)
+ return ESRCH;
+
+ /* Simple breadth first search of the children of L. */
+ parraysize = 50;
+ parray = malloc (sizeof (struct proc *) * parraysize);
+ parray[0] = l;
+ for (tail = parray, new = &parray[1]; tail != new; tail++)
+ {
+ for (p = (*tail)->p_ochild; p; p = p->p_sib)
+ if (!p->p_loginleader)
+ {
+ /* Add P to the list at NEW */
+ if (new - parray > parraysize)
+ {
+ struct proc **newparray;
+ newparray = realloc (parray, ((parraysize *= 2)
+ * sizeof (struct proc *)));
+ tail = newparray + (tail - parray);
+ new = newparray + (new - parray);
+ parray = newparray;
+ }
+ *new++ = p;
+ }
+ }
+
+ if (*npids < new - parray)
+ *pids = mmap (0, (new - parray) * sizeof (pid_t), PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ *npids = new - parray;
+ for (i = 0; i < *npids; i++)
+ (*pids)[i] = parray[i]->p_pid;
+ free (parray);
+ return 0;
+}
+
+/* Implement proc_setlogin as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_setlogin (struct proc *p,
+ char *login)
+{
+ struct login *l;
+
+ if (!p)
+ return EOPNOTSUPP;
+
+ if (!check_uid (p, 0))
+ return EPERM;
+
+ l = malloc (sizeof (struct login) + strlen (login) + 1);
+ l->l_refcnt = 1;
+ strcpy (l->l_name, login);
+ if (!--p->p_login->l_refcnt)
+ free (p->p_login);
+ p->p_login = l;
+ return 0;
+}
+
+/* Implement proc_getlogin as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_getlogin (struct proc *p,
+ char *login)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ strcpy (login, p->p_login->l_name);
+ return 0;
+}
+
+/* Implement proc_get_tty as described in <hurd/proc.defs>. */
+kern_return_t
+S_proc_get_tty (struct proc *p, pid_t pid,
+ mach_port_t *tty, mach_msg_type_name_t *tty_type)
+{
+ return EOPNOTSUPP; /* XXX */
+}