1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 /*
7  * os2misc.c
8  *
9  */
10 
11 #ifdef MOZ_OS2_HIGH_MEMORY
12 /* os2safe.h has to be included before os2.h, needed for high mem */
13 #include <os2safe.h>
14 #endif
15 
16 #include <string.h>
17 #include "primpl.h"
18 
19 extern int   _CRT_init(void);
20 extern void  _CRT_term(void);
21 extern void __ctordtorInit(int flag);
22 extern void __ctordtorTerm(int flag);
23 
24 char *
_PR_MD_GET_ENV(const char * name)25 _PR_MD_GET_ENV(const char *name)
26 {
27     return getenv(name);
28 }
29 
30 PRIntn
_PR_MD_PUT_ENV(const char * name)31 _PR_MD_PUT_ENV(const char *name)
32 {
33     return putenv(name);
34 }
35 
36 
37 /*
38  **************************************************************************
39  **************************************************************************
40  **
41  **     Date and time routines
42  **
43  **************************************************************************
44  **************************************************************************
45  */
46 
47 #include <sys/timeb.h>
48 /*
49  *-----------------------------------------------------------------------
50  *
51  * PR_Now --
52  *
53  *     Returns the current time in microseconds since the epoch.
54  *     The epoch is midnight January 1, 1970 GMT.
55  *     The implementation is machine dependent.  This is the
56  *     implementation for OS/2.
57  *     Cf. time_t time(time_t *tp)
58  *
59  *-----------------------------------------------------------------------
60  */
61 
62 PR_IMPLEMENT(PRTime)
PR_Now(void)63 PR_Now(void)
64 {
65     PRInt64 s, ms, ms2us, s2us;
66     struct timeb b;
67 
68     ftime(&b);
69     LL_I2L(ms2us, PR_USEC_PER_MSEC);
70     LL_I2L(s2us, PR_USEC_PER_SEC);
71     LL_I2L(s, b.time);
72     LL_I2L(ms, b.millitm);
73     LL_MUL(ms, ms, ms2us);
74     LL_MUL(s, s, s2us);
75     LL_ADD(s, s, ms);
76     return s;
77 }
78 
79 
80 /*
81  ***********************************************************************
82  ***********************************************************************
83  *
84  * Process creation routines
85  *
86  ***********************************************************************
87  ***********************************************************************
88  */
89 
90 /*
91  * Assemble the command line by concatenating the argv array.
92  * Special characters intentionally do not get escaped, and it is
93  * expected that the caller wraps arguments in quotes if needed
94  * (e.g. for filename with spaces).
95  *
96  * On success, this function returns 0 and the resulting command
97  * line is returned in *cmdLine.  On failure, it returns -1.
98  */
assembleCmdLine(char * const * argv,char ** cmdLine)99 static int assembleCmdLine(char *const *argv, char **cmdLine)
100 {
101     char *const *arg;
102     int cmdLineSize;
103 
104     /*
105      * Find out how large the command line buffer should be.
106      */
107     cmdLineSize = 1; /* final null */
108     for (arg = argv+1; *arg; arg++) {
109         cmdLineSize += strlen(*arg) + 1; /* space in between */
110     }
111     *cmdLine = PR_MALLOC(cmdLineSize);
112     if (*cmdLine == NULL) {
113         return -1;
114     }
115 
116     (*cmdLine)[0] = '\0';
117 
118     for (arg = argv+1; *arg; arg++) {
119         if (arg > argv +1) {
120             strcat(*cmdLine, " ");
121         }
122         strcat(*cmdLine, *arg);
123     }
124     return 0;
125 }
126 
127 /*
128  * Assemble the environment block by concatenating the envp array
129  * (preserving the terminating null byte in each array element)
130  * and adding a null byte at the end.
131  *
132  * Returns 0 on success.  The resulting environment block is returned
133  * in *envBlock.  Note that if envp is NULL, a NULL pointer is returned
134  * in *envBlock.  Returns -1 on failure.
135  */
assembleEnvBlock(char ** envp,char ** envBlock)136 static int assembleEnvBlock(char **envp, char **envBlock)
137 {
138     char *p;
139     char *q;
140     char **env;
141     char *curEnv;
142     char *cwdStart, *cwdEnd;
143     int envBlockSize;
144 
145     PPIB ppib = NULL;
146     PTIB ptib = NULL;
147 
148     if (envp == NULL) {
149         *envBlock = NULL;
150         return 0;
151     }
152 
153     if(DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
154         return -1;
155     }
156 
157     curEnv = ppib->pib_pchenv;
158 
159     cwdStart = curEnv;
160     while (*cwdStart) {
161         if (cwdStart[0] == '=' && cwdStart[1] != '\0'
162             && cwdStart[2] == ':' && cwdStart[3] == '=') {
163             break;
164         }
165         cwdStart += strlen(cwdStart) + 1;
166     }
167     cwdEnd = cwdStart;
168     if (*cwdEnd) {
169         cwdEnd += strlen(cwdEnd) + 1;
170         while (*cwdEnd) {
171             if (cwdEnd[0] != '=' || cwdEnd[1] == '\0'
172                 || cwdEnd[2] != ':' || cwdEnd[3] != '=') {
173                 break;
174             }
175             cwdEnd += strlen(cwdEnd) + 1;
176         }
177     }
178     envBlockSize = cwdEnd - cwdStart;
179 
180     for (env = envp; *env; env++) {
181         envBlockSize += strlen(*env) + 1;
182     }
183     envBlockSize++;
184 
185     p = *envBlock = PR_MALLOC(envBlockSize);
186     if (p == NULL) {
187         return -1;
188     }
189 
190     q = cwdStart;
191     while (q < cwdEnd) {
192         *p++ = *q++;
193     }
194 
195     for (env = envp; *env; env++) {
196         q = *env;
197         while (*q) {
198             *p++ = *q++;
199         }
200         *p++ = '\0';
201     }
202     *p = '\0';
203     return 0;
204 }
205 
206 /*
207  * For qsort.  We sort (case-insensitive) the environment strings
208  * before generating the environment block.
209  */
compare(const void * arg1,const void * arg2)210 static int compare(const void *arg1, const void *arg2)
211 {
212     return stricmp(* (char**)arg1, * (char**)arg2);
213 }
214 
_PR_CreateOS2Process(const char * path,char * const * argv,char * const * envp,const PRProcessAttr * attr)215 PRProcess * _PR_CreateOS2Process(
216     const char *path,
217     char *const *argv,
218     char *const *envp,
219     const PRProcessAttr *attr)
220 {
221     PRProcess *proc = NULL;
222     char *cmdLine = NULL;
223     char **newEnvp = NULL;
224     char *envBlock = NULL;
225 
226     STARTDATA startData = {0};
227     APIRET    rc;
228     ULONG     ulAppType = 0;
229     PID       pid = 0;
230     char     *pszComSpec;
231     char      pszEXEName[CCHMAXPATH] = "";
232     char      pszFormatString[CCHMAXPATH];
233     char      pszObjectBuffer[CCHMAXPATH];
234     char     *pszFormatResult = NULL;
235 
236     /*
237      * Variables for DosExecPgm
238      */
239     char szFailed[CCHMAXPATH];
240     char *pszCmdLine = NULL;
241     RESULTCODES procInfo;
242     HFILE hStdIn  = 0,
243           hStdOut = 0,
244           hStdErr = 0;
245     HFILE hStdInSave  = -1,
246           hStdOutSave = -1,
247           hStdErrSave = -1;
248 
249     proc = PR_NEW(PRProcess);
250     if (!proc) {
251         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
252         goto errorExit;
253     }
254 
255     if (assembleCmdLine(argv, &cmdLine) == -1) {
256         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
257         goto errorExit;
258     }
259 
260 #ifdef MOZ_OS2_HIGH_MEMORY
261     /*
262      * DosQueryAppType() fails if path (the char* in the first argument) is in
263      * high memory. If that is the case, the following moves it to low memory.
264      */
265     if ((ULONG)path >= 0x20000000) {
266         size_t len = strlen(path) + 1;
267         char *copy = (char *)alloca(len);
268         memcpy(copy, path, len);
269         path = copy;
270     }
271 #endif
272 
273     if (envp == NULL) {
274         newEnvp = NULL;
275     } else {
276         int i;
277         int numEnv = 0;
278         while (envp[numEnv]) {
279             numEnv++;
280         }
281         newEnvp = (char **) PR_MALLOC((numEnv+1) * sizeof(char *));
282         for (i = 0; i <= numEnv; i++) {
283             newEnvp[i] = envp[i];
284         }
285         qsort((void *) newEnvp, (size_t) numEnv, sizeof(char *), compare);
286     }
287     if (assembleEnvBlock(newEnvp, &envBlock) == -1) {
288         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
289         goto errorExit;
290     }
291 
292     rc = DosQueryAppType(path, &ulAppType);
293     if (rc != NO_ERROR) {
294         char *pszDot = strrchr(path, '.');
295         if (pszDot) {
296             /* If it is a CMD file, launch the users command processor */
297             if (!stricmp(pszDot, ".cmd")) {
298                 rc = DosScanEnv("COMSPEC", (PSZ *)&pszComSpec);
299                 if (!rc) {
300                     strcpy(pszFormatString, "/C %s %s");
301                     strcpy(pszEXEName, pszComSpec);
302                     ulAppType = FAPPTYP_WINDOWCOMPAT;
303                 }
304             }
305         }
306     }
307     if (ulAppType == 0) {
308         PR_SetError(PR_UNKNOWN_ERROR, 0);
309         goto errorExit;
310     }
311 
312     if ((ulAppType & FAPPTYP_WINDOWAPI) == FAPPTYP_WINDOWAPI) {
313         startData.SessionType = SSF_TYPE_PM;
314     }
315     else if (ulAppType & FAPPTYP_WINDOWCOMPAT) {
316         startData.SessionType = SSF_TYPE_WINDOWABLEVIO;
317     }
318     else {
319         startData.SessionType = SSF_TYPE_DEFAULT;
320     }
321 
322     if (ulAppType & (FAPPTYP_WINDOWSPROT31 | FAPPTYP_WINDOWSPROT | FAPPTYP_WINDOWSREAL))
323     {
324         strcpy(pszEXEName, "WINOS2.COM");
325         startData.SessionType = PROG_31_STDSEAMLESSVDM;
326         strcpy(pszFormatString, "/3 %s %s");
327     }
328 
329     startData.InheritOpt = SSF_INHERTOPT_SHELL;
330 
331     if (pszEXEName[0]) {
332         pszFormatResult = PR_MALLOC(strlen(pszFormatString)+strlen(path)+strlen(cmdLine));
333         sprintf(pszFormatResult, pszFormatString, path, cmdLine);
334         startData.PgmInputs = pszFormatResult;
335     } else {
336         strcpy(pszEXEName, path);
337         startData.PgmInputs = cmdLine;
338     }
339     startData.PgmName = pszEXEName;
340 
341     startData.Length = sizeof(startData);
342     startData.Related = SSF_RELATED_INDEPENDENT;
343     startData.ObjectBuffer = pszObjectBuffer;
344     startData.ObjectBuffLen = CCHMAXPATH;
345     startData.Environment = envBlock;
346 
347     if (attr) {
348         /* On OS/2, there is really no way to pass file handles for stdin,
349          * stdout, and stderr to a new process.  Instead, we can make it
350          * a child process and make the given file handles a copy of our
351          * stdin, stdout, and stderr.  The child process then inherits
352          * ours, and we set ours back.  Twisted and gross I know. If you
353          * know a better way, please use it.
354          */
355         if (attr->stdinFd) {
356             hStdIn = 0;
357             DosDupHandle(hStdIn, &hStdInSave);
358             DosDupHandle((HFILE) attr->stdinFd->secret->md.osfd, &hStdIn);
359         }
360 
361         if (attr->stdoutFd) {
362             hStdOut = 1;
363             DosDupHandle(hStdOut, &hStdOutSave);
364             DosDupHandle((HFILE) attr->stdoutFd->secret->md.osfd, &hStdOut);
365         }
366 
367         if (attr->stderrFd) {
368             hStdErr = 2;
369             DosDupHandle(hStdErr, &hStdErrSave);
370             DosDupHandle((HFILE) attr->stderrFd->secret->md.osfd, &hStdErr);
371         }
372         /*
373          * Build up the Command Line for DosExecPgm
374          */
375         pszCmdLine = PR_MALLOC(strlen(pszEXEName) +
376                                strlen(startData.PgmInputs) + 3);
377         sprintf(pszCmdLine, "%s%c%s%c", pszEXEName, '\0',
378                 startData.PgmInputs, '\0');
379         rc = DosExecPgm(szFailed,
380                         CCHMAXPATH,
381                         EXEC_ASYNCRESULT,
382                         pszCmdLine,
383                         envBlock,
384                         &procInfo,
385                         pszEXEName);
386         PR_DELETE(pszCmdLine);
387 
388         /* Restore our old values.  Hope this works */
389         if (hStdInSave != -1) {
390             DosDupHandle(hStdInSave, &hStdIn);
391             DosClose(hStdInSave);
392         }
393 
394         if (hStdOutSave != -1) {
395             DosDupHandle(hStdOutSave, &hStdOut);
396             DosClose(hStdOutSave);
397         }
398 
399         if (hStdErrSave != -1) {
400             DosDupHandle(hStdErrSave, &hStdErr);
401             DosClose(hStdErrSave);
402         }
403 
404         if (rc != NO_ERROR) {
405             /* XXX what error code? */
406             PR_SetError(PR_UNKNOWN_ERROR, rc);
407             goto errorExit;
408         }
409 
410         proc->md.pid = procInfo.codeTerminate;
411     } else {
412         /*
413          * If no STDIN/STDOUT redirection is not needed, use DosStartSession
414          * to create a new, independent session
415          */
416         rc = DosStartSession(&startData, &ulAppType, &pid);
417 
418         if ((rc != NO_ERROR) && (rc != ERROR_SMG_START_IN_BACKGROUND)) {
419             PR_SetError(PR_UNKNOWN_ERROR, rc);
420             goto errorExit;
421         }
422 
423         proc->md.pid = pid;
424     }
425 
426     if (pszFormatResult) {
427         PR_DELETE(pszFormatResult);
428     }
429 
430     PR_DELETE(cmdLine);
431     if (newEnvp) {
432         PR_DELETE(newEnvp);
433     }
434     if (envBlock) {
435         PR_DELETE(envBlock);
436     }
437     return proc;
438 
439 errorExit:
440     if (cmdLine) {
441         PR_DELETE(cmdLine);
442     }
443     if (newEnvp) {
444         PR_DELETE(newEnvp);
445     }
446     if (envBlock) {
447         PR_DELETE(envBlock);
448     }
449     if (proc) {
450         PR_DELETE(proc);
451     }
452     return NULL;
453 }  /* _PR_CreateOS2Process */
454 
_PR_DetachOS2Process(PRProcess * process)455 PRStatus _PR_DetachOS2Process(PRProcess *process)
456 {
457     /* On OS/2, a process is either created as a child or not.
458      * You can't 'detach' it later on.
459      */
460     PR_DELETE(process);
461     return PR_SUCCESS;
462 }
463 
464 /*
465  * XXX: This will currently only work on a child process.
466  */
_PR_WaitOS2Process(PRProcess * process,PRInt32 * exitCode)467 PRStatus _PR_WaitOS2Process(PRProcess *process,
468                             PRInt32 *exitCode)
469 {
470     ULONG ulRetVal;
471     RESULTCODES results;
472     PID pidEnded = 0;
473 
474     ulRetVal = DosWaitChild(DCWA_PROCESS, DCWW_WAIT,
475                             &results,
476                             &pidEnded, process->md.pid);
477 
478     if (ulRetVal != NO_ERROR) {
479         printf("\nDosWaitChild rc = %lu\n", ulRetVal);
480         PR_SetError(PR_UNKNOWN_ERROR, ulRetVal);
481         return PR_FAILURE;
482     }
483     PR_DELETE(process);
484     return PR_SUCCESS;
485 }
486 
_PR_KillOS2Process(PRProcess * process)487 PRStatus _PR_KillOS2Process(PRProcess *process)
488 {
489     ULONG ulRetVal;
490     if ((ulRetVal = DosKillProcess(DKP_PROCESS, process->md.pid)) == NO_ERROR) {
491         return PR_SUCCESS;
492     }
493     PR_SetError(PR_UNKNOWN_ERROR, ulRetVal);
494     return PR_FAILURE;
495 }
496 
_MD_OS2GetHostName(char * name,PRUint32 namelen)497 PRStatus _MD_OS2GetHostName(char *name, PRUint32 namelen)
498 {
499     PRIntn rv;
500 
501     rv = gethostname(name, (PRInt32) namelen);
502     if (0 == rv) {
503         return PR_SUCCESS;
504     }
505     _PR_MD_MAP_GETHOSTNAME_ERROR(sock_errno());
506     return PR_FAILURE;
507 }
508 
509 void
_PR_MD_WAKEUP_CPUS(void)510 _PR_MD_WAKEUP_CPUS( void )
511 {
512     return;
513 }
514 
515 /*
516  **********************************************************************
517  *
518  * Memory-mapped files
519  *
520  * A credible emulation of memory-mapped i/o on OS/2 would require
521  * an exception-handler on each thread that might access the mapped
522  * memory.  In the Mozilla environment, that would be impractical.
523  * Instead, the following simulates those modes which don't modify
524  * the mapped file.  It reads the entire mapped file segment at once
525  * when MemMap is called, and frees it on MemUnmap.  CreateFileMap
526  * only does sanity-checks, while CloseFileMap does almost nothing.
527  *
528  **********************************************************************
529  */
530 
_MD_CreateFileMap(PRFileMap * fmap,PRInt64 size)531 PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
532 {
533     PRFileInfo64 info;
534 
535     /* assert on PR_PROT_READWRITE which modifies the underlying file */
536     PR_ASSERT(fmap->prot == PR_PROT_READONLY ||
537               fmap->prot == PR_PROT_WRITECOPY);
538     if (fmap->prot != PR_PROT_READONLY &&
539         fmap->prot != PR_PROT_WRITECOPY) {
540         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
541         return PR_FAILURE;
542     }
543     if (PR_GetOpenFileInfo64(fmap->fd, &info) == PR_FAILURE) {
544         return PR_FAILURE;
545     }
546     /* reject zero-byte mappings & zero-byte files */
547     if (!size || !info.size) {
548         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
549         return PR_FAILURE;
550     }
551     /* file size rounded up to the next page boundary */
552     fmap->md.maxExtent = (info.size + 0xfff) & ~(0xfff);
553 
554     return PR_SUCCESS;
555 }
556 
_MD_GetMemMapAlignment(void)557 PRInt32 _MD_GetMemMapAlignment(void)
558 {
559     /* OS/2 pages are 4k */
560     return 0x1000;
561 }
562 
_MD_MemMap(PRFileMap * fmap,PROffset64 offset,PRUint32 len)563 void * _MD_MemMap(PRFileMap *fmap, PROffset64 offset, PRUint32 len)
564 {
565     PRUint32 rv;
566     void *addr;
567 
568     /* prevent mappings beyond EOF + remainder of page */
569     if (offset + len > fmap->md.maxExtent) {
570         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
571         return NULL;
572     }
573     if (PR_Seek64(fmap->fd, offset, PR_SEEK_SET) == -1) {
574         return NULL;
575     }
576     /* try for high memory, fall back to low memory if hi-mem fails */
577     rv = DosAllocMem(&addr, len, OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE);
578     if (rv) {
579         rv = DosAllocMem(&addr, len, PAG_COMMIT | PAG_READ | PAG_WRITE);
580         if (rv) {
581             PR_SetError(PR_OUT_OF_MEMORY_ERROR, rv);
582             return NULL;
583         }
584     }
585     if (PR_Read(fmap->fd, addr, len) == -1) {
586         DosFreeMem(addr);
587         return NULL;
588     }
589     /* don't permit writes if readonly */
590     if (fmap->prot == PR_PROT_READONLY) {
591         rv = DosSetMem(addr, len, PAG_READ);
592         if (rv) {
593             DosFreeMem(addr);
594             PR_SetError(PR_UNKNOWN_ERROR, rv);
595             return NULL;
596         }
597     }
598     return addr;
599 }
600 
_MD_MemUnmap(void * addr,PRUint32 len)601 PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
602 {
603     PRUint32 rv;
604 
605     /* we just have to trust that addr & len are those used by MemMap */
606     rv = DosFreeMem(addr);
607     if (rv) {
608         PR_SetError(PR_INVALID_ARGUMENT_ERROR, rv);
609         return PR_FAILURE;
610     }
611     return PR_SUCCESS;
612 }
613 
_MD_CloseFileMap(PRFileMap * fmap)614 PRStatus _MD_CloseFileMap(PRFileMap *fmap)
615 {
616     /* nothing to do except free the PRFileMap struct */
617     PR_Free(fmap);
618     return PR_SUCCESS;
619 }
620 
621