diff options
author | Justus Winter <justus@gnupg.org> | 2017-03-09 23:55:12 +0100 |
---|---|---|
committer | Justus Winter <justus@gnupg.org> | 2017-03-11 18:07:23 +0100 |
commit | 34a94ce86b1bada9c0768f631540735d44f41100 (patch) | |
tree | c0e2871ecb2466ea001d9be505b05fdc10702087 | |
parent | baf7e5c8ce176aead15c2559952d8bdf0da41ffd (diff) | |
download | hurd-34a94ce86b1bada9c0768f631540735d44f41100.tar.gz hurd-34a94ce86b1bada9c0768f631540735d44f41100.tar.bz2 hurd-34a94ce86b1bada9c0768f631540735d44f41100.zip |
proc: Hierarchical proc servers.
Previously, a Subhurd's tasks were shown as weird processes in the
Motherhurd. This change connects the proc server in the Motherhurd
with the proc server in the Subhurd, embedding the Subhurd's process
hierarchy. Subhurd's processes can now be inspected and debugged like
any other process.
* NEWS: Update.
* boot/boot.c (mach_msg_forward): New function.
(boot_demuxer): Forward messages arriving on the new task notification
port from the proc server, and forward them to the proc server inside
the Subhurd via the notification port.
* proc/info.c (S_proc_task2proc): Relay request for processes in a task
namespace to the Subhurd's proc server.
(S_proc_pid2proc): Likewise.
(S_proc_getprocargs): Likewise.
(S_proc_getprocenv): Likewise.
(S_proc_getprocinfo): Likewise. Translate PIDs.
(S_proc_getloginid): Likewise.
(S_proc_getloginpids): Likewise.
* proc/mgt.c (namespace_is_subprocess): New function.
(namespace_translate_pids): Likewise.
* proc/msg.c (S_proc_getmsgport): Relay request for processes in a task
namespace to the Subhurd's proc server.
* proc/pgrp.c (S_proc_getsid): Likewise. Translate PIDs.
(S_proc_getsessionpids): Likewise.
(S_proc_getsessionpgids): Likewise.
(S_proc_getpgrppids): Likewise.
* proc/proc.h (namespace_is_subprocess): New prototype.
(namespace_translate_pids): Likewise.
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | boot/boot.c | 54 | ||||
-rw-r--r-- | proc/info.c | 216 | ||||
-rw-r--r-- | proc/mgt.c | 56 | ||||
-rw-r--r-- | proc/msg.c | 26 | ||||
-rw-r--r-- | proc/pgrp.c | 100 | ||||
-rw-r--r-- | proc/proc.h | 2 |
7 files changed, 458 insertions, 2 deletions
@@ -1,3 +1,9 @@ +Version X (2017-06-XX) + +Subhurd's processes are now properly embedded in the Motherhurds +process hierarchy. They can be inspected and debugged just like any +other process. + Version 0.9 (2016-12-18) The 'boot' program can now be run as unprivileged user, allowing any diff --git a/boot/boot.c b/boot/boot.c index 950aedb9..13a19d28 100644 --- a/boot/boot.c +++ b/boot/boot.c @@ -196,12 +196,66 @@ mig_reply_setup ( #undef OutP } +error_t +mach_msg_forward (mach_msg_header_t *inp, + mach_port_t destination, mach_msg_type_name_t destination_type) +{ + /* Put the reply port back at the correct position, insert new + destination. */ + inp->msgh_local_port = inp->msgh_remote_port; + inp->msgh_remote_port = destination; + inp->msgh_bits = + MACH_MSGH_BITS (destination_type, MACH_MSGH_BITS_REMOTE (inp->msgh_bits)) + | MACH_MSGH_BITS_OTHER (inp->msgh_bits); + + /* A word about resources carried in complex messages. + + "In a received message, msgt_deallocate is TRUE in type + descriptors for out-of-line memory". Therefore, "[the + out-of-line memory] is implicitly deallocated from the sender + [when we resend the message], as if by vm_deallocate". + + Similarly, rights in messages will be either + MACH_MSG_TYPE_PORT_SEND, MACH_MSG_TYPE_PORT_SEND_ONCE, or + MACH_MSG_TYPE_PORT_RECEIVE. These types are aliases for, + respectively, MACH_MSG_TYPE_MOVE_SEND, + MACH_MSG_TYPE_MOVE_SEND_ONCE, and MACH_MSG_TYPE_MOVE_RECEIVE. + Therefore, the rights are moved when we resend the message. */ + + return mach_msg (inp, MACH_SEND_MSG, inp->msgh_size, + 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); +} + int boot_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp) { + error_t err; mig_routine_t routine; mig_reply_setup (inp, outp); + + if (inp->msgh_local_port == task_notification_port + && MACH_PORT_VALID (new_task_notification) + && 24000 <= inp->msgh_id && inp->msgh_id < 24100) + { + /* This is a message of the Process subsystem. We relay this to + allow the "outer" proc servers to communicate with the "inner" + one. */ + mig_reply_header_t *reply = (mig_reply_header_t *) outp; + + if (MACH_PORT_VALID (new_task_notification)) + err = mach_msg_forward (inp, new_task_notification, MACH_MSG_TYPE_COPY_SEND); + else + err = EOPNOTSUPP; + + if (err) + reply->RetCode = err; + else + reply->RetCode = MIG_NO_REPLY; + + return TRUE; + } + if ((routine = io_server_routine (inp)) || (routine = device_server_routine (inp)) || (routine = notify_server_routine (inp)) || diff --git a/proc/info.c b/proc/info.c index 97321408..79a4c37f 100644 --- a/proc/info.c +++ b/proc/info.c @@ -109,6 +109,28 @@ S_proc_task2proc (struct proc *callerp, if (!p) return ESRCH; + if (namespace_is_subprocess (p)) + { + /* Relay it to the Subhurd's proc server (if any). */ + error_t err; + + /* Release global lock while talking to the other proc server. */ + pthread_mutex_unlock (&global_lock); + + err = proc_task2proc (p->p_task_namespace, t, outproc); + + pthread_mutex_lock (&global_lock); + + if (! err) + { + *outproc_type = MACH_MSG_TYPE_MOVE_SEND; + mach_port_deallocate (mach_task_self (), t); + return 0; + } + + /* Fallback. */ + } + *outproc = ports_get_right (p); *outproc_type = MACH_MSG_TYPE_MAKE_SEND; mach_port_deallocate (mach_task_self (), t); @@ -151,6 +173,27 @@ S_proc_pid2proc (struct proc *callerp, if (! check_owner (callerp, p)) return EPERM; + if (namespace_is_subprocess (p)) + { + /* Relay it to the Subhurd's proc server (if any). */ + error_t err; + + /* Release global lock while talking to the other proc server. */ + pthread_mutex_unlock (&global_lock); + + err = proc_task2proc (p->p_task_namespace, p->p_task, outproc); + + pthread_mutex_lock (&global_lock); + + if (! err) + { + *outproc_type = MACH_MSG_TYPE_MOVE_SEND; + return 0; + } + + /* Fallback. */ + } + *outproc = ports_get_right (p); *outproc_type = MACH_MSG_TYPE_MAKE_SEND; return 0; @@ -345,6 +388,27 @@ S_proc_getprocargs (struct proc *callerp, if (!p) return ESRCH; + if (namespace_is_subprocess (p)) + { + /* Relay it to the Subhurd's proc server (if any). */ + error_t err; + pid_t pid_sub; + + /* Release global lock while talking to the other proc server. */ + pthread_mutex_unlock (&global_lock); + + err = proc_task2pid (p->p_task_namespace, p->p_task, &pid_sub); + if (! err) + err = proc_getprocargs (p->p_task_namespace, pid_sub, buf, buflen); + + pthread_mutex_lock (&global_lock); + + if (! err) + return 0; + + /* Fallback. */ + } + return get_string_array (p->p_task, p->p_argv, (vm_address_t *) buf, buflen); } @@ -362,6 +426,27 @@ S_proc_getprocenv (struct proc *callerp, if (!p) return ESRCH; + if (namespace_is_subprocess (p)) + { + /* Relay it to the Subhurd's proc server (if any). */ + error_t err; + pid_t pid_sub; + + /* Release global lock while talking to the other proc server. */ + pthread_mutex_unlock (&global_lock); + + err = proc_task2pid (p->p_task_namespace, p->p_task, &pid_sub); + if (! err) + err = proc_getprocenv (p->p_task_namespace, pid_sub, buf, buflen); + + pthread_mutex_lock (&global_lock); + + if (! err) + return 0; + + /* Fallback. */ + } + return get_string_array (p->p_task, p->p_envp, (vm_address_t *)buf, buflen); } @@ -398,6 +483,82 @@ S_proc_getprocinfo (struct proc *callerp, if (!p) return ESRCH; + if (namespace_is_subprocess (p)) + { + /* Relay it to the Subhurd's proc server (if any). */ + error_t err; + pid_t pid_sub; + + /* Release global lock while talking to the other proc server. */ + pthread_mutex_unlock (&global_lock); + + err = proc_task2pid (p->p_task_namespace, p->p_task, &pid_sub); + if (! err) + err = proc_getprocinfo (p->p_task_namespace, pid_sub, flags, + piarray, piarraylen, waits, waits_len); + + if (! err && *piarray && *piarraylen * sizeof (int) >= sizeof *pi) + { + /* Fixup the PIDs to refer to this Hurd's processes. */ + task_t t_ppid = MACH_PORT_NULL; + task_t t_pgrp = MACH_PORT_NULL; + task_t t_session = MACH_PORT_NULL; + task_t t_logincollection = MACH_PORT_NULL; + + pi = (struct procinfo *) *piarray; + + /* We handle errors by checking each returned task. */ + if (pi->ppid != pid_sub) + proc_pid2task (p->p_task_namespace, pi->ppid, &t_ppid); + proc_pid2task (p->p_task_namespace, pi->pgrp, &t_pgrp); + proc_pid2task (p->p_task_namespace, pi->session, &t_session); + proc_pid2task (p->p_task_namespace, pi->logincollection, + &t_logincollection); + + /* Reacquire the global lock for the hash table lookups. */ + pthread_mutex_lock (&global_lock); + + if (MACH_PORT_VALID (t_ppid)) + { + struct proc *q = task_find (t_ppid); + pi->ppid = q ? q->p_pid : (pid_t) -1; + mach_port_deallocate (mach_task_self (), t_ppid); + } + else + { + /* Either the pid2task lookup failed, or this process is + a root of a process hierarchy in the Subhurd. Either + way, we attach it to the creator of the task + namespace. */ + pi->ppid = namespace_find_root (p)->p_pid; + } + if (MACH_PORT_VALID (t_pgrp)) + { + struct proc *q = task_find (t_pgrp); + pi->pgrp = q ? q->p_pid : (pid_t) -1; + mach_port_deallocate (mach_task_self (), t_pgrp); + } + if (MACH_PORT_VALID (t_session)) + { + struct proc *q = task_find (t_session); + pi->session = q ? q->p_pid : (pid_t) -1; + mach_port_deallocate (mach_task_self (), t_session); + } + if (MACH_PORT_VALID (t_logincollection)) + { + struct proc *q = task_find (t_logincollection); + pi->logincollection = q ? q->p_pid : (pid_t) -1; + mach_port_deallocate (mach_task_self (), t_logincollection); + } + + return 0; + } + + pthread_mutex_lock (&global_lock); + err = 0; + /* Fallback. */ + } + task = p->p_task; check_msgport_death (p); @@ -644,13 +805,37 @@ S_proc_getloginid (struct proc *callerp, pid_t *leader) { struct proc *proc = pid_find (pid); - struct proc *p; + struct proc *p = proc; /* No need to check CALLERP here; we don't use it. */ if (!proc) return ESRCH; + if (namespace_is_subprocess (p)) + { + /* Relay it to the Subhurd's proc server (if any). */ + error_t err; + pid_t pid_sub; + + /* Release global lock while talking to the other proc server. */ + pthread_mutex_unlock (&global_lock); + + err = proc_task2pid (p->p_task_namespace, p->p_task, &pid_sub); + if (! err) + err = proc_getloginid (p->p_task_namespace, pid_sub, leader); + if (! err) + /* Acquires global_lock. */ + err = namespace_translate_pids (p->p_task_namespace, leader, 1); + else + pthread_mutex_lock (&global_lock); + + if (! err) + return 0; + + /* Fallback. */ + } + for (p = proc; !p->p_loginleader; p = p->p_parent) assert (p); @@ -674,6 +859,35 @@ S_proc_getloginpids (struct proc *callerp, /* No need to check CALLERP here; we don't use it. */ + if (!l) + return ESRCH; + + if (namespace_is_subprocess (l)) + { + /* Relay it to the Subhurd's proc server (if any). */ + error_t err; + pid_t pid_sub; + pid_t leader_sub; + task_t leader_task; + + /* Release global lock while talking to the other proc server. */ + pthread_mutex_unlock (&global_lock); + + err = proc_task2pid (l->p_task_namespace, l->p_task, &pid_sub); + if (! err) + err = proc_getloginpids (l->p_task_namespace, pid_sub, pids, npids); + if (! err) + /* Acquires global_lock. */ + err = namespace_translate_pids (l->p_task_namespace, *pids, *npids); + else + pthread_mutex_lock (&global_lock); + + if (! err) + return 0; + + /* Fallback. */ + } + if (!l || !l->p_loginleader) return ESRCH; @@ -732,7 +732,61 @@ new_proc (task_t task) complete_proc (p, genpid ()); return p; } + + +/* Task namespace support. */ + +/* Check if a given process is part of a task namespace but is not the + root. The root is managed by us, while all other tasks are managed + by the root itself. */ +int +namespace_is_subprocess (struct proc *p) +{ + return (p + && MACH_PORT_VALID (p->p_task_namespace) + && p->p_parent + && MACH_PORT_VALID (p->p_parent->p_task_namespace)); +} + +/* Translate PIDs valid in NAMESPACE into PIDs valid in our own + process space. + + Conditions: global_lock is unlocked before calling, and is locked + afterwards. */ +error_t +namespace_translate_pids (mach_port_t namespace, pid_t *pids, size_t pids_len) +{ + size_t i; + task_t *tasks; + + tasks = calloc (pids_len, sizeof *tasks); + if (tasks == NULL) + { + pthread_mutex_lock (&global_lock); + return ENOMEM; + } + + for (i = 0; i < pids_len; i++) + /* We handle errors by checking each returned task. */ + proc_pid2task (namespace, pids[i], &tasks[i]); + + pthread_mutex_lock (&global_lock); + + for (i = 0; i < pids_len; i++) + if (MACH_PORT_VALID (tasks[i])) + { + struct proc *p = task_find_nocreate (tasks[i]); + mach_port_deallocate (mach_task_self (), tasks[i]); + pids[i] = p ? p->p_pid : (pid_t) -1; + } + else + pids[i] = (pid_t) -1; + + free (tasks); + return 0; +} + /* Find the creator of the task namespace that P is in. */ struct proc * namespace_find_root (struct proc *p) @@ -759,6 +813,8 @@ namespace_terminate (struct proc *p, void *cookie) task_terminate (p->p_task); } + + /* The task associated with process P has died. Drop most state, and then record us as dead. Our parent will eventually complete the deallocation. */ @@ -137,7 +137,31 @@ S_proc_getmsgport (struct proc *callerp, p = pid_find_allow_zombie (pid); -restart: + if (namespace_is_subprocess (p)) + { + /* Relay it to the Subhurd's proc server (if any). */ + error_t err; + pid_t pid_sub; + + /* Release global lock while talking to the other proc server. */ + pthread_mutex_unlock (&global_lock); + + err = proc_task2pid (p->p_task_namespace, p->p_task, &pid_sub); + if (! err) + err = proc_getmsgport (p->p_task_namespace, pid_sub, msgport); + + pthread_mutex_lock (&global_lock); + + if (! err) + { + *msgport_type = MACH_MSG_TYPE_MOVE_SEND; + return 0; + } + + /* Fallback. */ + } + + restart: while (p && p->p_deadmsg && !p->p_dead) { callerp->p_msgportwait = 1; diff --git a/proc/pgrp.c b/proc/pgrp.c index d7c562fa..9db1dba6 100644 --- a/proc/pgrp.c +++ b/proc/pgrp.c @@ -147,6 +147,30 @@ S_proc_getsid (struct proc *callerp, /* No need to check CALLERP; we don't use it. */ + if (namespace_is_subprocess (p)) + { + /* Relay it to the Subhurd's proc server (if any). */ + error_t err; + pid_t pid_sub; + + /* Release global lock while talking to the other proc server. */ + pthread_mutex_unlock (&global_lock); + + err = proc_task2pid (p->p_task_namespace, p->p_task, &pid_sub); + if (! err) + err = proc_getsid (p->p_task_namespace, pid_sub, sid); + if (! err) + /* Acquires global_lock. */ + err = namespace_translate_pids (p->p_task_namespace, sid, 1); + else + pthread_mutex_lock (&global_lock); + + if (! err) + return 0; + + /* Fallback. */ + } + *sid = p->p_pgrp->pg_session->s_sid; return 0; } @@ -167,6 +191,31 @@ S_proc_getsessionpids (struct proc *callerp, /* No need to check CALLERP; we don't use it. */ + p = pid_find (sid); + if (namespace_is_subprocess (p)) + { + /* Relay it to the Subhurd's proc server (if any). */ + error_t err; + pid_t pid_sub; + + /* Release global lock while talking to the other proc server. */ + pthread_mutex_unlock (&global_lock); + + err = proc_task2pid (p->p_task_namespace, p->p_task, &pid_sub); + if (! err) + err = proc_getsessionpids (p->p_task_namespace, pid_sub, pids, npidsp); + if (! err) + /* Acquires global_lock. */ + err = namespace_translate_pids (p->p_task_namespace, *pids, *npidsp); + else + pthread_mutex_lock (&global_lock); + + if (! err) + return 0; + + /* Fallback. */ + } + s = session_find (sid); if (!s) return ESRCH; @@ -206,6 +255,7 @@ S_proc_getsessionpgids (struct proc *callerp, size_t *npgidsp) { int count; + struct proc *p; struct pgrp *pg; struct session *s; pid_t *pp = *pgids; @@ -213,6 +263,31 @@ S_proc_getsessionpgids (struct proc *callerp, /* No need to check CALLERP; we don't use it. */ + p = pid_find (sid); + if (namespace_is_subprocess (p)) + { + /* Relay it to the Subhurd's proc server (if any). */ + error_t err; + pid_t pid_sub; + + /* Release global lock while talking to the other proc server. */ + pthread_mutex_unlock (&global_lock); + + err = proc_task2pid (p->p_task_namespace, p->p_task, &pid_sub); + if (! err) + err = proc_getsessionpgids (p->p_task_namespace, pid_sub, pgids, npgidsp); + if (! err) + /* Acquires global_lock. */ + err = namespace_translate_pids (p->p_task_namespace, *pgids, *npgidsp); + else + pthread_mutex_lock (&global_lock); + + if (! err) + return 0; + + /* Fallback. */ + } + s = session_find (sid); if (!s) return ESRCH; @@ -254,6 +329,31 @@ S_proc_getpgrppids (struct proc *callerp, /* No need to check CALLERP; we don't use it. */ + p = pid_find (pgid); + if (namespace_is_subprocess (p)) + { + /* Relay it to the Subhurd's proc server (if any). */ + error_t err; + pid_t pid_sub; + + /* Release global lock while talking to the other proc server. */ + pthread_mutex_unlock (&global_lock); + + err = proc_task2pid (p->p_task_namespace, p->p_task, &pid_sub); + if (! err) + err = proc_getpgrppids (p->p_task_namespace, pid_sub, pids, npidsp); + if (! err) + /* Acquires global_lock. */ + err = namespace_translate_pids (p->p_task_namespace, *pids, *npidsp); + else + pthread_mutex_lock (&global_lock); + + if (! err) + return 0; + + /* Fallback. */ + } + if (pgid == 0) pg = callerp->p_pgrp; else diff --git a/proc/proc.h b/proc/proc.h index c0696149..333e8840 100644 --- a/proc/proc.h +++ b/proc/proc.h @@ -201,6 +201,8 @@ void leave_pgrp (struct proc *); void join_pgrp (struct proc *); void boot_setsid (struct proc *); +int namespace_is_subprocess (struct proc *p); +error_t namespace_translate_pids (mach_port_t namespace, pid_t *pids, size_t pids_len); struct proc *namespace_find_root (struct proc *); void process_has_exited (struct proc *); void alert_parent (struct proc *); |