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