diff --git a/CMakeLists.txt b/CMakeLists.txt index c8946b5..bf5214c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,8 @@ foreach(_I doscalls;msg;nls;quecalls;viocalls;kbdcalls;sesmgr) set_target_properties(${_I} PROPERTIES LINK_FLAGS "-m32 -ggdb3") endforeach() +target_link_libraries(doscalls "pthread") + add_executable(lx_dump lx_dump.c) add_executable(lx_loader lx_loader.c) diff --git a/lx_loader.c b/lx_loader.c index c5fb2b6..dfaabc8 100644 --- a/lx_loader.c +++ b/lx_loader.c @@ -9,6 +9,7 @@ #include #include #include +#include // 16-bit selector kernel nonsense... #include @@ -279,8 +280,7 @@ static void *generateMissingTrampoline(const char *_module, const char *_entry) // the EMX runtime) touch the register directly, so we have to deal with it. // You must call this once for each thread that will go into LX land, from // that thread, as soon as possible after starting. -// !!! FIXME: eventually, we'll have a deinit equivalent for the end of threads, too. -static void initOs2Tib(void *_topOfStack, const size_t stacklen) +static uint16 initOs2Tib(void *_topOfStack, const size_t stacklen, const uint32 tid) { uint8 *topOfStack = (uint8 *) _topOfStack; const size_t tiblen = (sizeof (LxTIB) + sizeof (LxTIB2)); @@ -300,7 +300,7 @@ static void initOs2Tib(void *_topOfStack, const size_t stacklen) tib->tib_version = 20; // !!! FIXME tib->tib_ordinal = 79; // !!! FIXME - tib2->tib2_ultid = 1; + tib2->tib2_ultid = tid; tib2->tib2_ulpri = 512; tib2->tib2_version = 20; tib2->tib2_usMCCount = 0; @@ -325,15 +325,27 @@ static void initOs2Tib(void *_topOfStack, const size_t stacklen) // Wine does it. I still don't know why. const unsigned int segment = (entry.entry_number << 3) | 3; __asm__ __volatile__ ( "movw %%ax, %%fs \n\t" : : "a" (segment) ); + return (uint16) segment; } // initOs2Tib - -static __attribute__((noreturn)) void runLxModule(LxModule *lxmod, const int argc, char **argv) +static void deinitOs2Tib(const uint16 selector) { - // !!! FIXME: right now, we don't list any environment variables, because they probably don't make sense to drop in (even PATH uses a different separator on Unix). - // !!! FIXME: eventually, the environment table looks like this (double-null to terminate list): var1=a\0var2=b\0var3=c\0\0 - // The command line looks like this... - // \0argv0\0argv1 argv2 argvN\0\0 + // !!! FIXME: I barely know what I'm doing here, this could all be wrong. + struct user_desc entry; + memset(&entry, '\0', sizeof (entry)); + entry.entry_number = selector; + entry.read_exec_only = 1; + entry.seg_not_present = 1; + + const long rc = syscall(SYS_set_thread_area, &entry); + assert(rc == 0); FIXME("this can legit fail, though!"); +} // deinitOs2Tib + + +static __attribute__((noreturn)) void runLxModule(LxModule *lxmod, const int argc, char **argv, char **envp) +{ + // Eventually, the environment table looks like this (double-null to terminate list): var1=a\0var2=b\0var3=c\0\0 + // The command line looks like this: \0argv0\0argv1 argv2 argvN\0\0 size_t len = 1; for (int i = 0; i < argc; i++) { @@ -353,11 +365,20 @@ static __attribute__((noreturn)) void runLxModule(LxModule *lxmod, const int arg } // if len++; // terminator - } + } // for + + const char *default_os2path = "PATH=C:\\home\\icculus\\Dropbox\\emx\\bin;C:\\home\\icculus"; // !!! FIXME: noooooope. + for (int i = 0; envp[i]; i++) { + const char *str = envp[i]; + if (strncmp(str, "PATH=", 5) == 0) { + if (!GLoaderState->subprocess) + str = default_os2path; + } else if (strncmp(str, "IS_2INE=", 8) == 0) { + continue; + } // if + len += strlen(str) + 1; + } // for - const char *tmpenv = "IS_2INE=1"; - const size_t envlen = strlen(tmpenv); - len += envlen; len += 4; // null terminators. char *env = (char *) malloc(len); @@ -366,10 +387,21 @@ static __attribute__((noreturn)) void runLxModule(LxModule *lxmod, const int arg exit(1); } // if - strcpy(env, tmpenv); - char *ptr = env + envlen; - *(ptr++) = '\0'; + char *ptr = env; + for (int i = 0; envp[i]; i++) { + const char *str = envp[i]; + if (strncmp(str, "PATH=", 5) == 0) { + if (!GLoaderState->subprocess) + str = default_os2path; + } else if (strncmp(str, "IS_2INE=", 8) == 0) { + continue; + } // if + + strcpy(ptr, str); + ptr += strlen(str) + 1; + } *(ptr++) = '\0'; + char *cmd = ptr; strcpy(ptr, argv[0]); ptr += strlen(argv[0]); @@ -920,7 +952,7 @@ static LxModule *loadLxModule(const char *fname, uint8 *exe, uint32 exelen, int if (!isDLL) { // This needs to be set up now, so it's available to any library // init code that runs in LX land. - initOs2Tib((void *) ((size_t) retval->esp), retval->mmaps[retval->lx.esp_object - 1].size); + initOs2Tib((void *) ((size_t) retval->esp), retval->mmaps[retval->lx.esp_object - 1].size, 0); } // if // Set up our exports... @@ -1137,8 +1169,10 @@ static LxModule *loadNativeModule(const char *fname, const char *modname) // !!! FIXME: mutex this. lib = dlopen(fname, RTLD_LOCAL | RTLD_NOW); - if (lib == NULL) + if (lib == NULL) { + fprintf(stderr, "dlopen('%s') failed: %s\n", fname, dlerror()); goto loadnative_failed; + } // if fn = (LxNativeModuleInitEntryPoint) dlsym(lib, "lxNativeModuleInit"); if (!fn) @@ -1206,23 +1240,31 @@ static inline LxModule *loadLxModuleByModuleName(const char *modname) return loadLxModuleByModuleNameInternal(modname, 0); } // loadLxModuleByModuleName -int main(int argc, char **argv) +int main(int argc, char **argv, char **envp) { if (argc < 2) { fprintf(stderr, "USAGE: %s [...programargs...]\n", argv[0]); return 1; } + const char *envr = getenv("IS_2INE"); + GLoaderState->subprocess = (envr != NULL); GLoaderState->initOs2Tib = initOs2Tib; + GLoaderState->deinitOs2Tib = deinitOs2Tib; - LxModule *lxmod = loadLxModuleByPath(argv[1]); - if (lxmod) { + LxModule *lxmod = loadLxModuleByPath(envr ? envr : argv[1]); + if (!lxmod) { + return 1; + } + + // standalone, drop argv[0]. As a subprocess, keep it. + if (!GLoaderState->subprocess) { argc--; argv++; - runLxModule(lxmod, argc, argv); } // if + runLxModule(lxmod, argc, argv, envp); - return 0; + return 1; // you shouldn't hit this, but if you do, report failure. } // main // end of lx_loader.c ... diff --git a/lx_loader.h b/lx_loader.h index 803907e..bbc5ffc 100644 --- a/lx_loader.h +++ b/lx_loader.h @@ -173,7 +173,6 @@ typedef struct LxTIB2 uint16 tib2_usMCCount; uint16 tib2_fMCForceFlag; } LxTIB2; -#pragma pack(pop) typedef struct LxTIB { @@ -184,12 +183,15 @@ typedef struct LxTIB uint32 tib_version; uint32 tib_ordinal; } LxTIB; +#pragma pack(pop) typedef struct LxLoaderState { LxModule *loaded_modules; LxModule *main_module; - void (*initOs2Tib)(void *_topOfStack, const size_t stacklen); + int subprocess; + uint16 (*initOs2Tib)(void *_topOfStack, const size_t stacklen, const uint32 tid); + void (*deinitOs2Tib)(const uint16 selector); } LxLoaderState; typedef const LxExport *(*LxNativeModuleInitEntryPoint)(LxLoaderState *lx_state, uint32 *lx_num_exports); diff --git a/native/doscalls.c b/native/doscalls.c index 186e7f9..4467bbe 100644 --- a/native/doscalls.c +++ b/native/doscalls.c @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -18,6 +20,8 @@ static LxLoaderState *GLoaderState = NULL; +static pthread_mutex_t GMutexDosCalls; + typedef struct ExitListItem { PFNEXITLIST fn; @@ -62,22 +66,58 @@ typedef struct HFileInfo static HFileInfo *HFiles = NULL; static uint32 MaxHFiles = 0; +typedef struct Thread +{ + pthread_t thread; + size_t stacklen; + PFNTHREAD fn; + ULONG fnarg; + struct Thread *prev; + struct Thread *next; +} Thread; + +static Thread *GDeadThreads = NULL; + + +typedef struct +{ + pthread_mutex_t mutex; + pthread_cond_t condition; + volatile int posted; + volatile int waiting; +} EventSem; + LX_NATIVE_MODULE_DEINIT({ ExitListItem *next = GExitList; GExitList = NULL; + for (ExitListItem *item = next; item; item = next) { next = item->next; free(item); } // for + + for (uint32 i = 0; i < MaxHFiles; i++) { + if (HFiles[i].fd != -1) + close(HFiles[i].fd); + } // for free(HFiles); HFiles = NULL; MaxHFiles = 0; + + pthread_mutex_destroy(&GMutexDosCalls); + GLoaderState = NULL; }) static int initDoscalls(LxLoaderState *lx_state) { GLoaderState = lx_state; + + if (pthread_mutex_init(&GMutexDosCalls, NULL) == -1) { + fprintf(stderr, "pthread_mutex_init failed!\n"); + return 0; + } // if + MaxHFiles = 20; // seems to be OS/2's default. HFiles = (HFileInfo *) malloc(sizeof (HFileInfo) * MaxHFiles); if (!HFiles) { @@ -112,6 +152,7 @@ static int initDoscalls(LxLoaderState *lx_state) } // initDoscalls LX_NATIVE_MODULE_INIT({ if (!initDoscalls(lx_state)) return NULL; }) + LX_NATIVE_EXPORT(DosSetPathInfo, 219), LX_NATIVE_EXPORT(DosQueryPathInfo, 223), LX_NATIVE_EXPORT(DosQueryHType, 224), LX_NATIVE_EXPORT(DosScanEnv, 227), @@ -119,21 +160,33 @@ LX_NATIVE_MODULE_INIT({ if (!initDoscalls(lx_state)) return NULL; }) LX_NATIVE_EXPORT(DosExit, 234), LX_NATIVE_EXPORT(DosSetFilePtr, 256), LX_NATIVE_EXPORT(DosClose, 257), + LX_NATIVE_EXPORT(DosDelete, 259), LX_NATIVE_EXPORT(DosOpen, 273), + LX_NATIVE_EXPORT(DosQueryCurrentDir, 274), LX_NATIVE_EXPORT(DosQueryFileInfo, 279), + LX_NATIVE_EXPORT(DosWaitChild, 280), LX_NATIVE_EXPORT(DosRead, 281), LX_NATIVE_EXPORT(DosWrite, 282), + LX_NATIVE_EXPORT(DosExecPgm, 283), LX_NATIVE_EXPORT(DosExitList, 296), LX_NATIVE_EXPORT(DosAllocMem, 299), + LX_NATIVE_EXPORT(DosFreeMem, 304), LX_NATIVE_EXPORT(DosSetMem, 305), + LX_NATIVE_EXPORT(DosCreateThread, 311), LX_NATIVE_EXPORT(DosGetInfoBlocks, 312), LX_NATIVE_EXPORT(DosQueryModuleName, 320), LX_NATIVE_EXPORT(DosCreateEventSem, 324), + LX_NATIVE_EXPORT(DosCloseEventSem, 326), + LX_NATIVE_EXPORT(DosResetEventSem, 327), + LX_NATIVE_EXPORT(DosPostEventSem, 328), + LX_NATIVE_EXPORT(DosWaitEventSem, 329), + LX_NATIVE_EXPORT(DosQueryEventSem, 330), LX_NATIVE_EXPORT(DosCreateMutexSem, 331), LX_NATIVE_EXPORT(DosRequestMutexSem, 334), LX_NATIVE_EXPORT(DosReleaseMutexSem, 335), LX_NATIVE_EXPORT(DosSubSetMem, 344), LX_NATIVE_EXPORT(DosSubAllocMem, 345), + LX_NATIVE_EXPORT(DosSubFreeMem, 346), LX_NATIVE_EXPORT(DosQuerySysInfo, 348), LX_NATIVE_EXPORT(DosSetExceptionHandler, 354), LX_NATIVE_EXPORT(DosSetSignalExceptionFocus, 378), @@ -144,6 +197,28 @@ LX_NATIVE_MODULE_INIT({ if (!initDoscalls(lx_state)) return NULL; }) LX_NATIVE_MODULE_INIT_END() +static int grabLock(pthread_mutex_t *lock) +{ + return (pthread_mutex_lock(lock) == 0); +} // grabLock + +static void ungrabLock(pthread_mutex_t *lock) +{ + pthread_mutex_unlock(lock); +} // ungrabLock + +static void timespecNowPlusMilliseconds(struct timespec *waittime, const ULONG ulTimeout) +{ + clock_gettime(CLOCK_REALTIME, waittime); + waittime->tv_sec += ulTimeout / 1000; + waittime->tv_nsec += (ulTimeout % 1000) * 1000000; + if (waittime->tv_nsec >= 1000000000) { + waittime->tv_sec++; + waittime->tv_nsec -= 1000000000; + assert(waittime->tv_nsec < 1000000000); + } // if +} // timespecNowPlusMilliseconds + static PTIB2 getTib2(void) { // just read the FS register, since we have to stick it there anyhow... @@ -296,16 +371,26 @@ APIRET DosScanEnv(PSZ name, PSZ *outval) return ERROR_ENVVAR_NOT_FOUND; } // DosScanEnv + +static int getHFileUnixDescriptor(HFILE h) +{ + grabLock(&GMutexDosCalls); + const int fd = (h < MaxHFiles) ? HFiles[h].fd : -1; + ungrabLock(&GMutexDosCalls); + return fd; +} // getHFileUnixDescriptor + + APIRET DosWrite(HFILE h, PVOID buf, ULONG buflen, PULONG actual) { TRACE_NATIVE("DosWrite(%u, %p, %u, %p)", (uint) h, buf, (uint) buflen, actual); - // !!! FIXME: writing to a terminal should probably convert CR/LF to LF. - - if ((h >= MaxHFiles) || (HFiles[h].fd == -1)) + const int fd = getHFileUnixDescriptor(h); + if (fd == -1) return ERROR_INVALID_HANDLE; - const int rc = write(HFiles[h].fd, buf, buflen); + // !!! FIXME: writing to a terminal should probably convert CR/LF to LF. + const int rc = write(fd, buf, buflen); if (rc < 0) { *actual = 0; return ERROR_DISK_FULL; // !!! FIXME: map these errors. @@ -336,8 +421,7 @@ VOID DosExit(ULONG action, ULONG exitcode) // !!! FIXME: what does a value other than 0 or 1 do here? if (action == EXIT_THREAD) { // !!! FIXME: terminate thread. If last thread: terminate process. - fprintf(stderr, "FIXME: DosExit(0) should terminate thread, not process.\n"); - fflush(stderr); + FIXME("DosExit(0) should terminate thread, not process"); } // if // terminate the process. @@ -348,7 +432,10 @@ VOID DosExit(ULONG action, ULONG exitcode) // OS/2's docs say this only keeps the lower 16 bits of exitcode. // !!! FIXME: ...but Unix only keeps the lowest 8 bits. Will have to // !!! FIXME: tapdance to pass larger values back to OS/2 parent processes. - exit((int) (exitcode & 0xFFFF)); + if (exitcode > 255) + FIXME("deal with process exit codes > 255. We clamped this one!"); + + _exit((int) (exitcode & 0xFF)); } // DosExit APIRET DosExitList(ULONG ordercode, PFNEXITLIST fn) @@ -415,14 +502,76 @@ APIRET DosExitList(ULONG ordercode, PFNEXITLIST fn) APIRET DosCreateEventSem(PSZ name, PHEV phev, ULONG attr, BOOL32 state) { TRACE_NATIVE("DosCreateEventSem('%s', %p, %u, %u)", name, phev, (uint) attr, (uint) state); - // !!! FIXME: write me + + if (name != NULL) { + FIXME("implement named semaphores"); + return ERROR_NOT_ENOUGH_MEMORY; + } // if + + if (attr & DC_SEM_SHARED) { + FIXME("implement shared semaphores"); + } // if + + assert(sizeof (HEV) == sizeof (EventSem *)); + EventSem *sem = (EventSem *) malloc(sizeof (EventSem)); + if (sem == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + if (pthread_mutex_init(&sem->mutex, NULL) == -1) { + free(sem); + return ERROR_NOT_ENOUGH_MEMORY; + } // if + + if (pthread_cond_init(&sem->condition, NULL) == -1) { + pthread_mutex_destroy(&sem->mutex); + free(sem); + return ERROR_NOT_ENOUGH_MEMORY; + } // if + + sem->posted = state ? 1 : 0; + + *phev = (HEV) sem; + return NO_ERROR; } // DosCreateEventSem APIRET DosCreateMutexSem(PSZ name, PHMTX phmtx, ULONG attr, BOOL32 state) { TRACE_NATIVE("DosCreateMutexSem('%s', %p, %u, %u)", name, phmtx, (uint) attr, (uint) state); - // !!! FIXME: write me + + if (name != NULL) { + FIXME("implement named mutexes"); + return ERROR_NOT_ENOUGH_MEMORY; + } // if + + if (attr & DC_SEM_SHARED) { + FIXME("implement shared mutexes"); + } // if + + assert(sizeof (HMTX) == sizeof (pthread_mutex_t *)); + pthread_mutex_t *mtx = (pthread_mutex_t *) malloc(sizeof (pthread_mutex_t)); + if (mtx == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // OS/2 mutexes are recursive. + pthread_mutexattr_t muxattr; + pthread_mutexattr_init(&muxattr); + pthread_mutexattr_settype(&muxattr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutexattr_setrobust(&muxattr, PTHREAD_MUTEX_ROBUST); + + const int rc = pthread_mutex_init(mtx, &muxattr); + pthread_mutexattr_destroy(&muxattr); + + if (rc == -1) { + free(mtx); + return ERROR_NOT_ENOUGH_MEMORY; + } // if + + if (state) + pthread_mutex_lock(mtx); + + *phmtx = (HMTX) mtx; + return NO_ERROR; } // DosCreateMutexSem @@ -492,6 +641,7 @@ APIRET DosSetRelMaxFH(PLONG pincr, PULONG pcurrent) // OS/2 API docs say reductions in file handles are advisory, so we // ignore them outright. Fight me. if (incr > 0) { + grabLock(&GMutexDosCalls); HFileInfo *info = (HFileInfo *) realloc(HFiles, sizeof (HFileInfo) * (MaxHFiles + incr)); if (info != NULL) { HFiles = info; @@ -502,11 +652,14 @@ APIRET DosSetRelMaxFH(PLONG pincr, PULONG pcurrent) } // for MaxHFiles += incr; } // if + ungrabLock(&GMutexDosCalls); } // if } // if if (pcurrent != NULL) { + grabLock(&GMutexDosCalls); *pcurrent = MaxHFiles; + ungrabLock(&GMutexDosCalls); } // if return NO_ERROR; // always returns NO_ERROR even if it fails. @@ -545,14 +698,19 @@ APIRET DosQueryHType(HFILE hFile, PULONG pType, PULONG pAttr) { TRACE_NATIVE("DosQueryHType(%u, %p, %p)", (uint) hFile, pType, pAttr); + APIRET retval = NO_ERROR; + + grabLock(&GMutexDosCalls); if ((hFile >= MaxHFiles) || (HFiles[hFile].fd == -1)) - return ERROR_INVALID_HANDLE; + retval = ERROR_INVALID_HANDLE; + else { + // OS/2 will dereference a NULL pointer here, but I can't do it...!!! + if (pType) *pType = HFiles[hFile].type; + if (pAttr) *pAttr = HFiles[hFile].attr; + } // else + ungrabLock(&GMutexDosCalls); - // OS/2 will dereference a NULL pointer here, but I can't do it...!!! - if (pType) *pType = HFiles[hFile].type; - if (pAttr) *pAttr = HFiles[hFile].attr; - - return NO_ERROR; + return retval; } // DosQueryHType APIRET DosSetMem(PVOID pb, ULONG cb, ULONG flag) @@ -621,7 +779,7 @@ static int locateOneElement(char *buf) return 0; } // locateOneElement -int locatePathCaseInsensitive(char *buf) +static int locatePathCaseInsensitive(char *buf) { int rc; char *ptr = buf; @@ -775,14 +933,21 @@ APIRET DosOpen(PSZ pszFileName, PHFILE pHf, PULONG pulAction, ULONG cbFile, ULON HFileInfo *info = HFiles; uint32 i; - // !!! FIXME: mutex this. + if (!grabLock(&GMutexDosCalls)) + return ERROR_SYS_INTERNAL; + for (i = 0; i < MaxHFiles; i++, info++) { - if (info->fd == -1) // available? + if (info->fd == -1) { // available? + info->fd = -2; break; + } // if } // for - if (i == MaxHFiles) + ungrabLock(&GMutexDosCalls); + + if (i == MaxHFiles) { return ERROR_TOO_MANY_OPEN_FILES; + } const HFILE hf = i; @@ -793,44 +958,46 @@ APIRET DosOpen(PSZ pszFileName, PHFILE pHf, PULONG pulAction, ULONG cbFile, ULON return err; } // if - if (strcmp(pszFileName, unixpath) != 0) { printf("DosOpen: converted '%s' to '%s'\n", pszFileName, unixpath); } + if (strcmp(pszFileName, unixpath) != 0) { fprintf(stderr, "DosOpen: converted '%s' to '%s'\n", pszFileName, unixpath); } - info->fd = open(unixpath, flags, mode); + int fd = open(unixpath, flags, mode); // if failed trying exclusive-create because file already exists, but we // didn't explicitly _need_ exclusivity, retry without O_EXCL. We always // try O_EXCL at first when using O_CREAT, so we can tell atomically if // we were the one that created the file, to satisfy pulAction. int existed = 0; - if ((info->fd == -1) && (flags & (O_CREAT|O_EXCL)) && (errno == EEXIST) && !isExclusive) { + if ((fd == -1) && (flags & (O_CREAT|O_EXCL)) && (errno == EEXIST) && !isExclusive) { existed = 1; - info->fd = open(unixpath, flags & ~O_EXCL, mode); - } else if (info->fd != -1) { + fd = open(unixpath, flags & ~O_EXCL, mode); + } else if (fd != -1) { existed = 1; } // else if free(unixpath); - if (info->fd != -1) { + if (fd != -1) { if (mustNotExist) { - close(info->fd); + close(fd); info->fd = -1; return ERROR_OPEN_FAILED; // !!! FIXME: what error does OS/2 return for this? } else if ( (!existed || isReplacing) && (cbFile > 0) ) { if (ftruncate(info->fd, cbFile) == -1) { const int e = errno; - close(info->fd); + close(fd); info->fd = -1; errno = e; // let the switch below sort out the OS/2 error code. } // if } // else if } // if - if (info->fd != -1) { + if (fd != -1) { if (isReplacing) FIXME("Replacing a file should delete all its EAs, too"); } // if - if (info->fd == -1) { + info->fd = fd; + + if (fd == -1) { switch (errno) { case EACCES: return ERROR_ACCESS_DENIED; case EDQUOT: return ERROR_CANNOT_MAKE; @@ -865,15 +1032,49 @@ APIRET DosOpen(PSZ pszFileName, PHFILE pHf, PULONG pulAction, ULONG cbFile, ULON APIRET DosRequestMutexSem(HMTX hmtx, ULONG ulTimeout) { TRACE_NATIVE("DosRequestMutexSem(%u, %u)", (uint) hmtx, (uint) ulTimeout); - FIXME("implement this when we can spin threads"); - return NO_ERROR; + + if (!hmtx) + return ERROR_INVALID_HANDLE; + + pthread_mutex_t *mutex = (pthread_mutex_t *) hmtx; + int rc; + + if (ulTimeout == SEM_IMMEDIATE_RETURN) { + rc = pthread_mutex_trylock(mutex); + } else if (ulTimeout == SEM_INDEFINITE_WAIT) { + rc = pthread_mutex_lock(mutex); + } else { + struct timespec waittime; + timespecNowPlusMilliseconds(&waittime, ulTimeout); + rc = pthread_mutex_timedlock(mutex, &waittime); + } // else + + switch (rc) { + case 0: return NO_ERROR; + case EAGAIN: return ERROR_TOO_MANY_SEM_REQUESTS; + case EOWNERDEAD: return ERROR_SEM_OWNER_DIED; + case ETIMEDOUT: return ERROR_TIMEOUT; + default: break; + } // switch + + return ERROR_INVALID_HANDLE; // oh well. } // DosRequestMutexSem APIRET DosReleaseMutexSem(HMTX hmtx) { TRACE_NATIVE("DosReleaseMutexSem(%u)", (uint) hmtx); - FIXME("implement this when we can spin threads"); - return NO_ERROR; + + if (!hmtx) + return ERROR_INVALID_HANDLE; + + pthread_mutex_t *mutex = (pthread_mutex_t *) hmtx; + const int rc = pthread_mutex_unlock(mutex); + if (rc == 0) + return NO_ERROR; + else if (rc == EPERM) + return ERROR_NOT_OWNER; + + return ERROR_INVALID_HANDLE; } // DosReleaseMutexSem APIRET DosSetFilePtr(HFILE hFile, LONG ib, ULONG method, PULONG ibActual) @@ -888,10 +1089,11 @@ APIRET DosSetFilePtr(HFILE hFile, LONG ib, ULONG method, PULONG ibActual) default: return ERROR_INVALID_FUNCTION; } // switch - if ((hFile >= MaxHFiles) || (HFiles[hFile].fd == -1)) + const int fd = getHFileUnixDescriptor(hFile); + if (fd == -1) return ERROR_INVALID_HANDLE; - const off_t pos = lseek(HFiles[hFile].fd, (off_t) ib, whence); + const off_t pos = lseek(fd, (off_t) ib, whence); if (pos == -1) { if (errno == EINVAL) return ERROR_NEGATIVE_SEEK; @@ -908,10 +1110,11 @@ APIRET DosRead(HFILE hFile, PVOID pBuffer, ULONG cbRead, PULONG pcbActual) { TRACE_NATIVE("DosRead(%u, %p, %u, %p)", (uint) hFile, pBuffer, (uint) cbRead, pcbActual); - if ((hFile >= MaxHFiles) || (HFiles[hFile].fd == -1)) + const int fd = getHFileUnixDescriptor(hFile); + if (fd == -1) return ERROR_INVALID_HANDLE; - const ssize_t br = read(HFiles[hFile].fd, pBuffer, cbRead); + const ssize_t br = read(fd, pBuffer, cbRead); if (br == -1) return ERROR_INVALID_FUNCTION; // !!! FIXME: ? @@ -925,16 +1128,22 @@ APIRET DosClose(HFILE hFile) { TRACE_NATIVE("DosClose(%u)", (uint) hFile); - if ((hFile >= MaxHFiles) || (HFiles[hFile].fd == -1)) + const int fd = getHFileUnixDescriptor(hFile); + if (fd == -1) return ERROR_INVALID_HANDLE; - const int rc = close(HFiles[hFile].fd); + if (fd <= 2) { FIXME("remove me"); return NO_ERROR; } // for debugging purposes, remove me later. + + const int rc = close(fd); if (rc == -1) return ERROR_ACCESS_DENIED; // !!! FIXME: ? + grabLock(&GMutexDosCalls); HFiles[hFile].fd = -1; HFiles[hFile].type = 0; HFiles[hFile].attr = 0; + ungrabLock(&GMutexDosCalls); + return NO_ERROR; } // DosClose @@ -1135,11 +1344,12 @@ APIRET DosQueryFileInfo(HFILE hf, ULONG ulInfoLevel, PVOID pInfo, ULONG cbInfoBu { TRACE_NATIVE("DosQueryFileInfo(%u, %u, %p, %u)", (uint) hf, (uint) ulInfoLevel, pInfo, (uint) cbInfoBuf); - if ((hf >= MaxHFiles) || (HFiles[hf].fd == -1)) + const int fd = getHFileUnixDescriptor(hf); + if (fd == -1) return ERROR_INVALID_HANDLE; struct stat statbuf; - if (fstat(HFiles[hf].fd, &statbuf) == -1) + if (fstat(fd, &statbuf) == -1) return ERROR_INVALID_HANDLE; // !!! FIXME: ...? switch (ulInfoLevel) { @@ -1152,5 +1362,726 @@ APIRET DosQueryFileInfo(HFILE hf, ULONG ulInfoLevel, PVOID pInfo, ULONG cbInfoBu return ERROR_INVALID_LEVEL; } // DosQueryFileInfo + +static void *os2ThreadEntry(void *_arg) +{ + Thread *thread = (Thread *) _arg; + + // put our thread's TIB structs on the stack and call that the top of the stack. + uint8 tibdata[sizeof (TIB) + sizeof (TIB2)]; + const uint16 selector = GLoaderState->initOs2Tib(tibdata + sizeof (tibdata), thread->stacklen, (TID) thread); + thread->fn(thread->fnarg); + GLoaderState->deinitOs2Tib(selector); + + grabLock(&GMutexDosCalls); + thread->prev = NULL; + thread->next = GDeadThreads; + GDeadThreads = thread; + ungrabLock(&GMutexDosCalls); + + return NULL; // OS/2 threads don't return a value here. +} // os2ThreadEntry + +APIRET DosCreateThread(PTID ptid, PFNTHREAD pfn, ULONG param, ULONG flag, ULONG cbStack) +{ + TRACE_NATIVE("DosCreateThread(%p, %p, %u, %u, %u)", ptid, pfn, (uint) param, (uint) flag, (uint) cbStack); + + if (flag & CREATE_SUSPENDED) + FIXME("Can't start threads in suspended state"); + + Thread *thread = (Thread *) malloc(sizeof (Thread)); + if (!thread) + return ERROR_NOT_ENOUGH_MEMORY; + memset(thread, '\0', sizeof (*thread)); + + size_t stacksize = cbStack; + if ((stacksize % 4096) != 0) + stacksize += 4096 - (cbStack % 4096); + + thread->stacklen = stacksize; + thread->fn = pfn; + thread->fnarg = param; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, stacksize); + const int rc = pthread_create(&thread->thread, &attr, os2ThreadEntry, thread); + pthread_attr_destroy(&attr); + + if (rc != 0) { + free(thread); + return ERROR_MAX_THRDS_REACHED; // I guess...? + } // if + + if (ptid) + *ptid = (TID) thread; + + return NO_ERROR; +} // DosCreateThread + + +static void __attribute__((noreturn)) execOs2Child(const char *exe, char * const *argv, char * const *envp) +{ + // !!! FIXME: wire up any other things we need here. + execve(exe, argv, envp); + fprintf(stderr, "Failed to exec child process: %s\n", strerror(errno)); + _exit(1); // uh oh. +} // execOs2Child + +static char *findExePath(const char *exe, APIRET *err) +{ + FIXME("eventually this needs to search the path and maybe launch native (non-OS/2) processes too"); + char *retval = strdup("/proc/self/exe"); + if (!retval) + *err = ERROR_NOT_ENOUGH_MEMORY; + return retval; +} // findExePath + +static char **calcEnvp(const char *os2env, const char *exe, APIRET *err) +{ + char **retval = NULL; + size_t len = 0; + size_t idx = 0; + + const char *src = os2env; + if (*src == '\0') { + retval = (char **) malloc(sizeof (char *)); + if (retval) + retval[0] = NULL; + return retval; + } // if + + while (1) { + if (*(src++)) + len++; + else { + idx++; + len += sizeof (char *) + 1; + if (*src == '\0') + break; + } // else + } // while + + len += sizeof (char *); // NULL at the end. + len += strlen(exe) + 16 + sizeof (char *); // IS_2INE environment var. + idx++; // IS_2INE + + retval = (char **) malloc(len); + if (!retval) { + *err = ERROR_NOT_ENOUGH_MEMORY; + return NULL; + } // if + + char *dst = (char *) (retval + (idx + 1)); + char *start = dst; + src = os2env; + idx = 0; + while (1) { + const char ch = *(src++); + *(dst++) = ch; + if (!ch) { + retval[idx++] = start; + start = dst; + if (*src == '\0') + break; + } // if + } // while + strcpy(dst, "IS_2INE="); + dst += strlen(dst); + strcpy(dst, exe); + dst += strlen(dst) + 1; + *(dst++) = '\0'; + retval[idx++] = start; + + retval[idx] = NULL; + return retval; +} // calcEnvp + +static char **calcArgv(const char *os2args, APIRET *err) +{ + const size_t argv0len = strlen(os2args); + if (argv0len == 0) { + *err = ERROR_BAD_ENVIRONMENT; + return NULL; // bad. + } // if + + size_t len = 0; + size_t idx = 1; + + const char *src = os2args + argv0len + 1; + + // this can overallocate a little, but that's alright. + while (*src) { + const char ch = *(src++); + if ((ch == ' ') || (ch == '\t')) + idx++; + len++; + } // while + + idx++; // one more for NULL terminator element. + + len += argv0len + 1 + sizeof (char *); + len += idx * (sizeof (char *) + 1); + + void *allocated = malloc(len); + if (!allocated) { + *err = ERROR_NOT_ENOUGH_MEMORY; + return NULL; + } // if + char **retval = (char **) allocated; + char *dst = (char *) (retval + idx + 1); + + idx = 0; + + retval[idx++] = dst; + strcpy(dst, os2args); + dst += argv0len + 1; + + src = os2args + argv0len + 1; + while ((*src == ' ') || (*src == '\t')) + src++; // skip whitespace. + + const char *srcstart = src; + + while (*src) { + const char ch = *(src++); + if ((ch == ' ') || (ch == '\t')) { + retval[idx++] = dst; + const size_t cpylen = ((size_t) (src - srcstart)) - 1; + memcpy(dst, srcstart, cpylen); + dst += cpylen; + *(dst++) = '\0'; + while ((*src == ' ') || (*src == '\t')) + src++; + srcstart = src; + } else if (ch == '"') { + srcstart++; + while ((*src != '"') && (*src != '\0')) + src++; + retval[idx++] = dst; + const size_t cpylen = (size_t) (src - srcstart); + memcpy(dst, srcstart, cpylen); + dst += cpylen; + *(dst++) = '\0'; + if (*src == '"') + src++; + while ((*src == ' ') || (*src == '\t')) + src++; + srcstart = src; + } // else if + } // for + + const size_t cpylen = (size_t) (src - srcstart); + if (cpylen) { + retval[idx++] = dst; + memcpy(dst, srcstart, cpylen); + dst += cpylen; + *(dst++) = '\0'; + } // if + + retval[idx] = NULL; + return retval; +} // calcArgv + +static void setProcessResultCode(PRESULTCODES pRes, const int status) +{ + if (!pRes) + return; + + if (WIFEXITED(status)) { + // !!! FIXME: OS/2 processes exit with a ULONG, and returns bottom 16-bits of it for codeResult. Unix only gives you 8, though! + pRes->codeTerminate = TC_EXIT; + pRes->codeResult = WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + FIXME("These might not be exactly right"); + pRes->codeResult = 0; + switch (WTERMSIG(status)) { + case SIGTERM: + case SIGKILL: + case SIGINT: + case SIGQUIT: + pRes->codeTerminate = TC_KILLPROCESS; + break; + case SIGPIPE: + case SIGABRT: + pRes->codeTerminate = TC_HARDERROR; + break; + default: + pRes->codeTerminate = TC_EXCEPTION; + break; + } // switch + } // else if +} // setProcessResultCode + +APIRET DosExecPgm(PCHAR pObjname, LONG cbObjname, ULONG execFlag, PSZ pArg, PSZ pEnv, PRESULTCODES pRes, PSZ pName) +{ + TRACE_NATIVE("DosExecPgm(%p, %u, %u, %p, %p, %p, '%s')", pObjname, (uint) cbObjname, (uint) execFlag, pArg, pEnv, pRes, pName); + + APIRET retval = NO_ERROR; + + if (pObjname) // !!! FIXME: what does it want to put in here for failures? + *pObjname = '\0'; + + if ((execFlag == EXEC_TRACE) || (execFlag == EXEC_ASYNCRESULTDB)) { // sorry debuggers! + FIXME("tried to launch a process for debugging!"); + return ERROR_ACCESS_DENIED; + } // if + + if (execFlag == EXEC_LOAD) { + FIXME("What is this, exactly?"); + return ERROR_GEN_FAILURE; + } // if + + APIRET err; + char *pNameUnix = makeUnixPath(pName, &err); + if (!pNameUnix) + return err; + + // calculate the args and environment first, so the child process is literally just calling execve(). + char *exe = findExePath(pName, &retval); + if (!exe) { + free(pNameUnix); + return retval; + } // if + + char **argv = calcArgv(pArg, &retval); + if (!argv) { + free(exe); + free(pNameUnix); + return retval; + } // if + + char **envp = calcEnvp(pEnv, pNameUnix, &retval); + if (!argv) { + free(argv); + free(exe); + free(pNameUnix); + return retval; + } // if + + //printf("EXEC'ING! exe='%s'\n", exe); + //for (char * const *ptr = argv; *ptr; ptr++) { printf(" ARGV[%d] = '%s'\n", (int) (ptr - argv), *ptr); } + //for (char * const *ptr = envp; *ptr; ptr++) { printf(" ENVP[%d] = '%s'\n", (int) (ptr - envp), *ptr); } + + const int doubleFork = (execFlag == EXEC_ASYNC) || (execFlag == EXEC_BACKGROUND); + int status = 0; + pid_t pid = fork(); + if (pid == -1) // failed? + retval = ERROR_NO_PROC_SLOTS; + + else if (pid == 0) { // we're the child. + if (doubleFork) { // fork again so we aren't a direct child of the original parent. + pid = fork(); + if (pid == -1) { // uhoh. + fprintf(stderr, "Failed to double-fork child process for EXEC_ASYNC: %s\n", strerror(errno)); + _exit(1); + } else if (pid != 0) { // we're the parent + _exit(0); // just die now. + } + // we're the double-forked child. + if (setsid() < 0) { + fprintf(stderr, "Failed to setsid(): %s\n", strerror(errno)); + _exit(1); + } // if + // !!! FIXME: need to report pid to original parent now via a temporary pipe. + } // if + + // run the new program! + execOs2Child(exe, argv, envp); + + } else { // we're the parent. + switch (execFlag) { + case EXEC_SYNC: { + const int rc = waitpid(pid, &status, 0); + assert(rc == 0); // !!! FIXME + setProcessResultCode(pRes, status); + break; + } // case + + case EXEC_ASYNC: + case EXEC_ASYNCRESULT: + case EXEC_BACKGROUND: + if (pRes) { + pRes->codeResult = 0; + pRes->codeTerminate = (ULONG) pid; + } // if + break; + + default: + assert(!"shouldn't hit this code"); + retval = ERROR_INVALID_FUNCTION; + break; + } // switch + } // else + + free(envp); + free(argv); + free(exe); + + return retval; +} // DosExecPgm + +APIRET DosResetEventSem(HEV hev, PULONG pulPostCt) +{ + TRACE_NATIVE("DosResetEventSem(%u, %p)", (uint) hev, pulPostCt); + + if (!hev) + return ERROR_INVALID_HANDLE; + + APIRET retval = NO_ERROR; + ULONG count = 0; + EventSem *sem = (EventSem *) hev; + + pthread_mutex_lock(&sem->mutex); + count = sem->posted; + if (!sem->posted) + retval = ERROR_ALREADY_RESET; + else + sem->posted = 0; + pthread_mutex_unlock(&sem->mutex); + + if (pulPostCt) + *pulPostCt = count; + + return retval; +} // DosResetEventSem + +APIRET DosPostEventSem(HEV hev) +{ + TRACE_NATIVE("DosPostEventSem(%u)", (uint) hev); + + if (!hev) + return ERROR_INVALID_HANDLE; + + APIRET retval = NO_ERROR; + EventSem *sem = (EventSem *) hev; + + pthread_mutex_lock(&sem->mutex); + if (sem->posted) + retval = ERROR_ALREADY_POSTED; + else { + sem->posted = 1; + pthread_cond_broadcast(&sem->condition); + } // else + pthread_mutex_unlock(&sem->mutex); + + return retval; +} // DosPostEventSem + +APIRET DosWaitEventSem(HEV hev, ULONG ulTimeout) +{ + TRACE_NATIVE("DosWaitEventSem(%u, %u)", (uint) hev, (uint) ulTimeout); + + if (!hev) + return ERROR_INVALID_HANDLE; + + EventSem *sem = (EventSem *) hev; + int posted = 0; + + if (ulTimeout == SEM_IMMEDIATE_RETURN) { + pthread_mutex_lock(&sem->mutex); + posted = sem->posted; + pthread_mutex_unlock(&sem->mutex); + return posted ? NO_ERROR : ERROR_TIMEOUT; + } else if (ulTimeout == SEM_INDEFINITE_WAIT) { + pthread_mutex_lock(&sem->mutex); + sem->waiting++; + while (1) { + posted = sem->posted; + if (!posted) { + pthread_cond_wait(&sem->condition, &sem->mutex); + posted = sem->posted; + } // if + + if (posted) { + sem->waiting--; + pthread_mutex_unlock(&sem->mutex); + return NO_ERROR; + } // if + + // spurious wakeup? Block again. + } // while + } else { // wait for X milliseconds. + struct timespec waittime; + timespecNowPlusMilliseconds(&waittime, ulTimeout); + + pthread_mutex_lock(&sem->mutex); + sem->waiting++; + while (1) { + int rc = 0; + posted = sem->posted; + if (!posted) { + rc = pthread_cond_timedwait(&sem->condition, &sem->mutex, &waittime); + posted = sem->posted; + } // if + + if ((posted) || (rc == ETIMEDOUT)) { + sem->waiting--; + pthread_mutex_unlock(&sem->mutex); + if (posted) + return NO_ERROR; // we're good. + else + return ERROR_TIMEOUT; + } // if + + // otherwise, try again...spurious wakeup? + } // while + } // else + + return NO_ERROR; +} // DosWaitEventSem + +APIRET DosQueryEventSem(HEV hev, PULONG pulPostCt) +{ + TRACE_NATIVE("DosQueryEventSem(%u, %p)", (uint) hev, pulPostCt); + + if (!hev) + return ERROR_INVALID_HANDLE; + + EventSem *sem = (EventSem *) hev; + pthread_mutex_lock(&sem->mutex); + const ULONG count = (ULONG) sem->posted; + pthread_mutex_unlock(&sem->mutex); + + if (pulPostCt) + *pulPostCt = count; + + return NO_ERROR; +} // DosQueryEventSem + +APIRET DosCloseEventSem(HEV hev) +{ + TRACE_NATIVE("DosCloseEventSem(%u)", (uint) hev); + + if (!hev) + return ERROR_INVALID_HANDLE; + + EventSem *sem = (EventSem *) hev; + + pthread_mutex_lock(&sem->mutex); + const int waiting = sem->waiting; + pthread_mutex_unlock(&sem->mutex); + + if (waiting > 0) + return ERROR_SEM_BUSY; + + pthread_mutex_destroy(&sem->mutex); + pthread_cond_destroy(&sem->condition); + free(sem); + + return NO_ERROR; +} // DosCloseEventSem + +APIRET DosFreeMem(PVOID pb) +{ + TRACE_NATIVE("DosFreeMem(%p)", pb); + + // !!! FIXME: this API is actually much more complicated than this. + free(pb); + return NO_ERROR; +} // DosFreeMem + +APIRET DosWaitChild(ULONG action, ULONG option, PRESULTCODES pres, PPID ppid, PID pid) +{ + TRACE_NATIVE("DosWaitChild(%u, %u, %p, %p, %u)", (uint) action, (uint) option, pres, ppid, (uint) pid); + + if (action == DCWA_PROCESSTREE) { + // we need a wait to wait on the whole tree (including trees of a + // terminated child we already waited on, which the API reference + // lists as an example. + FIXME("implement me"); + return ERROR_INVALID_PARAMETER; + } // if + + pid_t unixpid; + if (action == DCWA_PROCESS) + unixpid = (pid == 0) ? (pid_t) -1 : (pid_t) pid; + + int waitoptions; + if (option == DCWW_WAIT) { + waitoptions = 0; + } else if (option == DCWW_NOWAIT) { + waitoptions = WNOHANG; + } else { + return ERROR_INVALID_PARAMETER; + } // else + + int status = 0; + pid_t rc; + while (1) { + rc = waitpid(unixpid, &status, waitoptions); + if (rc == 0) { // WNOHANG and no children were done. + assert(option == DCWW_NOWAIT); + return ERROR_CHILD_NOT_COMPLETE; + } else if (rc == -1) { + if (errno == EINTR) + continue; // try again, I guess. + else if (errno == ECHILD) + return (pid == 0) ? ERROR_WAIT_NO_CHILDREN : ERROR_INVALID_PROCID; + } else { + break; // we're good. + } // else + + assert(!"Shouldn't hit this code"); + return ERROR_CHILD_NOT_COMPLETE; + } // while + + if (ppid) + *ppid = (PID) rc; + + setProcessResultCode(pres, status); + + return NO_ERROR; +} // DosWaitChild + +APIRET OS2API DosWaitThread(PTID ptid, ULONG option) +{ + TRACE_NATIVE("DosWaitThread(%p, %u)", ptid, (uint) option); + + Thread *thread = (Thread *) *ptid; + void *exitcode = NULL; + + if ((option != DCWW_WAIT) && (option != DCWW_NOWAIT)) + return ERROR_INVALID_PARAMETER; + + if (thread == NULL) { // check our dead thread list for anything. + while (1) { + grabLock(&GMutexDosCalls); + if (GDeadThreads) { + thread = GDeadThreads; + GDeadThreads = thread->next; + if (GDeadThreads) + GDeadThreads->prev = NULL; + assert(!thread->prev); + } // if + ungrabLock(&GMutexDosCalls); + + if (thread) { + pthread_join(thread->thread, &exitcode); // reap it. + *ptid = (TID) thread; + free(thread); + return NO_ERROR; + } // if + + if (option == DCWW_NOWAIT) + return ERROR_THREAD_NOT_TERMINATED; + + usleep(10000); // sleep then try again, for DCWW_WAIT. + } // while + + assert(!"shouldn't hit this code"); + return ERROR_THREAD_NOT_TERMINATED; // !!! FIXME + } // if + + // codepath if we were waiting for a specific thread. + int rc; + if (option == DCWW_NOWAIT) { + rc = pthread_tryjoin_np(thread->thread, &exitcode); + } else { //if (option == DCWW_WAIT) + rc = pthread_join(thread->thread, &exitcode); + } // else + + if (rc == EBUSY) + return ERROR_THREAD_NOT_TERMINATED; + else if (rc != 0) + return ERROR_INVALID_THREADID; // oh well. + + grabLock(&GMutexDosCalls); + if (thread->prev) + thread->prev->next = thread->next; + else + GDeadThreads = thread->next; + + if (thread->next) + thread->next->prev = thread->prev; + ungrabLock(&GMutexDosCalls); + + free(thread); + return NO_ERROR; +} // DosWaitThread + +APIRET OS2API DosSleep(ULONG msec) +{ + TRACE_NATIVE("DosSleep(%u)", (uint) msec); + + if (msec == 0) + sched_yield(); + else + usleep(msec * 1000); + return NO_ERROR; +} // DosSleep + +APIRET DosSubFreeMem(PVOID pbBase, PVOID pb, ULONG cb) +{ + TRACE_NATIVE("DosSubFreeMem(%p, %p, %u)", pbBase, pb, (uint) cb); + free(pb); // !!! FIXME: obviously wrong + return NO_ERROR; +} // DosSubAllocMem + +APIRET DosDelete(PSZ pszFile) +{ + TRACE_NATIVE("DosDelete('%s')", pszFile); + + APIRET err; + char *unixpath = makeUnixPath(pszFile, &err); + if (!unixpath) + return err; + + if (unlink(unixpath) == -1) { + switch (errno) { + case EACCES: return ERROR_ACCESS_DENIED; + case EBUSY: return ERROR_ACCESS_DENIED; + case EISDIR: return ERROR_ACCESS_DENIED; + case ENAMETOOLONG: return ERROR_FILENAME_EXCED_RANGE; + case ENOENT: return ERROR_FILE_NOT_FOUND; // !!! FIXME: could be PATH_NOT_FOUND too, depending on circumstances. + case ENOTDIR: return ERROR_PATH_NOT_FOUND; + case EPERM: return ERROR_ACCESS_DENIED; + case EROFS: return ERROR_ACCESS_DENIED; + case ETXTBSY: return ERROR_ACCESS_DENIED; + default: return ERROR_INVALID_PARAMETER; // !!! FIXME: debug logging about missin errno case. + } // switch + __builtin_unreachable(); + } // if + + return NO_ERROR; +} // DosDelete + +APIRET DosQueryCurrentDir(ULONG disknum, PBYTE pBuf, PULONG pcbBuf) +{ + TRACE_NATIVE("DosQueryCurrentDir(%u, %p, %p)", (uint) disknum, pBuf, pcbBuf); + + if (disknum != 3) // C: + return ERROR_INVALID_DRIVE; + + // !!! FIXME: this is lazy. + char *cwd = getcwd(NULL, 0); + if (!cwd) + return ERROR_NOT_ENOUGH_MEMORY; // this doesn't ever return this error on OS/2. + + const size_t len = strlen(cwd); + if ((len + 3) > *pcbBuf) { + *pcbBuf = len + 3; + return ERROR_BUFFER_OVERFLOW; + } // if + + char *ptr = (char *) pBuf; + *(ptr++) = 'C'; + *(ptr++) = ':'; + for (char *src = cwd; *src; src++) { + const char ch = *src; + *(ptr++) = ((ch == '/') ? '\\' : ch); + } // for + *ptr = '\0'; + + return NO_ERROR; +} // DosQueryCurrentDir + +APIRET DosSetPathInfo(PSZ pszPathName, ULONG ulInfoLevel, PVOID pInfoBuf, ULONG cbInfoBuf, ULONG flOptions) +{ + TRACE_NATIVE("DosSetPathInfo('%s', %u, %p, %u, %u)", pszPathName, (uint) ulInfoLevel, pInfoBuf, (uint) cbInfoBuf, (uint) flOptions); + FIXME("write me"); + return NO_ERROR; +} // DosSetPathInfo + // end of doscalls.c ... diff --git a/native/doscalls.h b/native/doscalls.h index a07ba50..a29962f 100644 --- a/native/doscalls.h +++ b/native/doscalls.h @@ -51,7 +51,6 @@ typedef struct TIB2 USHORT tib2_usMCCount; USHORT tib2_fMCForceFlag; } TIB2, *PTIB2; -#pragma pack(pop) typedef struct TIB { @@ -62,6 +61,7 @@ typedef struct TIB ULONG tib_version; ULONG tib_ordinal; } TIB, *PTIB; +#pragma pack(pop) typedef struct PIB { @@ -222,6 +222,74 @@ typedef struct ULONG cbList; } FILESTATUS4, *PFILESTATUS4; +enum +{ + CREATE_READY, + CREATE_SUSPENDED +}; + +enum +{ + STACK_SPARSE = 0, + STACK_COMMITTED = 2 +}; + +typedef VOID APIENTRY FNTHREAD(ULONG); +typedef FNTHREAD *PFNTHREAD; + +typedef struct +{ + ULONG codeTerminate; + ULONG codeResult; +} RESULTCODES, *PRESULTCODES; + +enum +{ + EXEC_SYNC, + EXEC_ASYNC, + EXEC_ASYNCRESULT, + EXEC_TRACE, + EXEC_BACKGROUND, + EXEC_LOAD, + EXEC_ASYNCRESULTDB +}; + + +enum +{ + DC_SEM_SHARED = 1 +}; + +enum +{ + DCMW_WAIT_ANY = (1<<1), + DCMW_WAIT_ALL = (1<<2) +}; + +enum +{ + SEM_INDEFINITE_WAIT = -1, + SEM_IMMEDIATE_RETURN = 0 +}; + +enum +{ + DCWW_WAIT, + DCWW_NOWAIT +}; + +enum +{ + DCWA_PROCESS, + DCWA_PROCESSTREE +}; + +enum +{ + DSPI_WRTTHRU = 0x10, +}; + + // !!! FIXME: these should probably get sorted alphabetically and/or grouped // !!! FIXME: into areas of functionality, but for now, I'm just listing them // !!! FIXME: in the order I implemented them to get things running. @@ -255,6 +323,22 @@ APIRET OS2API DosEnterMustComplete(PULONG pulNesting); APIRET OS2API DosExitMustComplete(PULONG pulNesting); APIRET OS2API DosQueryPathInfo(PSZ pszPathName, ULONG ulInfoLevel, PVOID pInfoBuf, ULONG cbInfoBuf); APIRET OS2API DosQueryFileInfo(HFILE hf, ULONG ulInfoLevel, PVOID pInfo, ULONG cbInfoBuf); +APIRET OS2API DosQueryFileInfo(HFILE hf, ULONG ulInfoLevel, PVOID pInfo, ULONG cbInfoBuf); +APIRET OS2API DosCreateThread(PTID ptid, PFNTHREAD pfn, ULONG param, ULONG flag, ULONG cbStack); +APIRET OS2API DosExecPgm(PCHAR pObjname, LONG cbObjname, ULONG execFlag, PSZ pArg, PSZ pEnv, PRESULTCODES pRes, PSZ pName); +APIRET OS2API DosResetEventSem(HEV hev, PULONG pulPostCt); +APIRET OS2API DosPostEventSem(HEV hev); +APIRET OS2API DosCloseEventSem(HEV hev); +APIRET OS2API DosWaitEventSem(HEV hev, ULONG ulTimeout); +APIRET OS2API DosQueryEventSem(HEV hev, PULONG pulPostCt); +APIRET OS2API DosFreeMem(PVOID pb); +APIRET OS2API DosWaitChild(ULONG action, ULONG option, PRESULTCODES pres, PPID ppid, PID pid); +APIRET OS2API DosWaitThread(PTID ptid, ULONG option); +APIRET OS2API DosSleep(ULONG msec); +APIRET OS2API DosSubFreeMem(PVOID pbBase, PVOID pb, ULONG cb); +APIRET OS2API DosDelete(PSZ pszFile); +APIRET OS2API DosQueryCurrentDir(ULONG disknum, PBYTE pBuf, PULONG pcbBuf); +APIRET OS2API DosSetPathInfo(PSZ pszPathName, ULONG ulInfoLevel, PVOID pInfoBuf, ULONG cbInfoBuf, ULONG flOptions); #ifdef __cplusplus } diff --git a/native/os2native.h b/native/os2native.h index 0eb1557..ade5db3 100644 --- a/native/os2native.h +++ b/native/os2native.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "os2types.h" diff --git a/native/os2types.h b/native/os2types.h index 2d4f2fa..542e79b 100644 --- a/native/os2types.h +++ b/native/os2types.h @@ -33,11 +33,21 @@ typedef uint16_t USHORT, *PUSHORT; typedef int32_t LONG, *PLONG; typedef uint32_t ULONG, *PULONG; typedef uint32_t BOOL32, *PBOOL32; -typedef uint32_t HANDLE, *PHANDLE; +typedef uint8_t BYTE, *PBYTE; + +// !!! FIXME: HANDLE should either be 64-bits on x86_64, or we need to +// !!! FIXME: refactor and mutex a bunch of stuff...if it has to be a 32-bit +// !!! FIXME: int, it needs to be an index into a resizable array, but at the +// !!! FIXME: native word size, these handles can just be pointers cast to ints. +typedef uint16_t SHANDLE, *PSHANDLE; +typedef uint32_t LHANDLE, *PLHANDLE; +typedef LHANDLE HANDLE, *PHANDLE; typedef HANDLE HMODULE, *PHMODULE; typedef HANDLE HFILE, *PHFILE; typedef HANDLE HEV, *PHEV; typedef HANDLE HMTX, *PHMTX; +typedef HANDLE PID, *PPID; +typedef HANDLE TID, *PTID; typedef PCHAR PSZ; #ifdef __cplusplus