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