Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
6 /*
7 * os2misc.c
8 *
9 */
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
16 #include <string.h>
17 #include "primpl.h"
19 extern int _CRT_init(void);
20 extern void _CRT_term(void);
21 extern void __ctordtorInit(int flag);
22 extern void __ctordtorTerm(int flag);
24 char *
25 _PR_MD_GET_ENV(const char *name)
26 {
27 return getenv(name);
28 }
30 PRIntn
31 _PR_MD_PUT_ENV(const char *name)
32 {
33 return putenv(name);
34 }
37 /*
38 **************************************************************************
39 **************************************************************************
40 **
41 ** Date and time routines
42 **
43 **************************************************************************
44 **************************************************************************
45 */
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 */
62 PR_IMPLEMENT(PRTime)
63 PR_Now(void)
64 {
65 PRInt64 s, ms, ms2us, s2us;
66 struct timeb b;
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 }
80 /*
81 ***********************************************************************
82 ***********************************************************************
83 *
84 * Process creation routines
85 *
86 ***********************************************************************
87 ***********************************************************************
88 */
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 */
99 static int assembleCmdLine(char *const *argv, char **cmdLine)
100 {
101 char *const *arg;
102 int cmdLineSize;
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 }
116 (*cmdLine)[0] = '\0';
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 }
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 */
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;
145 PPIB ppib = NULL;
146 PTIB ptib = NULL;
148 if (envp == NULL) {
149 *envBlock = NULL;
150 return 0;
151 }
153 if(DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR)
154 return -1;
156 curEnv = ppib->pib_pchenv;
158 cwdStart = curEnv;
159 while (*cwdStart) {
160 if (cwdStart[0] == '=' && cwdStart[1] != '\0'
161 && cwdStart[2] == ':' && cwdStart[3] == '=') {
162 break;
163 }
164 cwdStart += strlen(cwdStart) + 1;
165 }
166 cwdEnd = cwdStart;
167 if (*cwdEnd) {
168 cwdEnd += strlen(cwdEnd) + 1;
169 while (*cwdEnd) {
170 if (cwdEnd[0] != '=' || cwdEnd[1] == '\0'
171 || cwdEnd[2] != ':' || cwdEnd[3] != '=') {
172 break;
173 }
174 cwdEnd += strlen(cwdEnd) + 1;
175 }
176 }
177 envBlockSize = cwdEnd - cwdStart;
179 for (env = envp; *env; env++) {
180 envBlockSize += strlen(*env) + 1;
181 }
182 envBlockSize++;
184 p = *envBlock = PR_MALLOC(envBlockSize);
185 if (p == NULL) {
186 return -1;
187 }
189 q = cwdStart;
190 while (q < cwdEnd) {
191 *p++ = *q++;
192 }
194 for (env = envp; *env; env++) {
195 q = *env;
196 while (*q) {
197 *p++ = *q++;
198 }
199 *p++ = '\0';
200 }
201 *p = '\0';
202 return 0;
203 }
205 /*
206 * For qsort. We sort (case-insensitive) the environment strings
207 * before generating the environment block.
208 */
209 static int compare(const void *arg1, const void *arg2)
210 {
211 return stricmp(* (char**)arg1, * (char**)arg2);
212 }
214 PRProcess * _PR_CreateOS2Process(
215 const char *path,
216 char *const *argv,
217 char *const *envp,
218 const PRProcessAttr *attr)
219 {
220 PRProcess *proc = NULL;
221 char *cmdLine = NULL;
222 char **newEnvp = NULL;
223 char *envBlock = NULL;
225 STARTDATA startData = {0};
226 APIRET rc;
227 ULONG ulAppType = 0;
228 PID pid = 0;
229 char *pszComSpec;
230 char pszEXEName[CCHMAXPATH] = "";
231 char pszFormatString[CCHMAXPATH];
232 char pszObjectBuffer[CCHMAXPATH];
233 char *pszFormatResult = NULL;
235 /*
236 * Variables for DosExecPgm
237 */
238 char szFailed[CCHMAXPATH];
239 char *pszCmdLine = NULL;
240 RESULTCODES procInfo;
241 HFILE hStdIn = 0,
242 hStdOut = 0,
243 hStdErr = 0;
244 HFILE hStdInSave = -1,
245 hStdOutSave = -1,
246 hStdErrSave = -1;
248 proc = PR_NEW(PRProcess);
249 if (!proc) {
250 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
251 goto errorExit;
252 }
254 if (assembleCmdLine(argv, &cmdLine) == -1) {
255 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
256 goto errorExit;
257 }
259 #ifdef MOZ_OS2_HIGH_MEMORY
260 /*
261 * DosQueryAppType() fails if path (the char* in the first argument) is in
262 * high memory. If that is the case, the following moves it to low memory.
263 */
264 if ((ULONG)path >= 0x20000000) {
265 size_t len = strlen(path) + 1;
266 char *copy = (char *)alloca(len);
267 memcpy(copy, path, len);
268 path = copy;
269 }
270 #endif
272 if (envp == NULL) {
273 newEnvp = NULL;
274 } else {
275 int i;
276 int numEnv = 0;
277 while (envp[numEnv]) {
278 numEnv++;
279 }
280 newEnvp = (char **) PR_MALLOC((numEnv+1) * sizeof(char *));
281 for (i = 0; i <= numEnv; i++) {
282 newEnvp[i] = envp[i];
283 }
284 qsort((void *) newEnvp, (size_t) numEnv, sizeof(char *), compare);
285 }
286 if (assembleEnvBlock(newEnvp, &envBlock) == -1) {
287 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
288 goto errorExit;
289 }
291 rc = DosQueryAppType(path, &ulAppType);
292 if (rc != NO_ERROR) {
293 char *pszDot = strrchr(path, '.');
294 if (pszDot) {
295 /* If it is a CMD file, launch the users command processor */
296 if (!stricmp(pszDot, ".cmd")) {
297 rc = DosScanEnv("COMSPEC", (PSZ *)&pszComSpec);
298 if (!rc) {
299 strcpy(pszFormatString, "/C %s %s");
300 strcpy(pszEXEName, pszComSpec);
301 ulAppType = FAPPTYP_WINDOWCOMPAT;
302 }
303 }
304 }
305 }
306 if (ulAppType == 0) {
307 PR_SetError(PR_UNKNOWN_ERROR, 0);
308 goto errorExit;
309 }
311 if ((ulAppType & FAPPTYP_WINDOWAPI) == FAPPTYP_WINDOWAPI) {
312 startData.SessionType = SSF_TYPE_PM;
313 }
314 else if (ulAppType & FAPPTYP_WINDOWCOMPAT) {
315 startData.SessionType = SSF_TYPE_WINDOWABLEVIO;
316 }
317 else {
318 startData.SessionType = SSF_TYPE_DEFAULT;
319 }
321 if (ulAppType & (FAPPTYP_WINDOWSPROT31 | FAPPTYP_WINDOWSPROT | FAPPTYP_WINDOWSREAL))
322 {
323 strcpy(pszEXEName, "WINOS2.COM");
324 startData.SessionType = PROG_31_STDSEAMLESSVDM;
325 strcpy(pszFormatString, "/3 %s %s");
326 }
328 startData.InheritOpt = SSF_INHERTOPT_SHELL;
330 if (pszEXEName[0]) {
331 pszFormatResult = PR_MALLOC(strlen(pszFormatString)+strlen(path)+strlen(cmdLine));
332 sprintf(pszFormatResult, pszFormatString, path, cmdLine);
333 startData.PgmInputs = pszFormatResult;
334 } else {
335 strcpy(pszEXEName, path);
336 startData.PgmInputs = cmdLine;
337 }
338 startData.PgmName = pszEXEName;
340 startData.Length = sizeof(startData);
341 startData.Related = SSF_RELATED_INDEPENDENT;
342 startData.ObjectBuffer = pszObjectBuffer;
343 startData.ObjectBuffLen = CCHMAXPATH;
344 startData.Environment = envBlock;
346 if (attr) {
347 /* On OS/2, there is really no way to pass file handles for stdin,
348 * stdout, and stderr to a new process. Instead, we can make it
349 * a child process and make the given file handles a copy of our
350 * stdin, stdout, and stderr. The child process then inherits
351 * ours, and we set ours back. Twisted and gross I know. If you
352 * know a better way, please use it.
353 */
354 if (attr->stdinFd) {
355 hStdIn = 0;
356 DosDupHandle(hStdIn, &hStdInSave);
357 DosDupHandle((HFILE) attr->stdinFd->secret->md.osfd, &hStdIn);
358 }
360 if (attr->stdoutFd) {
361 hStdOut = 1;
362 DosDupHandle(hStdOut, &hStdOutSave);
363 DosDupHandle((HFILE) attr->stdoutFd->secret->md.osfd, &hStdOut);
364 }
366 if (attr->stderrFd) {
367 hStdErr = 2;
368 DosDupHandle(hStdErr, &hStdErrSave);
369 DosDupHandle((HFILE) attr->stderrFd->secret->md.osfd, &hStdErr);
370 }
371 /*
372 * Build up the Command Line for DosExecPgm
373 */
374 pszCmdLine = PR_MALLOC(strlen(pszEXEName) +
375 strlen(startData.PgmInputs) + 3);
376 sprintf(pszCmdLine, "%s%c%s%c", pszEXEName, '\0',
377 startData.PgmInputs, '\0');
378 rc = DosExecPgm(szFailed,
379 CCHMAXPATH,
380 EXEC_ASYNCRESULT,
381 pszCmdLine,
382 envBlock,
383 &procInfo,
384 pszEXEName);
385 PR_DELETE(pszCmdLine);
387 /* Restore our old values. Hope this works */
388 if (hStdInSave != -1) {
389 DosDupHandle(hStdInSave, &hStdIn);
390 DosClose(hStdInSave);
391 }
393 if (hStdOutSave != -1) {
394 DosDupHandle(hStdOutSave, &hStdOut);
395 DosClose(hStdOutSave);
396 }
398 if (hStdErrSave != -1) {
399 DosDupHandle(hStdErrSave, &hStdErr);
400 DosClose(hStdErrSave);
401 }
403 if (rc != NO_ERROR) {
404 /* XXX what error code? */
405 PR_SetError(PR_UNKNOWN_ERROR, rc);
406 goto errorExit;
407 }
409 proc->md.pid = procInfo.codeTerminate;
410 } else {
411 /*
412 * If no STDIN/STDOUT redirection is not needed, use DosStartSession
413 * to create a new, independent session
414 */
415 rc = DosStartSession(&startData, &ulAppType, &pid);
417 if ((rc != NO_ERROR) && (rc != ERROR_SMG_START_IN_BACKGROUND)) {
418 PR_SetError(PR_UNKNOWN_ERROR, rc);
419 goto errorExit;
420 }
422 proc->md.pid = pid;
423 }
425 if (pszFormatResult) {
426 PR_DELETE(pszFormatResult);
427 }
429 PR_DELETE(cmdLine);
430 if (newEnvp) {
431 PR_DELETE(newEnvp);
432 }
433 if (envBlock) {
434 PR_DELETE(envBlock);
435 }
436 return proc;
438 errorExit:
439 if (cmdLine) {
440 PR_DELETE(cmdLine);
441 }
442 if (newEnvp) {
443 PR_DELETE(newEnvp);
444 }
445 if (envBlock) {
446 PR_DELETE(envBlock);
447 }
448 if (proc) {
449 PR_DELETE(proc);
450 }
451 return NULL;
452 } /* _PR_CreateOS2Process */
454 PRStatus _PR_DetachOS2Process(PRProcess *process)
455 {
456 /* On OS/2, a process is either created as a child or not.
457 * You can't 'detach' it later on.
458 */
459 PR_DELETE(process);
460 return PR_SUCCESS;
461 }
463 /*
464 * XXX: This will currently only work on a child process.
465 */
466 PRStatus _PR_WaitOS2Process(PRProcess *process,
467 PRInt32 *exitCode)
468 {
469 ULONG ulRetVal;
470 RESULTCODES results;
471 PID pidEnded = 0;
473 ulRetVal = DosWaitChild(DCWA_PROCESS, DCWW_WAIT,
474 &results,
475 &pidEnded, process->md.pid);
477 if (ulRetVal != NO_ERROR) {
478 printf("\nDosWaitChild rc = %lu\n", ulRetVal);
479 PR_SetError(PR_UNKNOWN_ERROR, ulRetVal);
480 return PR_FAILURE;
481 }
482 PR_DELETE(process);
483 return PR_SUCCESS;
484 }
486 PRStatus _PR_KillOS2Process(PRProcess *process)
487 {
488 ULONG ulRetVal;
489 if ((ulRetVal = DosKillProcess(DKP_PROCESS, process->md.pid)) == NO_ERROR) {
490 return PR_SUCCESS;
491 }
492 PR_SetError(PR_UNKNOWN_ERROR, ulRetVal);
493 return PR_FAILURE;
494 }
496 PRStatus _MD_OS2GetHostName(char *name, PRUint32 namelen)
497 {
498 PRIntn rv;
500 rv = gethostname(name, (PRInt32) namelen);
501 if (0 == rv) {
502 return PR_SUCCESS;
503 }
504 _PR_MD_MAP_GETHOSTNAME_ERROR(sock_errno());
505 return PR_FAILURE;
506 }
508 void
509 _PR_MD_WAKEUP_CPUS( void )
510 {
511 return;
512 }
514 /*
515 **********************************************************************
516 *
517 * Memory-mapped files
518 *
519 * A credible emulation of memory-mapped i/o on OS/2 would require
520 * an exception-handler on each thread that might access the mapped
521 * memory. In the Mozilla environment, that would be impractical.
522 * Instead, the following simulates those modes which don't modify
523 * the mapped file. It reads the entire mapped file segment at once
524 * when MemMap is called, and frees it on MemUnmap. CreateFileMap
525 * only does sanity-checks, while CloseFileMap does almost nothing.
526 *
527 **********************************************************************
528 */
530 PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
531 {
532 PRFileInfo64 info;
534 /* assert on PR_PROT_READWRITE which modifies the underlying file */
535 PR_ASSERT(fmap->prot == PR_PROT_READONLY ||
536 fmap->prot == PR_PROT_WRITECOPY);
537 if (fmap->prot != PR_PROT_READONLY &&
538 fmap->prot != PR_PROT_WRITECOPY) {
539 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
540 return PR_FAILURE;
541 }
542 if (PR_GetOpenFileInfo64(fmap->fd, &info) == PR_FAILURE) {
543 return PR_FAILURE;
544 }
545 /* reject zero-byte mappings & zero-byte files */
546 if (!size || !info.size) {
547 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
548 return PR_FAILURE;
549 }
550 /* file size rounded up to the next page boundary */
551 fmap->md.maxExtent = (info.size + 0xfff) & ~(0xfff);
553 return PR_SUCCESS;
554 }
556 PRInt32 _MD_GetMemMapAlignment(void)
557 {
558 /* OS/2 pages are 4k */
559 return 0x1000;
560 }
562 void * _MD_MemMap(PRFileMap *fmap, PROffset64 offset, PRUint32 len)
563 {
564 PRUint32 rv;
565 void *addr;
567 /* prevent mappings beyond EOF + remainder of page */
568 if (offset + len > fmap->md.maxExtent) {
569 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
570 return NULL;
571 }
572 if (PR_Seek64(fmap->fd, offset, PR_SEEK_SET) == -1) {
573 return NULL;
574 }
575 /* try for high memory, fall back to low memory if hi-mem fails */
576 rv = DosAllocMem(&addr, len, OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE);
577 if (rv) {
578 rv = DosAllocMem(&addr, len, PAG_COMMIT | PAG_READ | PAG_WRITE);
579 if (rv) {
580 PR_SetError(PR_OUT_OF_MEMORY_ERROR, rv);
581 return NULL;
582 }
583 }
584 if (PR_Read(fmap->fd, addr, len) == -1) {
585 DosFreeMem(addr);
586 return NULL;
587 }
588 /* don't permit writes if readonly */
589 if (fmap->prot == PR_PROT_READONLY) {
590 rv = DosSetMem(addr, len, PAG_READ);
591 if (rv) {
592 DosFreeMem(addr);
593 PR_SetError(PR_UNKNOWN_ERROR, rv);
594 return NULL;
595 }
596 }
597 return addr;
598 }
600 PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
601 {
602 PRUint32 rv;
604 /* we just have to trust that addr & len are those used by MemMap */
605 rv = DosFreeMem(addr);
606 if (rv) {
607 PR_SetError(PR_INVALID_ARGUMENT_ERROR, rv);
608 return PR_FAILURE;
609 }
610 return PR_SUCCESS;
611 }
613 PRStatus _MD_CloseFileMap(PRFileMap *fmap)
614 {
615 /* nothing to do except free the PRFileMap struct */
616 PR_Free(fmap);
617 return PR_SUCCESS;
618 }