Remove building with NOCRYPTO option
[minix3.git] / minix / servers / pm / trace.c
blob7906b0018b37e198a8d03d9d0423b46df6109207
1 /* This file handles the process manager's part of debugging, using the
2 * ptrace system call. Most of the commands are passed on to the system
3 * task for completion.
5 * The debugging commands available are:
6 * T_STOP stop the process
7 * T_OK enable tracing by parent for this process
8 * T_GETINS return value from instruction space
9 * T_GETDATA return value from data space
10 * T_GETUSER return value from user process table
11 * T_SETINS set value in instruction space
12 * T_SETDATA set value in data space
13 * T_SETUSER set value in user process table
14 * T_RESUME resume execution
15 * T_EXIT exit
16 * T_STEP set trace bit
17 * T_SYSCALL trace system call
18 * T_ATTACH attach to an existing process
19 * T_DETACH detach from a traced process
20 * T_SETOPT set trace options
21 * T_GETRANGE get range of values
22 * T_SETRANGE set range of values
24 * The T_OK, T_ATTACH, T_EXIT, and T_SETOPT commands are handled here, and the
25 * T_RESUME, T_STEP, T_SYSCALL, and T_DETACH commands are partially handled
26 * here and completed by the system task. The rest are handled entirely by the
27 * system task.
30 #include "pm.h"
31 #include <minix/com.h>
32 #include <minix/callnr.h>
33 #include <sys/ptrace.h>
34 #include <sys/wait.h>
35 #include <signal.h>
36 #include "mproc.h"
38 /*===========================================================================*
39 * do_trace *
40 *===========================================================================*/
41 int
42 do_trace(void)
44 register struct mproc *child;
45 struct ptrace_range pr;
46 int i, r, req;
48 req = m_in.m_lc_pm_ptrace.req;
50 /* The T_OK call is made by the child fork of the debugger before it execs
51 * the process to be traced. The T_ATTACH call is made by the debugger itself
52 * to attach to an existing process.
54 switch (req) {
55 case T_OK: /* enable tracing by parent for this proc */
56 if (mp->mp_tracer != NO_TRACER) return(EBUSY);
58 mp->mp_tracer = mp->mp_parent;
59 mp->mp_reply.m_pm_lc_ptrace.data = 0;
60 return(OK);
62 case T_ATTACH: /* attach to an existing process */
63 if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH);
64 if (child->mp_flags & EXITING) return(ESRCH);
66 /* For non-root processes, user and group ID must match. */
67 if (mp->mp_effuid != SUPER_USER &&
68 (mp->mp_effuid != child->mp_effuid ||
69 mp->mp_effgid != child->mp_effgid ||
70 child->mp_effuid != child->mp_realuid ||
71 child->mp_effgid != child->mp_realgid)) return(EPERM);
73 /* Only root may trace system servers. */
74 if (mp->mp_effuid != SUPER_USER && (child->mp_flags & PRIV_PROC))
75 return(EPERM);
77 /* System servers may not trace anyone. They can use sys_trace(). */
78 if (mp->mp_flags & PRIV_PROC) return(EPERM);
80 /* Can't trace self, PM or VM. */
81 if (child == mp || child->mp_endpoint == PM_PROC_NR ||
82 child->mp_endpoint == VM_PROC_NR) return(EPERM);
84 /* Can't trace a process that is already being traced. */
85 if (child->mp_tracer != NO_TRACER) return(EBUSY);
87 child->mp_tracer = who_p;
88 child->mp_trace_flags = TO_NOEXEC;
90 sig_proc(child, SIGSTOP, TRUE /*trace*/, FALSE /* ksig */);
92 mp->mp_reply.m_pm_lc_ptrace.data = 0;
93 return(OK);
95 case T_STOP: /* stop the process */
96 /* This call is not exposed to user programs, because its effect can be
97 * achieved better by sending the traced process a signal with kill(2).
99 return(EINVAL);
101 case T_READB_INS: /* special hack for reading text segments */
102 if (mp->mp_effuid != SUPER_USER) return(EPERM);
103 if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH);
104 if (child->mp_flags & EXITING) return(ESRCH);
106 r = sys_trace(req, child->mp_endpoint, m_in.m_lc_pm_ptrace.addr,
107 &m_in.m_lc_pm_ptrace.data);
108 if (r != OK) return(r);
110 mp->mp_reply.m_pm_lc_ptrace.data = m_in.m_lc_pm_ptrace.data;
111 return(OK);
113 case T_WRITEB_INS: /* special hack for patching text segments */
114 if (mp->mp_effuid != SUPER_USER) return(EPERM);
115 if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH);
116 if (child->mp_flags & EXITING) return(ESRCH);
118 #if 0
119 /* Should check for shared text */
121 /* Make sure the text segment is not used as a source for shared
122 * text.
124 child->mp_ino = 0;
125 child->mp_dev = 0;
126 child->mp_ctime = 0;
127 #endif
129 r = sys_trace(req, child->mp_endpoint, m_in.m_lc_pm_ptrace.addr,
130 &m_in.m_lc_pm_ptrace.data);
131 if (r != OK) return(r);
133 mp->mp_reply.m_pm_lc_ptrace.data = m_in.m_lc_pm_ptrace.data;
134 return(OK);
137 /* All the other calls are made by the tracing process to control execution
138 * of the child. For all these calls, the child must be stopped.
140 if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH);
141 if (child->mp_flags & EXITING) return(ESRCH);
142 if (child->mp_tracer != who_p) return(ESRCH);
143 if (!(child->mp_flags & TRACE_STOPPED)) return(EBUSY);
145 switch (req) {
146 case T_EXIT: /* exit */
147 child->mp_flags |= TRACE_EXIT;
149 /* Defer the exit if the traced process has a call pending. */
150 if (child->mp_flags & (VFS_CALL | EVENT_CALL))
151 child->mp_exitstatus = m_in.m_lc_pm_ptrace.data; /* save it */
152 else
153 exit_proc(child, m_in.m_lc_pm_ptrace.data,
154 FALSE /*dump_core*/);
156 /* Do not reply to the caller until VFS has processed the exit
157 * request.
159 return(SUSPEND);
161 case T_SETOPT: /* set trace options */
162 child->mp_trace_flags = m_in.m_lc_pm_ptrace.data;
164 mp->mp_reply.m_pm_lc_ptrace.data = 0;
165 return(OK);
167 case T_GETRANGE:
168 case T_SETRANGE: /* get/set range of values */
169 r = sys_datacopy(who_e, m_in.m_lc_pm_ptrace.addr, SELF, (vir_bytes)&pr,
170 (phys_bytes)sizeof(pr));
171 if (r != OK) return(r);
173 if (pr.pr_space != TS_INS && pr.pr_space != TS_DATA) return(EINVAL);
174 if (pr.pr_size == 0 || pr.pr_size > LONG_MAX) return(EINVAL);
176 if (req == T_GETRANGE)
177 r = sys_vircopy(child->mp_endpoint, (vir_bytes) pr.pr_addr,
178 who_e, (vir_bytes) pr.pr_ptr,
179 (phys_bytes) pr.pr_size, 0);
180 else
181 r = sys_vircopy(who_e, (vir_bytes) pr.pr_ptr,
182 child->mp_endpoint, (vir_bytes) pr.pr_addr,
183 (phys_bytes) pr.pr_size, 0);
185 if (r != OK) return(r);
187 mp->mp_reply.m_pm_lc_ptrace.data = 0;
188 return(OK);
190 case T_DETACH: /* detach from traced process */
191 if (m_in.m_lc_pm_ptrace.data < 0 || m_in.m_lc_pm_ptrace.data >= _NSIG)
192 return(EINVAL);
194 child->mp_tracer = NO_TRACER;
196 /* Let all tracer-pending signals through the filter. */
197 for (i = 1; i < _NSIG; i++) {
198 if (sigismember(&child->mp_sigtrace, i)) {
199 sigdelset(&child->mp_sigtrace, i);
200 check_sig(child->mp_pid, i, FALSE /* ksig */);
204 if (m_in.m_lc_pm_ptrace.data > 0) { /* issue signal */
205 sig_proc(child, m_in.m_lc_pm_ptrace.data, TRUE /*trace*/,
206 FALSE /* ksig */);
209 /* Resume the child as if nothing ever happened. */
210 child->mp_flags &= ~TRACE_STOPPED;
211 child->mp_trace_flags = 0;
213 check_pending(child);
215 break;
217 case T_RESUME:
218 case T_STEP:
219 case T_SYSCALL: /* resume execution */
220 if (m_in.m_lc_pm_ptrace.data < 0 || m_in.m_lc_pm_ptrace.data >= _NSIG)
221 return(EINVAL);
223 if (m_in.m_lc_pm_ptrace.data > 0) { /* issue signal */
224 sig_proc(child, m_in.m_lc_pm_ptrace.data, FALSE /*trace*/,
225 FALSE /* ksig */);
228 /* If there are any other signals waiting to be delivered,
229 * feign a successful resumption.
231 for (i = 1; i < _NSIG; i++) {
232 if (sigismember(&child->mp_sigtrace, i)) {
233 mp->mp_reply.m_pm_lc_ptrace.data = 0;
234 return(OK);
238 child->mp_flags &= ~TRACE_STOPPED;
240 check_pending(child);
242 break;
244 r = sys_trace(req, child->mp_endpoint, m_in.m_lc_pm_ptrace.addr,
245 &m_in.m_lc_pm_ptrace.data);
246 if (r != OK) return(r);
248 mp->mp_reply.m_pm_lc_ptrace.data = m_in.m_lc_pm_ptrace.data;
249 return(OK);
252 /*===========================================================================*
253 * trace_stop *
254 *===========================================================================*/
255 void
256 trace_stop(register struct mproc *rmp, int signo)
258 /* A traced process got a signal so stop it. */
260 register struct mproc *rpmp = mproc + rmp->mp_tracer;
261 int r;
263 r = sys_trace(T_STOP, rmp->mp_endpoint, 0L, (long *) 0);
264 if (r != OK) panic("sys_trace failed: %d", r);
266 rmp->mp_flags |= TRACE_STOPPED;
267 if (wait_test(rpmp, rmp)) {
268 /* TODO: rusage support */
270 sigdelset(&rmp->mp_sigtrace, signo);
272 rpmp->mp_flags &= ~WAITING; /* parent is no longer waiting */
273 rpmp->mp_reply.m_pm_lc_wait4.status = W_STOPCODE(signo);
274 reply(rmp->mp_tracer, rmp->mp_pid);