source: vendor/emx/current/src/os2/process.c

Last change on this file was 18, checked in by bird, 22 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 46.6 KB
Line 
1/* process.c -- Manage processes
2 Copyright (c) 1993-2000 by Eberhard Mattes
3
4This file is part of emx.
5
6emx is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11emx is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with emx; see the file COPYING. If not, write to
18the Free Software Foundation, 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA.
20
21As special exception, emx.dll can be distributed without source code
22unless it has been changed. If you modify emx.dll, this exception
23no longer applies and you must remove this paragraph from all source
24files for emx.dll. */
25
26
27#define INCL_DOSPROCESS
28#define INCL_DOSEXCEPTIONS
29#define INCL_DOSSEMAPHORES
30#define INCL_DOSSESMGR
31#define INCL_DOSMODULEMGR
32#define INCL_DOSQUEUES
33#define INCL_DOSERRORS
34#define INCL_DOSDEVIOCTL /* For files.h */
35#include <os2emx.h>
36#include <os2thunk.h>
37#include <emx/syscalls.h>
38#include <sys/signal.h>
39#include <sys/errno.h>
40#include <sys/wait.h>
41#include <sys/process.h>
42#include <sys/ptrace.h>
43#include <sys/uflags.h>
44#include "emxdll.h"
45#include "files.h"
46#include "clib.h"
47
48/* Maximum sizes of various buffers and tables. */
49
50#define MAX_THREADS 1024 /* Number of threads */
51#define MAX_PROCESSES 256 /* Number of child processes */
52#define FORK_REGMEM_MAX 256 /* Number of DLLs supported by fork() */
53#define FORK_REGDLL_MAX 256 /* Number of DLLs supported by fork() */
54
55/* Values for the `status' field of `struct process'. */
56
57#define PS_FREE 0 /* Empty slot */
58#define PS_PROCESS 1 /* Child is a child process */
59#define PS_SESSION 2 /* Child is a session */
60#define PS_ZOMBIE 3 /* Process ended, return code available */
61#define PS_ATTACH 4 /* Attached debuggee */
62
63/* Bits for the `flags' field of `struct process'. */
64
65#define PF_SYNC 0x0001 /* Synchronous child process */
66#define PF_DEBUG 0x0002 /* Child process is being debugged */
67#define PF_WAIT 0x0004 /* wait() status available */
68
69
70/* `process_table' is protected by a Mutex semaphore. These macros
71 are used for requesting and releasing that semaphore. */
72
73#define LOCK_PROCESS request_mutex (process_access)
74#define UNLOCK_PROCESS DosReleaseMutexSem (process_access)
75
76
77/* This structure describes a child process. */
78
79struct process
80{
81 ULONG status; /* Process status: PS_FREE etc. */
82 ULONG pid; /* Process ID */
83 ULONG sid; /* Session ID (for PS_SESSION) */
84 ULONG wait_tid; /* Thread ID for wait() */
85 ULONG wait_ret; /* Return value for wait() */
86 ULONG flags; /* Process flags: PF_SYNC */
87};
88
89/* This structure describes one registered memory area for fork(),
90 such as a DLL data segment. */
91
92struct fork_regmem
93{
94 ULONG start;
95 ULONG end;
96 HMODULE hmod;
97};
98
99/* This structure describes one registered DLL for fork(). */
100
101struct fork_regdll
102{
103 HMODULE hmod;
104};
105
106/* This table keeps track of child processes. */
107
108static struct process process_table[MAX_PROCESSES];
109
110/* For each thread, a pointer to its thread data block is stored in
111 this table indexed by thread IDs. */
112
113thread_data *threads[MAX_THREADS];
114
115/* `process_table' is protected by this Mutex semaphore. */
116
117static HMTX process_access;
118
119/* These event semaphores are posted when a process has been started
120 or has ended, respectively. */
121
122static HEV process_birth;
123static HEV process_death;
124
125/* These variables are true if cwait_thread() or qwait_thread(),
126 respectively, have been started. */
127
128static BYTE cwait_started;
129static BYTE qwait_started;
130
131/* These variables hold the thread IDs for cwait_thread() and
132 qwait_thread(), respectively. */
133
134static ULONG cwait_tid;
135static ULONG qwait_tid;
136
137/* The handle and name of the termination queue read by
138 qwait_thread(). */
139
140static HQUEUE termq_handle;
141static char termq_name[64];
142static char termq_created;
143
144/* Number of zombies. */
145
146int zombie_count;
147
148/* Number of registered memory areas for fork(). This variable must
149 be implicitely initialized to zero. */
150
151static int fork_regmem_count;
152
153/* This variable is made non-zero when `fork_regmem' overflows. This
154 variable must be implicitely initialized to zero. */
155
156static char fork_regmem_overflow;
157
158/* Table of registered memory areas for fork(). The first
159 `fork_regmem_count' entries are used. TODO: Use linked list. */
160
161static struct fork_regmem fork_regmem[FORK_REGMEM_MAX];
162
163/* Number of registered DLLs for fork(). This variable must be
164 implicitely initialized to zero. */
165
166static int fork_regdll_count;
167
168/* This variable is made non-zero when `fork_regdll' overflows. This
169 variable must be implicitely initialized to zero. */
170
171static char fork_regdll_overflow;
172
173/* Table of DLLs for fork(). The first `fork_regdll_count' entries
174 are used. TODO: Use linked list. */
175
176static struct fork_regdll fork_regdll[FORK_REGDLL_MAX];
177
178/* This variable is non-zero in the parent process during fork()'s
179 DosExecPgm call; it's used by ptrace() to decide whether
180 PTNPROC_FORK should be set or not. */
181
182BYTE ptrace_forking;
183
184/* If `ptrace_forking' is non-zero, the following variable will
185 contain the address at which the two processes will continue/start
186 after the fork. This is the return address of the system call. */
187
188ULONG ptrace_fork_addr;
189
190
191/* Initialize the process table etc. */
192
193void init_process (void)
194{
195 ULONG i;
196
197 /* Create the semaphores. */
198
199 create_mutex_sem (&process_access);
200 create_event_sem (&process_death, DC_SEM_SHARED);
201 create_event_sem (&process_birth, DC_SEM_SHARED);
202
203 /* There are no child processes. */
204
205 for (i = 0; i < MAX_PROCESSES; ++i)
206 process_table[i].status = PS_FREE;
207 zombie_count = 0;
208}
209
210
211/* Find free process table slot. Return a pointer to a free process
212 table entry. If no free slot is found, return NULL. It is assumed
213 that exclusive access to the process table has been gained. */
214
215static struct process *proc_new (void)
216{
217 ULONG i;
218
219 for (i = 0; i < MAX_PROCESSES; ++i)
220 if (process_table[i].status == PS_FREE)
221 return &process_table[i];
222 return NULL;
223}
224
225
226/* Find process table slot by process ID. Return a pointer to the
227 process table entry. If there's no such process in the process
228 table, return NULL. It is assumed that exclusive access to the
229 process table has been gained. */
230
231static struct process *proc_find (ULONG pid)
232{
233 ULONG i;
234
235 for (i = 0; i < MAX_PROCESSES; ++i)
236 if (process_table[i].pid == pid && process_table[i].status != PS_FREE)
237 return &process_table[i];
238 return NULL;
239}
240
241
242/* Return true if process PID is in the process table. */
243
244int proc_check (int pid)
245{
246 struct process *proc;
247
248 LOCK_PROCESS;
249 proc = proc_find (pid);
250 UNLOCK_PROCESS;
251 return proc != NULL;
252}
253
254
255/* A child has been started; notify cwait_thread(). */
256
257void child_started (void)
258{
259 DosPostEventSem (process_birth);
260}
261
262
263/* Child process ended, post the `process_death' semaphore and raise
264 SIGCLD. */
265
266static void proc_death (int thread1)
267{
268 struct signal_entry *p;
269
270 DosPostEventSem (process_death);
271 p = threads[1]->sig_table + SIGCLD;
272 if (p->handler != SIG_DFL && p->handler != SIG_IGN)
273 {
274 generate_signal (threads[1], SIGCLD);
275 if (get_tid () == 1)
276 deliver_pending_signals (threads[1], NULL, NULL);
277 }
278}
279
280
281/* Add a PS_ATTACH entry to the process table. Such an entry is used
282 when attaching to a debuggee which is not a child process. Return
283 errno. */
284
285ULONG proc_add_attach (ULONG pid)
286{
287 struct process *proc;
288
289 LOCK_PROCESS;
290 proc = proc_new ();
291 if (proc != NULL)
292 {
293 proc->status = PS_ATTACH;
294 proc->pid = pid;
295 proc->sid = 0; /* TODO */
296 proc->flags = PF_DEBUG;
297 }
298 UNLOCK_PROCESS;
299 return (proc == NULL ? EAGAIN : 0);
300}
301
302
303void proc_remove_attach (ULONG pid)
304{
305 struct process *proc;
306
307 LOCK_PROCESS;
308 proc = proc_find (pid);
309 if (proc->status == PS_ATTACH)
310 proc->status = PS_FREE;
311 UNLOCK_PROCESS;
312}
313
314
315/* A child process has ended. PID is the process ID of the process.
316 If PID is zero, use the session ID SID instead for identifying the
317 process. RET is the return value for wait(). Update the process
318 table and call proc_death(). */
319
320static void bury (ULONG pid, ULONG sid, ULONG ret)
321{
322 struct process *proc;
323 int i;
324
325 if (pid == 0)
326 {
327 /* Find the process ID for the head process of session SID. */
328
329 LOCK_PROCESS;
330 for (i = 0; i < MAX_PROCESSES; ++i)
331 if (process_table[i].sid == sid
332 && process_table[i].status == PS_SESSION)
333 {
334 pid = process_table[i].pid;
335 break;
336 }
337 UNLOCK_PROCESS;
338 }
339 if (pid != 0)
340 {
341 /* Turn the process into a zombie and call proc_death(). */
342
343 LOCK_PROCESS;
344 proc = proc_find (pid);
345 if (proc == NULL)
346 UNLOCK_PROCESS;
347 else
348 {
349 proc->wait_ret = ret;
350 proc->status = PS_ZOMBIE;
351 ++zombie_count;
352 UNLOCK_PROCESS;
353 proc_death (FALSE);
354 }
355 }
356}
357
358
359/* Exception handler for cwait_thread(). On termination of the
360 thread, cwait_tid will be set to zero. */
361
362static ULONG cwait_exception (EXCEPTIONREPORTRECORD *report,
363 EXCEPTIONREGISTRATIONRECORD *registration,
364 CONTEXTRECORD *context,
365 void *dummy)
366{
367 if (report->fHandlerFlags & (EH_EXIT_UNWIND|EH_UNWINDING))
368 return XCPT_CONTINUE_SEARCH;
369 switch (report->ExceptionNum)
370 {
371 case XCPT_ASYNC_PROCESS_TERMINATE: /* This is the correct one */
372 case XCPT_PROCESS_TERMINATE: /* OS/2 bug */
373 cwait_tid = 0;
374 break;
375 }
376 return XCPT_CONTINUE_SEARCH;
377}
378
379
380/* This thread handles termination of child processes started with
381 DosExec. */
382
383static void cwait_thread (ULONG arg)
384{
385 RESULTCODES ret_codes;
386 ULONG rc, pid, count, ret, tmp;
387 EXCEPTIONREGISTRATIONRECORD registration;
388
389 registration.prev_structure = NULL;
390 registration.ExceptionHandler = cwait_exception;
391 DosSetExceptionHandler (&registration);
392
393 arg = 0; /* keep the compiler happy */
394 for (;;)
395 {
396 DosResetEventSem (process_birth, &count);
397
398 /* Originally, DCWA_PROCESSTREE was used instead of DCW_PROCESS
399 to avoid reporting the death of a forked process that exec's
400 another process. Now, exec() in a process that has been
401 created by fork() stays alive until the exec'd process
402 terminates, so DCWA_PROCESSTREE is no longer required,
403 avoiding some anomalies. */
404
405 rc = DosWaitChild (DCWA_PROCESS, DCWW_WAIT, &ret_codes, &pid, 0);
406 if (rc == ERROR_WAIT_NO_CHILDREN)
407 DosWaitEventSem (process_birth, 5000);
408 else
409 {
410 ret = ret_codes.codeResult;
411 if (ret > 0xff)
412 ret = 0xff;
413 switch (ret_codes.codeTerminate)
414 {
415 case TC_EXIT:
416 tmp = ret << 8;
417 break;
418 case TC_HARDERROR:
419 case TC_KILLPROCESS:
420 tmp = SIGTERM;
421 break;
422 default:
423 tmp = SIGSEGV;
424 break;
425 }
426 bury (pid, 0, tmp);
427 }
428 }
429}
430
431
432/* Start cwait_thread() if not already done. */
433
434void start_cwait_thread (void)
435{
436 ULONG rc;
437
438 if (!cwait_started)
439 {
440 cwait_started = TRUE;
441 rc = DosCreateThread (&cwait_tid, cwait_thread, 0,
442 CREATE_READY | STACK_COMMITTED, 0x4000);
443 }
444}
445
446
447/* Exception handler for qwait_thread(). On termination of the
448 thread, qwait_tid will be set to zero. */
449
450static ULONG qwait_exception (EXCEPTIONREPORTRECORD *report,
451 EXCEPTIONREGISTRATIONRECORD *registration,
452 CONTEXTRECORD *context,
453 void *dummy)
454{
455 if (report->fHandlerFlags & (EH_EXIT_UNWIND|EH_UNWINDING))
456 return XCPT_CONTINUE_SEARCH;
457 switch (report->ExceptionNum)
458 {
459 case XCPT_ASYNC_PROCESS_TERMINATE: /* This is the correct one */
460 case XCPT_PROCESS_TERMINATE: /* OS/2 bug */
461 qwait_tid = 0;
462 break;
463 }
464 return XCPT_CONTINUE_SEARCH;
465}
466
467
468/* This thread handles termination of child processes started with
469 DosStartSession. */
470
471static void qwait_thread (ULONG arg)
472{
473 ULONG rc, len, sid, ret, tmp;
474 REQUESTDATA req;
475 BYTE priority;
476 ULONG *data;
477 EXCEPTIONREGISTRATIONRECORD registration;
478
479 registration.prev_structure = NULL;
480 registration.ExceptionHandler = qwait_exception;
481 DosSetExceptionHandler (&registration);
482
483 arg = 0; data = 0; /* keep the compiler happy */
484 for (;;)
485 {
486 rc = DosReadQueue (termq_handle, &req, &len, (void **)&data, 0,
487 DCWW_WAIT, &priority, 0);
488 if (rc == ERROR_QUE_INVALID_HANDLE)
489 DosExit (EXIT_THREAD, 0);
490 if (rc != 0)
491 error (rc, "DosReadQueue");
492 else if (req.ulData == 0)
493 {
494 /* Child session ended. */
495
496 sid = *data & 0xffff;
497 ret = (*data >> 16) & 0xffff;
498 DosFreeMem (data);
499 if (ret > 0xff)
500 ret = 0xff;
501 tmp = ret << 8;
502 bury (0, sid, tmp);
503 }
504 else
505 {
506 /* Child session created (SSF_TRACEOPT_TRACEALL only). */
507
508 DosFreeMem (data);
509 }
510 }
511}
512
513
514/* Start qwait_thread() if not already done. */
515
516static void start_qwait_thread (void)
517{
518 ULONG qn, rc;
519
520 if (!qwait_started)
521 {
522 qwait_started = TRUE;
523 LOCK_COMMON;
524 qn = ++queue_number;
525 UNLOCK_COMMON;
526 sprintf (termq_name, "/queues/emx/termq/%.8x.tqe", (unsigned)qn);
527#if 0
528 prt ("Queue name: ");
529 prt (termq_name);
530 prt ("\r\n");
531#endif
532 rc = DosCreateQueue (&termq_handle, QUE_FIFO | QUE_CONVERT_ADDRESS,
533 termq_name);
534 if (rc == 0)
535 termq_created = TRUE;
536 else
537 error (rc, "DosCreateQueue");
538 rc = DosCreateThread (&qwait_tid, qwait_thread, 0,
539 CREATE_READY | STACK_COMMITTED, 0x4000);
540 if (rc != 0) error (rc, "DosCreateThread");
541 }
542}
543
544
545/* This function is called on termination of emx.dll. */
546
547void term_process (void)
548{
549 ULONG i;
550
551 if (termq_created)
552 {
553 termq_created = FALSE;
554 DosCloseQueue (termq_handle);
555 }
556
557 /* Closing termq_handle should terminate qwait_thread(). Give it a
558 chance to terminate. */
559
560 for (i = 0; qwait_tid != 0 && i < 10; ++i)
561 DosSleep (10);
562
563 /* If qwait_thread() is still alive, kill it. */
564
565 if (qwait_tid != 0)
566 DosKillThread (qwait_tid);
567
568 /* Kill cwait_thread() if it is alive. */
569
570 if (cwait_tid != 0)
571 DosKillThread (cwait_tid);
572}
573
574
575/* Set the process status of the debuggee PID to WAIT_RET, to be
576 returned by wait(). Also set the thread ID to be returned by
577 wait() to TID. If END is true, turn the process into a zombie. */
578
579void debug_set_wait (ULONG pid, ULONG tid, ULONG wait_ret, ULONG end)
580{
581 ULONG i;
582 struct process *p;
583 static struct process *cache;
584
585 LOCK_PROCESS;
586 if (!(cache != NULL
587 && cache->pid == pid
588 && (cache->status == PS_PROCESS || cache->status == PS_SESSION
589 || cache->status == PS_ATTACH)))
590 {
591 p = process_table; cache = NULL;
592 for (i = 0; i < MAX_PROCESSES; ++i, ++p)
593 if (p->pid == pid
594 && (p->status == PS_PROCESS || p->status == PS_SESSION
595 || p->status == PS_ATTACH))
596 {
597 cache = p;
598 break;
599 }
600 }
601 if (cache != NULL)
602 {
603 cache->wait_ret = wait_ret;
604 cache->wait_tid = tid;
605 if (end)
606 {
607 cache->status = PS_ZOMBIE;
608 ++zombie_count;
609 }
610 else
611 cache->flags |= PF_WAIT;
612 }
613 UNLOCK_PROCESS;
614 if (cache != NULL && end)
615 proc_death (TRUE);
616
617}
618
619
620/* Wait for termination of process *PPID. Update the PID (perhaps
621 including the TID) pointed to by PPID. Store the termination
622 status (with the return code in bits 8..15, for wait()) to
623 *RET_CODE. Return 0 on success. Otherwise, return the errno code
624 (for instance EINTR when interrupted, unless IGNORE_INT is true).
625 If NOHANG is true, wait_for() returns EAGAIN in case it would
626 block. */
627
628static ULONG wait_for (ULONG *ppid, ULONG *ret_code, ULONG nohang,
629 ULONG ignore_int)
630{
631 ULONG rc, count, i, children;
632 struct process *p;
633
634 for (;;)
635 {
636 DosResetEventSem (process_death, &count);
637
638 /* Note that LOCK_PROCESS ignores interrupts. */
639
640 do
641 {
642 rc = DosRequestMutexSem (process_access, -1);
643 } while (rc == ERROR_SEM_OWNER_DIED
644 || (rc == ERROR_INTERRUPT && ignore_int));
645 if (rc != 0) return set_error (rc);
646
647 p = process_table; children = 0;
648 for (i = 0; i < MAX_PROCESSES; ++i, ++p)
649 switch (p->status)
650 {
651 case PS_ZOMBIE:
652 if (p->pid == *ppid)
653 {
654 if (!(p->flags & PF_SYNC))
655 p->status = PS_FREE;
656 --zombie_count;
657 *ret_code = p->wait_ret;
658 UNLOCK_PROCESS;
659 return 0;
660 }
661 break;
662 case PS_FREE:
663 break;
664 default:
665 if (p->pid == *ppid)
666 {
667 if (p->flags & PF_WAIT)
668 {
669 p->flags &= ~PF_WAIT;
670 *ret_code = p->wait_ret;
671 *ppid = PTRACE_PIDTID (p->pid, p->wait_tid);
672 UNLOCK_PROCESS;
673 return 0;
674 }
675 ++children;
676 }
677 break;
678 }
679 UNLOCK_PROCESS;
680 if (children == 0)
681 return ECHILD;
682 if (nohang)
683 return EAGAIN;
684 rc = DosWaitEventSem (process_death, SEM_INDEFINITE_WAIT);
685 if (rc != 0 && !(rc == ERROR_INTERRUPT && ignore_int))
686 return set_error (rc);
687 }
688}
689
690
691/* Wait for termination of any child process. Store the termination
692 status (with the return code in bits 8..15, for wait()) to
693 *RET_CODE. Return 0 on success. Otherwise, return the errno code
694 (for instance EINTR when interrupted). If NOHANG is true,
695 wait_any() returns EAGAIN in case it would block. */
696
697static ULONG wait_any (ULONG *ppid, ULONG *ret_code, ULONG nohang)
698{
699 ULONG rc, count, i, children;
700 struct process *p;
701
702 for (;;)
703 {
704 DosResetEventSem (process_death, &count);
705
706 /* Note that LOCK_PROCESS ignores interrupts. */
707
708 do
709 {
710 rc = DosRequestMutexSem (process_access, -1);
711 } while (rc == ERROR_SEM_OWNER_DIED);
712 if (rc != 0) return set_error (rc);
713
714 p = process_table; children = 0;
715 for (i = 0; i < MAX_PROCESSES; ++i, ++p)
716 switch (p->status)
717 {
718 case PS_ZOMBIE:
719 if (!(p->flags & PF_SYNC))
720 p->status = PS_FREE;
721 --zombie_count;
722 *ppid = p->pid;
723 *ret_code = p->wait_ret;
724 UNLOCK_PROCESS;
725 return 0;
726 case PS_FREE:
727 break;
728 default:
729 if (p->flags & PF_WAIT)
730 {
731 p->flags &= ~PF_WAIT;
732 *ppid = PTRACE_PIDTID (p->pid, p->wait_tid);
733 *ret_code = p->wait_ret;
734 UNLOCK_PROCESS;
735 return 0;
736 }
737 ++children;
738 break;
739 }
740 UNLOCK_PROCESS;
741 if (children == 0)
742 return ECHILD;
743 if (nohang)
744 return EAGAIN;
745 rc = DosWaitEventSem (process_death, SEM_INDEFINITE_WAIT);
746 if (rc != 0) return set_error (rc);
747 }
748}
749
750
751/* This function implements the __wait() system call. */
752
753ULONG do_wait (ULONG *ptermstatus, ULONG *errnop)
754{
755 ULONG rc, pid;
756
757 rc = wait_any (&pid, ptermstatus, FALSE);
758 *errnop = rc;
759 return (rc == 0 ? pid : -1);
760}
761
762
763/* This function implements the __waitpid() system call. */
764
765ULONG do_waitpid (ULONG pid, ULONG opt, ULONG *ptermstatus, ULONG *errnop)
766{
767 ULONG rc;
768
769 if (pid == (ULONG)-1)
770 rc = wait_any (&pid, ptermstatus, opt & WNOHANG);
771 else
772 rc = wait_for (&pid, ptermstatus, opt & WNOHANG, FALSE);
773 if (rc == EAGAIN)
774 {
775 *errnop = 0;
776 return 0;
777 }
778 *errnop = rc;
779 return (rc == 0 ? pid : -1);
780}
781
782#define ADDCHR(c) do { \
783 if (arg_buf != NULL) \
784 { \
785 if (size + 1 > arg_buf_size) return -E2BIG; /* Oops */ \
786 arg_buf[size] = (char)c; \
787 } \
788 size += 1; \
789 } while (0)
790
791#define ADDCHRS(c,n) do { \
792 if (arg_buf != NULL) \
793 { \
794 if (size + (n) > arg_buf_size) return -E2BIG; /* Oops */ \
795 memset (arg_buf + size, c, n); \
796 } \
797 size += n; \
798 } while (0)
799
800#define ADDSTR(s,n) do { \
801 if (arg_buf != NULL) \
802 { \
803 if (size + (n) > arg_buf_size) return -E2BIG; /* Oops */ \
804 memcpy (arg_buf + size, s, n); \
805 } \
806 size += n; \
807 } while (0)
808
809/* Build the command line for a child process. Return the number of
810 bytes required/used for the buffer or -errno. Well, there's not
811 much point in allocating the buffer dynamically as OS/2 limits the
812 size to 64KB anyway. */
813
814static LONG spawn_args (char *arg_buf, ULONG arg_buf_size,
815 const char *arg_ptr,
816 ULONG argc, ULONG mode32, UCHAR session,
817 const char *prog_name)
818{
819 size_t len, size;
820 const char *src, *s, *base;
821 int i, quote, bs, method;
822
823 base = (const char *)_getname (prog_name);
824 method = 0;
825 if (stricmp (base, "cmd.exe") == 0 || stricmp (base, "4os2.exe") == 0)
826 method = 1;
827 src = arg_ptr;
828 size = 0;
829 if (argc > 0)
830 {
831 ++src; /* skip flags byte */
832 len = strlen (src) + 1;
833 if (!session)
834 ADDSTR (src, len);
835 src += len;
836 }
837 for (i = 1; i < argc; ++i)
838 {
839 if (i > 1)
840 ADDCHR (' ');
841 ++src; /* skip flags byte */
842 quote = FALSE;
843 if (*src == 0)
844 quote = TRUE;
845 else if (opt_quote || (mode32 & P_QUOTE))
846 {
847 if (src[0] == '@' && src[1] != 0)
848 quote = TRUE;
849 else
850 for (s = src; *s != 0; ++s)
851 if (*s == '?' || *s == '*')
852 {
853 quote = TRUE;
854 break;
855 }
856 }
857 if (!quote)
858 for (s = src; *s != 0; ++s)
859 if (*s == ' ' || *s == '\t' || (*s == '"' && method == 1))
860 {
861 quote = TRUE;
862 break;
863 }
864 if (quote)
865 ADDCHR ('"');
866 bs = 0;
867 while (*src != 0)
868 {
869 if (*src == '"' && method == 0)
870 {
871 ADDCHRS ('\\', bs+1);
872 bs = 0;
873 }
874 else if (*src == '\\' && method == 0)
875 ++bs;
876 else
877 bs = 0;
878 ADDCHR (*src);
879 ++src;
880 }
881 if (quote)
882 {
883 ADDCHRS ('\\', bs);
884 bs = 0;
885 ADDCHR ('"');
886 }
887 ++src;
888 }
889 ADDCHR (0);
890 if (!session && argc > 0 && (mode32 & P_TILDE))
891 {
892 ADDCHR ('~');
893 src = arg_ptr;
894 for (i = 0; i < argc; ++i)
895 {
896 ++src; /* skip flags byte */
897 if (*src == 0 || *src == '~')
898 ADDCHR ('~');
899 len = strlen (src) + 1;
900 ADDSTR (src, len);
901 src += len;
902 }
903 }
904 ADDCHR (0);
905 return size;
906}
907
908
909/* Build environment for child process: Set the `_emx_sig' environment
910 variable to let the child process inherit signal settings. Store a
911 pointer to the new environment, allocated with private_alloc(), to
912 *DST. SRC points to the original environment to be passed to the
913 child process. SRC_SIZE is the size of that environment. */
914
915static ULONG spawn_env (char **dst, const char *src, ULONG src_size)
916{
917 void *pv;
918 char *env;
919 ULONG rc, size;
920 char temp[40];
921 unsigned sigset;
922 int len, i;
923 thread_data *td;
924
925 /* Do some sanity checks to avoid crashing. */
926
927 if (src_size < 1)
928 return EINVAL;
929 if (src[src_size-1] != 0)
930 return EINVAL;
931 if (src_size >= 2 && src[src_size-2] != 0)
932 return EINVAL;
933
934 /* Build the string to be added to the environment. Let the child
935 process inherit the signal settings of the *current* thread. */
936
937 td = get_thread ();
938 sigset = 0;
939 for (i = 1; i < NSIG; ++i)
940 if (td->sig_table[i].handler == SIG_IGN)
941 sigset |= (1 << i);
942 len = sprintf (temp, "_emx_sig=%.8x:%.8x", (unsigned)my_pid, sigset);
943
944 /* Allocate memory for the new environment. */
945
946 size = src_size + len + 1;
947 rc = private_alloc (&pv, size);
948 if (rc != 0) return set_error (rc);
949 *dst = env = (char *)pv;
950
951 /* Copy the environment, dropping `_emx_sig' if present. */
952
953 while (*src != 0)
954 {
955 if (*src == '_' && strncmp (src, temp, 9) == 0)
956 while (*src++ != 0)
957 ;
958 else
959 {
960 while (*src != 0)
961 *env++ = *src++;
962 *env++ = *src++;
963 }
964 }
965
966 /* Add the new variable. */
967
968 memcpy (env, temp, len + 1);
969 env[len+1] = 0;
970
971 return 0;
972}
973
974
975/* When calling exec() in a fork()ed process, we do not exit after
976 starting the process as our parent (who forked us) cannot retrieve
977 the return code of our child process. Wait for termination of the
978 child process, passing on signals. After termination of the child
979 process, return its return code. */
980
981static void spawn_fork_exec (ULONG pid)
982{
983 ULONG wait_ret, i;
984
985#if 0
986 if (get_tid () != 1)
987 {
988 /* TODO: transfer control to the main thread and call
989 spawn_fork_exec() there */
990 }
991#endif
992
993 stop_alarm ();
994
995 /* TODO: stop all user threads */
996
997 fork_exec_pid = pid;
998
999 /* Close all handles but socket handles. We can't close socket
1000 handles as that would create a time window. */
1001
1002 for (i = 0; i < handle_count; ++i)
1003 if ((files[i].flags & (HF_OPEN|HF_SOCKET)) == HF_OPEN)
1004 close_handle (i);
1005
1006 wait_for (&pid, &wait_ret, FALSE, TRUE);
1007 quit (wait_ret >> 8);
1008}
1009
1010
1011/* This function implements the __spawnve() system call. */
1012
1013ULONG do_spawn (struct _new_proc *np, ULONG *result)
1014{
1015 STARTDATA sd;
1016 struct process *proc;
1017 ULONG rc, pid, sid;
1018 RESULTCODES ret_codes;
1019 ULONG exec_type;
1020 ULONG mode32;
1021 UCHAR mode8;
1022 long arg_buf_size;
1023 char fname[512];
1024 char obj_buf[40];
1025 BYTE session;
1026 PVOID arg_buf;
1027 char *env_ptr = NULL;
1028
1029 /* Kludge alert: Unfortunately, the `mode' member was made only 16
1030 bits wide originally. Bit 15 is used to indicate that the
1031 `mode2' member is used, which holds the upper 16 bits of MODE.
1032 This allows using emx 0.8 applications with emx 0.9 as emx
1033 doesn't look at `mode2' unless bit 15 of `mode' is set. Of
1034 course, this fails if an (old) application program sets bit 15 of
1035 MODE. */
1036
1037 mode32 = np->mode;
1038 if (np->mode & 0x8000)
1039 mode32 |= np->mode2 << 16;
1040
1041 mode8 = np->mode & 0xff;
1042 switch (mode8)
1043 {
1044 case P_NOWAIT:
1045 case P_WAIT:
1046 case P_OVERLAY:
1047 session = FALSE;
1048 exec_type = EXEC_ASYNCRESULT;
1049 break;
1050 case P_DETACH:
1051 session = FALSE;
1052 exec_type = EXEC_BACKGROUND;
1053 break;
1054 case P_DEBUG:
1055 if ((mode32 & P_DEBUGDESC)
1056 && (uflags & _UF_PTRACE_MODEL) == _UF_PTRACE_STANDARD)
1057 return EINVAL;
1058 exec_type = (mode32 & P_DEBUGDESC) ? EXEC_ASYNCRESULTDB : EXEC_TRACE;
1059 session = !(debug_same_sess || (mode32 & P_NOSESSION));
1060 if (session & (mode32 & P_UNRELATED))
1061 return EINVAL;
1062 break;
1063 case P_SESSION:
1064 case P_PM:
1065 exec_type = 0;
1066 session = TRUE;
1067 break;
1068 default:
1069 return EINVAL;
1070 }
1071 truncate_name (fname, (char *)np->fname_off);
1072 _defext (fname, "exe");
1073
1074 /* Compute the size of the argument buffer. */
1075
1076 arg_buf_size = spawn_args (NULL, 0, (const char *)np->arg_off,
1077 np->arg_count, mode32, session, fname);
1078 if (arg_buf_size < 0)
1079 return -arg_buf_size; /* errno */
1080 if (arg_buf_size > 65536)
1081 return E2BIG;
1082
1083 /* The buffer pointed to by the ArgPointer argument of DosExecPgm
1084 must not cross a 64KByte boundary. Note that objects are aligned
1085 on a 64KByte boundary. */
1086
1087 rc = DosAllocMem (&arg_buf, (ULONG)arg_buf_size,
1088 PAG_READ | PAG_WRITE | PAG_COMMIT);
1089 if (rc != 0) return ENOMEM;
1090
1091 /* Build the argument buffer. */
1092
1093 arg_buf_size = spawn_args (arg_buf, (ULONG)arg_buf_size,
1094 (const char *)np->arg_off,
1095 np->arg_count, mode32, session, fname);
1096 if (arg_buf_size < 0)
1097 {
1098 DosFreeMem (arg_buf);
1099 return -arg_buf_size; /* errno */
1100 }
1101
1102 rc = spawn_env (&env_ptr, (const char *)np->env_off, np->env_size);
1103 if (rc != 0)
1104 {
1105 DosFreeMem (arg_buf);
1106 return rc;
1107 }
1108 LOCK_PROCESS;
1109 proc = proc_new ();
1110 if (proc == 0)
1111 {
1112 UNLOCK_PROCESS;
1113 DosFreeMem (arg_buf);
1114 private_free (env_ptr);
1115 return EAGAIN;
1116 }
1117 if (session)
1118 {
1119 if (!(mode32 & P_UNRELATED))
1120 start_qwait_thread ();
1121 sd.Length = 50;
1122 sd.Related = ((mode32 & P_UNRELATED)
1123 ? SSF_RELATED_INDEPENDENT : SSF_RELATED_CHILD);
1124 sd.PgmTitle = 0;
1125 sd.PgmName = fname;
1126 sd.PgmInputs = arg_buf;
1127 sd.TermQ = termq_name;
1128 sd.Environment = env_ptr;
1129 sd.InheritOpt = SSF_INHERTOPT_PARENT;
1130 sd.IconFile = 0;
1131 sd.PgmHandle = 0;
1132 sd.InitXPos = 0;
1133 sd.InitYPos = 0;
1134 sd.InitXSize = 0;
1135 sd.InitYSize = 0;
1136 sd.Reserved = 0;
1137 sd.ObjectBuffer = 0;
1138 sd.ObjectBuffLen = 0;
1139 switch (mode32 & 0x0f00)
1140 {
1141 case P_FULLSCREEN:
1142 sd.SessionType = SSF_TYPE_FULLSCREEN;
1143 break;
1144 case P_WINDOWED:
1145 sd.SessionType = SSF_TYPE_WINDOWABLEVIO;
1146 break;
1147 default:
1148 if (mode8 == P_PM)
1149 sd.SessionType = SSF_TYPE_PM;
1150 else
1151 sd.SessionType = SSF_TYPE_DEFAULT;
1152 break;
1153 }
1154 switch (mode32 & 0xf00)
1155 {
1156 case P_MINIMIZE:
1157 sd.PgmControl = SSF_CONTROL_MINIMIZE;
1158 break;
1159 case P_MAXIMIZE:
1160 sd.PgmControl = SSF_CONTROL_MAXIMIZE;
1161 break;
1162 default:
1163 sd.PgmControl = 0;
1164 break;
1165 }
1166 if (mode32 & P_NOCLOSE)
1167 sd.PgmControl |= SSF_CONTROL_NOAUTOCLOSE;
1168 if (mode8 != P_DEBUG)
1169 sd.TraceOpt = SSF_TRACEOPT_NONE;
1170 else if (mode32 & P_DEBUGDESC)
1171 sd.TraceOpt = SSF_TRACEOPT_TRACEALL;
1172 else
1173 sd.TraceOpt = SSF_TRACEOPT_TRACE;
1174 sd.FgBg = (mode32 & P_BACKGROUND ? SSF_FGBG_BACK : SSF_FGBG_FORE);
1175 rc = DosStartSession (&sd, &sid, &pid);
1176 DosFreeMem (arg_buf);
1177 private_free (env_ptr);
1178 if (rc != 0 && rc != ERROR_SMG_START_IN_BACKGROUND)
1179 {
1180 UNLOCK_PROCESS;
1181 return set_error (rc);
1182 }
1183 if (!(mode32 & P_UNRELATED))
1184 {
1185 proc->status = PS_SESSION;
1186 proc->pid = pid;
1187 proc->sid = sid;
1188 proc->wait_ret = 0;
1189 proc->flags = 0;
1190 if (mode8 == P_DEBUG)
1191 {
1192 proc->flags |= PF_DEBUG | PF_WAIT;
1193 proc->wait_ret = 0x7f + (SIGTRAP << 8);
1194 proc->wait_tid = 1;
1195 }
1196 }
1197 UNLOCK_PROCESS;
1198 if (!(mode32 & P_UNRELATED))
1199 child_started ();
1200 if (mode8 == P_DEBUG)
1201 {
1202 rc = spawn_debug (pid, sid);
1203 if (rc != 0) return rc;
1204 }
1205 *result = pid;
1206 return 0;
1207 }
1208 else
1209 {
1210 kbd_stop ();
1211 start_cwait_thread ();
1212 rc = DosExecPgm (obj_buf, sizeof (obj_buf),
1213 exec_type, arg_buf, env_ptr,
1214 &ret_codes, fname);
1215 DosFreeMem (arg_buf);
1216 private_free (env_ptr);
1217 if (rc != 0)
1218 {
1219 UNLOCK_PROCESS;
1220 kbd_restart ();
1221 return set_error (rc);
1222 }
1223 UNLOCK_COMMON;
1224 pid = ret_codes.codeTerminate;
1225 proc->status = PS_PROCESS;
1226 proc->pid = pid;
1227 proc->sid = 0;
1228 proc->wait_ret = 0;
1229 proc->flags = 0;
1230 if (mode8 == P_WAIT)
1231 proc->flags |= PF_SYNC;
1232 if (mode8 == P_DEBUG)
1233 {
1234 proc->flags |= PF_DEBUG | PF_WAIT;
1235 proc->wait_ret = 0x7f + (SIGTRAP << 8);
1236 proc->wait_tid = 1;
1237 }
1238 UNLOCK_PROCESS;
1239 child_started ();
1240 switch (mode8)
1241 {
1242 case P_DEBUG:
1243 rc = spawn_debug (pid, 0);
1244 kbd_restart ();
1245 if (rc != 0) return rc;
1246 *result = pid;
1247 return 0;
1248 case P_OVERLAY:
1249 if (fork_flag)
1250 spawn_fork_exec (pid);
1251 quit (0);
1252 case P_NOWAIT:
1253 case P_DETACH:
1254 *result = pid;
1255 kbd_restart ();
1256 return 0;
1257 default:
1258 rc = wait_for (&pid, result, FALSE, TRUE);
1259 *result = (*result >> 8) & 0xff;
1260 proc->status = PS_FREE;
1261 kbd_restart ();
1262 return rc;
1263 }
1264 }
1265}
1266
1267
1268/* Copy the name of the EXE file to BUF. Copy at most BUFSIZE bytes,
1269 including the terminating null character. */
1270
1271int execname (char *buf, ULONG bufsize)
1272{
1273 size_t len;
1274
1275 if (bufsize == 0)
1276 return -1;
1277
1278 if (exe_name[0] == 0)
1279 {
1280 *buf = 0;
1281 return -1;
1282 }
1283 len = strlen (exe_name);
1284 if (len >= bufsize)
1285 {
1286 *buf = 0;
1287 return -1;
1288 }
1289 memcpy (buf, exe_name, len + 1);
1290 return 0;
1291}
1292
1293
1294/* Create and initialize thread data block for a new thread. TID is
1295 the thread ID. Return 0 on success, -1 on failure. Store the
1296 error number to ERRNOP unless ERRNOP is NULL. */
1297
1298ULONG new_thread (ULONG tid, int *errnop)
1299{
1300 thread_data *td;
1301 ULONG rc;
1302 int i;
1303
1304 if (tid >= MAX_THREADS)
1305 {
1306 if (errnop != NULL) *errnop = EAGAIN;
1307 return -1;
1308 }
1309
1310 /* Silently ignore multiple initializations of the same thread. */
1311
1312 if (threads[tid] != NULL)
1313 return 0;
1314
1315 rc = private_alloc ((void **)&td, sizeof (thread_data));
1316 if (rc != 0)
1317 {
1318 if (errnop != NULL) *errnop = set_error (rc);
1319 return -1;
1320 }
1321 threads[tid] = td;
1322 td->last_sys_errno = 0; /* No error occured yet */
1323 td->prev_sys_errno = 0; /* No error occured yet */
1324 td->sig_blocked = 0; /* No signals are blocked */
1325 td->sig_prev_blocked = 0; /* Ditto */
1326 td->sig_pending = 0; /* No signals are pending */
1327 for (i = 0; i < NSIG; ++i)
1328 {
1329 td->sig_table[i].handler = SIG_DFL;
1330 td->sig_table[i].sa_mask = 0;
1331 td->sig_table[i].sa_flags = SA_ACK;
1332 }
1333 td->find_handle = HDIR_CREATE; /* Handle not open */
1334 td->find_next = NULL;
1335 td->find_count = 0;
1336 if (errnop != NULL) *errnop = 0;
1337 return 0;
1338}
1339
1340
1341/* Deallocate the thread data block of thread TID. Return 0 on
1342 success, -1 on failure. Store the error number to *ERRNOP. */
1343
1344ULONG end_thread (ULONG tid, int *errnop)
1345{
1346 thread_data *td;
1347 ULONG rc;
1348
1349 if (tid >= MAX_THREADS || threads[tid] == NULL)
1350 {
1351 *errnop = EINVAL;
1352 return -1;
1353 }
1354 td = threads[tid];
1355 if (td->find_handle != HDIR_CREATE)
1356 DosFindClose (td->find_handle);
1357 rc = private_free (td);
1358 threads[tid] = NULL;
1359 if (rc != 0)
1360 {
1361 *errnop = set_error (rc);
1362 return -1;
1363 }
1364 *errnop = 0;
1365 return 0;
1366}
1367
1368
1369/* Check if the direct child process PID is alive. Return FALSE if
1370 the child process is not alive. */
1371
1372static int alive (ULONG pid)
1373{
1374 ULONG rc;
1375
1376 rc = DosSetPriority (PRTYS_PROCESS, PRTYC_NOCHANGE, 0, pid);
1377 return (rc == 0 ? TRUE : FALSE);
1378}
1379
1380
1381/* Register a memory area for fork(). This is used for copying DLL
1382 data segments. */
1383
1384void fork_register_mem (ULONG start, ULONG end, HMODULE hmod)
1385{
1386 if (fork_regmem_count >= FORK_REGMEM_MAX)
1387 fork_regmem_overflow = TRUE;
1388 else
1389 {
1390 fork_regmem[fork_regmem_count].start = start;
1391 fork_regmem[fork_regmem_count].end = end;
1392 fork_regmem[fork_regmem_count].hmod = hmod;
1393 fork_regmem_count += 1;
1394 }
1395}
1396
1397
1398/* Register a DLL for fork(). This is used for loading DLLs in the
1399 child process. */
1400
1401void fork_register_dll (HMODULE hmod)
1402{
1403 /* If the module handle is already registered, the DLL was unloaded
1404 and another one loaded. (We don't record unloading of DLLs.)
1405 Here, we are only interested in the module handles, no matter
1406 what DLL they are associated with. */
1407
1408 if (fork_dll_registered (hmod))
1409 return;
1410
1411 if (fork_regdll_count >= FORK_REGDLL_MAX)
1412 fork_regdll_overflow = TRUE;
1413 else
1414 {
1415 fork_regdll[fork_regdll_count].hmod = hmod;
1416 fork_regdll_count += 1;
1417 }
1418}
1419
1420/* Return TRUE iff the DLL HMOD is registered. */
1421
1422int fork_dll_registered (HMODULE hmod)
1423{
1424 int i;
1425
1426 for (i = 0; i < fork_regdll_count; ++i)
1427 if (fork_regdll[i].hmod == hmod)
1428 return TRUE;
1429 return FALSE;
1430}
1431
1432
1433/* Remove entries for unloaded modules. */
1434
1435static void fork_reg_cleanup (void)
1436{
1437 int i, j;
1438
1439 if (fork_regdll_overflow || fork_regmem_overflow)
1440 {
1441 /* We cannot recover from overflow. Just keep the deleted
1442 entries. */
1443 return;
1444 }
1445
1446 /* Remove deleted entries by moving the remaining entries down. We
1447 keep the order of the entries in an attempt to cope with
1448 unloading and reloading modules. (Actually, the order might be
1449 irrelevant, as loading a DLL automatically causes all DLLs it
1450 depends on to be loaded.) */
1451
1452 j = 0;
1453 for (i = 0; i < fork_regdll_count; ++i)
1454 if (fork_regdll[i].hmod != 0)
1455 {
1456 if (i != j)
1457 fork_regdll[j] = fork_regdll[i];
1458 ++j;
1459 }
1460 fork_regdll_count = j;
1461
1462 j = 0;
1463 for (i = 0; i < fork_regmem_count; ++i)
1464 if (fork_regmem[i].hmod != 0)
1465 {
1466 if (i != j)
1467 fork_regmem[j] = fork_regmem[i];
1468 ++j;
1469 }
1470 fork_regmem_count = j;
1471}
1472
1473
1474/* Wait for acknowledge from child process. */
1475
1476ULONG fork_wait (ULONG ack_sem, ULONG pid)
1477{
1478 ULONG rc;
1479
1480 for (;;)
1481 {
1482 rc = DosWaitEventSem (ack_sem, 500);
1483 if (rc == ERROR_TIMEOUT)
1484 {
1485 if (!alive (pid))
1486 return ERROR_INVALID_PROCID; /* -> ESRCH */
1487 }
1488 else if (rc != ERROR_INTERRUPT)
1489 return rc;
1490 }
1491}
1492
1493
1494/* Send a data block to the child process. */
1495
1496ULONG fork_send (ULONG req_sem, ULONG ack_sem, ULONG pid)
1497{
1498 ULONG rc, post_count;
1499
1500 rc = DosResetEventSem (ack_sem, &post_count);
1501 if (rc != 0 && rc != ERROR_ALREADY_RESET)
1502 return rc;
1503 rc = DosPostEventSem (req_sem);
1504 if (rc != 0)
1505 return rc;
1506 return fork_wait (ack_sem, pid);
1507}
1508
1509
1510/* Make the child process load a DLL. */
1511
1512ULONG fork_dll (fork_data *shmem, ULONG req_sem, ULONG ack_sem, ULONG pid,
1513 HMODULE hmod)
1514{
1515 ULONG rc;
1516 int i;
1517
1518 shmem->dll.req_code = FORK_REQ_DLL;
1519 shmem->dll.hmod = hmod;
1520 rc = DosQueryModuleName (hmod, sizeof (shmem->dll.path), shmem->dll.path);
1521 if (rc == 0)
1522 {
1523 /* The module handle is valid. Now check if all the registered
1524 memory regions are readable. If the process has unloaded a
1525 DLL which is still loaded by another process the module
1526 handle will remain valid! */
1527
1528 for (i = 0; i < fork_regmem_count; ++i)
1529 if (fork_regmem[i].hmod == hmod
1530 && !verify_memory (fork_regmem[i].start,
1531 fork_regmem[i].end - fork_regmem[i].start))
1532 {
1533 rc = ERROR_INVALID_HANDLE;
1534 break;
1535 }
1536 }
1537
1538 if (rc == ERROR_INVALID_HANDLE)
1539 {
1540 int i;
1541
1542 /* We should get exactly one match. */
1543
1544 for (i = 0; i < fork_regdll_count; ++i)
1545 if (fork_regdll[i].hmod == hmod)
1546 fork_regdll[i].hmod = 0;
1547
1548 /* We should get 0 through 2 matches. */
1549
1550 for (i = 0; i < fork_regmem_count; ++i)
1551 if (fork_regmem[i].hmod == hmod)
1552 fork_regmem[i].hmod = 0;
1553
1554 return 0;
1555 }
1556 if (rc != 0)
1557 return rc;
1558 return fork_send (req_sem, ack_sem, pid);
1559}
1560
1561
1562/* Send a block of memory to the child process. The memory is owned
1563 by DLL HMOD (if HMOD is not NULLHANDLE) or by the base process (if
1564 HMOD is NULLHANDLE). This assumes that no DLL has module handle
1565 NULLHANDLE. */
1566
1567ULONG fork_mem (fork_data *shmem, ULONG req_sem, ULONG ack_sem, ULONG pid,
1568 ULONG base, ULONG top, HMODULE hmod)
1569{
1570 ULONG rc, count, chunk;
1571
1572 count = top - base;
1573 while (count != 0)
1574 {
1575 shmem->mem.req_code = FORK_REQ_MEM;
1576 shmem->mem.address = base;
1577 shmem->mem.shared = shmem->mem.buf;
1578 shmem->mem.hmod = hmod;
1579 chunk = FORK_OBJ_SIZE - (sizeof (shmem->mem) - 1);
1580 if (count < chunk)
1581 chunk = count;
1582 shmem->mem.count = chunk;
1583 memcpy (shmem->mem.buf, (void *)base, chunk);
1584 rc = fork_send (req_sem, ack_sem, pid);
1585 if (rc != 0)
1586 return rc;
1587 base += chunk; count -= chunk;
1588 }
1589 return 0;
1590}
1591
1592
1593/* Implementation of fork().
1594
1595 The following string is passed in the 3rd argument string:
1596
1597 ^mmmmmmmm pppppppp
1598
1599 mmmmmmmm Base address of shared memory object (hexadecimal)
1600 pppppppp Process ID of parent process
1601
1602 Note: emx_syscall (in emxdll.asm) pushes and pops all registers,
1603 including EBX, ESI, and EDI. As the stack is copied to the child
1604 process, the child process will inherit register variables. */
1605
1606#define FORK_RSC_PROC 0x01
1607#define FORK_RSC_MEM 0x02
1608#define FORK_RSC_REQ 0x04
1609#define FORK_RSC_ACK 0x08
1610#define FORK_RSC_SOCK 0x10
1611#define FORK_RSC_FILEIO 0x20
1612
1613int do_fork (syscall_frame *frame, ULONG *errnop)
1614{
1615 static char arg_buf[64]; /* Must not cross a 64KByte boundary */
1616 char *p;
1617 char obj_buf[32];
1618 RESULTCODES ret_codes;
1619 ULONG rc, req_sem, ack_sem, stk_low, pid, resources, *sp, ip;
1620 fork_data *shmem;
1621 struct process *pte;
1622 thread_data *td;
1623 int result, i;
1624
1625 resources = 0; result = -1;
1626 if (heap_obj_count > 1 || fork_regmem_overflow || fork_regdll_overflow
1627 || (heap_obj_count != 0 && !first_heap_obj_fixed))
1628 {
1629 /* Cannot copy more than one heap object and not more than
1630 FORK_REGMEM_MAX DLL data segments. Cannot load more than
1631 FORK_REGDLL_MAX DLLs. Cannot copy a heap object which isn't
1632 an object of the EXE file (malloc() in _DLL_InitTerm()!). */
1633
1634 *errnop = ENOMEM;
1635 goto done;
1636 }
1637 if (layout_flags & L_FLAG_LINK386)
1638 {
1639 *errnop = EACCES;
1640 goto done;
1641 }
1642 LOCK_PROCESS;
1643 resources |= FORK_RSC_PROC;
1644 pte = proc_new ();
1645 if (pte == NULL)
1646 {
1647 *errnop = EAGAIN;
1648 goto done;
1649 }
1650 rc = DosAllocSharedMem ((void **)&shmem, NULL, FORK_OBJ_SIZE,
1651 PAG_READ|PAG_WRITE|PAG_COMMIT|OBJ_GETTABLE);
1652 if (rc != 0)
1653 goto error;
1654 resources |= FORK_RSC_MEM;
1655 p = arg_buf;
1656 *p++ = ' '; /* Program name */
1657 *p++ = 0;
1658 *p++ = ' '; /* Command line */
1659 *p++ = 0;
1660 sprintf (p, "^%.8x %.8x", (unsigned)shmem, (unsigned)my_pid); /* fork */
1661 stk_low = (ULONG)alloca (0) & ~0xfff;
1662
1663 shmem->init.req_code = FORK_REQ_INIT;
1664 shmem->init.msize = FORK_OBJ_SIZE;
1665 shmem->init.brk = heap_obj_count == 0 ? 0 : heap_objs[0].brk;
1666 shmem->init.reg_ebp = (ULONG)frame;
1667 shmem->init.umask = umask_bits;
1668 shmem->init.umask1 = umask_bits1;
1669 shmem->init.uflags = uflags;
1670 shmem->init.stack_page = stk_low;
1671 shmem->init.stack_base = stack_base;
1672
1673 rc = create_event_sem (&req_sem, DC_SEM_SHARED);
1674 if (rc != 0)
1675 goto error;
1676 shmem->init.req_sem = req_sem;
1677 resources |= FORK_RSC_REQ;
1678
1679 rc = create_event_sem (&ack_sem, DC_SEM_SHARED);
1680 if (rc != 0)
1681 goto error;
1682 shmem->init.ack_sem = ack_sem;
1683 resources |= FORK_RSC_ACK;
1684
1685 fileio_fork_parent_init ();
1686 resources |= FORK_RSC_FILEIO;
1687
1688 start_cwait_thread ();
1689 xf86sup_all_enadup (TRUE);
1690
1691 /* Compute the return address, for use by ptrace(). We add 4 to the
1692 stack pointer to compensate for the PUSHFD of emx_syscall. */
1693
1694 sp = (ULONG *)(frame->e.esp + 4);
1695 ip = frame->e.eip;
1696
1697 /* Check whether we have been called from __syscall(). That should
1698 always be the case, but... We check only the lower 16 bits
1699 because we don't know the address of __syscall() in a DLL such as
1700 emxlibcs.dll. */
1701
1702 if ((ip & 0xffff) == 12 + 5)
1703 {
1704 ++sp; /* Remove CALL emx_syscall */
1705 ip = *sp++; /* Get and remove EIP */
1706
1707 /* IP is now in __fork(). See /emx/src/lib/emx_386/fork.s for
1708 details -- note that __fork() does not create a stack frame.
1709 Get the function which called __fork(). */
1710
1711 ip = *sp++;
1712 }
1713 ptrace_fork_addr = ip;
1714
1715 /* Create the child process. */
1716
1717 ptrace_forking = TRUE;
1718 rc = DosExecPgm (obj_buf, sizeof (obj_buf), EXEC_ASYNCRESULT,
1719 arg_buf, startup_env, &ret_codes, exe_name);
1720 ptrace_forking = FALSE;
1721
1722 xf86sup_all_enadup (FALSE);
1723 if (rc != 0)
1724 goto error;
1725
1726 pid = ret_codes.codeTerminate;
1727 pte->status = PS_PROCESS;
1728 pte->pid = pid;
1729 pte->sid = 0;
1730 pte->wait_ret = 0;
1731 pte->flags = 0;
1732 UNLOCK_PROCESS;
1733 resources &= ~FORK_RSC_PROC;
1734
1735 child_started ();
1736 rc = fork_wait (ack_sem, pid);
1737 if (rc != 0)
1738 goto error;
1739
1740 rc = fork_mem (shmem, req_sem, ack_sem, pid,
1741 layout->data_base, layout->bss_end, NULLHANDLE);
1742 if (rc != 0)
1743 goto error;
1744 if (heap_obj_count != 0 && heap_objs[0].brk != 0)
1745 {
1746 rc = fork_mem (shmem, req_sem, ack_sem, pid, heap_objs[0].base,
1747 heap_objs[0].brk, NULLHANDLE);
1748 if (rc != 0)
1749 goto error;
1750 }
1751
1752 /* Load registered DLLs. */
1753
1754 if (!(debug_flags & DEBUG_NOFORKDLL))
1755 for (i = 0; i < fork_regdll_count; ++i)
1756 if (fork_regdll[i].hmod != 0)
1757 {
1758 rc = fork_dll (shmem, req_sem, ack_sem, pid, fork_regdll[i].hmod);
1759 if (rc != 0)
1760 goto error;
1761 }
1762
1763 /* Copy registered memory areas (DLL data segments). */
1764
1765 for (i = 0; i < fork_regmem_count; ++i)
1766 if (fork_regmem[i].hmod != 0)
1767 {
1768 rc = fork_mem (shmem, req_sem, ack_sem, pid,
1769 fork_regmem[i].start, fork_regmem[i].end,
1770 fork_regmem[i].hmod);
1771 if (rc != 0)
1772 goto error;
1773 }
1774
1775 rc = fork_mem (shmem, req_sem, ack_sem, pid, stk_low, stack_end, NULLHANDLE);
1776 if (rc != 0)
1777 goto error;
1778
1779 /* Send the final request to the child process. The structure
1780 contains data which will be kept by the child process in the
1781 shared memory object until it can be used. Therefore, this data
1782 must be sent last. */
1783
1784 shmem->done.req_code = FORK_REQ_DONE;
1785 shmem->done.size = sizeof (shmem->done);
1786 td = threads[1]; /* Copy signal handlers of thread 1 */
1787 memcpy (shmem->done.sig_actions, td->sig_table,
1788 sizeof (shmem->done.sig_actions));
1789 fileio_fork_parent_fillin (&shmem->done);
1790 tcpip_fork_sock (&shmem->done);
1791 resources |= FORK_RSC_SOCK;
1792 rc = fork_send (req_sem, ack_sem, pid);
1793 if (rc != 0)
1794 goto error;
1795 result = pid;
1796 *errnop = 0;
1797 goto done;
1798
1799error:
1800 if (resources & FORK_RSC_SOCK)
1801 tcpip_undo_fork_sock (&shmem->done);
1802 result = -1;
1803 *errnop = set_error (rc);
1804
1805done:
1806 if (resources & FORK_RSC_PROC)
1807 UNLOCK_PROCESS;
1808 if (resources & FORK_RSC_FILEIO)
1809 fileio_fork_parent_restore ();
1810 if (resources & FORK_RSC_REQ)
1811 close_event_sem (req_sem);
1812 if (resources & FORK_RSC_ACK)
1813 close_event_sem (ack_sem);
1814 if (resources & FORK_RSC_MEM)
1815 DosFreeMem (shmem);
1816 fork_reg_cleanup ();
1817 return result;
1818}
Note: See TracBrowser for help on using the repository browser.