michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * os2misc.c michael@0: * michael@0: */ michael@0: michael@0: #ifdef MOZ_OS2_HIGH_MEMORY michael@0: /* os2safe.h has to be included before os2.h, needed for high mem */ michael@0: #include michael@0: #endif michael@0: michael@0: #include michael@0: #include "primpl.h" michael@0: michael@0: extern int _CRT_init(void); michael@0: extern void _CRT_term(void); michael@0: extern void __ctordtorInit(int flag); michael@0: extern void __ctordtorTerm(int flag); michael@0: michael@0: char * michael@0: _PR_MD_GET_ENV(const char *name) michael@0: { michael@0: return getenv(name); michael@0: } michael@0: michael@0: PRIntn michael@0: _PR_MD_PUT_ENV(const char *name) michael@0: { michael@0: return putenv(name); michael@0: } michael@0: michael@0: michael@0: /* michael@0: ************************************************************************** michael@0: ************************************************************************** michael@0: ** michael@0: ** Date and time routines michael@0: ** michael@0: ************************************************************************** michael@0: ************************************************************************** michael@0: */ michael@0: michael@0: #include michael@0: /* michael@0: *----------------------------------------------------------------------- michael@0: * michael@0: * PR_Now -- michael@0: * michael@0: * Returns the current time in microseconds since the epoch. michael@0: * The epoch is midnight January 1, 1970 GMT. michael@0: * The implementation is machine dependent. This is the michael@0: * implementation for OS/2. michael@0: * Cf. time_t time(time_t *tp) michael@0: * michael@0: *----------------------------------------------------------------------- michael@0: */ michael@0: michael@0: PR_IMPLEMENT(PRTime) michael@0: PR_Now(void) michael@0: { michael@0: PRInt64 s, ms, ms2us, s2us; michael@0: struct timeb b; michael@0: michael@0: ftime(&b); michael@0: LL_I2L(ms2us, PR_USEC_PER_MSEC); michael@0: LL_I2L(s2us, PR_USEC_PER_SEC); michael@0: LL_I2L(s, b.time); michael@0: LL_I2L(ms, b.millitm); michael@0: LL_MUL(ms, ms, ms2us); michael@0: LL_MUL(s, s, s2us); michael@0: LL_ADD(s, s, ms); michael@0: return s; michael@0: } michael@0: michael@0: michael@0: /* michael@0: *********************************************************************** michael@0: *********************************************************************** michael@0: * michael@0: * Process creation routines michael@0: * michael@0: *********************************************************************** michael@0: *********************************************************************** michael@0: */ michael@0: michael@0: /* michael@0: * Assemble the command line by concatenating the argv array. michael@0: * Special characters intentionally do not get escaped, and it is michael@0: * expected that the caller wraps arguments in quotes if needed michael@0: * (e.g. for filename with spaces). michael@0: * michael@0: * On success, this function returns 0 and the resulting command michael@0: * line is returned in *cmdLine. On failure, it returns -1. michael@0: */ michael@0: static int assembleCmdLine(char *const *argv, char **cmdLine) michael@0: { michael@0: char *const *arg; michael@0: int cmdLineSize; michael@0: michael@0: /* michael@0: * Find out how large the command line buffer should be. michael@0: */ michael@0: cmdLineSize = 1; /* final null */ michael@0: for (arg = argv+1; *arg; arg++) { michael@0: cmdLineSize += strlen(*arg) + 1; /* space in between */ michael@0: } michael@0: *cmdLine = PR_MALLOC(cmdLineSize); michael@0: if (*cmdLine == NULL) { michael@0: return -1; michael@0: } michael@0: michael@0: (*cmdLine)[0] = '\0'; michael@0: michael@0: for (arg = argv+1; *arg; arg++) { michael@0: if (arg > argv +1) { michael@0: strcat(*cmdLine, " "); michael@0: } michael@0: strcat(*cmdLine, *arg); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: * Assemble the environment block by concatenating the envp array michael@0: * (preserving the terminating null byte in each array element) michael@0: * and adding a null byte at the end. michael@0: * michael@0: * Returns 0 on success. The resulting environment block is returned michael@0: * in *envBlock. Note that if envp is NULL, a NULL pointer is returned michael@0: * in *envBlock. Returns -1 on failure. michael@0: */ michael@0: static int assembleEnvBlock(char **envp, char **envBlock) michael@0: { michael@0: char *p; michael@0: char *q; michael@0: char **env; michael@0: char *curEnv; michael@0: char *cwdStart, *cwdEnd; michael@0: int envBlockSize; michael@0: michael@0: PPIB ppib = NULL; michael@0: PTIB ptib = NULL; michael@0: michael@0: if (envp == NULL) { michael@0: *envBlock = NULL; michael@0: return 0; michael@0: } michael@0: michael@0: if(DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) michael@0: return -1; michael@0: michael@0: curEnv = ppib->pib_pchenv; michael@0: michael@0: cwdStart = curEnv; michael@0: while (*cwdStart) { michael@0: if (cwdStart[0] == '=' && cwdStart[1] != '\0' michael@0: && cwdStart[2] == ':' && cwdStart[3] == '=') { michael@0: break; michael@0: } michael@0: cwdStart += strlen(cwdStart) + 1; michael@0: } michael@0: cwdEnd = cwdStart; michael@0: if (*cwdEnd) { michael@0: cwdEnd += strlen(cwdEnd) + 1; michael@0: while (*cwdEnd) { michael@0: if (cwdEnd[0] != '=' || cwdEnd[1] == '\0' michael@0: || cwdEnd[2] != ':' || cwdEnd[3] != '=') { michael@0: break; michael@0: } michael@0: cwdEnd += strlen(cwdEnd) + 1; michael@0: } michael@0: } michael@0: envBlockSize = cwdEnd - cwdStart; michael@0: michael@0: for (env = envp; *env; env++) { michael@0: envBlockSize += strlen(*env) + 1; michael@0: } michael@0: envBlockSize++; michael@0: michael@0: p = *envBlock = PR_MALLOC(envBlockSize); michael@0: if (p == NULL) { michael@0: return -1; michael@0: } michael@0: michael@0: q = cwdStart; michael@0: while (q < cwdEnd) { michael@0: *p++ = *q++; michael@0: } michael@0: michael@0: for (env = envp; *env; env++) { michael@0: q = *env; michael@0: while (*q) { michael@0: *p++ = *q++; michael@0: } michael@0: *p++ = '\0'; michael@0: } michael@0: *p = '\0'; michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: * For qsort. We sort (case-insensitive) the environment strings michael@0: * before generating the environment block. michael@0: */ michael@0: static int compare(const void *arg1, const void *arg2) michael@0: { michael@0: return stricmp(* (char**)arg1, * (char**)arg2); michael@0: } michael@0: michael@0: PRProcess * _PR_CreateOS2Process( michael@0: const char *path, michael@0: char *const *argv, michael@0: char *const *envp, michael@0: const PRProcessAttr *attr) michael@0: { michael@0: PRProcess *proc = NULL; michael@0: char *cmdLine = NULL; michael@0: char **newEnvp = NULL; michael@0: char *envBlock = NULL; michael@0: michael@0: STARTDATA startData = {0}; michael@0: APIRET rc; michael@0: ULONG ulAppType = 0; michael@0: PID pid = 0; michael@0: char *pszComSpec; michael@0: char pszEXEName[CCHMAXPATH] = ""; michael@0: char pszFormatString[CCHMAXPATH]; michael@0: char pszObjectBuffer[CCHMAXPATH]; michael@0: char *pszFormatResult = NULL; michael@0: michael@0: /* michael@0: * Variables for DosExecPgm michael@0: */ michael@0: char szFailed[CCHMAXPATH]; michael@0: char *pszCmdLine = NULL; michael@0: RESULTCODES procInfo; michael@0: HFILE hStdIn = 0, michael@0: hStdOut = 0, michael@0: hStdErr = 0; michael@0: HFILE hStdInSave = -1, michael@0: hStdOutSave = -1, michael@0: hStdErrSave = -1; michael@0: michael@0: proc = PR_NEW(PRProcess); michael@0: if (!proc) { michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: goto errorExit; michael@0: } michael@0: michael@0: if (assembleCmdLine(argv, &cmdLine) == -1) { michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: goto errorExit; michael@0: } michael@0: michael@0: #ifdef MOZ_OS2_HIGH_MEMORY michael@0: /* michael@0: * DosQueryAppType() fails if path (the char* in the first argument) is in michael@0: * high memory. If that is the case, the following moves it to low memory. michael@0: */ michael@0: if ((ULONG)path >= 0x20000000) { michael@0: size_t len = strlen(path) + 1; michael@0: char *copy = (char *)alloca(len); michael@0: memcpy(copy, path, len); michael@0: path = copy; michael@0: } michael@0: #endif michael@0: michael@0: if (envp == NULL) { michael@0: newEnvp = NULL; michael@0: } else { michael@0: int i; michael@0: int numEnv = 0; michael@0: while (envp[numEnv]) { michael@0: numEnv++; michael@0: } michael@0: newEnvp = (char **) PR_MALLOC((numEnv+1) * sizeof(char *)); michael@0: for (i = 0; i <= numEnv; i++) { michael@0: newEnvp[i] = envp[i]; michael@0: } michael@0: qsort((void *) newEnvp, (size_t) numEnv, sizeof(char *), compare); michael@0: } michael@0: if (assembleEnvBlock(newEnvp, &envBlock) == -1) { michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: goto errorExit; michael@0: } michael@0: michael@0: rc = DosQueryAppType(path, &ulAppType); michael@0: if (rc != NO_ERROR) { michael@0: char *pszDot = strrchr(path, '.'); michael@0: if (pszDot) { michael@0: /* If it is a CMD file, launch the users command processor */ michael@0: if (!stricmp(pszDot, ".cmd")) { michael@0: rc = DosScanEnv("COMSPEC", (PSZ *)&pszComSpec); michael@0: if (!rc) { michael@0: strcpy(pszFormatString, "/C %s %s"); michael@0: strcpy(pszEXEName, pszComSpec); michael@0: ulAppType = FAPPTYP_WINDOWCOMPAT; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: if (ulAppType == 0) { michael@0: PR_SetError(PR_UNKNOWN_ERROR, 0); michael@0: goto errorExit; michael@0: } michael@0: michael@0: if ((ulAppType & FAPPTYP_WINDOWAPI) == FAPPTYP_WINDOWAPI) { michael@0: startData.SessionType = SSF_TYPE_PM; michael@0: } michael@0: else if (ulAppType & FAPPTYP_WINDOWCOMPAT) { michael@0: startData.SessionType = SSF_TYPE_WINDOWABLEVIO; michael@0: } michael@0: else { michael@0: startData.SessionType = SSF_TYPE_DEFAULT; michael@0: } michael@0: michael@0: if (ulAppType & (FAPPTYP_WINDOWSPROT31 | FAPPTYP_WINDOWSPROT | FAPPTYP_WINDOWSREAL)) michael@0: { michael@0: strcpy(pszEXEName, "WINOS2.COM"); michael@0: startData.SessionType = PROG_31_STDSEAMLESSVDM; michael@0: strcpy(pszFormatString, "/3 %s %s"); michael@0: } michael@0: michael@0: startData.InheritOpt = SSF_INHERTOPT_SHELL; michael@0: michael@0: if (pszEXEName[0]) { michael@0: pszFormatResult = PR_MALLOC(strlen(pszFormatString)+strlen(path)+strlen(cmdLine)); michael@0: sprintf(pszFormatResult, pszFormatString, path, cmdLine); michael@0: startData.PgmInputs = pszFormatResult; michael@0: } else { michael@0: strcpy(pszEXEName, path); michael@0: startData.PgmInputs = cmdLine; michael@0: } michael@0: startData.PgmName = pszEXEName; michael@0: michael@0: startData.Length = sizeof(startData); michael@0: startData.Related = SSF_RELATED_INDEPENDENT; michael@0: startData.ObjectBuffer = pszObjectBuffer; michael@0: startData.ObjectBuffLen = CCHMAXPATH; michael@0: startData.Environment = envBlock; michael@0: michael@0: if (attr) { michael@0: /* On OS/2, there is really no way to pass file handles for stdin, michael@0: * stdout, and stderr to a new process. Instead, we can make it michael@0: * a child process and make the given file handles a copy of our michael@0: * stdin, stdout, and stderr. The child process then inherits michael@0: * ours, and we set ours back. Twisted and gross I know. If you michael@0: * know a better way, please use it. michael@0: */ michael@0: if (attr->stdinFd) { michael@0: hStdIn = 0; michael@0: DosDupHandle(hStdIn, &hStdInSave); michael@0: DosDupHandle((HFILE) attr->stdinFd->secret->md.osfd, &hStdIn); michael@0: } michael@0: michael@0: if (attr->stdoutFd) { michael@0: hStdOut = 1; michael@0: DosDupHandle(hStdOut, &hStdOutSave); michael@0: DosDupHandle((HFILE) attr->stdoutFd->secret->md.osfd, &hStdOut); michael@0: } michael@0: michael@0: if (attr->stderrFd) { michael@0: hStdErr = 2; michael@0: DosDupHandle(hStdErr, &hStdErrSave); michael@0: DosDupHandle((HFILE) attr->stderrFd->secret->md.osfd, &hStdErr); michael@0: } michael@0: /* michael@0: * Build up the Command Line for DosExecPgm michael@0: */ michael@0: pszCmdLine = PR_MALLOC(strlen(pszEXEName) + michael@0: strlen(startData.PgmInputs) + 3); michael@0: sprintf(pszCmdLine, "%s%c%s%c", pszEXEName, '\0', michael@0: startData.PgmInputs, '\0'); michael@0: rc = DosExecPgm(szFailed, michael@0: CCHMAXPATH, michael@0: EXEC_ASYNCRESULT, michael@0: pszCmdLine, michael@0: envBlock, michael@0: &procInfo, michael@0: pszEXEName); michael@0: PR_DELETE(pszCmdLine); michael@0: michael@0: /* Restore our old values. Hope this works */ michael@0: if (hStdInSave != -1) { michael@0: DosDupHandle(hStdInSave, &hStdIn); michael@0: DosClose(hStdInSave); michael@0: } michael@0: michael@0: if (hStdOutSave != -1) { michael@0: DosDupHandle(hStdOutSave, &hStdOut); michael@0: DosClose(hStdOutSave); michael@0: } michael@0: michael@0: if (hStdErrSave != -1) { michael@0: DosDupHandle(hStdErrSave, &hStdErr); michael@0: DosClose(hStdErrSave); michael@0: } michael@0: michael@0: if (rc != NO_ERROR) { michael@0: /* XXX what error code? */ michael@0: PR_SetError(PR_UNKNOWN_ERROR, rc); michael@0: goto errorExit; michael@0: } michael@0: michael@0: proc->md.pid = procInfo.codeTerminate; michael@0: } else { michael@0: /* michael@0: * If no STDIN/STDOUT redirection is not needed, use DosStartSession michael@0: * to create a new, independent session michael@0: */ michael@0: rc = DosStartSession(&startData, &ulAppType, &pid); michael@0: michael@0: if ((rc != NO_ERROR) && (rc != ERROR_SMG_START_IN_BACKGROUND)) { michael@0: PR_SetError(PR_UNKNOWN_ERROR, rc); michael@0: goto errorExit; michael@0: } michael@0: michael@0: proc->md.pid = pid; michael@0: } michael@0: michael@0: if (pszFormatResult) { michael@0: PR_DELETE(pszFormatResult); michael@0: } michael@0: michael@0: PR_DELETE(cmdLine); michael@0: if (newEnvp) { michael@0: PR_DELETE(newEnvp); michael@0: } michael@0: if (envBlock) { michael@0: PR_DELETE(envBlock); michael@0: } michael@0: return proc; michael@0: michael@0: errorExit: michael@0: if (cmdLine) { michael@0: PR_DELETE(cmdLine); michael@0: } michael@0: if (newEnvp) { michael@0: PR_DELETE(newEnvp); michael@0: } michael@0: if (envBlock) { michael@0: PR_DELETE(envBlock); michael@0: } michael@0: if (proc) { michael@0: PR_DELETE(proc); michael@0: } michael@0: return NULL; michael@0: } /* _PR_CreateOS2Process */ michael@0: michael@0: PRStatus _PR_DetachOS2Process(PRProcess *process) michael@0: { michael@0: /* On OS/2, a process is either created as a child or not. michael@0: * You can't 'detach' it later on. michael@0: */ michael@0: PR_DELETE(process); michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: /* michael@0: * XXX: This will currently only work on a child process. michael@0: */ michael@0: PRStatus _PR_WaitOS2Process(PRProcess *process, michael@0: PRInt32 *exitCode) michael@0: { michael@0: ULONG ulRetVal; michael@0: RESULTCODES results; michael@0: PID pidEnded = 0; michael@0: michael@0: ulRetVal = DosWaitChild(DCWA_PROCESS, DCWW_WAIT, michael@0: &results, michael@0: &pidEnded, process->md.pid); michael@0: michael@0: if (ulRetVal != NO_ERROR) { michael@0: printf("\nDosWaitChild rc = %lu\n", ulRetVal); michael@0: PR_SetError(PR_UNKNOWN_ERROR, ulRetVal); michael@0: return PR_FAILURE; michael@0: } michael@0: PR_DELETE(process); michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: PRStatus _PR_KillOS2Process(PRProcess *process) michael@0: { michael@0: ULONG ulRetVal; michael@0: if ((ulRetVal = DosKillProcess(DKP_PROCESS, process->md.pid)) == NO_ERROR) { michael@0: return PR_SUCCESS; michael@0: } michael@0: PR_SetError(PR_UNKNOWN_ERROR, ulRetVal); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: PRStatus _MD_OS2GetHostName(char *name, PRUint32 namelen) michael@0: { michael@0: PRIntn rv; michael@0: michael@0: rv = gethostname(name, (PRInt32) namelen); michael@0: if (0 == rv) { michael@0: return PR_SUCCESS; michael@0: } michael@0: _PR_MD_MAP_GETHOSTNAME_ERROR(sock_errno()); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: void michael@0: _PR_MD_WAKEUP_CPUS( void ) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: /* michael@0: ********************************************************************** michael@0: * michael@0: * Memory-mapped files michael@0: * michael@0: * A credible emulation of memory-mapped i/o on OS/2 would require michael@0: * an exception-handler on each thread that might access the mapped michael@0: * memory. In the Mozilla environment, that would be impractical. michael@0: * Instead, the following simulates those modes which don't modify michael@0: * the mapped file. It reads the entire mapped file segment at once michael@0: * when MemMap is called, and frees it on MemUnmap. CreateFileMap michael@0: * only does sanity-checks, while CloseFileMap does almost nothing. michael@0: * michael@0: ********************************************************************** michael@0: */ michael@0: michael@0: PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size) michael@0: { michael@0: PRFileInfo64 info; michael@0: michael@0: /* assert on PR_PROT_READWRITE which modifies the underlying file */ michael@0: PR_ASSERT(fmap->prot == PR_PROT_READONLY || michael@0: fmap->prot == PR_PROT_WRITECOPY); michael@0: if (fmap->prot != PR_PROT_READONLY && michael@0: fmap->prot != PR_PROT_WRITECOPY) { michael@0: PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); michael@0: return PR_FAILURE; michael@0: } michael@0: if (PR_GetOpenFileInfo64(fmap->fd, &info) == PR_FAILURE) { michael@0: return PR_FAILURE; michael@0: } michael@0: /* reject zero-byte mappings & zero-byte files */ michael@0: if (!size || !info.size) { michael@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); michael@0: return PR_FAILURE; michael@0: } michael@0: /* file size rounded up to the next page boundary */ michael@0: fmap->md.maxExtent = (info.size + 0xfff) & ~(0xfff); michael@0: michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: PRInt32 _MD_GetMemMapAlignment(void) michael@0: { michael@0: /* OS/2 pages are 4k */ michael@0: return 0x1000; michael@0: } michael@0: michael@0: void * _MD_MemMap(PRFileMap *fmap, PROffset64 offset, PRUint32 len) michael@0: { michael@0: PRUint32 rv; michael@0: void *addr; michael@0: michael@0: /* prevent mappings beyond EOF + remainder of page */ michael@0: if (offset + len > fmap->md.maxExtent) { michael@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); michael@0: return NULL; michael@0: } michael@0: if (PR_Seek64(fmap->fd, offset, PR_SEEK_SET) == -1) { michael@0: return NULL; michael@0: } michael@0: /* try for high memory, fall back to low memory if hi-mem fails */ michael@0: rv = DosAllocMem(&addr, len, OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE); michael@0: if (rv) { michael@0: rv = DosAllocMem(&addr, len, PAG_COMMIT | PAG_READ | PAG_WRITE); michael@0: if (rv) { michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, rv); michael@0: return NULL; michael@0: } michael@0: } michael@0: if (PR_Read(fmap->fd, addr, len) == -1) { michael@0: DosFreeMem(addr); michael@0: return NULL; michael@0: } michael@0: /* don't permit writes if readonly */ michael@0: if (fmap->prot == PR_PROT_READONLY) { michael@0: rv = DosSetMem(addr, len, PAG_READ); michael@0: if (rv) { michael@0: DosFreeMem(addr); michael@0: PR_SetError(PR_UNKNOWN_ERROR, rv); michael@0: return NULL; michael@0: } michael@0: } michael@0: return addr; michael@0: } michael@0: michael@0: PRStatus _MD_MemUnmap(void *addr, PRUint32 len) michael@0: { michael@0: PRUint32 rv; michael@0: michael@0: /* we just have to trust that addr & len are those used by MemMap */ michael@0: rv = DosFreeMem(addr); michael@0: if (rv) { michael@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, rv); michael@0: return PR_FAILURE; michael@0: } michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: PRStatus _MD_CloseFileMap(PRFileMap *fmap) michael@0: { michael@0: /* nothing to do except free the PRFileMap struct */ michael@0: PR_Free(fmap); michael@0: return PR_SUCCESS; michael@0: } michael@0: