Add some Str* prototypes to shlwapi.h.
[wine] / server / ptrace.c
1 /*
2  * Server-side ptrace support
3  *
4  * Copyright (C) 1999 Alexandre Julliard
5  */
6
7 #include "config.h"
8
9 #include <assert.h>
10 #include <errno.h>
11 #include <stdio.h>
12 #include <signal.h>
13 #include <sys/types.h>
14 #include <sys/ptrace.h>
15 #ifdef HAVE_SYS_WAIT_H
16 #include <sys/wait.h>
17 #endif
18 #include <unistd.h>
19
20 #include "process.h"
21 #include "thread.h"
22
23
24 #ifndef PTRACE_CONT
25 #define PTRACE_CONT PT_CONTINUE
26 #endif
27 #ifndef PTRACE_ATTACH
28 #define PTRACE_ATTACH PT_ATTACH
29 #endif
30 #ifndef PTRACE_DETACH
31 #define PTRACE_DETACH PT_DETACH
32 #endif
33 #ifndef PTRACE_PEEKDATA
34 #define PTRACE_PEEKDATA PT_READ_D
35 #endif
36 #ifndef PTRACE_POKEDATA
37 #define PTRACE_POKEDATA PT_WRITE_D
38 #endif
39
40 static const int use_ptrace = 1;  /* set to 0 to disable ptrace */
41
42 /* handle a status returned by wait4 */
43 static int handle_child_status( struct thread *thread, int pid, int status )
44 {
45     if (WIFSTOPPED(status))
46     {
47         int sig = WSTOPSIG(status);
48         if (debug_level && thread)
49             fprintf( stderr, "%08x: *signal* signal=%d\n", (unsigned int)thread, sig );
50         switch(sig)
51         {
52         case SIGSTOP:  /* continue at once if not suspended */
53             if (!thread || !(thread->process->suspend + thread->suspend))
54                 ptrace( PTRACE_CONT, pid, 1, sig );
55             break;
56         default:  /* ignore other signals for now */
57             ptrace( PTRACE_CONT, pid, 1, sig );
58             break;
59         }
60         return sig;
61     }
62     if (thread && (WIFSIGNALED(status) || WIFEXITED(status)))
63     {
64         thread->attached = 0;
65         thread->unix_pid = 0;
66         if (debug_level)
67         {
68             if (WIFSIGNALED(status))
69                 fprintf( stderr, "%08x: *exited* signal=%d\n",
70                          (unsigned int)thread, WTERMSIG(status) );
71             else
72                 fprintf( stderr, "%08x: *exited* status=%d\n",
73                          (unsigned int)thread, WEXITSTATUS(status) );
74         }
75     }
76     return 0;
77 }
78
79 /* handle a SIGCHLD signal */
80 void sigchld_handler()
81 {
82     int pid, status;
83
84     for (;;)
85     {
86         if (!(pid = wait4( -1, &status, WUNTRACED | WNOHANG, NULL ))) break;
87         if (pid != -1) handle_child_status( get_thread_from_pid(pid), pid, status );
88         else break;
89     }
90 }
91
92 /* wait for a ptraced child to get a certain signal */
93 void wait4_thread( struct thread *thread, int signal )
94 {
95     int res, status;
96
97     do
98     {
99         if ((res = wait4( thread->unix_pid, &status, WUNTRACED, NULL )) == -1)
100         {
101             perror( "wait4" );
102             return;
103         }
104         res = handle_child_status( thread, res, status );
105     } while (res && res != signal);
106 }
107
108 /* attach to a Unix thread */
109 static int attach_thread( struct thread *thread )
110 {
111     /* this may fail if the client is already being debugged */
112     if (!use_ptrace || (ptrace( PTRACE_ATTACH, thread->unix_pid, 0, 0 ) == -1)) return 0;
113     if (debug_level) fprintf( stderr, "%08x: *attached*\n", (unsigned int)thread );
114     thread->attached = 1;
115     wait4_thread( thread, SIGSTOP );
116     return 1;
117 }
118
119 /* detach from a Unix thread and kill it */
120 void detach_thread( struct thread *thread, int sig )
121 {
122     if (!thread->unix_pid) return;
123     if (thread->attached)
124     {
125         /* make sure it is stopped */
126         if (!(thread->suspend + thread->process->suspend)) stop_thread( thread );
127         if (sig) kill( thread->unix_pid, sig );
128         if (debug_level) fprintf( stderr, "%08x: *detached*\n", (unsigned int)thread );
129         ptrace( PTRACE_DETACH, thread->unix_pid, 1, sig );
130         thread->attached = 0;
131     }
132     else
133     {
134         if (sig) kill( thread->unix_pid, sig );
135         if (thread->suspend + thread->process->suspend) continue_thread( thread );
136     }
137 }
138
139 /* stop a thread (at the Unix level) */
140 void stop_thread( struct thread *thread )
141 {
142     /* can't stop a thread while initialisation is in progress */
143     if (!thread->unix_pid || thread->process->init_event) return;
144     /* first try to attach to it */
145     if (!thread->attached)
146         if (attach_thread( thread )) return;  /* this will have stopped it */
147     /* attached already, or attach failed -> send a signal */
148     kill( thread->unix_pid, SIGSTOP );
149     if (thread->attached) wait4_thread( thread, SIGSTOP );
150 }
151
152 /* make a thread continue (at the Unix level) */
153 void continue_thread( struct thread *thread )
154 {
155     if (!thread->unix_pid) return;
156     if (!thread->attached) kill( thread->unix_pid, SIGCONT );
157     else ptrace( PTRACE_CONT, thread->unix_pid, 1, SIGSTOP );
158 }
159
160 /* suspend a thread to allow using ptrace on it */
161 /* you must do a resume_thread when finished with the thread */
162 int suspend_for_ptrace( struct thread *thread )
163 {
164     if (thread->attached)
165     {
166         suspend_thread( thread, 0 );
167         return 1;
168     }
169     /* can't stop a thread while initialisation is in progress */
170     if (!thread->unix_pid || thread->process->init_event) goto error;
171     thread->suspend++;
172     if (attach_thread( thread )) return 1;
173     thread->suspend--;
174  error:
175     set_error( STATUS_ACCESS_DENIED );
176     return 0;
177 }
178
179 /* read an int from a thread address space */
180 int read_thread_int( struct thread *thread, const int *addr, int *data )
181 {
182     if (((*data = ptrace( PTRACE_PEEKDATA, thread->unix_pid, addr, 0 )) == -1) && errno)
183     {
184         file_set_error();
185         return -1;
186     }
187     return 0;
188 }
189
190 /* write an int to a thread address space */
191 int write_thread_int( struct thread *thread, int *addr, int data, unsigned int mask )
192 {
193     int res;
194     if (mask != ~0)
195     {
196         if (read_thread_int( thread, addr, &res ) == -1) return -1;
197         data = (data & mask) | (res & ~mask);
198     }
199     if ((res = ptrace( PTRACE_POKEDATA, thread->unix_pid, addr, data )) == -1) file_set_error();
200     return res;
201 }