source: vendor/emx/current/src/os2/init.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: 32.6 KB
Line 
1/* init.c -- Initialization
2 Copyright (c) 1994-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_DOSSEMAPHORES
29#define INCL_DOSMODULEMGR
30#define INCL_DOSMISC
31#define INCL_DOSEXCEPTIONS
32#define INCL_DOSERRORS
33#define INCL_REXXSAA
34#include <os2emx.h>
35#include <emx/startup.h>
36#include "emxdll.h"
37#include "clib.h"
38#include "version.h"
39
40/* Pointer to the layout table of the executable. */
41
42layout_table *layout;
43
44/* Flags from layout table. */
45
46ULONG layout_flags;
47
48/* Interface number, taken from the layout table. */
49
50BYTE interface;
51
52/* The top heap object. This points into heap_objs[] or is NULL.
53 While this variable is NULL, no memory has been allocated. */
54
55struct heap_obj *top_heap_obj;
56
57/* This array holds information on all the heap objects. The heap
58 objects are managed in LIFO fashion. */
59
60struct heap_obj heap_objs[MAX_HEAP_OBJS];
61
62/* This is the number of heap objects. */
63
64unsigned heap_obj_count;
65
66/* This variable is true iff the first heap object (heap_objs[0]) is
67 an object from the EXE file, that is, has not been allocated with
68 DosAllocMem. */
69
70char first_heap_obj_fixed;
71
72/* This is the default size for heap objects. */
73
74ULONG heap_obj_size;
75
76/* Address of the heap (first object) in the executable file. */
77
78ULONG heap_off;
79
80/* Base address of the stack object. */
81
82ULONG stack_base;
83
84/* End address of the stack object (address of first byte after the
85 stack object). */
86
87ULONG stack_end;
88
89/* Run debuggee in same session as the debugger. */
90
91BYTE debug_same_sess;
92
93/* Flag bits for debugging, set with the -! option. */
94
95ULONG debug_flags;
96
97/* If this variable is zero, if file names should not be truncated to
98 8.3 format. If bit 0 is set, UNC pathnames are truncated to 8.3
99 format. Bits 1 through 26 control truncation of file names on
100 drives A: through Z:. The user can set this variable by putting
101 the -t option into the EMXOPT environment variable. */
102
103ULONG opt_trunc;
104
105/* Default drive: 0=none, "A".."Z", "a".."z": prepend this drive
106 letter. */
107
108BYTE opt_drive;
109
110/* The -x option is used for getting the old (0.8h) behavior of not
111 quoting arguments passed using the `MKS Korn shell' method (3rd
112 argument string starting with `~'). Expanding arguments in that
113 case was wrong. */
114
115BYTE opt_expand;
116
117/* The -q option forces quoting of all command line arguments passed
118 to child processes. */
119
120BYTE opt_quote;
121
122/* This handle is used for diagnostic output including error
123 messages. */
124
125ULONG errout_handle;
126
127/* This flag is TRUE if this is process is a forked process. */
128
129BYTE fork_flag;
130
131/* fork(): EBP of parent process. */
132
133ULONG fork_ebp;
134
135/* fork(): Low end of the stack. */
136
137ULONG fork_stack_page;
138
139/* fork(): Pointer to final data packet from parent process. */
140
141static struct fork_data_done *p_fork_done;
142
143/* Pointer to the PIB. */
144
145PIB *init_pib_ptr;
146
147/* This variable points to the main exception registration record of
148 emx.dll. */
149
150EXCEPTIONREGISTRATIONRECORD *exc_reg_ptr;
151
152/* The process ID of the process using this instance of emx.dll. */
153
154ULONG my_pid;
155
156/* Pointer to the command line. */
157
158char *startup_args;
159
160/* Pointer to the environment. */
161
162char *startup_env;
163
164/* Number of environment strings. */
165
166ULONG env_count;
167
168/* Number of arguments. */
169
170ULONG arg_count;
171
172/* Size of argument strings (bytes). */
173
174ULONG arg_size;
175
176/* The third argument string of the program. */
177
178char *argstr3;
179
180/* The name of the EXE file. */
181
182BYTE exe_name[257];
183
184/* The file handle used for reading from the EXE file if the heap is
185 being loaded from the EXE file. */
186
187HFILE exe_fhandle;
188
189/* This flag is true if the heap is loaded from the EXE file. */
190
191BYTE exe_heap;
192
193/* This flag is true if we should ignore too small a stack. It is set
194 by the -I option. */
195
196static BYTE ignore_stack;
197
198/* Error message to be displayed by options(). */
199
200static const char *opt_errmsg;
201
202/* User flags, initially 0, set by __uflags(). */
203
204ULONG uflags;
205
206/* Set the following variable to TRUE to avoid using DosKillThread. */
207
208BYTE dont_doskillthread;
209
210/* The major and minor version numbers of OS/2. */
211
212ULONG version_major;
213ULONG version_minor;
214
215/* Define our own version of `_osmode' which is uninitialized; do not
216 use the library version which would add a non-shared page to
217 emx.dll, slowing down loading when it's already loaded. */
218
219unsigned char _osmode;
220
221
222/* Prototypes. */
223
224static void init_exit_list (void);
225static void options (const char *s, const char *errmsg);
226static void connect_fork (void);
227static void init2_fork (void);
228
229
230/* This REXX-callable entrypoint returns a string indicating the
231 revision index of emx.dll as integer. The number will be
232 incremented on each revision and is used only for comparing. */
233
234ULONG emx_revision (PCSZ name, LONG argc, const RXSTRING *argv,
235 PCSZ queuename, PRXSTRING retstr)
236{
237 static char const revision[] = XSTR(REV_INDEX);
238
239 if (argc != 0)
240 return 1;
241 strcpy (retstr->strptr, revision);
242 retstr->strlength = strlen (revision);
243 return 0;
244}
245
246
247/* DLL initialization and termination function. This function is
248 called by the system to initialize the DLL (if FLAG is zero) or to
249 terminate the DLL (if FLAG is one). */
250
251ULONG _DLL_InitTerm (ULONG mod_handle, ULONG flag)
252{
253 switch (flag)
254 {
255 case 0: /* Initialization */
256 init_heap ();
257 break;
258
259 case 1: /* Termination */
260#if 0
261 /* OS/2 does not terminate DLLs in the correct sequence under
262 certain circumstances. It might happen that EMXLIBCS.DLL is
263 terminated *after* EMX.DLL, causing problems in functions of
264 EMX.DLL called by the termination code of EMXLIBCS.DLL.
265 Don't terminate EMX.DLL -- a memory leak is preferable to a
266 crash. */
267
268 pm_term ();
269 term_signal ();
270 term_process ();
271 term_tcpip ();
272 term_semaphores ();
273 term_memory ();
274#endif
275 break;
276 }
277 return 1; /* Success! */
278}
279
280
281/* Fetch the flags and the interface level from the layout table. */
282
283static void use_layout (void)
284{
285 layout_flags = layout->flags;
286 interface = (BYTE)(layout_flags >> 24);
287}
288
289
290/* Use the heap stored in the EXE file, if present. */
291
292static void use_heap (void)
293{
294 if (layout->heap_base == 0 || layout->heap_end == 0 || heap_obj_count != 0)
295 return;
296 heap_objs[0].base = layout->heap_base;
297 heap_objs[0].end = layout->heap_end;
298 if (layout->heap_brk != 0)
299 {
300 ULONG rc, size, flags;
301
302 /* There is a heap stored in the EXE file. */
303
304 heap_off = layout->heap_off;
305 heap_objs[0].brk = layout->heap_brk;
306
307 /* Check if the heap object maps the heap pages. */
308
309 size = 0x1000;
310 rc = DosQueryMem ((PVOID)heap_objs[0].base, &size, &flags);
311 if (rc == 0 && (flags & PAG_COMMIT))
312 {
313 ULONG next;
314
315 /* The heap pages are mapped by the heap object. Decommit
316 those pages which are not preloaded to improve security
317 and to make sbrk() and brk() work. */
318
319 next = ROUND_PAGE (heap_objs[0].brk);
320 if (heap_objs[0].end > next)
321 setmem (next, heap_objs[0].end - next, PAG_DECOMMIT, 0);
322 }
323 else
324 {
325 ULONG action;
326
327 /* The heap pages are not mapped by the heap object. We
328 have to use an exception handler and guard pages to load
329 the pages. */
330
331 rc = DosOpen (exe_name, &exe_fhandle, &action, 0, 0,
332 OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
333 (OPEN_ACCESS_READONLY | OPEN_SHARE_DENYWRITE
334 | OPEN_FLAGS_RANDOM | OPEN_FLAGS_NOINHERIT), NULL);
335 if (rc != 0)
336 {
337 otext ("Cannot open EXE file\r\n");
338 quit (255);
339 }
340 /* Commit the guard pages. */
341 if (heap_objs[0].brk != heap_objs[0].base)
342 setmem (heap_objs[0].base, heap_objs[0].brk - heap_objs[0].base,
343 PAG_COMMIT | PAG_GUARD | PAG_READ | PAG_WRITE, 0);
344 exe_heap = TRUE;
345 }
346 }
347 else
348 heap_objs[0].brk = heap_objs[0].base;
349 top_heap_obj = &heap_objs[0];
350 heap_obj_count = 1;
351 first_heap_obj_fixed = TRUE;
352
353 /* All dynamically heap objects will have at least the same size as
354 the heap object of the EXE file. */
355
356 heap_obj_size = heap_objs[0].end - heap_objs[0].base;
357}
358
359
360/* First part of initialization. After return from initialize1(),
361 stacks will be switched and, if this process has been forked, data
362 will be copied from the parent process. */
363
364void initialize1 (void)
365{
366 TIB *tib_ptr;
367 ULONG rc, action;
368 PSZ str;
369 char *p;
370 size_t len;
371
372 __asm__ ("cld");
373
374 _osmode = 1; /* OS2_MODE */
375
376 use_layout ();
377
378 /* Get pointers to the TIB and PIB, and fetch environment pointers
379 and the PID from the PIB. */
380
381 DosGetInfoBlocks (&tib_ptr, &init_pib_ptr);
382 startup_args = init_pib_ptr->pib_pchcmd;
383 startup_env = init_pib_ptr->pib_pchenv;
384 my_pid = init_pib_ptr->pib_ulpid;
385
386 /* Get the version number of OS/2. */
387
388 version_major = querysysinfo (QSV_VERSION_MAJOR);
389 version_minor = querysysinfo (QSV_VERSION_MINOR);
390
391 /* Set `dont_doskillthread' depending on the version number of OS/2.
392 Assume that DosKillThread works in OS/2 2.11 and later. */
393
394 if (!(version_major > 20 || (version_major == 20 && version_minor >= 11)))
395 dont_doskillthread = TRUE;
396
397 /* Retrieve the name of the executable file of the application. */
398
399 rc = DosQueryModuleName (init_pib_ptr->pib_hmte,
400 sizeof (exe_name), exe_name);
401 if (rc != 0)
402 exe_name[0] = 0;
403 exe_fhandle = (HFILE)(-1);
404
405 /* Initialize __clock(). */
406
407 get_clock (TRUE);
408
409 /* Get a pointer to the 3rd argument string. Check whether we have
410 been forked. */
411
412 p = startup_args;
413 len = strlen (p);
414 p += len + 1;
415 len = strlen (p);
416 p += len + 1;
417 argstr3 = p;
418 if (*p == '^')
419 init_fork (p);
420
421 /* Set default values for options. */
422
423 heap_obj_size = HEAP_OBJ_SIZE;
424
425 /* Parse emx options put into the executable by emxbind. */
426
427 options (layout->options, "Invalid option in .exe file\r\n");
428
429 /* Parse emx options in the EMXOPT environment variable. */
430
431 rc = DosScanEnv ("EMXOPT", &str);
432 if (rc == 0)
433 options (str, "Invalid option in EMXOPT\r\n");
434
435 /* Get the stack base and end addresses. */
436
437 stack_base = (ULONG)tib_ptr->tib_pstack;
438 stack_end = (ULONG)tib_ptr->tib_pstacklimit;
439
440 /* Refuse to work if the stack is too small. */
441
442 if (stack_end - stack_base <= 16*1024 && !ignore_stack)
443 {
444 char buf[512];
445 ULONG written;
446
447 if (!pm_init ())
448 {
449 len = sprintf (buf, "emx.dll: Stack size too small. Run\r\n"
450 " emxstack -f %s\r\n"
451 "and try again.\r\n", exe_name);
452 DosWrite (2, buf, len, &written);
453 }
454 else
455 {
456 sprintf (buf, "Stack size too small. Run \"emxstack -f %s\" "
457 "and try again.", exe_name);
458 pm_message_box (buf);
459 }
460
461 quit (255);
462 }
463
464 /* Initialize errout_handle. */
465
466 if (debug_flags & DEBUG_STDERR)
467 {
468 rc = DosOpen ("con", &errout_handle, &action, 0, 0,
469 OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
470 (OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYNONE
471 | OPEN_FLAGS_NOINHERIT),
472 NULL);
473 if (rc != 0)
474 errout_handle = 2; /* In case it was modified by DosOpen */
475 }
476
477 /* Initialize the user flags. */
478
479 uflags = 0;
480
481 /* Use the heap stored in the EXE file, if present. */
482
483 use_heap ();
484
485 /* Install the exit list procedure. This should be done before
486 calling copy_fork() in order to be able to adjust the socket
487 reference counts if this process terminates prematurely. */
488
489 init_exit_list ();
490
491 /* If this process has been forked, connect to the parent process
492 and get the initial block of data. Among other things, that
493 block contains a pointer to the lowest page of stack in use by
494 the parent process. We set the stack pointer to that address
495 before copying the stack from the parent process. As moving the
496 stack causes trouble with compiled code, initialization is split
497 into two parts, initialize1() and initialize2(), the stack being
498 switched between those parts. */
499
500 if (fork_flag)
501 connect_fork ();
502}
503
504
505/* Second part of initialization. Here, initialization is continued
506 from initialize1(), after switching stacks and, if this process has
507 been forked, copying data from the parent process. */
508
509void initialize2 (void)
510{
511 init_exceptions ();
512 receive_signals ();
513 get_dbcs_lead ();
514 init_fileio ();
515 init_process ();
516 new_thread (1, NULL);
517 if (fork_flag)
518 init2_fork ();
519 else
520 init2_signal ();
521}
522
523
524/* This function is called from the startup code to initialize DLLs.
525 Dirty hack: We access our caller's parameters on the stack as
526 there's no space in dll0.s for pushing them. */
527
528void dll_init (layout_table *nl, ULONG ret_addr,
529 HMODULE hmod, ULONG flag)
530{
531 if (layout == NULL)
532 {
533 layout = nl;
534 use_layout ();
535 }
536
537 /* Register the DLL's data segment for fork(). */
538
539 if (nl->data_base < nl->data_end && nl->bss_base < nl->bss_end
540 && nl->bss_base == nl->data_end)
541 fork_register_mem (nl->data_base, nl->bss_end, hmod);
542 else
543 {
544 if (nl->data_base < nl->data_end)
545 fork_register_mem (nl->data_base, nl->data_end, hmod);
546 if (nl->bss_base < nl->bss_end)
547 fork_register_mem (nl->bss_base, nl->bss_end, hmod);
548 }
549 if (flag == 0) /* Initialization */
550 fork_register_dll (hmod);
551}
552
553
554/* This is our exit list procedure. */
555
556static void APIENTRY exit_list_proc (ULONG term_code)
557{
558 exit_tcpip ();
559 DosExitList (EXLST_EXIT, exit_list_proc);
560}
561
562
563/* Install our exit list procedure. This is always done, whether
564 sockets are used or not, to make it run after any exit list
565 procedure installed by the application. If tcpip_init() installed
566 the exit list procedure, the invocation order would depend on
567 whether the application installs the exit list procedure before or
568 after the first call to tcpip_init(). */
569
570static void init_exit_list (void)
571{
572 ULONG rc;
573
574 /* Note: SO32DLL.DLL installs its exit list procedure with
575 invocation order 0x99. Our exit list procedure should run before
576 SO32DLL's to be able to use SO32DLL. Moreover, it should run
577 after any exit list procedure installed by the application, to
578 make any sockets available to that exit list procedure. Assume
579 that the invocation orders of the application's exit list
580 procedures are less than 0x80 (0x80 through 0xff seem to be
581 reserved for OS/2). */
582
583 rc = DosExitList (EXLST_ADD | (0x7f << 8), exit_list_proc);
584 if (rc != 0)
585 error (rc, "DosExitList");
586}
587
588
589/* There's an error in an option. Display an error message and abort.
590 `opt_errmsg' contains the error message, which depends on where the
591 options come from (emxbind or EMXOPT). */
592
593static void opt_error (void)
594{
595 otext (opt_errmsg);
596 quit (1);
597}
598
599
600/* Parse a numeric, decimal, unsigned argument for an option. Return
601 the number and update *P which initially points to the beginning of
602 the argument to point to the first character after the number. The
603 number must be terminated by a null character or whitespace. On
604 error, display an error message and abort. */
605
606static ULONG opt_number (const char **p)
607{
608 const char *s;
609 ULONG n, d;
610 char flag;
611
612 s = *p;
613 flag = FALSE; n = 0;
614 if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
615 {
616 /* Base 16 */
617 s += 2;
618 for (;;)
619 {
620 if (*s >= '0' && *s <= '9')
621 d = *s - '0';
622 else if (*s >= 'a' && *s <= 'f')
623 d = *s - 'a' + 10;
624 else if (*s >= 'A' && *s <= 'F')
625 d = *s - 'A' + 10;
626 else
627 break;
628 if (n >= 0x10000000)
629 opt_error ();
630 n = n * 16 + d;
631 flag = TRUE; ++s;
632 }
633 }
634 else
635 {
636 /* Base 10 */
637 while (*s >= '0' && *s <= '9')
638 {
639 /* This check for overflow isn't perfect -- it rejects some
640 valid numbers. However, those numbers aren't valid
641 arguments, anyway. */
642
643 if (n >= 429496728)
644 opt_error ();
645 n = n * 10 + (*s - '0');
646 flag = TRUE; ++s;
647 }
648 }
649 if (!flag || (*s != 0 && *s != ' ' && *s != '\t'))
650 opt_error ();
651 *p = s;
652 return n;
653}
654
655
656#define ISLETTER(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
657#define ISEND(c) ((c) == 0 || (c) == ' ' || (c) == '\t')
658
659/* Parse options. S points to the null-terminated string of options,
660 ERRMSG points to the error message to be used. */
661
662static void options (const char *s, const char *errmsg)
663{
664 char c;
665 ULONG n;
666
667 /* Make the pointer to the error message available to
668 opt_error(). */
669
670 opt_errmsg = errmsg;
671
672 /* Parse the options, one by one. Options must be separated by
673 whitespace (blank or tab). */
674
675 do
676 {
677 /* Skip whitespace preceding the first option or following any
678 other option. Return when reaching the end of the string. */
679
680 do
681 {
682 c = *s++;
683 if (c == 0)
684 return;
685 } while (c == ' ' || c == '\t');
686
687 /* All options start with `-'. */
688
689 if (c != '-')
690 opt_error ();
691
692 /* Parse the various options. */
693
694 c = *s++;
695 switch (c)
696 {
697 case 'c':
698
699 /* Suppress core dumps. */
700
701 nocore_flag = TRUE;
702 break;
703
704 case 'f':
705
706 /* This option was used for setting the maximum stack frame
707 size. Parse the value, and discard it. As there are
708 still some programs which have, say, -f0 set with
709 emxbind, we have to accept this option though the maximum
710 stack frame size is no longer used. */
711
712 n = opt_number (&s);
713 if (n != 0 && (n < 4 || n > 32768))
714 opt_error ();
715 break;
716
717 case 'h':
718
719 /* Set the number of file handles. Note that DosSetMaxFH
720 cannot lower the number of file handles below the initial
721 value for this process (DosSetRelMaxFH can). */
722
723 n = opt_number (&s);
724 if (n < 10 || n > 65536)
725 opt_error ();
726 DosSetMaxFH (n);
727 break;
728
729 case 'n':
730
731 /* Don't display a harderror popup. This is done by
732 terminating the process instead of returning
733 XCPT_CONTINUE_SEARCH from the exception handler. */
734
735 no_popup = TRUE;
736 break;
737
738 case 'q':
739
740 /* Quote all command line arguments passed to child
741 processes. */
742
743 opt_quote = TRUE;
744 break;
745
746 case 'r':
747
748 /* Set the default drive for path names starting with `/' or
749 `\'. The argument of this option is a drive letter. */
750
751 if (ISLETTER (*s))
752 opt_drive = *s++;
753 else
754 opt_error ();
755 break;
756
757 case 't':
758
759 /* Truncate all components of certain pathnames to 8.3
760 format. `-t' enables truncation on all drives, `-t-'
761 disables truncation on all drives, `-tabc' enables
762 truncation on drives A, B, and C, `-t-abc' disables
763 truncation on drives A, B, and C. UNC pathnames are
764 specified by the special `drive name' `/'. */
765
766 if (ISEND (*s))
767 opt_trunc = TRUNC_ALL;
768 else if (*s == '-')
769 {
770 ++s;
771 if (ISEND (*s))
772 opt_trunc = 0;
773 else
774 do
775 {
776 c = *s++;
777 if (ISEND (c))
778 break;
779 if (ISLETTER (c))
780 opt_trunc &= ~TRUNC_DRV (c);
781 else if (c == '/')
782 opt_trunc &= ~TRUNC_UNC;
783 else
784 opt_error ();
785 } while (!ISEND (*s));
786 }
787 else
788 do
789 {
790 c = *s++;
791 if (ISLETTER (c))
792 opt_trunc |= TRUNC_DRV (c);
793 else if (c == '/')
794 opt_trunc |= TRUNC_UNC;
795 else
796 opt_error ();
797 } while (!ISEND (*s));
798 break;
799
800 case 'x':
801
802 /* Expand all command line arguments passed with the `MKS
803 Korn shell' method to this process. */
804
805 opt_expand = TRUE;
806 break;
807
808 case 'E':
809
810 /* Run debugees in the same session as the debugger. */
811
812 debug_same_sess = TRUE;
813 break;
814
815 case 'K':
816
817 /* Don't use DosKillThread. */
818
819 dont_doskillthread = TRUE;
820 break;
821
822 case 'I':
823
824 /* Don't abort when the stack size is too small. This is
825 required for old programs which assume that emx.dll
826 creates a private stack object if the stack size is too
827 small. */
828
829 ignore_stack = TRUE;
830 break;
831
832 case 'V':
833
834 /* Display the emx banner. */
835
836 otext ("emx " VERSION " (rev " XSTR (REV_INDEX) ")"
837 " -- Copyright (c) 1992-2000 by Eberhard Mattes\r\n");
838 break;
839
840 case '!':
841
842 /* Set debugging flags. The argument as a (decimal) number,
843 interpreted bitwise. See emxdll.h for the meaning of the
844 bits (DEBUG_STDERR, for instance). */
845
846 debug_flags = opt_number (&s);
847 break;
848
849 default:
850
851 /* The option is not known, abort. */
852
853 opt_error ();
854 }
855 } while (*s == ' ' || *s == '\t');
856
857 /* If there are characters remaining after an option, complain and
858 abort. */
859
860 if (*s != 0)
861 opt_error ();
862}
863
864
865/* Parse the command line. Store the argument strings at POOL (unless
866 POOL is NULL). The pointers are stored at VEC (unless VEC is
867 NULL). */
868
869#define BEGIN do {
870#define END } while (0)
871#define WHITE(C) ((C) == ' ' || (C) == '\t')
872#define PUTC(C) BEGIN ++arg_size; if (pool != NULL) *pool++ = (C); END
873#define PUTV BEGIN ++arg_count; if (vec != NULL) *vec++ = pool; END
874
875static void parse_arg (char *pool, char **vec, const char *str1,
876 const char *str3)
877{
878 const char *s;
879 char *flag_ptr;
880 int bs, quote;
881
882 arg_count = 0; arg_size = 0;
883
884 /* Look for the 3rd string -- if it starts with ~ we can get the
885 parsed argument words from that string and the following
886 strings. */
887
888 if (str3[0] == '~' && strcmp (str3 + 1, str1) == 0)
889 {
890 char flag;
891
892 if (opt_expand)
893 flag = _ARG_NONZERO;
894 else
895 flag = _ARG_NONZERO|_ARG_DQUOTE;
896 s = str3 + 1;
897 while (*s != 0)
898 {
899 if (*s == '~')
900 ++s;
901 PUTC (flag); PUTV;
902 do
903 {
904 PUTC (*s);
905 } while (*s++ != 0);
906 }
907 }
908 else
909 {
910 s = str1;
911
912 /* argv[0] contains the program name. */
913
914 PUTC (_ARG_NONZERO); PUTV;
915 do
916 {
917 PUTC (*s);
918 } while (*s++ != 0);
919
920 /* Now scan the arguments, one by one. */
921
922 for (;;)
923 {
924 /* Skip leading whitespace. */
925
926 while (WHITE (*s))
927 ++s;
928
929 /* Work is completed when reaching the end of the string. */
930
931 if (*s == 0)
932 break;
933
934 /* Flags will be stored to `*flag_ptr' while parsing the
935 current argument. Initially, no flags are set. */
936
937 flag_ptr = pool;
938 PUTC (_ARG_NONZERO);
939 PUTV;
940 bs = 0; quote = 0;
941 for (;;)
942 {
943 if (*s == '"')
944 {
945 /* Backslashes preceding a double quote character
946 are treated specially: A backslash escapes either
947 a backslash or a double quote character. */
948
949 while (bs >= 2)
950 {
951 PUTC ('\\');
952 bs -= 2;
953 }
954 if (bs & 1)
955 PUTC ('"');
956 else
957 {
958 /* The number of backslashes preceding the
959 double quote character is even (including
960 zero), therefore this double quote character
961 starts or ends a quoted string. */
962
963 quote = !quote;
964 if (flag_ptr != NULL)
965 *flag_ptr |= _ARG_DQUOTE;
966 }
967
968 /* We have eaten all backslashes. */
969
970 bs = 0;
971 }
972 else if (*s == '\\')
973 {
974 /* Instead of looking ahead to learn whether the
975 backslash is followed by (backslashes and) a
976 double quote character, we count the number of
977 successive backslashes and consider them when
978 processing another character. */
979
980 ++bs;
981 }
982 else
983 {
984 /* Process backslashes preceding this character. */
985
986 while (bs != 0)
987 {
988 PUTC ('\\');
989 --bs;
990 }
991
992 /* Whitespace ends the current argument unless we
993 are inside a quoted string. */
994
995 if (*s == 0 || (WHITE (*s) && !quote))
996 break;
997 PUTC (*s);
998 }
999 ++s;
1000 }
1001
1002 /* Mark the end of the argument string. */
1003
1004 PUTC (0);
1005 }
1006 }
1007
1008 /* Mark the end of the vector of pointers to argument strings. */
1009
1010 if (vec != NULL)
1011 *vec = NULL;
1012}
1013
1014
1015/* Build the table of environment pointers; don't store if VEC is
1016 NULL. */
1017
1018static char **parse_env (char **vec)
1019{
1020 char *s;
1021
1022 env_count = 0;
1023 s = startup_env;
1024 while (*s != 0)
1025 {
1026 ++env_count;
1027 if (vec != NULL)
1028 *vec++ = s;
1029 while (*s != 0)
1030 ++s;
1031 ++s;
1032 }
1033 if (vec != NULL)
1034 *vec++ = NULL;
1035 return vec;
1036}
1037
1038
1039/* Compute the number and size of argument strings and environment
1040 strings. */
1041
1042void count_arg_env (void)
1043{
1044 parse_env (NULL);
1045 parse_arg (NULL, NULL, startup_args, argstr3);
1046}
1047
1048
1049/* Build the tables of argument strings and environment strings. */
1050
1051void build_arg_env (char *str, char **vec)
1052{
1053 vec = parse_env (vec);
1054 parse_arg (str, vec, startup_args, argstr3);
1055}
1056
1057
1058/* Semaphores for communicating with the parent process after a fork. */
1059
1060static HEV fork_req_sem;
1061static HEV fork_ack_sem;
1062
1063/* Various values reveived from the parent process. */
1064
1065static ULONG fork_msize;
1066static ULONG fork_addr;
1067static ULONG fork_ppid;
1068
1069
1070/* This process seems to have been forked off; parse the arguments
1071 passed on the command line. */
1072
1073void init_fork (const char *s)
1074{
1075 ++s; /* Skip the caret */
1076 if (!conv_hex8 (s, &fork_addr))
1077 return;
1078 s += 8;
1079 if (*s != ' ')
1080 return;
1081 ++s;
1082 if (!conv_hex8 (s, &fork_ppid))
1083 return;
1084 s += 8;
1085 if (*s != 0)
1086 return;
1087 fork_flag = TRUE;
1088}
1089
1090
1091/* Final initializations for a forked process. */
1092
1093static void init2_fork (void)
1094{
1095 thread_data *td;
1096
1097 /* Copy the signal handlers from the shared memory object to the
1098 thread data block for thread 1. */
1099
1100 td = threads[1];
1101 memcpy (td->sig_table, p_fork_done->sig_actions, sizeof (td->sig_table));
1102
1103 /* Install socket handles inherited from the parent process. */
1104
1105 copy_fork_sock (p_fork_done);
1106
1107 /* Initalize other file handles. This must be done after calling
1108 copy_fork_sock()! */
1109
1110 fileio_fork_child (p_fork_done);
1111
1112 /* Now free the shared memory object, it's no longer needed. */
1113
1114 DosFreeMem ((void *)fork_addr);
1115 p_fork_done = NULL;
1116}
1117
1118
1119/* Connect to the parent process and process the initial block of
1120 data. */
1121
1122static void connect_fork (void)
1123{
1124 ULONG rc;
1125 struct fork_data_init *pi;
1126
1127 rc = DosGetSharedMem ((void *)fork_addr, PAG_READ);
1128 if (rc != 0)
1129 {
1130 error (rc, "DosGetSharedMem");
1131 quit (255);
1132 }
1133
1134 pi = (struct fork_data_init *)fork_addr;
1135 fork_msize = pi->msize;
1136
1137 fork_req_sem = pi->req_sem;
1138 rc = DosOpenEventSem (NULL, &fork_req_sem);
1139 if (rc != 0)
1140 {
1141 error (rc, "DosOpenEventSem");
1142 quit (255);
1143 }
1144
1145 fork_ack_sem = pi->ack_sem;
1146 rc = DosOpenEventSem (NULL, &fork_ack_sem);
1147 if (rc != 0)
1148 {
1149 error (rc, "DosOpenEventSem");
1150 quit (255);
1151 }
1152
1153 heap_objs[0].brk = pi->brk;
1154 if (heap_objs[0].brk != 0)
1155 {
1156 ULONG size;
1157
1158 size = heap_objs[0].brk - heap_objs[0].base;
1159 if (size != 0)
1160 setmem (heap_objs[0].base, size, PAG_DEFAULT | PAG_COMMIT, 0);
1161 }
1162
1163 if (pi->stack_base != stack_base)
1164 {
1165 /* Moving the stack is no longer supported. */
1166 error (0, "fork stack problem");
1167 quit (255);
1168 }
1169 fork_stack_page = pi->stack_page;
1170 fork_ebp = pi->reg_ebp;
1171 umask_bits = pi->umask;
1172 umask_bits1 = pi->umask1;
1173 uflags = pi->uflags;
1174}
1175
1176
1177/* Copy data from the parent process. */
1178
1179void copy_fork (void)
1180{
1181 fork_data *p;
1182 ULONG rc;
1183 HMODULE hmod;
1184 static HMODULE cache_hmod; /* = NULLHANDLE */
1185 static int cache_loaded; /* = whatever (0) */
1186
1187 for (;;)
1188 {
1189 reset_event_sem (fork_req_sem);
1190 DosPostEventSem (fork_ack_sem);
1191 do
1192 {
1193 rc = DosWaitEventSem (fork_req_sem, 500);
1194
1195 /* Repeat if interrupted by a signal. Repeat if timed out
1196 and the parent process is still the same (that is, it is
1197 still alive). */
1198
1199 } while (rc == ERROR_INTERRUPT
1200 || (rc == ERROR_TIMEOUT
1201 && init_pib_ptr->pib_ulppid == fork_ppid));
1202 if (rc != 0)
1203 {
1204 error (rc, "DosWaitEventSem");
1205 quit (255);
1206 }
1207
1208 /* Don't move this assignment outside the loop, unless you make
1209 `p' a volatile pointer. */
1210
1211 p = (fork_data *)fork_addr;
1212
1213 switch (p->req_code)
1214 {
1215 case FORK_REQ_DONE:
1216 /* Last transaction. */
1217 if (p->done.sock_count != 0)
1218 {
1219 /* To keep reference counts correct in case TCP/IP
1220 initialization fails, we have to initialize now. */
1221
1222 if (!tcpip_init_fork (&p->done))
1223 quit (255);
1224 }
1225
1226 /* Fork completed. Don't free the shared memory object now,
1227 as we need the information on signals and sockets later,
1228 after proceeding with initializing. */
1229
1230 p_fork_done = &p->done;
1231 DosPostEventSem (fork_ack_sem);
1232
1233 /* Note: Do not call close_event_sem() as these semaphores
1234 have been opened with DosOpenEventSem, not created with
1235 create_event_sem()! */
1236
1237 DosCloseEventSem (fork_req_sem);
1238 DosCloseEventSem (fork_ack_sem);
1239 return;
1240
1241 case FORK_REQ_DLL:
1242 /* Load a DLL. There's not much point in checking if the
1243 module is already loaded (by querying the name for the
1244 module handle) -- if it is already loaded, the following
1245 code will be redundant. */
1246
1247 rc = load_module (p->dll.path, &hmod);
1248 if (rc != 0)
1249 {
1250 error (rc, "DosLoadModule");
1251 quit (255);
1252 }
1253 if (hmod != p->dll.hmod)
1254 {
1255 oprintf ("HMODULE mismatch: %u vs. %u\r\n",
1256 (unsigned)hmod, (unsigned)p->dll.hmod);
1257 quit (255);
1258 }
1259 break;
1260
1261 case FORK_REQ_MEM:
1262 /* Receive memory from parent process. */
1263 if (p->mem.hmod != 0 && p->mem.hmod != cache_hmod)
1264 {
1265 cache_hmod = p->mem.hmod;
1266 cache_loaded = fork_dll_registered (p->mem.hmod);
1267 }
1268 if (p->mem.hmod == 0 || cache_loaded)
1269 memcpy ((void *)p->mem.address, p->mem.shared, p->mem.count);
1270 break;
1271
1272 default:
1273 /* Ignore unknown request codes. */
1274 break;
1275 }
1276 }
1277}
Note: See TracBrowser for help on using the repository browser.