Commit | Line | Data |
---|---|---|
642d3136 AJ |
1 | /* |
2 | * Server-side process management | |
3 | * | |
4 | * Copyright (C) 1998 Alexandre Julliard | |
0799c1a7 AJ |
5 | * |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
360a3f91 | 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
642d3136 AJ |
19 | */ |
20 | ||
fb32c7bb | 21 | #include "config.h" |
5769d1de | 22 | #include "wine/port.h" |
fb32c7bb | 23 | |
642d3136 | 24 | #include <assert.h> |
767e6f6f | 25 | #include <limits.h> |
3da5f84d | 26 | #include <signal.h> |
85ed45e3 | 27 | #include <string.h> |
e37c6e18 | 28 | #include <stdarg.h> |
767e6f6f | 29 | #include <stdio.h> |
642d3136 | 30 | #include <stdlib.h> |
767e6f6f | 31 | #include <sys/time.h> |
fb32c7bb PS |
32 | #ifdef HAVE_SYS_SOCKET_H |
33 | # include <sys/socket.h> | |
34 | #endif | |
767e6f6f | 35 | #include <unistd.h> |
57279184 SE |
36 | #ifdef HAVE_POLL_H |
37 | #include <poll.h> | |
38 | #endif | |
767e6f6f | 39 | |
1a1583a3 GG |
40 | #include "ntstatus.h" |
41 | #define WIN32_NO_STATUS | |
b3badc7d | 42 | #include "winternl.h" |
642d3136 | 43 | |
863637b1 | 44 | #include "file.h" |
43c190e7 AJ |
45 | #include "handle.h" |
46 | #include "process.h" | |
47 | #include "thread.h" | |
5bc78089 | 48 | #include "request.h" |
0b83d4cb | 49 | #include "console.h" |
bfce151a | 50 | #include "user.h" |
d2ea92d1 | 51 | #include "security.h" |
642d3136 | 52 | |
f692d446 | 53 | /* process structure */ |
43c190e7 | 54 | |
48c0d51d | 55 | static struct list process_list = LIST_INIT(process_list); |
fdc92bae | 56 | static int running_processes; |
642d3136 AJ |
57 | |
58 | /* process operations */ | |
59 | ||
338e757d | 60 | static void process_dump( struct object *obj, int verbose ); |
85ed45e3 | 61 | static int process_signaled( struct object *obj, struct thread *thread ); |
46d1b3e8 | 62 | static unsigned int process_map_access( struct object *obj, unsigned int access ); |
cf27a7fa | 63 | static void process_poll_event( struct fd *fd, int event ); |
338e757d | 64 | static void process_destroy( struct object *obj ); |
642d3136 AJ |
65 | |
66 | static const struct object_ops process_ops = | |
67 | { | |
1dca5e24 AJ |
68 | sizeof(struct process), /* size */ |
69 | process_dump, /* dump */ | |
70 | add_queue, /* add_queue */ | |
71 | remove_queue, /* remove_queue */ | |
72 | process_signaled, /* signaled */ | |
73 | no_satisfied, /* satisfied */ | |
b9b1ea9c | 74 | no_signal, /* signal */ |
863637b1 | 75 | no_get_fd, /* get_fd */ |
46d1b3e8 | 76 | process_map_access, /* map_access */ |
baffcb95 | 77 | no_lookup_name, /* lookup_name */ |
b9b1ea9c | 78 | no_close_handle, /* close_handle */ |
863637b1 AJ |
79 | process_destroy /* destroy */ |
80 | }; | |
81 | ||
82 | static const struct fd_ops process_fd_ops = | |
83 | { | |
1dca5e24 | 84 | NULL, /* get_poll_events */ |
f5242405 | 85 | process_poll_event, /* poll_event */ |
1dca5e24 AJ |
86 | no_flush, /* flush */ |
87 | no_get_file_info, /* get_file_info */ | |
4634447d EP |
88 | no_queue_async, /* queue_async */ |
89 | no_cancel_async /* cancel async */ | |
642d3136 AJ |
90 | }; |
91 | ||
5b4f3e8d AJ |
92 | /* process startup info */ |
93 | ||
94 | struct startup_info | |
2fe57779 | 95 | { |
5b4f3e8d | 96 | struct object obj; /* object header */ |
77c8b1dc | 97 | struct list entry; /* entry in list of startup infos */ |
5b4f3e8d | 98 | int inherit_all; /* inherit all handles from parent */ |
01caa5e6 | 99 | unsigned int create_flags; /* creation flags */ |
baf0a064 | 100 | int unix_pid; /* Unix pid of new process */ |
51885749 AJ |
101 | obj_handle_t hstdin; /* handle for stdin */ |
102 | obj_handle_t hstdout; /* handle for stdout */ | |
103 | obj_handle_t hstderr; /* handle for stderr */ | |
d27624be | 104 | struct file *exe_file; /* file handle for main exe */ |
e9936d96 | 105 | struct thread *owner; /* owner thread (the one that created the new process) */ |
5b4f3e8d AJ |
106 | struct process *process; /* created process */ |
107 | struct thread *thread; /* created thread */ | |
6543a652 | 108 | size_t data_size; /* size of startup data */ |
841f8986 | 109 | void *data; /* data for startup info */ |
5b4f3e8d | 110 | }; |
85ed45e3 | 111 | |
5b4f3e8d AJ |
112 | static void startup_info_dump( struct object *obj, int verbose ); |
113 | static int startup_info_signaled( struct object *obj, struct thread *thread ); | |
114 | static void startup_info_destroy( struct object *obj ); | |
115 | ||
116 | static const struct object_ops startup_info_ops = | |
2fe57779 | 117 | { |
5b4f3e8d AJ |
118 | sizeof(struct startup_info), /* size */ |
119 | startup_info_dump, /* dump */ | |
120 | add_queue, /* add_queue */ | |
121 | remove_queue, /* remove_queue */ | |
122 | startup_info_signaled, /* signaled */ | |
123 | no_satisfied, /* satisfied */ | |
f92fff66 | 124 | no_signal, /* signal */ |
1ab243ba | 125 | no_get_fd, /* get_fd */ |
28beba31 | 126 | no_map_access, /* map_access */ |
baffcb95 | 127 | no_lookup_name, /* lookup_name */ |
b9b1ea9c | 128 | no_close_handle, /* close_handle */ |
5b4f3e8d AJ |
129 | startup_info_destroy /* destroy */ |
130 | }; | |
131 | ||
2fe57779 | 132 | |
77c8b1dc AJ |
133 | static struct list startup_info_list = LIST_INIT(startup_info_list); |
134 | ||
91befe1d AJ |
135 | struct ptid_entry |
136 | { | |
137 | void *ptr; /* entry ptr */ | |
138 | unsigned int next; /* next free entry */ | |
139 | }; | |
140 | ||
141 | static struct ptid_entry *ptid_entries; /* array of ptid entries */ | |
142 | static unsigned int used_ptid_entries; /* number of entries in use */ | |
143 | static unsigned int alloc_ptid_entries; /* number of allocated entries */ | |
144 | static unsigned int next_free_ptid; /* next free entry */ | |
145 | static unsigned int last_free_ptid; /* last free entry */ | |
146 | ||
147 | #define PTID_OFFSET 8 /* offset for first ptid value */ | |
148 | ||
149 | /* allocate a new process or thread id */ | |
150 | unsigned int alloc_ptid( void *ptr ) | |
151 | { | |
152 | struct ptid_entry *entry; | |
153 | unsigned int id; | |
154 | ||
155 | if (used_ptid_entries < alloc_ptid_entries) | |
156 | { | |
157 | id = used_ptid_entries + PTID_OFFSET; | |
158 | entry = &ptid_entries[used_ptid_entries++]; | |
159 | } | |
160 | else if (next_free_ptid) | |
161 | { | |
162 | id = next_free_ptid; | |
163 | entry = &ptid_entries[id - PTID_OFFSET]; | |
164 | if (!(next_free_ptid = entry->next)) last_free_ptid = 0; | |
165 | } | |
166 | else /* need to grow the array */ | |
167 | { | |
168 | unsigned int count = alloc_ptid_entries + (alloc_ptid_entries / 2); | |
169 | if (!count) count = 64; | |
170 | if (!(entry = realloc( ptid_entries, count * sizeof(*entry) ))) | |
171 | { | |
172 | set_error( STATUS_NO_MEMORY ); | |
173 | return 0; | |
174 | } | |
175 | ptid_entries = entry; | |
176 | alloc_ptid_entries = count; | |
177 | id = used_ptid_entries + PTID_OFFSET; | |
178 | entry = &ptid_entries[used_ptid_entries++]; | |
179 | } | |
180 | ||
181 | entry->ptr = ptr; | |
182 | return id; | |
183 | } | |
184 | ||
185 | /* free a process or thread id */ | |
186 | void free_ptid( unsigned int id ) | |
187 | { | |
188 | struct ptid_entry *entry = &ptid_entries[id - PTID_OFFSET]; | |
189 | ||
190 | entry->ptr = NULL; | |
191 | entry->next = 0; | |
192 | ||
193 | /* append to end of free list so that we don't reuse it too early */ | |
194 | if (last_free_ptid) ptid_entries[last_free_ptid - PTID_OFFSET].next = id; | |
195 | else next_free_ptid = id; | |
196 | ||
197 | last_free_ptid = id; | |
198 | } | |
199 | ||
200 | /* retrieve the pointer corresponding to a process or thread id */ | |
201 | void *get_ptid_entry( unsigned int id ) | |
202 | { | |
203 | if (id < PTID_OFFSET) return NULL; | |
204 | if (id - PTID_OFFSET >= used_ptid_entries) return NULL; | |
205 | return ptid_entries[id - PTID_OFFSET].ptr; | |
206 | } | |
207 | ||
0502638e AJ |
208 | /* return the main thread of the process */ |
209 | struct thread *get_process_first_thread( struct process *process ) | |
210 | { | |
211 | struct list *ptr = list_head( &process->thread_list ); | |
212 | if (!ptr) return NULL; | |
213 | return LIST_ENTRY( ptr, struct thread, proc_entry ); | |
214 | } | |
215 | ||
9d802152 AJ |
216 | /* set the state of the process startup info */ |
217 | static void set_process_startup_state( struct process *process, enum startup_state state ) | |
218 | { | |
ca96de34 AJ |
219 | if (process->startup_state == STARTUP_IN_PROGRESS) process->startup_state = state; |
220 | if (process->startup_info) | |
221 | { | |
222 | wake_up( &process->startup_info->obj, 0 ); | |
223 | release_object( process->startup_info ); | |
224 | process->startup_info = NULL; | |
225 | } | |
9d802152 AJ |
226 | } |
227 | ||
2fe57779 | 228 | /* create a new process and its main thread */ |
5b4f3e8d | 229 | struct thread *create_process( int fd ) |
f692d446 | 230 | { |
eb2e77fd | 231 | struct process *process; |
2fe57779 | 232 | struct thread *thread = NULL; |
8859d772 | 233 | int request_pipe[2]; |
eb2e77fd | 234 | |
e66207eb | 235 | if (!(process = alloc_object( &process_ops ))) goto error; |
0b83d4cb | 236 | process->parent = NULL; |
3da5f84d | 237 | process->debugger = NULL; |
eb2e77fd | 238 | process->handles = NULL; |
e66207eb | 239 | process->msg_fd = NULL; |
717bf7e9 | 240 | process->exit_code = STILL_ACTIVE; |
767e6f6f | 241 | process->running_threads = 0; |
b3badc7d | 242 | process->priority = PROCESS_PRIOCLASS_NORMAL; |
62a8b433 | 243 | process->affinity = 1; |
3da5f84d | 244 | process->suspend = 0; |
d083a7bd | 245 | process->create_flags = 0; |
0b83d4cb | 246 | process->console = NULL; |
ca96de34 | 247 | process->startup_state = STARTUP_IN_PROGRESS; |
9d802152 | 248 | process->startup_info = NULL; |
c5e433a3 AJ |
249 | process->idle_event = NULL; |
250 | process->queue = NULL; | |
e55d5937 | 251 | process->peb = NULL; |
0a7c1f6c | 252 | process->ldt_copy = NULL; |
1bf96e09 | 253 | process->winstation = 0; |
78a3e633 | 254 | process->desktop = 0; |
d2ea92d1 | 255 | process->token = token_create_admin(); |
0502638e | 256 | list_init( &process->thread_list ); |
ce613493 | 257 | list_init( &process->locks ); |
bfce151a | 258 | list_init( &process->classes ); |
a9e0fb1b | 259 | list_init( &process->dlls ); |
0b83d4cb | 260 | |
f692d446 | 261 | gettimeofday( &process->start_time, NULL ); |
48c0d51d | 262 | list_add_head( &process_list, &process->entry ); |
f692d446 | 263 | |
01caa5e6 | 264 | if (!(process->id = process->group_id = alloc_ptid( process ))) goto error; |
580da246 | 265 | if (!(process->msg_fd = create_anonymous_fd( &process_fd_ops, fd, &process->obj ))) goto error; |
91befe1d | 266 | |
7f74824d | 267 | /* create the main thread */ |
8859d772 AJ |
268 | if (pipe( request_pipe ) == -1) |
269 | { | |
270 | file_set_error(); | |
271 | goto error; | |
272 | } | |
4144b5b8 AJ |
273 | if (send_client_fd( process, request_pipe[1], 0 ) == -1) |
274 | { | |
275 | close( request_pipe[0] ); | |
276 | close( request_pipe[1] ); | |
277 | goto error; | |
278 | } | |
8859d772 AJ |
279 | close( request_pipe[1] ); |
280 | if (!(thread = create_thread( request_pipe[0], process ))) goto error; | |
7f74824d | 281 | |
e66207eb | 282 | set_fd_events( process->msg_fd, POLLIN ); /* start listening to events */ |
5b4f3e8d AJ |
283 | release_object( process ); |
284 | return thread; | |
285 | ||
286 | error: | |
4144b5b8 AJ |
287 | if (process) release_object( process ); |
288 | /* if we failed to start our first process, close everything down */ | |
289 | if (!running_processes) close_master_socket(); | |
5b4f3e8d AJ |
290 | return NULL; |
291 | } | |
292 | ||
77c8b1dc | 293 | /* find the startup info for a given Unix process */ |
baf0a064 | 294 | inline static struct startup_info *find_startup_info( int unix_pid ) |
77c8b1dc AJ |
295 | { |
296 | struct list *ptr; | |
297 | ||
298 | LIST_FOR_EACH( ptr, &startup_info_list ) | |
299 | { | |
300 | struct startup_info *info = LIST_ENTRY( ptr, struct startup_info, entry ); | |
301 | if (info->unix_pid == unix_pid) return info; | |
302 | } | |
303 | return NULL; | |
304 | } | |
305 | ||
5b4f3e8d | 306 | /* initialize the current process and fill in the request */ |
11ad6a0a | 307 | size_t init_process( struct thread *thread ) |
5b4f3e8d | 308 | { |
0424f381 | 309 | struct process *process = thread->process; |
77c8b1dc | 310 | struct thread *parent_thread = NULL; |
5b4f3e8d | 311 | struct process *parent = NULL; |
0424f381 AJ |
312 | struct startup_info *info; |
313 | ||
11ad6a0a | 314 | if (process->startup_info) return process->startup_info->data_size; /* already initialized */ |
5b4f3e8d | 315 | |
0424f381 | 316 | if ((info = find_startup_info( thread->unix_pid ))) |
5b4f3e8d | 317 | { |
11ad6a0a | 318 | if (info->thread) return info->data_size; /* already initialized */ |
0424f381 AJ |
319 | |
320 | info->thread = (struct thread *)grab_object( thread ); | |
321 | info->process = (struct process *)grab_object( process ); | |
322 | process->startup_info = (struct startup_info *)grab_object( info ); | |
323 | ||
77c8b1dc AJ |
324 | parent_thread = info->owner; |
325 | parent = parent_thread->process; | |
0b83d4cb | 326 | process->parent = (struct process *)grab_object( parent ); |
5b4f3e8d | 327 | |
0424f381 AJ |
328 | /* set the process flags */ |
329 | process->create_flags = info->create_flags; | |
5b4f3e8d | 330 | |
0424f381 AJ |
331 | if (info->inherit_all) process->handles = copy_handle_table( process, parent ); |
332 | } | |
f692d446 | 333 | |
0424f381 AJ |
334 | /* create the handle table */ |
335 | if (!process->handles) process->handles = alloc_handle_table( process, 0 ); | |
336 | if (!process->handles) | |
337 | { | |
338 | fatal_protocol_error( thread, "Failed to allocate handle table\n" ); | |
11ad6a0a | 339 | return 0; |
0424f381 | 340 | } |
67a24c8c | 341 | |
90af5a0d AJ |
342 | /* connect to the window station */ |
343 | connect_process_winstation( process, parent_thread ); | |
0424f381 | 344 | |
11ad6a0a | 345 | if (!info) return 0; |
0424f381 | 346 | |
0424f381 AJ |
347 | /* thread will be actually suspended in init_done */ |
348 | if (info->create_flags & CREATE_SUSPENDED) thread->suspend++; | |
1bf96e09 | 349 | |
6a72dc52 | 350 | /* set the process console */ |
0424f381 | 351 | if (!(info->create_flags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE))) |
01caa5e6 AJ |
352 | { |
353 | /* FIXME: some better error checking should be done... | |
354 | * like if hConOut and hConIn are console handles, then they should be on the same | |
355 | * physical console | |
356 | */ | |
357 | inherit_console( parent_thread, process, info->inherit_all ? info->hstdin : 0 ); | |
358 | } | |
6a72dc52 | 359 | |
0424f381 AJ |
360 | /* attach to the debugger if requested */ |
361 | if (process->create_flags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) | |
362 | set_process_debugger( process, parent_thread ); | |
363 | else if (parent->debugger && !(parent->create_flags & DEBUG_ONLY_THIS_PROCESS)) | |
364 | set_process_debugger( process, parent->debugger ); | |
f692d446 | 365 | |
0424f381 AJ |
366 | if (!(process->create_flags & CREATE_NEW_PROCESS_GROUP)) |
367 | process->group_id = parent->group_id; | |
11ad6a0a AJ |
368 | |
369 | return info->data_size; | |
642d3136 AJ |
370 | } |
371 | ||
372 | /* destroy a process when its refcount is 0 */ | |
338e757d | 373 | static void process_destroy( struct object *obj ) |
642d3136 AJ |
374 | { |
375 | struct process *process = (struct process *)obj; | |
376 | assert( obj->ops == &process_ops ); | |
377 | ||
767e6f6f | 378 | /* we can't have a thread remaining */ |
0502638e | 379 | assert( list_empty( &process->thread_list )); |
9d802152 AJ |
380 | |
381 | set_process_startup_state( process, STARTUP_ABORTED ); | |
0b83d4cb EP |
382 | if (process->console) release_object( process->console ); |
383 | if (process->parent) release_object( process->parent ); | |
e66207eb | 384 | if (process->msg_fd) release_object( process->msg_fd ); |
48c0d51d | 385 | list_remove( &process->entry ); |
c5e433a3 AJ |
386 | if (process->idle_event) release_object( process->idle_event ); |
387 | if (process->queue) release_object( process->queue ); | |
91befe1d | 388 | if (process->id) free_ptid( process->id ); |
36cd6f5d | 389 | if (process->token) release_object( process->token ); |
642d3136 AJ |
390 | } |
391 | ||
767e6f6f | 392 | /* dump a process on stdout for debugging purposes */ |
338e757d | 393 | static void process_dump( struct object *obj, int verbose ) |
767e6f6f AJ |
394 | { |
395 | struct process *process = (struct process *)obj; | |
396 | assert( obj->ops == &process_ops ); | |
397 | ||
48c0d51d | 398 | fprintf( stderr, "Process id=%04x handles=%p\n", process->id, process->handles ); |
767e6f6f AJ |
399 | } |
400 | ||
85ed45e3 AJ |
401 | static int process_signaled( struct object *obj, struct thread *thread ) |
402 | { | |
403 | struct process *process = (struct process *)obj; | |
dbc033a2 | 404 | return !process->running_threads; |
85ed45e3 AJ |
405 | } |
406 | ||
46d1b3e8 AJ |
407 | static unsigned int process_map_access( struct object *obj, unsigned int access ) |
408 | { | |
409 | if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | SYNCHRONIZE; | |
410 | if (access & GENERIC_WRITE) access |= STANDARD_RIGHTS_WRITE | SYNCHRONIZE; | |
411 | if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE; | |
412 | if (access & GENERIC_ALL) access |= PROCESS_ALL_ACCESS; | |
413 | return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); | |
414 | } | |
415 | ||
cf27a7fa | 416 | static void process_poll_event( struct fd *fd, int event ) |
f5242405 | 417 | { |
cf27a7fa AJ |
418 | struct process *process = get_fd_user( fd ); |
419 | assert( process->obj.ops == &process_ops ); | |
f5242405 | 420 | |
e66207eb | 421 | if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 ); |
f5242405 AJ |
422 | else if (event & POLLIN) receive_fd( process ); |
423 | } | |
424 | ||
5b4f3e8d AJ |
425 | static void startup_info_destroy( struct object *obj ) |
426 | { | |
427 | struct startup_info *info = (struct startup_info *)obj; | |
428 | assert( obj->ops == &startup_info_ops ); | |
77c8b1dc | 429 | list_remove( &info->entry ); |
6543a652 | 430 | if (info->data) free( info->data ); |
d27624be | 431 | if (info->exe_file) release_object( info->exe_file ); |
5b4f3e8d AJ |
432 | if (info->process) release_object( info->process ); |
433 | if (info->thread) release_object( info->thread ); | |
77c8b1dc | 434 | if (info->owner) release_object( info->owner ); |
5b4f3e8d AJ |
435 | } |
436 | ||
437 | static void startup_info_dump( struct object *obj, int verbose ) | |
438 | { | |
439 | struct startup_info *info = (struct startup_info *)obj; | |
440 | assert( obj->ops == &startup_info_ops ); | |
441 | ||
b3332d74 | 442 | fprintf( stderr, "Startup info flags=%x in=%p out=%p err=%p\n", |
ca96de34 | 443 | info->create_flags, info->hstdin, info->hstdout, info->hstderr ); |
5b4f3e8d AJ |
444 | } |
445 | ||
446 | static int startup_info_signaled( struct object *obj, struct thread *thread ) | |
447 | { | |
448 | struct startup_info *info = (struct startup_info *)obj; | |
49266107 | 449 | return info->process && info->process->startup_state != STARTUP_IN_PROGRESS; |
5b4f3e8d AJ |
450 | } |
451 | ||
642d3136 | 452 | /* get a process from an id (and increment the refcount) */ |
54f22873 | 453 | struct process *get_process_from_id( process_id_t id ) |
642d3136 | 454 | { |
91befe1d AJ |
455 | struct object *obj = get_ptid_entry( id ); |
456 | ||
457 | if (obj && obj->ops == &process_ops) return (struct process *)grab_object( obj ); | |
458 | set_error( STATUS_INVALID_PARAMETER ); | |
459 | return NULL; | |
642d3136 | 460 | } |
767e6f6f AJ |
461 | |
462 | /* get a process from a handle (and increment the refcount) */ | |
51885749 | 463 | struct process *get_process_from_handle( obj_handle_t handle, unsigned int access ) |
767e6f6f AJ |
464 | { |
465 | return (struct process *)get_handle_obj( current->process, handle, | |
466 | access, &process_ops ); | |
467 | } | |
468 | ||
a9e0fb1b AJ |
469 | /* find a dll from its base address */ |
470 | static inline struct process_dll *find_process_dll( struct process *process, void *base ) | |
471 | { | |
472 | struct process_dll *dll; | |
473 | ||
a9e0fb1b AJ |
474 | LIST_FOR_EACH_ENTRY( dll, &process->dlls, struct process_dll, entry ) |
475 | { | |
476 | if (dll->base == base) return dll; | |
477 | } | |
478 | return NULL; | |
479 | } | |
480 | ||
05f0b71b AJ |
481 | /* add a dll to a process list */ |
482 | static struct process_dll *process_load_dll( struct process *process, struct file *file, | |
c30cefb2 | 483 | void *base, const WCHAR *filename, size_t name_len ) |
05f0b71b AJ |
484 | { |
485 | struct process_dll *dll; | |
486 | ||
487 | /* make sure we don't already have one with the same base address */ | |
a9e0fb1b | 488 | if (find_process_dll( process, base )) |
05f0b71b AJ |
489 | { |
490 | set_error( STATUS_INVALID_PARAMETER ); | |
491 | return NULL; | |
492 | } | |
493 | ||
494 | if ((dll = mem_alloc( sizeof(*dll) ))) | |
495 | { | |
05f0b71b AJ |
496 | dll->file = NULL; |
497 | dll->base = base; | |
aeb56605 AJ |
498 | dll->filename = NULL; |
499 | dll->namelen = name_len; | |
500 | if (name_len && !(dll->filename = memdup( filename, name_len ))) | |
501 | { | |
502 | free( dll ); | |
503 | return NULL; | |
504 | } | |
05f0b71b | 505 | if (file) dll->file = (struct file *)grab_object( file ); |
e6374cbe | 506 | list_add_tail( &process->dlls, &dll->entry ); |
05f0b71b AJ |
507 | } |
508 | return dll; | |
509 | } | |
510 | ||
511 | /* remove a dll from a process list */ | |
512 | static void process_unload_dll( struct process *process, void *base ) | |
513 | { | |
a9e0fb1b | 514 | struct process_dll *dll = find_process_dll( process, base ); |
05f0b71b | 515 | |
e6374cbe | 516 | if (dll && (&dll->entry != list_head( &process->dlls ))) /* main exe can't be unloaded */ |
05f0b71b | 517 | { |
a9e0fb1b AJ |
518 | if (dll->file) release_object( dll->file ); |
519 | if (dll->filename) free( dll->filename ); | |
520 | list_remove( &dll->entry ); | |
521 | free( dll ); | |
522 | generate_debug_event( current, UNLOAD_DLL_DEBUG_EVENT, base ); | |
05f0b71b | 523 | } |
a9e0fb1b | 524 | else set_error( STATUS_INVALID_PARAMETER ); |
05f0b71b AJ |
525 | } |
526 | ||
40043ed2 AJ |
527 | /* kill all processes */ |
528 | void kill_all_processes( struct process *skip, int exit_code ) | |
529 | { | |
530 | for (;;) | |
531 | { | |
48c0d51d | 532 | struct process *process; |
40043ed2 | 533 | |
48c0d51d AJ |
534 | LIST_FOR_EACH_ENTRY( process, &process_list, struct process, entry ) |
535 | { | |
536 | if (process == skip) continue; | |
537 | if (process->running_threads) break; | |
538 | } | |
539 | if (&process->entry == &process_list) break; /* no process found */ | |
40043ed2 AJ |
540 | kill_process( process, NULL, exit_code ); |
541 | } | |
542 | } | |
543 | ||
0b83d4cb | 544 | /* kill all processes being attached to a console renderer */ |
3940d8a2 | 545 | void kill_console_processes( struct thread *renderer, int exit_code ) |
0b83d4cb EP |
546 | { |
547 | for (;;) /* restart from the beginning of the list every time */ | |
548 | { | |
48c0d51d | 549 | struct process *process; |
0b83d4cb EP |
550 | |
551 | /* find the first process being attached to 'renderer' and still running */ | |
48c0d51d | 552 | LIST_FOR_EACH_ENTRY( process, &process_list, struct process, entry ) |
0b83d4cb | 553 | { |
48c0d51d AJ |
554 | if (process == renderer->process) continue; |
555 | if (!process->running_threads) continue; | |
556 | if (process->console && process->console->renderer == renderer) break; | |
0b83d4cb | 557 | } |
48c0d51d | 558 | if (&process->entry == &process_list) break; /* no process found */ |
0b83d4cb EP |
559 | kill_process( process, NULL, exit_code ); |
560 | } | |
561 | } | |
562 | ||
767e6f6f | 563 | /* a process has been killed (i.e. its last thread died) */ |
05f0b71b | 564 | static void process_killed( struct process *process ) |
767e6f6f | 565 | { |
36b85d02 | 566 | struct handle_table *handles; |
a9e0fb1b AJ |
567 | struct list *ptr; |
568 | ||
0502638e | 569 | assert( list_empty( &process->thread_list )); |
767e6f6f | 570 | gettimeofday( &process->end_time, NULL ); |
f978f615 | 571 | close_process_desktop( process ); |
36b85d02 | 572 | handles = process->handles; |
eb2e77fd | 573 | process->handles = NULL; |
36b85d02 | 574 | if (handles) release_object( handles ); |
0b83d4cb EP |
575 | |
576 | /* close the console attached to this process, if any */ | |
eb2e77fd | 577 | free_console( process ); |
0b83d4cb | 578 | |
a9e0fb1b | 579 | while ((ptr = list_head( &process->dlls ))) |
05f0b71b | 580 | { |
a9e0fb1b | 581 | struct process_dll *dll = LIST_ENTRY( ptr, struct process_dll, entry ); |
05f0b71b | 582 | if (dll->file) release_object( dll->file ); |
aeb56605 | 583 | if (dll->filename) free( dll->filename ); |
a9e0fb1b | 584 | list_remove( &dll->entry ); |
05f0b71b AJ |
585 | free( dll ); |
586 | } | |
bfce151a | 587 | destroy_process_classes( process ); |
5f22e7ca | 588 | remove_process_locks( process ); |
9d802152 | 589 | set_process_startup_state( process, STARTUP_ABORTED ); |
85ed45e3 | 590 | wake_up( &process->obj, 0 ); |
4144b5b8 | 591 | if (!--running_processes) close_master_socket(); |
767e6f6f AJ |
592 | } |
593 | ||
767e6f6f AJ |
594 | /* add a thread to a process running threads list */ |
595 | void add_process_thread( struct process *process, struct thread *thread ) | |
596 | { | |
0502638e | 597 | list_add_head( &process->thread_list, &thread->proc_entry ); |
fdc92bae | 598 | if (!process->running_threads++) running_processes++; |
767e6f6f AJ |
599 | grab_object( thread ); |
600 | } | |
601 | ||
602 | /* remove a thread from a process running threads list */ | |
603 | void remove_process_thread( struct process *process, struct thread *thread ) | |
604 | { | |
605 | assert( process->running_threads > 0 ); | |
0502638e | 606 | assert( !list_empty( &process->thread_list )); |
767e6f6f | 607 | |
0502638e | 608 | list_remove( &thread->proc_entry ); |
767e6f6f AJ |
609 | |
610 | if (!--process->running_threads) | |
611 | { | |
612 | /* we have removed the last running thread, exit the process */ | |
05f0b71b AJ |
613 | process->exit_code = thread->exit_code; |
614 | generate_debug_event( thread, EXIT_PROCESS_DEBUG_EVENT, process ); | |
615 | process_killed( process ); | |
767e6f6f | 616 | } |
05f0b71b | 617 | else generate_debug_event( thread, EXIT_THREAD_DEBUG_EVENT, thread ); |
767e6f6f AJ |
618 | release_object( thread ); |
619 | } | |
620 | ||
3da5f84d AJ |
621 | /* suspend all the threads of a process */ |
622 | void suspend_process( struct process *process ) | |
623 | { | |
624 | if (!process->suspend++) | |
625 | { | |
0502638e AJ |
626 | struct list *ptr, *next; |
627 | ||
628 | LIST_FOR_EACH_SAFE( ptr, next, &process->thread_list ) | |
3da5f84d | 629 | { |
0502638e | 630 | struct thread *thread = LIST_ENTRY( ptr, struct thread, proc_entry ); |
0a70783c | 631 | if (!thread->suspend) stop_thread( thread ); |
3da5f84d AJ |
632 | } |
633 | } | |
634 | } | |
635 | ||
636 | /* resume all the threads of a process */ | |
637 | void resume_process( struct process *process ) | |
638 | { | |
639 | assert (process->suspend > 0); | |
640 | if (!--process->suspend) | |
641 | { | |
0502638e AJ |
642 | struct list *ptr, *next; |
643 | ||
644 | LIST_FOR_EACH_SAFE( ptr, next, &process->thread_list ) | |
3da5f84d | 645 | { |
0502638e | 646 | struct thread *thread = LIST_ENTRY( ptr, struct thread, proc_entry ); |
d04ccb8e | 647 | if (!thread->suspend) wake_thread( thread ); |
3da5f84d AJ |
648 | } |
649 | } | |
650 | } | |
651 | ||
767e6f6f | 652 | /* kill a process on the spot */ |
f5242405 | 653 | void kill_process( struct process *process, struct thread *skip, int exit_code ) |
767e6f6f | 654 | { |
0502638e | 655 | struct list *ptr, *next; |
0b83d4cb | 656 | |
0502638e | 657 | LIST_FOR_EACH_SAFE( ptr, next, &process->thread_list ) |
12f29b50 | 658 | { |
0502638e AJ |
659 | struct thread *thread = LIST_ENTRY( ptr, struct thread, proc_entry ); |
660 | ||
0a364629 | 661 | if (exit_code) thread->exit_code = exit_code; |
12f29b50 | 662 | if (thread != skip) kill_thread( thread, 1 ); |
12f29b50 | 663 | } |
767e6f6f AJ |
664 | } |
665 | ||
17cf8101 AJ |
666 | /* kill all processes being debugged by a given thread */ |
667 | void kill_debugged_processes( struct thread *debugger, int exit_code ) | |
668 | { | |
669 | for (;;) /* restart from the beginning of the list every time */ | |
670 | { | |
48c0d51d AJ |
671 | struct process *process; |
672 | ||
17cf8101 | 673 | /* find the first process being debugged by 'debugger' and still running */ |
48c0d51d AJ |
674 | LIST_FOR_EACH_ENTRY( process, &process_list, struct process, entry ) |
675 | { | |
676 | if (!process->running_threads) continue; | |
677 | if (process->debugger == debugger) break; | |
678 | } | |
679 | if (&process->entry == &process_list) break; /* no process found */ | |
17cf8101 | 680 | process->debugger = NULL; |
12f29b50 | 681 | kill_process( process, NULL, exit_code ); |
17cf8101 AJ |
682 | } |
683 | } | |
684 | ||
0b83d4cb | 685 | |
17de8290 AJ |
686 | /* trigger a breakpoint event in a given process */ |
687 | void break_process( struct process *process ) | |
688 | { | |
689 | struct thread *thread; | |
690 | ||
691 | suspend_process( process ); | |
692 | ||
693 | LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry ) | |
694 | { | |
695 | if (thread->context) /* inside an exception event already */ | |
696 | { | |
697 | break_thread( thread ); | |
698 | goto done; | |
699 | } | |
700 | } | |
701 | if ((thread = get_process_first_thread( process ))) thread->debug_break = 1; | |
702 | else set_error( STATUS_ACCESS_DENIED ); | |
703 | done: | |
704 | resume_process( process ); | |
705 | } | |
706 | ||
707 | ||
fbccb38e EP |
708 | /* detach a debugger from all its debuggees */ |
709 | void detach_debugged_processes( struct thread *debugger ) | |
710 | { | |
711 | struct process *process; | |
48c0d51d AJ |
712 | |
713 | LIST_FOR_EACH_ENTRY( process, &process_list, struct process, entry ) | |
fbccb38e EP |
714 | { |
715 | if (process->debugger == debugger && process->running_threads) | |
716 | { | |
717 | debugger_detach( process, debugger ); | |
718 | } | |
719 | } | |
720 | } | |
721 | ||
722 | ||
93bfa0d6 EP |
723 | void enum_processes( int (*cb)(struct process*, void*), void *user ) |
724 | { | |
48c0d51d AJ |
725 | struct list *ptr, *next; |
726 | ||
727 | LIST_FOR_EACH_SAFE( ptr, next, &process_list ) | |
93bfa0d6 | 728 | { |
48c0d51d | 729 | struct process *process = LIST_ENTRY( ptr, struct process, entry ); |
93bfa0d6 EP |
730 | if ((cb)(process, user)) break; |
731 | } | |
732 | } | |
733 | ||
e55d5937 AJ |
734 | /* set the debugged flag in the process PEB */ |
735 | int set_process_debug_flag( struct process *process, int flag ) | |
736 | { | |
959bbf8b | 737 | char data = (flag != 0); |
e55d5937 AJ |
738 | |
739 | /* BeingDebugged flag is the byte at offset 2 in the PEB */ | |
959bbf8b | 740 | return write_process_memory( process, (char *)process->peb + 2, 1, &data ); |
8b8828f5 AJ |
741 | } |
742 | ||
fdc92bae AJ |
743 | /* take a snapshot of currently running processes */ |
744 | struct process_snapshot *process_snap( int *count ) | |
745 | { | |
746 | struct process_snapshot *snapshot, *ptr; | |
747 | struct process *process; | |
748 | if (!running_processes) return NULL; | |
749 | if (!(snapshot = mem_alloc( sizeof(*snapshot) * running_processes ))) | |
750 | return NULL; | |
751 | ptr = snapshot; | |
48c0d51d | 752 | LIST_FOR_EACH_ENTRY( process, &process_list, struct process, entry ) |
fdc92bae AJ |
753 | { |
754 | if (!process->running_threads) continue; | |
755 | ptr->process = process; | |
756 | ptr->threads = process->running_threads; | |
07d84469 | 757 | ptr->count = process->obj.refcount; |
fdc92bae | 758 | ptr->priority = process->priority; |
9fd54b28 | 759 | ptr->handles = get_handle_table_count(process); |
fdc92bae AJ |
760 | grab_object( process ); |
761 | ptr++; | |
762 | } | |
763 | *count = running_processes; | |
764 | return snapshot; | |
765 | } | |
43c190e7 | 766 | |
07d84469 AJ |
767 | /* take a snapshot of the modules of a process */ |
768 | struct module_snapshot *module_snap( struct process *process, int *count ) | |
769 | { | |
770 | struct module_snapshot *snapshot, *ptr; | |
771 | struct process_dll *dll; | |
e6374cbe | 772 | int total = 0; |
07d84469 | 773 | |
a9e0fb1b | 774 | LIST_FOR_EACH_ENTRY( dll, &process->dlls, struct process_dll, entry ) total++; |
07d84469 AJ |
775 | if (!(snapshot = mem_alloc( sizeof(*snapshot) * total ))) return NULL; |
776 | ||
e6374cbe | 777 | ptr = snapshot; |
a9e0fb1b | 778 | LIST_FOR_EACH_ENTRY( dll, &process->dlls, struct process_dll, entry ) |
07d84469 | 779 | { |
aeb56605 AJ |
780 | ptr->base = dll->base; |
781 | ptr->size = dll->size; | |
782 | ptr->namelen = dll->namelen; | |
783 | ptr->filename = memdup( dll->filename, dll->namelen ); | |
a9e0fb1b | 784 | ptr++; |
07d84469 AJ |
785 | } |
786 | *count = total; | |
787 | return snapshot; | |
788 | } | |
789 | ||
790 | ||
43c190e7 AJ |
791 | /* create a new process */ |
792 | DECL_HANDLER(new_process) | |
793 | { | |
5b4f3e8d | 794 | struct startup_info *info; |
43c190e7 | 795 | |
5b4f3e8d | 796 | /* build the startup info for a new process */ |
e66207eb | 797 | if (!(info = alloc_object( &startup_info_ops ))) return; |
77c8b1dc | 798 | list_add_head( &startup_info_list, &info->entry ); |
5b4f3e8d AJ |
799 | info->inherit_all = req->inherit_all; |
800 | info->create_flags = req->create_flags; | |
77c8b1dc | 801 | info->unix_pid = req->unix_pid; |
5b4f3e8d AJ |
802 | info->hstdin = req->hstdin; |
803 | info->hstdout = req->hstdout; | |
804 | info->hstderr = req->hstderr; | |
d27624be | 805 | info->exe_file = NULL; |
e9936d96 | 806 | info->owner = (struct thread *)grab_object( current ); |
5b4f3e8d AJ |
807 | info->process = NULL; |
808 | info->thread = NULL; | |
6543a652 AJ |
809 | info->data_size = get_req_data_size(); |
810 | info->data = NULL; | |
5b4f3e8d | 811 | |
8081e5a1 | 812 | if (req->exe_file && |
a510a7e1 | 813 | !(info->exe_file = get_file_obj( current->process, req->exe_file, FILE_READ_DATA ))) |
e9936d96 AJ |
814 | goto done; |
815 | ||
841f8986 | 816 | if (!(info->data = memdup( get_req_data(), info->data_size ))) goto done; |
24560e70 | 817 | reply->info = alloc_handle( current->process, info, SYNCHRONIZE, 0 ); |
e9936d96 AJ |
818 | |
819 | done: | |
820 | release_object( info ); | |
5b4f3e8d | 821 | } |
6a72dc52 | 822 | |
e9936d96 AJ |
823 | /* Retrieve information about a newly started process */ |
824 | DECL_HANDLER(get_new_process_info) | |
5b4f3e8d | 825 | { |
e9936d96 AJ |
826 | struct startup_info *info; |
827 | ||
e9936d96 AJ |
828 | if ((info = (struct startup_info *)get_handle_obj( current->process, req->info, |
829 | 0, &startup_info_ops ))) | |
5b4f3e8d | 830 | { |
9caa71ee AJ |
831 | reply->pid = get_process_id( info->process ); |
832 | reply->tid = get_thread_id( info->thread ); | |
833 | reply->phandle = alloc_handle( current->process, info->process, | |
24560e70 | 834 | req->process_access, req->process_attr ); |
9caa71ee | 835 | reply->thandle = alloc_handle( current->process, info->thread, |
24560e70 | 836 | req->thread_access, req->thread_attr ); |
ca96de34 | 837 | reply->success = is_process_init_done( info->process ); |
e9936d96 | 838 | release_object( info ); |
5b4f3e8d AJ |
839 | } |
840 | else | |
841 | { | |
9caa71ee AJ |
842 | reply->pid = 0; |
843 | reply->tid = 0; | |
844 | reply->phandle = 0; | |
845 | reply->thandle = 0; | |
9d802152 | 846 | reply->success = 0; |
5b4f3e8d | 847 | } |
43c190e7 AJ |
848 | } |
849 | ||
6543a652 AJ |
850 | /* Retrieve the new process startup info */ |
851 | DECL_HANDLER(get_startup_info) | |
852 | { | |
01caa5e6 AJ |
853 | struct process *process = current->process; |
854 | struct startup_info *info = process->startup_info; | |
855 | size_t size; | |
6543a652 | 856 | |
01caa5e6 AJ |
857 | if (!info) return; |
858 | ||
01caa5e6 AJ |
859 | if (info->exe_file && |
860 | !(reply->exe_file = alloc_handle( process, info->exe_file, GENERIC_READ, 0 ))) return; | |
861 | ||
862 | if (!info->inherit_all && !(info->create_flags & CREATE_NEW_CONSOLE)) | |
863 | { | |
864 | struct process *parent_process = info->owner->process; | |
865 | reply->hstdin = duplicate_handle( parent_process, info->hstdin, process, | |
24560e70 | 866 | 0, OBJ_INHERIT, DUPLICATE_SAME_ACCESS ); |
01caa5e6 | 867 | reply->hstdout = duplicate_handle( parent_process, info->hstdout, process, |
24560e70 | 868 | 0, OBJ_INHERIT, DUPLICATE_SAME_ACCESS ); |
01caa5e6 | 869 | reply->hstderr = duplicate_handle( parent_process, info->hstderr, process, |
24560e70 | 870 | 0, OBJ_INHERIT, DUPLICATE_SAME_ACCESS ); |
01caa5e6 AJ |
871 | /* some handles above may have been invalid; this is not an error */ |
872 | if (get_error() == STATUS_INVALID_HANDLE || | |
873 | get_error() == STATUS_OBJECT_TYPE_MISMATCH) clear_error(); | |
6543a652 | 874 | } |
01caa5e6 AJ |
875 | else |
876 | { | |
877 | reply->hstdin = info->hstdin; | |
878 | reply->hstdout = info->hstdout; | |
879 | reply->hstderr = info->hstderr; | |
880 | } | |
881 | ||
882 | /* we return the data directly without making a copy so this can only be called once */ | |
883 | size = info->data_size; | |
884 | if (size > get_reply_max_size()) size = get_reply_max_size(); | |
885 | set_reply_data_ptr( info->data, size ); | |
886 | info->data = NULL; | |
887 | info->data_size = 0; | |
6543a652 AJ |
888 | } |
889 | ||
ec7bb239 AJ |
890 | /* signal the end of the process initialization */ |
891 | DECL_HANDLER(init_process_done) | |
892 | { | |
2b0033d5 | 893 | struct process_dll *dll; |
ec7bb239 | 894 | struct process *process = current->process; |
ac2e4f1e | 895 | |
9d802152 | 896 | if (is_process_init_done(process)) |
ec7bb239 | 897 | { |
9d802152 AJ |
898 | fatal_protocol_error( current, "init_process_done: called twice\n" ); |
899 | return; | |
900 | } | |
e6374cbe | 901 | if (!(dll = find_process_dll( process, req->module ))) |
2b0033d5 | 902 | { |
e27358ea AJ |
903 | set_error( STATUS_DLL_NOT_FOUND ); |
904 | return; | |
2b0033d5 | 905 | } |
aeb56605 | 906 | |
e6374cbe AJ |
907 | /* main exe is the first in the dll list */ |
908 | list_remove( &dll->entry ); | |
909 | list_add_head( &process->dlls, &dll->entry ); | |
910 | ||
9d802152 AJ |
911 | generate_startup_debug_events( process, req->entry ); |
912 | set_process_startup_state( process, STARTUP_DONE ); | |
913 | ||
f676bc8d | 914 | if (req->gui) process->idle_event = create_event( NULL, NULL, 0, 1, 0 ); |
ca96de34 | 915 | if (current->suspend + process->suspend > 0) stop_thread( current ); |
e55d5937 | 916 | if (process->debugger) set_process_debug_flag( process, 1 ); |
ec7bb239 AJ |
917 | } |
918 | ||
43c190e7 AJ |
919 | /* open a handle to a process */ |
920 | DECL_HANDLER(open_process) | |
921 | { | |
43c190e7 | 922 | struct process *process = get_process_from_id( req->pid ); |
9caa71ee | 923 | reply->handle = 0; |
43c190e7 AJ |
924 | if (process) |
925 | { | |
24560e70 | 926 | reply->handle = alloc_handle( current->process, process, req->access, req->attributes ); |
43c190e7 AJ |
927 | release_object( process ); |
928 | } | |
43c190e7 AJ |
929 | } |
930 | ||
931 | /* terminate a process */ | |
932 | DECL_HANDLER(terminate_process) | |
933 | { | |
934 | struct process *process; | |
935 | ||
936 | if ((process = get_process_from_handle( req->handle, PROCESS_TERMINATE ))) | |
937 | { | |
9caa71ee | 938 | reply->self = (current->process == process); |
12f29b50 | 939 | kill_process( process, current, req->exit_code ); |
43c190e7 AJ |
940 | release_object( process ); |
941 | } | |
43c190e7 AJ |
942 | } |
943 | ||
944 | /* fetch information about a process */ | |
945 | DECL_HANDLER(get_process_info) | |
946 | { | |
947 | struct process *process; | |
43c190e7 AJ |
948 | |
949 | if ((process = get_process_from_handle( req->handle, PROCESS_QUERY_INFORMATION ))) | |
950 | { | |
e55d5937 | 951 | reply->pid = get_process_id( process ); |
b0fd2ade | 952 | reply->ppid = process->parent ? get_process_id( process->parent ) : 0; |
e55d5937 AJ |
953 | reply->exit_code = process->exit_code; |
954 | reply->priority = process->priority; | |
b09582a8 | 955 | reply->affinity = process->affinity; |
b0fd2ade | 956 | reply->peb = process->peb; |
43c190e7 AJ |
957 | release_object( process ); |
958 | } | |
43c190e7 AJ |
959 | } |
960 | ||
961 | /* set information about a process */ | |
962 | DECL_HANDLER(set_process_info) | |
963 | { | |
964 | struct process *process; | |
965 | ||
966 | if ((process = get_process_from_handle( req->handle, PROCESS_SET_INFORMATION ))) | |
967 | { | |
e55d5937 AJ |
968 | if (req->mask & SET_PROCESS_INFO_PRIORITY) process->priority = req->priority; |
969 | if (req->mask & SET_PROCESS_INFO_AFFINITY) | |
970 | { | |
971 | if (req->affinity != 1) set_error( STATUS_INVALID_PARAMETER ); | |
972 | else process->affinity = req->affinity; | |
973 | } | |
43c190e7 AJ |
974 | release_object( process ); |
975 | } | |
43c190e7 | 976 | } |
8b8828f5 AJ |
977 | |
978 | /* read data from a process address space */ | |
979 | DECL_HANDLER(read_process_memory) | |
980 | { | |
981 | struct process *process; | |
9caa71ee | 982 | size_t len = get_reply_max_size(); |
8b8828f5 | 983 | |
9caa71ee AJ |
984 | if (!(process = get_process_from_handle( req->handle, PROCESS_VM_READ ))) return; |
985 | ||
986 | if (len) | |
8b8828f5 | 987 | { |
0b492c70 | 988 | char *buffer = mem_alloc( len ); |
9caa71ee AJ |
989 | if (buffer) |
990 | { | |
0b492c70 | 991 | if (read_process_memory( process, req->addr, len, buffer )) |
9caa71ee | 992 | set_reply_data_ptr( buffer, len ); |
0b492c70 AJ |
993 | else |
994 | free( buffer ); | |
9caa71ee | 995 | } |
8b8828f5 | 996 | } |
9caa71ee | 997 | release_object( process ); |
8b8828f5 | 998 | } |
eef7025e AJ |
999 | |
1000 | /* write data to a process address space */ | |
1001 | DECL_HANDLER(write_process_memory) | |
1002 | { | |
1003 | struct process *process; | |
1004 | ||
1005 | if ((process = get_process_from_handle( req->handle, PROCESS_VM_WRITE ))) | |
1006 | { | |
9caa71ee | 1007 | size_t len = get_req_data_size(); |
959bbf8b AJ |
1008 | if (len) write_process_memory( process, req->addr, len, get_req_data() ); |
1009 | else set_error( STATUS_INVALID_PARAMETER ); | |
eef7025e AJ |
1010 | release_object( process ); |
1011 | } | |
1012 | } | |
05f0b71b AJ |
1013 | |
1014 | /* notify the server that a dll has been loaded */ | |
1015 | DECL_HANDLER(load_dll) | |
1016 | { | |
1017 | struct process_dll *dll; | |
1018 | struct file *file = NULL; | |
1019 | ||
a510a7e1 | 1020 | if (req->handle && !(file = get_file_obj( current->process, req->handle, FILE_READ_DATA ))) |
670711ef | 1021 | return; |
8081e5a1 | 1022 | |
aeb56605 AJ |
1023 | if ((dll = process_load_dll( current->process, file, req->base, |
1024 | get_req_data(), get_req_data_size() ))) | |
05f0b71b | 1025 | { |
aeb56605 | 1026 | dll->size = req->size; |
05f0b71b AJ |
1027 | dll->dbg_offset = req->dbg_offset; |
1028 | dll->dbg_size = req->dbg_size; | |
1029 | dll->name = req->name; | |
1030 | /* only generate event if initialization is done */ | |
9d802152 | 1031 | if (is_process_init_done( current->process )) |
05f0b71b AJ |
1032 | generate_debug_event( current, LOAD_DLL_DEBUG_EVENT, dll ); |
1033 | } | |
1034 | if (file) release_object( file ); | |
1035 | } | |
1036 | ||
1037 | /* notify the server that a dll is being unloaded */ | |
1038 | DECL_HANDLER(unload_dll) | |
1039 | { | |
1040 | process_unload_dll( current->process, req->base ); | |
1041 | } | |
c5e433a3 | 1042 | |
2359b575 EP |
1043 | /* retrieve information about a module in a process */ |
1044 | DECL_HANDLER(get_dll_info) | |
1045 | { | |
1046 | struct process *process; | |
1047 | ||
1048 | if ((process = get_process_from_handle( req->handle, PROCESS_QUERY_INFORMATION ))) | |
1049 | { | |
a9e0fb1b | 1050 | struct process_dll *dll = find_process_dll( process, req->base_address ); |
2359b575 | 1051 | |
a9e0fb1b | 1052 | if (dll) |
2359b575 | 1053 | { |
a9e0fb1b | 1054 | reply->size = dll->size; |
c5165714 | 1055 | reply->entry_point = NULL; /* FIXME */ |
a9e0fb1b | 1056 | if (dll->filename) |
2359b575 | 1057 | { |
a9e0fb1b AJ |
1058 | size_t len = min( dll->namelen, get_reply_max_size() ); |
1059 | set_reply_data( dll->filename, len ); | |
2359b575 EP |
1060 | } |
1061 | } | |
a9e0fb1b AJ |
1062 | else |
1063 | set_error( STATUS_DLL_NOT_FOUND ); | |
1064 | ||
2359b575 EP |
1065 | release_object( process ); |
1066 | } | |
1067 | } | |
1068 | ||
c5e433a3 AJ |
1069 | /* wait for a process to start waiting on input */ |
1070 | /* FIXME: only returns event for now, wait is done in the client */ | |
1071 | DECL_HANDLER(wait_input_idle) | |
1072 | { | |
1073 | struct process *process; | |
1074 | ||
9caa71ee | 1075 | reply->event = 0; |
c5e433a3 AJ |
1076 | if ((process = get_process_from_handle( req->handle, PROCESS_QUERY_INFORMATION ))) |
1077 | { | |
27909237 | 1078 | if (process->idle_event && process != current->process) |
9caa71ee AJ |
1079 | reply->event = alloc_handle( current->process, process->idle_event, |
1080 | EVENT_ALL_ACCESS, 0 ); | |
c5e433a3 AJ |
1081 | release_object( process ); |
1082 | } | |
1083 | } |