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