ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / um / kernel / tt / ptproxy / proxy.c
1 /**********************************************************************
2 proxy.c
3
4 Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing
5 terms and conditions.
6
7 Jeff Dike (jdike@karaya.com) : Modified for integration into uml
8 **********************************************************************/
9
10 /* XXX This file shouldn't refer to CONFIG_* */
11
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <signal.h>
17 #include <string.h>
18 #include <fcntl.h>
19 #include <termios.h>
20 #include <sys/wait.h>
21 #include <sys/types.h>
22 #include <sys/ptrace.h>
23 #include <sys/ioctl.h>
24 #include <asm/unistd.h>
25
26 #include "ptproxy.h"
27 #include "sysdep.h"
28 #include "wait.h"
29
30 #include "user_util.h"
31 #include "user.h"
32 #include "os.h"
33 #include "tempfile.h"
34
35 static int debugger_wait(debugger_state *debugger, int *status, int options,
36                          int (*syscall)(debugger_state *debugger, pid_t child),
37                          int (*normal_return)(debugger_state *debugger, 
38                                               pid_t unused),
39                          int (*wait_return)(debugger_state *debugger, 
40                                             pid_t unused))
41 {
42         if(debugger->real_wait){
43                 debugger->handle_trace = normal_return;
44                 syscall_continue(debugger->pid);
45                 debugger->real_wait = 0;
46                 return(1);
47         }
48         debugger->wait_status_ptr = status;
49         debugger->wait_options = options;
50         if((debugger->debugee != NULL) && debugger->debugee->event){
51                 syscall_continue(debugger->pid);
52                 wait_for_stop(debugger->pid, SIGTRAP, PTRACE_SYSCALL,
53                               NULL);
54                 (*wait_return)(debugger, -1);
55                 return(0);
56         }
57         else if(debugger->wait_options & WNOHANG){
58                 syscall_cancel(debugger->pid, 0);
59                 debugger->handle_trace = syscall;
60                 return(0);
61         }
62         else {
63                 syscall_pause(debugger->pid);
64                 debugger->handle_trace = wait_return;
65                 debugger->waiting = 1;
66         }
67         return(1);
68 }
69
70 /*
71  * Handle debugger trap, i.e. syscall.
72  */
73
74 int debugger_syscall(debugger_state *debugger, pid_t child)
75 {
76         long arg1, arg2, arg3, arg4, arg5, result;
77         int syscall, ret = 0;
78
79         syscall = get_syscall(debugger->pid, &arg1, &arg2, &arg3, &arg4, 
80                               &arg5);
81
82         switch(syscall){
83         case __NR_execve:
84                 /* execve never returns */
85                 debugger->handle_trace = debugger_syscall; 
86                 break;
87
88         case __NR_ptrace:
89                 if(debugger->debugee->pid != 0) arg2 = debugger->debugee->pid;
90                 if(!debugger->debugee->in_context) 
91                         child = debugger->debugee->pid;
92                 result = proxy_ptrace(debugger, arg1, arg2, arg3, arg4, child,
93                                       &ret);
94                 syscall_cancel(debugger->pid, result);
95                 debugger->handle_trace = debugger_syscall;
96                 return(ret);
97
98         case __NR_waitpid:
99         case __NR_wait4:
100                 if(!debugger_wait(debugger, (int *) arg2, arg3, 
101                                   debugger_syscall, debugger_normal_return, 
102                                   proxy_wait_return))
103                         return(0);
104                 break;
105
106         case __NR_kill:
107                 if(!debugger->debugee->in_context) 
108                         child = debugger->debugee->pid;
109                 if(arg1 == debugger->debugee->pid){
110                         result = kill(child, arg2);
111                         syscall_cancel(debugger->pid, result);
112                         debugger->handle_trace = debugger_syscall;
113                         return(0);
114                 }
115                 else debugger->handle_trace = debugger_normal_return;
116                 break;
117
118         default:
119                 debugger->handle_trace = debugger_normal_return;
120         }
121
122         syscall_continue(debugger->pid);
123         return(0);
124 }
125
126 /* Used by the tracing thread */
127 static debugger_state parent;
128 static int parent_syscall(debugger_state *debugger, int pid);
129
130 int init_parent_proxy(int pid)
131 {
132         parent = ((debugger_state) { .pid               = pid,
133                                      .wait_options      = 0,
134                                      .wait_status_ptr   = NULL,
135                                      .waiting           = 0,
136                                      .real_wait         = 0,
137                                      .expecting_child   = 0,
138                                      .handle_trace      = parent_syscall,
139                                      .debugee           = NULL } );
140         return(0);
141 }
142
143 int parent_normal_return(debugger_state *debugger, pid_t unused)
144 {
145         debugger->handle_trace = parent_syscall;
146         syscall_continue(debugger->pid);
147         return(0);
148 }
149
150 static int parent_syscall(debugger_state *debugger, int pid)
151 {
152         long arg1, arg2, arg3, arg4, arg5;
153         int syscall;
154
155         syscall = get_syscall(pid, &arg1, &arg2, &arg3, &arg4, &arg5);
156                 
157         if((syscall == __NR_waitpid) || (syscall == __NR_wait4)){
158                 debugger_wait(&parent, (int *) arg2, arg3, parent_syscall,
159                               parent_normal_return, parent_wait_return);
160         }
161         else ptrace(PTRACE_SYSCALL, pid, 0, 0);
162         return(0);
163 }
164
165 int debugger_normal_return(debugger_state *debugger, pid_t unused)
166 {
167         debugger->handle_trace = debugger_syscall;
168         syscall_continue(debugger->pid);
169         return(0);
170 }
171
172 void debugger_cancelled_return(debugger_state *debugger, int result)
173 {
174         debugger->handle_trace = debugger_syscall;
175         syscall_set_result(debugger->pid, result);
176         syscall_continue(debugger->pid);
177 }
178
179 /* Used by the tracing thread */
180 static debugger_state debugger;
181 static debugee_state debugee;
182
183 void init_proxy (pid_t debugger_pid, int stopped, int status)
184 {
185         debugger.pid = debugger_pid;
186         debugger.handle_trace = debugger_syscall;
187         debugger.debugee = &debugee;
188         debugger.waiting = 0;
189         debugger.real_wait = 0;
190         debugger.expecting_child = 0;
191
192         debugee.pid = 0;
193         debugee.traced = 0;
194         debugee.stopped = stopped;
195         debugee.event = 0;
196         debugee.zombie = 0;
197         debugee.died = 0;
198         debugee.wait_status = status;
199         debugee.in_context = 1;
200 }
201
202 int debugger_proxy(int status, int pid)
203 {
204         int ret = 0, sig;
205
206         if(WIFSTOPPED(status)){
207                 sig = WSTOPSIG(status);
208                 if (sig == SIGTRAP)
209                         ret = (*debugger.handle_trace)(&debugger, pid);
210                                                        
211                 else if(sig == SIGCHLD){
212                         if(debugger.expecting_child){
213                                 ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
214                                 debugger.expecting_child = 0;
215                         }
216                         else if(debugger.waiting)
217                                 real_wait_return(&debugger);
218                         else {
219                                 ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
220                                 debugger.real_wait = 1;
221                         }
222                 }
223                 else ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
224         }
225         else if(WIFEXITED(status)){
226                 tracer_panic("debugger (pid %d) exited with status %d", 
227                              debugger.pid, WEXITSTATUS(status));
228         }
229         else if(WIFSIGNALED(status)){
230                 tracer_panic("debugger (pid %d) exited with signal %d", 
231                              debugger.pid, WTERMSIG(status));
232         }
233         else {
234                 tracer_panic("proxy got unknown status (0x%x) on debugger "
235                              "(pid %d)", status, debugger.pid);
236         }
237         return(ret);
238 }
239
240 void child_proxy(pid_t pid, int status)
241 {
242         debugee.event = 1;
243         debugee.wait_status = status;
244
245         if(WIFSTOPPED(status)){
246                 debugee.stopped = 1;
247                 debugger.expecting_child = 1;
248                 kill(debugger.pid, SIGCHLD);
249         }
250         else if(WIFEXITED(status) || WIFSIGNALED(status)){
251                 debugee.zombie = 1;
252                 debugger.expecting_child = 1;
253                 kill(debugger.pid, SIGCHLD);
254         }
255         else panic("proxy got unknown status (0x%x) on child (pid %d)", 
256                    status, pid);
257 }
258
259 void debugger_parent_signal(int status, int pid)
260 {
261         int sig;
262
263         if(WIFSTOPPED(status)){
264                 sig = WSTOPSIG(status);
265                 if(sig == SIGTRAP) (*parent.handle_trace)(&parent, pid);
266                 else ptrace(PTRACE_SYSCALL, pid, 0, sig);
267         }
268 }
269
270 void fake_child_exit(void)
271 {
272         int status, pid;
273
274         child_proxy(1, W_EXITCODE(0, 0));
275         while(debugger.waiting == 1){
276                 pid = waitpid(debugger.pid, &status, WUNTRACED);
277                 if(pid != debugger.pid){
278                         printk("fake_child_exit - waitpid failed, "
279                                "errno = %d\n", errno);
280                         return;
281                 }
282                 debugger_proxy(status, debugger.pid);
283         }
284         pid = waitpid(debugger.pid, &status, WUNTRACED);
285         if(pid != debugger.pid){
286                 printk("fake_child_exit - waitpid failed, "
287                        "errno = %d\n", errno);
288                 return;
289         }
290         if(ptrace(PTRACE_DETACH, debugger.pid, 0, SIGCONT) < 0)
291                 printk("fake_child_exit - PTRACE_DETACH failed, errno = %d\n",
292                        errno);
293 }
294
295 char gdb_init_string[] = 
296 "att 1
297 b panic
298 b stop
299 handle SIGWINCH nostop noprint pass
300 ";
301
302 int start_debugger(char *prog, int startup, int stop, int *fd_out)
303 {
304         int slave, child;
305
306         slave = open_gdb_chan();
307         if((child = fork()) == 0){
308                 char *tempname = NULL;
309                 int fd;
310
311                 if(setsid() < 0) perror("setsid");
312                 if((dup2(slave, 0) < 0) || (dup2(slave, 1) < 0) || 
313                    (dup2(slave, 2) < 0)){
314                         printk("start_debugger : dup2 failed, errno = %d\n",
315                                errno);
316                         exit(1);
317                 }
318                 if(ioctl(0, TIOCSCTTY, 0) < 0){
319                         printk("start_debugger : TIOCSCTTY failed, "
320                                "errno = %d\n", errno);
321                         exit(1);
322                 }
323                 if(tcsetpgrp (1, os_getpid()) < 0){
324                         printk("start_debugger : tcsetpgrp failed, "
325                                "errno = %d\n", errno);
326 #ifdef notdef
327                         exit(1);
328 #endif
329                 }
330                 if((fd = make_tempfile("/tmp/gdb_init-XXXXXX", &tempname, 0)) < 0){
331                         printk("start_debugger : make_tempfile failed, errno = %d\n",
332                                errno);
333                         exit(1);
334                 }
335                 write(fd, gdb_init_string, sizeof(gdb_init_string) - 1);
336                 if(startup){
337                         if(stop){
338                                 write(fd, "b start_kernel\n",
339                                       strlen("b start_kernel\n"));
340                         }
341                         write(fd, "c\n", strlen("c\n"));
342                 }
343                 if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){
344                         printk("start_debugger :  PTRACE_TRACEME failed, "
345                                "errno = %d\n", errno);
346                         exit(1);
347                 }
348                 execlp("gdb", "gdb", "--command", tempname, prog, NULL);
349                 printk("start_debugger : exec of gdb failed, errno = %d\n",
350                        errno);
351         }
352         if(child < 0){
353                 printk("start_debugger : fork for gdb failed, errno = %d\n",
354                        errno);
355                 return(-1);
356         }
357         *fd_out = slave;
358         return(child);
359 }
360
361 /*
362  * Overrides for Emacs so that we follow Linus's tabbing style.
363  * Emacs will notice this stuff at the end of the file and automatically
364  * adjust the settings for this buffer only.  This must remain at the end
365  * of the file.
366  * ---------------------------------------------------------------------------
367  * Local variables:
368  * c-file-style: "linux"
369  * End:
370  */