/*===========================================================================* * do_reboot * *===========================================================================*/ int do_reboot() { message m; /* Check permission to abort the system. */ if (mp->mp_effuid != SUPER_USER) return(EPERM); /* See how the system should be aborted. */ abort_flag = m_in.m_lc_pm_reboot.how; /* notify readclock (some arm systems power off via RTC alarms) */ if (abort_flag & RB_POWERDOWN) { endpoint_t readclock_ep; if (ds_retrieve_label_endpt("readclock.drv", &readclock_ep) == OK) { message m; /* no params to set, nothing we can do if it fails */ _taskcall(readclock_ep, RTCDEV_PWR_OFF, &m); } } /* Order matters here. When VFS is told to reboot, it exits all its * processes, and then would be confused if they're exited again by * SIGKILL. So first kill, then reboot. */ check_sig(-1, SIGKILL, FALSE /* ksig*/); /* kill all users except init */ sys_stop(INIT_PROC_NR); /* stop init, but keep it around */ /* Tell VFS to reboot */ memset(&m, 0, sizeof(m)); m.m_type = VFS_PM_REBOOT; tell_vfs(&mproc[VFS_PROC_NR], &m); return(SUSPEND); /* don't reply to caller */ }
/*===========================================================================* * do_exec * *===========================================================================*/ PUBLIC int do_exec() { message m; /* Forward call to VFS */ m.m_type = PM_EXEC; m.PM_PROC = mp->mp_endpoint; m.PM_PATH = m_in.exec_name; m.PM_PATH_LEN = m_in.exec_len; m.PM_FRAME = m_in.frame_ptr; m.PM_FRAME_LEN = m_in.frame_len; tell_vfs(mp, &m); /* Do not reply */ return SUSPEND; }
/*===========================================================================* * do_exec * *===========================================================================*/ int do_exec() { message m; /* Forward call to VFS */ memset(&m, 0, sizeof(m)); m.m_type = VFS_PM_EXEC; m.VFS_PM_ENDPT = mp->mp_endpoint; m.VFS_PM_PATH = (void *)m_in.m_lc_pm_exec.name; m.VFS_PM_PATH_LEN = m_in.m_lc_pm_exec.namelen; m.VFS_PM_FRAME = (void *)m_in.m_lc_pm_exec.frame; m.VFS_PM_FRAME_LEN = m_in.m_lc_pm_exec.framelen; m.VFS_PM_PS_STR = m_in.m_lc_pm_exec.ps_str; tell_vfs(mp, &m); /* Do not reply */ return SUSPEND; }
/*===========================================================================* * do_set * *===========================================================================*/ PUBLIC int do_set() { /* Handle SETUID, SETEUID, SETGID, SETEGID, SETSID. These calls have in common * that, if successful, they will be forwarded to VFS as well. */ register struct mproc *rmp = mp; message m; int r, i; int ngroups; char sgroups[NGROUPS_MAX]; /* XXX: Temp storage for SETGROUPS_O */ switch(call_nr) { case SETUID: case SETEUID: if (rmp->mp_realuid != (uid_t) m_in.usr_id && rmp->mp_effuid != SUPER_USER) return(EPERM); if(call_nr == SETUID) rmp->mp_realuid = (uid_t) m_in.usr_id; rmp->mp_effuid = (uid_t) m_in.usr_id; m.m_type = PM_SETUID; m.PM_PROC = rmp->mp_endpoint; m.PM_EID = rmp->mp_effuid; m.PM_RID = rmp->mp_realuid; break; case SETGID: case SETEGID: if (rmp->mp_realgid != (gid_t) m_in.grp_id && rmp->mp_effuid != SUPER_USER) return(EPERM); if(call_nr == SETGID) rmp->mp_realgid = (gid_t) m_in.grp_id; rmp->mp_effgid = (gid_t) m_in.grp_id; m.m_type = PM_SETGID; m.PM_PROC = rmp->mp_endpoint; m.PM_EID = rmp->mp_effgid; m.PM_RID = rmp->mp_realgid; break; case SETGROUPS: if (rmp->mp_effuid != SUPER_USER) return(EPERM); ngroups = m_in.grp_no; if (ngroups > NGROUPS_MAX || ngroups < 0) return(EINVAL); if (m_in.groupsp == NULL) return(EFAULT); r = sys_datacopy(who_e, (vir_bytes) m_in.groupsp, SELF, (vir_bytes) rmp->mp_sgroups, ngroups * sizeof(gid_t)); if (r != OK) return(r); for (i = 0; i < ngroups; i++) { if (rmp->mp_sgroups[i] > GID_MAX) return(EINVAL); } for (i = ngroups; i < NGROUPS_MAX; i++) { rmp->mp_sgroups[i] = 0; } rmp->mp_ngroups = ngroups; m.m_type = PM_SETGROUPS; m.PM_PROC = rmp->mp_endpoint; m.PM_GROUP_NO = rmp->mp_ngroups; m.PM_GROUP_ADDR = (char *) rmp->mp_sgroups; break; case SETGROUPS_O: if (rmp->mp_effuid != SUPER_USER) return(EPERM); ngroups = m_in.grp_no; if (ngroups > NGROUPS_MAX || ngroups < 0) return(EINVAL); if (m_in.groupsp == NULL) return(EFAULT); r = sys_datacopy(who_e, (vir_bytes) m_in.groupsp, SELF, (vir_bytes) &sgroups, ngroups * sizeof(char)); if (r != OK) return(r); for (i = 0; i < ngroups; i++) rmp->mp_sgroups[i] = (gid_t) sgroups[i]; for (i = ngroups; i < NGROUPS_MAX; i++) rmp->mp_sgroups[i] = 0; rmp->mp_ngroups = ngroups; m.m_type = PM_SETGROUPS; m.PM_PROC = rmp->mp_endpoint; m.PM_GROUP_NO = rmp->mp_ngroups; m.PM_GROUP_ADDR = (char *) rmp->mp_sgroups; break; case SETSID: if (rmp->mp_procgrp == rmp->mp_pid) return(EPERM); rmp->mp_procgrp = rmp->mp_pid; m.m_type = PM_SETSID; m.PM_PROC = rmp->mp_endpoint; break; default: return(EINVAL); } /* Send the request to VFS */ tell_vfs(rmp, &m); /* Do not reply until VFS has processed the request */ return(SUSPEND); }
/*===========================================================================* * do_fork * *===========================================================================*/ PUBLIC int do_fork() { /* The process pointed to by 'mp' has forked. Create a child process. */ register struct mproc *rmp; /* pointer to parent */ register struct mproc *rmc; /* pointer to child */ pid_t new_pid; static int next_child; int i, n = 0, s; endpoint_t child_ep; message m; /* If tables might fill up during FORK, don't even start since recovery half * way through is such a nuisance. */ rmp = mp; if ((procs_in_use == NR_PROCS) || (procs_in_use >= NR_PROCS-LAST_FEW && rmp->mp_effuid != 0)) { printf("PM: warning, process table is full!\n"); return(EAGAIN); } /* Find a slot in 'mproc' for the child process. A slot must exist. */ do { next_child = (next_child+1) % NR_PROCS; n++; } while((mproc[next_child].mp_flags & IN_USE) && n <= NR_PROCS); if(n > NR_PROCS) panic("do_fork can't find child slot"); if(next_child < 0 || next_child >= NR_PROCS || (mproc[next_child].mp_flags & IN_USE)) panic("do_fork finds wrong child slot: %d", next_child); /* Memory part of the forking. */ if((s=vm_fork(rmp->mp_endpoint, next_child, &child_ep)) != OK) { printf("PM: vm_fork failed: %d\n", s); return s; } /* PM may not fail fork after call to vm_fork(), as VM calls sys_fork(). */ rmc = &mproc[next_child]; /* Set up the child and its memory map; copy its 'mproc' slot from parent. */ procs_in_use++; *rmc = *rmp; /* copy parent's process slot to child's */ rmc->mp_parent = who_p; /* record child's parent */ if (!(rmc->mp_trace_flags & TO_TRACEFORK)) { rmc->mp_tracer = NO_TRACER; /* no tracer attached */ rmc->mp_trace_flags = 0; (void) sigemptyset(&rmc->mp_sigtrace); } /* Some system servers like to call regular fork, such as RS spawning * recovery scripts; in this case PM will take care of their scheduling * because RS cannot do so for non-system processes */ if (rmc->mp_flags & PRIV_PROC) { assert(rmc->mp_scheduler == NONE); rmc->mp_scheduler = SCHED_PROC_NR; } /* Inherit only these flags. In normal fork(), PRIV_PROC is not inherited. */ rmc->mp_flags &= (IN_USE|DELAY_CALL); rmc->mp_child_utime = 0; /* reset administration */ rmc->mp_child_stime = 0; /* reset administration */ rmc->mp_exitstatus = 0; rmc->mp_sigstatus = 0; rmc->mp_endpoint = child_ep; /* passed back by VM */ for (i = 0; i < NR_ITIMERS; i++) rmc->mp_interval[i] = 0; /* reset timer intervals */ /* Find a free pid for the child and put it in the table. */ new_pid = get_free_pid(); rmc->mp_pid = new_pid; /* assign pid to child */ m.m_type = PM_FORK; m.PM_PROC = rmc->mp_endpoint; m.PM_PPROC = rmp->mp_endpoint; m.PM_CPID = rmc->mp_pid; tell_vfs(rmc, &m); /* Tell the tracer, if any, about the new child */ if (rmc->mp_tracer != NO_TRACER) sig_proc(rmc, SIGSTOP, TRUE /*trace*/, FALSE /* ksig */); /* Do not reply until VFS is ready to process the fork * request */ return SUSPEND; }
/*===========================================================================* * do_srv_fork * *===========================================================================*/ PUBLIC int do_srv_fork() { /* The process pointed to by 'mp' has forked. Create a child process. */ register struct mproc *rmp; /* pointer to parent */ register struct mproc *rmc; /* pointer to child */ int s; pid_t new_pid; static int next_child; int i, n = 0; endpoint_t child_ep; message m; /* Only RS is allowed to use srv_fork. */ if (mp->mp_endpoint != RS_PROC_NR) return EPERM; /* If tables might fill up during FORK, don't even start since recovery half * way through is such a nuisance. */ rmp = mp; if ((procs_in_use == NR_PROCS) || (procs_in_use >= NR_PROCS-LAST_FEW && rmp->mp_effuid != 0)) { printf("PM: warning, process table is full!\n"); return(EAGAIN); } /* Find a slot in 'mproc' for the child process. A slot must exist. */ do { next_child = (next_child+1) % NR_PROCS; n++; } while((mproc[next_child].mp_flags & IN_USE) && n <= NR_PROCS); if(n > NR_PROCS) panic("do_fork can't find child slot"); if(next_child < 0 || next_child >= NR_PROCS || (mproc[next_child].mp_flags & IN_USE)) panic("do_fork finds wrong child slot: %d", next_child); if((s=vm_fork(rmp->mp_endpoint, next_child, &child_ep)) != OK) { printf("PM: vm_fork failed: %d\n", s); return s; } rmc = &mproc[next_child]; /* Set up the child and its memory map; copy its 'mproc' slot from parent. */ procs_in_use++; *rmc = *rmp; /* copy parent's process slot to child's */ rmc->mp_parent = who_p; /* record child's parent */ if (!(rmc->mp_trace_flags & TO_TRACEFORK)) { rmc->mp_tracer = NO_TRACER; /* no tracer attached */ rmc->mp_trace_flags = 0; (void) sigemptyset(&rmc->mp_sigtrace); } /* inherit only these flags */ rmc->mp_flags &= (IN_USE|PRIV_PROC|DELAY_CALL); rmc->mp_child_utime = 0; /* reset administration */ rmc->mp_child_stime = 0; /* reset administration */ rmc->mp_exitstatus = 0; rmc->mp_sigstatus = 0; rmc->mp_endpoint = child_ep; /* passed back by VM */ for (i = 0; i < NR_ITIMERS; i++) rmc->mp_interval[i] = 0; /* reset timer intervals */ /* Find a free pid for the child and put it in the table. */ new_pid = get_free_pid(); rmc->mp_pid = new_pid; /* assign pid to child */ m.m_type = PM_SRV_FORK; m.PM_PROC = rmc->mp_endpoint; m.PM_PPROC = rmp->mp_endpoint; m.PM_CPID = rmc->mp_pid; tell_vfs(rmc, &m); /* Tell the tracer, if any, about the new child */ if (rmc->mp_tracer != NO_TRACER) sig_proc(rmc, SIGSTOP, TRUE /*trace*/, FALSE /* ksig */); /* Wakeup the newly created process */ setreply(rmc-mproc, OK); return rmc->mp_pid; }
/*===========================================================================* * do_trace * *===========================================================================*/ int do_trace() { register struct mproc *child; struct ptrace_range pr; int i, r, seg, req; message m; req = m_in.request; /* The T_OK call is made by the child fork of the debugger before it execs * the process to be traced. The T_ATTACH call is made by the debugger itself * to attach to an existing process. */ switch (req) { case T_OK: /* enable tracing by parent for this proc */ if (mp->mp_tracer != NO_TRACER) return(EBUSY); mp->mp_tracer = mp->mp_parent; mp->mp_reply.reply_trace = 0; return(OK); case T_ATTACH: /* attach to an existing process */ if ((child = find_proc(m_in.pid)) == NULL) return(ESRCH); if (child->mp_flags & EXITING) return(ESRCH); /* For non-root processes, user and group ID must match. */ if (mp->mp_effuid != SUPER_USER && (mp->mp_effuid != child->mp_effuid || mp->mp_effgid != child->mp_effgid || child->mp_effuid != child->mp_realuid || child->mp_effgid != child->mp_realgid)) return(EPERM); /* Only root may trace system servers. */ if (mp->mp_effuid != SUPER_USER && (child->mp_flags & PRIV_PROC)) return(EPERM); /* System servers may not trace anyone. They can use sys_trace(). */ if (mp->mp_flags & PRIV_PROC) return(EPERM); /* Can't trace self, PM or VM. */ if (child == mp || child->mp_endpoint == PM_PROC_NR || child->mp_endpoint == VM_PROC_NR) return(EPERM); /* Can't trace a process that is already being traced. */ if (child->mp_tracer != NO_TRACER) return(EBUSY); child->mp_tracer = who_p; child->mp_trace_flags = TO_NOEXEC; sig_proc(child, SIGSTOP, TRUE /*trace*/, FALSE /* ksig */); mp->mp_reply.reply_trace = 0; return(OK); case T_DUMPCORE: if ((child = find_proc(m_in.pid)) == NULL) return(ESRCH); /* Allow dumpcore only if traced! */ if (child->mp_tracer != who_p) return(EPERM); /* Tell VFS to dump the core. */ m.m_type = PM_DUMPCORE; m.PM_PROC = mp->mp_endpoint; m.PM_TRACED_PROC = child->mp_endpoint; /* Note that m.PM_PROC != m.PM_TRACED_PROC * (we use this to differentiate between a VFS core dump reply for a * an exiting process and the one for a traced process) */ m.PM_TERM_SIG = child->mp_sigstatus; m.PM_PATH = child->mp_name; tell_vfs(mp, &m); return(SUSPEND); /* Suspend the process until we receive reply from VFS */ case T_STOP: /* stop the process */ /* This call is not exposed to user programs, because its effect can be * achieved better by sending the traced process a signal with kill(2). */ return(EINVAL); case T_READB_INS: /* special hack for reading text segments */ if (mp->mp_effuid != SUPER_USER) return(EPERM); if ((child = find_proc(m_in.pid)) == NULL) return(ESRCH); if (child->mp_flags & EXITING) return(ESRCH); r = sys_trace(req, child->mp_endpoint, m_in.PMTRACE_ADDR, &m_in.data); if (r != OK) return(r); mp->mp_reply.reply_trace = m_in.data; return(OK); case T_WRITEB_INS: /* special hack for patching text segments */ if (mp->mp_effuid != SUPER_USER) return(EPERM); if ((child = find_proc(m_in.pid)) == NULL) return(ESRCH); if (child->mp_flags & EXITING) return(ESRCH); #if 0 /* Should check for shared text */ /* Make sure the text segment is not used as a source for shared * text. */ child->mp_ino = 0; child->mp_dev = 0; child->mp_ctime = 0; #endif r = sys_trace(req, child->mp_endpoint, m_in.PMTRACE_ADDR, &m_in.data); if (r != OK) return(r); mp->mp_reply.reply_trace = m_in.data; return(OK); } /* All the other calls are made by the tracing process to control execution * of the child. For all these calls, the child must be stopped. */ if ((child = find_proc(m_in.pid)) == NULL) return(ESRCH); if (child->mp_flags & EXITING) return(ESRCH); if (child->mp_tracer != who_p) return(ESRCH); if (!(child->mp_flags & STOPPED)) return(EBUSY); switch (req) { case T_EXIT: /* exit */ child->mp_flags |= TRACE_EXIT; /* Defer the exit if the traced process has an VFS call pending. */ if (child->mp_flags & VFS_CALL) child->mp_exitstatus = (int) m_in.data; /* save for later */ else exit_proc(child, (int) m_in.data, FALSE /*dump_core*/); /* Do not reply to the caller until VFS has processed the exit * request. */ return(SUSPEND); case T_SETOPT: /* set trace options */ child->mp_trace_flags = m_in.data; mp->mp_reply.reply_trace = 0; return(OK); case T_GETRANGE: case T_SETRANGE: /* get/set range of values */ r = sys_datacopy(who_e, (vir_bytes) m_in.PMTRACE_ADDR, SELF, (vir_bytes) &pr, (phys_bytes) sizeof(pr)); if (r != OK) return(r); if (pr.pr_space != TS_INS && pr.pr_space != TS_DATA) return(EINVAL); if (pr.pr_size == 0 || pr.pr_size > LONG_MAX) return(EINVAL); seg = (pr.pr_space == TS_INS) ? T : D; if (req == T_GETRANGE) r = sys_vircopy(child->mp_endpoint, seg, (vir_bytes) pr.pr_addr, who_e, D, (vir_bytes) pr.pr_ptr, (phys_bytes) pr.pr_size); else r = sys_vircopy(who_e, D, (vir_bytes) pr.pr_ptr, child->mp_endpoint, seg, (vir_bytes) pr.pr_addr, (phys_bytes) pr.pr_size); if (r != OK) return(r); mp->mp_reply.reply_trace = 0; return(OK); case T_DETACH: /* detach from traced process */ if (m_in.data < 0 || m_in.data >= _NSIG) return(EINVAL); child->mp_tracer = NO_TRACER; /* Let all tracer-pending signals through the filter. */ for (i = 1; i < _NSIG; i++) { if (sigismember(&child->mp_sigtrace, i)) { sigdelset(&child->mp_sigtrace, i); check_sig(child->mp_pid, i, FALSE /* ksig */); } } if (m_in.data > 0) { /* issue signal */ sig_proc(child, (int) m_in.data, TRUE /*trace*/, FALSE /* ksig */); } /* Resume the child as if nothing ever happened. */ child->mp_flags &= ~STOPPED; child->mp_trace_flags = 0; check_pending(child); break; case T_RESUME: case T_STEP: case T_SYSCALL: /* resume execution */ if (m_in.data < 0 || m_in.data >= _NSIG) return(EINVAL); if (m_in.data > 0) { /* issue signal */ sig_proc(child, (int) m_in.data, FALSE /*trace*/, FALSE /* ksig */); } /* If there are any other signals waiting to be delivered, * feign a successful resumption. */ for (i = 1; i < _NSIG; i++) { if (sigismember(&child->mp_sigtrace, i)) { mp->mp_reply.reply_trace = 0; return(OK); } } child->mp_flags &= ~STOPPED; check_pending(child); break; } r = sys_trace(req, child->mp_endpoint, m_in.PMTRACE_ADDR, &m_in.data); if (r != OK) return(r); mp->mp_reply.reply_trace = m_in.data; return(OK); }