Index: trunk/src/emx/ChangeLog.LIBC
===================================================================
--- trunk/src/emx/ChangeLog.LIBC	(revision 1983)
+++ trunk/src/emx/ChangeLog.LIBC	(revision 1984)
@@ -1,3 +1,10 @@
 /* $Id$ */
+
+2005-05-08: knut st. osmundsen <bird-gccos2-spam@anduin.net>
+    - libc:
+        o Added _getenv_[int|long|longlong]. Using this for LIBC_THREAD_MIN_STACK_SIZE.
+        o Ported the BSD SysV Semaphore module.
+        o Added a signal notification callback to the thread structure.
+        o Changed tcpip term callback to more generic exitlist callback (SPM).
 
 2005-05-05: knut st. osmundsen <bird-gccos2-spam@anduin.net>
Index: trunk/src/emx/include/386/builtin.h
===================================================================
--- trunk/src/emx/include/386/builtin.h	(revision 1983)
+++ trunk/src/emx/include/386/builtin.h	(revision 1984)
@@ -77,5 +77,5 @@
 static __inline__ int __atomic_set_bit(__volatile__ void *pv, unsigned uBit)
 {
-    __asm__ __volatile__("lock btsl %2, %1\n\t"
+    __asm__ __volatile__("lock; btsl %2, %1\n\t"
                          "sbbl %0,%0"
                          : "=r" (uBit),
@@ -95,5 +95,5 @@
 static __inline__ void __atomic_clear_bit(__volatile__ void *pv, unsigned uBit)
 {
-    __asm__ __volatile__("lock btrl %1, %0"
+    __asm__ __volatile__("lock; btrl %1, %0"
                          : "=m" (*(__volatile__ unsigned *)pv)
                          : "r" (uBit));
@@ -128,7 +128,7 @@
 static __inline__ void __atomic_add(__volatile__ unsigned *pu, const unsigned uAdd)
 {
-    __asm__ __volatile__("lock addl %1, %0"
+    __asm__ __volatile__("lock; addl %1, %0"
                          : "=m" (*pu)
-                         : "nr" (uAdd), 
+                         : "nr" (uAdd),
                            "m"  (*pu));
 }
@@ -142,7 +142,7 @@
 static __inline__ void __atomic_sub(__volatile__ unsigned *pu, const unsigned uSub)
 {
-    __asm__ __volatile__("lock subl %1, %0"
+    __asm__ __volatile__("lock; subl %1, %0"
                          : "=m" (*pu)
-                         : "nr" (uSub), 
+                         : "nr" (uSub),
                            "m"  (*pu));
 }
@@ -156,10 +156,34 @@
 static __inline__ void __atomic_increment(__volatile__ unsigned *pu)
 {
-    __asm__ __volatile__("lock incl %0"
-                         : "=m" (*pu) 
+    __asm__ __volatile__("lock; incl %0"
+                         : "=m" (*pu)
                          : "m"  (*pu));
 }
 
 /**
+ * Atomically increments a 32-bit unsigned value.
+ *
+ * @param   pu32    Pointer to the value to increment.
+ */
+static __inline__ void __atomic_increment_u32(uint32_t __volatile__ *pu32)
+{
+    __asm__ __volatile__("lock; incl %0"
+                         : "=m" (*pu32)
+                         : "m"  (*pu32));
+}
+
+/**
+ * Atomically increments a 16-bit unsigned value.
+ *
+ * @param   pu16    Pointer to the value to increment.
+ */
+static __inline__ void __atomic_increment_u16(uint16_t __volatile__ *pu16)
+{
+    __asm__ __volatile__("lock; incw %0"
+                         : "=m" (*pu16)
+                         : "m"  (*pu16));
+}
+
+/**
  * Atomically decrements a 32-bit unsigned value.
  *
@@ -168,7 +192,31 @@
 static __inline__ void __atomic_decrement(__volatile__ unsigned *pu)
 {
-    __asm__ __volatile__("lock decl %0"
-                         : "=m" (*pu) 
+    __asm__ __volatile__("lock; decl %0"
+                         : "=m" (*pu)
                          : "m"  (*pu));
+}
+
+/**
+ * Atomically decrements a 32-bit unsigned value.
+ *
+ * @param   pu      Pointer to the value to decrement.
+ */
+static __inline__ void __atomic_decrement_u32(__volatile__ uint32_t *pu32)
+{
+    __asm__ __volatile__("lock; decl %0"
+                         : "=m" (*pu32)
+                         : "m"  (*pu32));
+}
+
+/**
+ * Atomically decrements a 16-bit unsigned value.
+ *
+ * @param   pu16    Pointer to the value to decrement.
+ */
+static __inline__ void __atomic_decrement_u16(uint16_t __volatile__ *pu16)
+{
+    __asm__ __volatile__("lock; decw %0"
+                         : "=m" (*pu16)
+                         : "m"  (*pu16));
 }
 
@@ -192,5 +240,5 @@
                          "2:\n\t"
                          "incl  %0\n\t"
-                         "lock  cmpxchgl %0, %1\n\t"
+                         "lock; cmpxchgl %0, %1\n\t"
                          "jz    3f\n\t"
                          "jmp   1b\n"
@@ -228,5 +276,5 @@
                          "2:\n\t"
                          "incw  %w0\n\t"
-                         "lock  cmpxchgw %w0, %2\n\t"
+                         "lock; cmpxchgw %w0, %2\n\t"
                          "jz    3f\n\t"
                          "jmp   1b\n\t"
@@ -261,5 +309,5 @@
                          "2:\n\t"
                          "decl  %0\n\t"
-                         "lock  cmpxchgl %0, %1\n\t"
+                         "lock; cmpxchgl %0, %1\n\t"
                          "jz    3f\n\t"
                          "jmp   1b\n"
@@ -297,5 +345,5 @@
                          "2:\n\t"
                          "decw  %%bx\n\t"
-                         "lock  cmpxchgw %w0, %1\n\t"
+                         "lock; cmpxchgw %w0, %1\n\t"
                          "jz    3f\n\t"
                          "jmp   1b\n"
@@ -321,5 +369,5 @@
 static inline unsigned __atomic_cmpxchg32(volatile uint32_t *pu32, uint32_t u32New, uint32_t u32Old)
 {
-    __asm__ __volatile__("lock  cmpxchgl %2, %1\n\t"
+    __asm__ __volatile__("lock; cmpxchgl %2, %1\n\t"
                          "setz  %%al\n\t"
                          "movzx %%al, %%eax\n\t"
Index: trunk/src/emx/include/InnoTekLIBC/FastInfoBlocks.h
===================================================================
--- trunk/src/emx/include/InnoTekLIBC/FastInfoBlocks.h	(revision 1983)
+++ trunk/src/emx/include/InnoTekLIBC/FastInfoBlocks.h	(revision 1984)
@@ -130,5 +130,8 @@
  * System stuff
  */
+/** Get the current millisecond counter value. */
 #define fibGetMsCount()         (__libc_GpFIBGIS->SIS_MsCount)
+/** Get the Unix timestamp (seconds). */
+#define fibGetUnixSeconds()     (__libc_GpFIBGIS->SIS_BigTime)
 
 
Index: trunk/src/emx/include/InnoTekLIBC/backend.h
===================================================================
--- trunk/src/emx/include/InnoTekLIBC/backend.h	(revision 1983)
+++ trunk/src/emx/include/InnoTekLIBC/backend.h	(revision 1984)
@@ -34,4 +34,5 @@
 #include <sys/time.h>
 #include <sys/wait.h>
+#include <sys/sem.h>
 #include <signal.h>
 #include <emx/io.h>
@@ -890,4 +891,24 @@
 /** @} */
 
+
+/** @defgroup grp_Back_sysvipc LIBC Backend - SysV IPC
+ * @{ */
+/**
+ * semctl syscall
+ */
+int __libc_Back_sysvSemCtl(int semid, int semnum, int cmd, union semun real_arg);
+
+/**
+ * sysget syscall.
+ */
+int __libc_Back_sysvSemGet(key_t key, int nsems, int semflg);
+
+/**
+ * semop syscall.
+ */
+int __libc_Back_sysvSemOp(int semid, struct sembuf *sops_user, size_t nsops);
+
+/** @} */
+
 __END_DECLS
 
Index: trunk/src/emx/include/InnoTekLIBC/sharedpm.h
===================================================================
--- trunk/src/emx/include/InnoTekLIBC/sharedpm.h	(revision 1983)
+++ trunk/src/emx/include/InnoTekLIBC/sharedpm.h	(revision 1984)
@@ -4,5 +4,6 @@
  * LIBC Shared Process Management.
  *
- *
+ * Copyright (c) 2004-2005 knut st. osmundsen <bird@anduin.net>
+ * Copyright (c) 2004 nickk
  *
  * This file is part of InnoTek LIBC.
@@ -30,4 +31,5 @@
 #include <sys/types.h>
 #include <sys/signal.h>
+#include <sys/ipc.h>
 
 
@@ -553,5 +555,8 @@
     unsigned long                       hevNotify;
 
-    /*  116 - The rest of the block, up to cbProcess, is undefined in this version.
+    /** 116 - Pointer to SysV Sempahore globals. */
+    struct __libc_SysV_Sem             *pSysVSem;
+
+    /*  120 - The rest of the block, up to cbProcess, is undefined in this version.
      * Future versions of LIBC may use this area assuming it's initalized with zeros.
      */
@@ -729,4 +734,23 @@
 
 /**
+ * Checks if the calling process is a member of the specified group.
+ *
+ * @returns 0 if member.
+ * @returns -EPERM if not member.
+ * @param   gid     The group id in question.
+ */
+int __libc_spmIsGroupMember(gid_t gid);
+
+/**
+ * Check if the caller can access the SysV IPC object as requested.
+ *
+ * @returns 0 if we can.
+ * @returns -EPERM if we cannot.
+ * @param   pPerm       The IPC permission structure.
+ * @param   Mode        The access request. IPC_M, IPC_W or IPC_R.
+ */
+int __libc_spmCanIPC(struct ipc_perm *pPerm, mode_t Mode);
+
+/**
  * Marks the current process (if we have it around) as zombie
  * or dead freeing all resources associated with it.
@@ -841,9 +865,12 @@
  *
  * @returns 0 on success.
- * @returns -1 and errno on failure.
+ * @returns Negative error code (errno.h) on failure.
+ *
  * @param   pRegRec     Pointer to the exception handler registration record.
+ * @param   ppSPMHdr    Where to store the pointer to the SPM header. Can be NULL.
+ *
  * @remark  Don't even think of calling this if you're not LIBC!
  */
-int __libc_spmLock(__LIBC_PSPMXCPTREGREC pRegRec);
+int __libc_spmLock(__LIBC_PSPMXCPTREGREC pRegRec, __LIBC_PSPMHEADER *ppSPMHdr);
 
 /**
@@ -851,6 +878,8 @@
  *
  * @returns 0 on success.
- * @returns -1 on and errno failure.
+ * @returns Negative error code (errno.h) on failure.
+ *
  * @param   pRegRec     Pointer to the exception handler registration record.
+ *
  * @remark  Don't even think of calling this if you're not LIBC!
  */
@@ -859,31 +888,61 @@
 /**
  * Allocate memory from the LIBC shared memory.
+ *
+ * The SPM must be locked using __libc_spmLock() prior to calling this function!
  *
  * @returns Pointer to allocated memory on success.
  * @returns NULL on failure.
+ *
  * @param   cbSize 	Size of memory to allocate.
+ *
  * @remark  Don't think of calling this if you're not LIBC!
  */
-void * __libc_spmAlloc(size_t cbSize);
-
-/**
- * Free memory allocated by __libc_SpmAlloc().
+void * __libc_spmAllocLocked(size_t cbSize);
+
+/**
+ * Free memory allocated by __libc_spmAllocLocked() or __libc_spmAlloc().
+ *
+ * The SPM must be locked using __libc_spmLock() prior to calling this function!
  *
  * @returns 0 on success.
  * @returns -1 and errno on failure.
+ *
  * @param   pv      Pointer to memory block returned by __libc_SpmAlloc().
  *                  NULL is allowed.
  * @remark  Don't think of calling this if you're not LIBC!
  */
+int __libc_spmFreeLocked(void *pv);
+
+/**
+ * Allocate memory from the LIBC shared memory.
+ *
+ * @returns Pointer to allocated memory on success.
+ * @returns NULL on failure.
+ *
+ * @param   cbSize 	Size of memory to allocate.
+ *
+ * @remark  Don't think of calling this if you're not LIBC!
+ */
+void * __libc_spmAlloc(size_t cbSize);
+
+/**
+ * Free memory allocated by __libc_SpmAlloc().
+ *
+ * @returns 0 on success.
+ * @returns -1 and errno on failure.
+ *
+ * @param   pv      Pointer to memory block returned by __libc_SpmAlloc().
+ *                  NULL is allowed.
+ *
+ * @remark  Don't think of calling this if you're not LIBC!
+ */
 int __libc_spmFree(void *pv);
 
-
-/**
- * Register TCPIP termination handler.
+/**
+ * Register termination handler.
  *
  * This is a manual way of by passing a.out's broken weak symbols.
- * @param   pfnTerm     Pointer to the termination function.
- */
-void    __libc_spmSocketRegTerm(void (*pfnTerm)(void));
+ */
+void __libc_spmRegTerm(void (*pfnTerm)(void));
 
 
Index: trunk/src/emx/include/InnoTekLIBC/thread.h
===================================================================
--- trunk/src/emx/include/InnoTekLIBC/thread.h	(revision 1983)
+++ trunk/src/emx/include/InnoTekLIBC/thread.h	(revision 1984)
@@ -73,4 +73,21 @@
     volatile int    fDone;
 } __LIBC_THREAD_SIGSUSPEND, *__LIBC_PTHREAD_SIGSUSPEND;
+
+
+/**
+ * Signal notification callback function.
+ *
+ * This is a notification which can be used to correct the state of
+ * a system object before any user code is executed.
+ *
+ * The semaphore lock is hold and signals are all on hold, so be very careful with waitin
+ * on other semphores and stuff like that. Crashing is totally forbidden. :-)
+ *
+ * @param   iSignalNo       The signal number.
+ * @param   pvUser          The user argument.
+ */
+typedef void __LIBC_FNSIGCALLBACK(int iSignalNo, void *pvUser);
+/** Pointer to a signal callback function. */
+typedef __LIBC_FNSIGCALLBACK *__LIBC_PFNSIGCALLBACK;
 
 
@@ -174,5 +191,8 @@
      * when ever a thread enters for processing a signal asynchronously. */
     volatile unsigned long      ulSigLastTS;
-
+    /** Callback on signal/exception. */
+    __LIBC_PFNSIGCALLBACK       pfnSigCallback;
+    /** User argument to signal/exception callback. */
+    void                       *pvSigCallbackUser;
 
     /** Thread status, chiefly used for the u member of the thread structure. */
Index: trunk/src/emx/include/sys/ipc.h
===================================================================
--- trunk/src/emx/include/sys/ipc.h	(revision 1983)
+++ trunk/src/emx/include/sys/ipc.h	(revision 1984)
@@ -126,4 +126,7 @@
 #define	IXSEQ_TO_IPCID(ix,perm)	(((perm.seq) << 16) | (ix & 0xffff))
 
+#ifdef __EMX__
+int	__libc_back_ipcperm(struct ipc_perm *, int);
+#else /* !__EMX__ */
 struct thread;
 struct proc;
@@ -133,4 +136,5 @@
 extern void (*shmfork_hook)(struct proc *, struct proc *);
 extern void (*shmexit_hook)(struct vmspace *);
+#endif /* !__EMX__ */
 
 #else /* ! _KERNEL */
Index: trunk/src/emx/include/sys/sem.h
===================================================================
--- trunk/src/emx/include/sys/sem.h	(revision 1983)
+++ trunk/src/emx/include/sys/sem.h	(revision 1984)
@@ -10,4 +10,5 @@
 /** @file
  * FreeBSD 5.3
+ * @changed     bird: no padding in semid_ds. seminfo and semexit are static in our 'kernel'.
  */
 
@@ -24,10 +25,14 @@
 	unsigned short	sem_nsems;	/* number of sems in set */
 	time_t		sem_otime;	/* last operation time */
+#ifndef __EMX__ /* space is precious */
 	long		sem_pad1;	/* SVABI/386 says I need this here */
+#endif
 	time_t		sem_ctime;	/* last change time */
     					/* Times measured in secs since */
     					/* 00:00:00 GMT, Jan. 1, 1970 */
+#ifndef __EMX__ /* space is precious */
 	long		sem_pad2;	/* SVABI/386 says I need this here */
 	long		sem_pad3[4];	/* SVABI/386 says I need this here */
+#endif
 };
 
@@ -87,5 +92,7 @@
 		semaem;		/* adjust on exit max value */
 };
+#ifndef __EMX__
 extern struct seminfo	seminfo;
+#endif
 
 /* internal "mode" bits */
@@ -93,8 +100,10 @@
 #define	SEM_DEST	02000	/* semaphore will be destroyed on last detach */
 
+#ifndef __EMX__
 /*
  * Process sem_undo vectors at proc exit.
  */
 void	semexit(struct proc *p);
+#endif /* !__EMX__ */
 #endif /* _KERNEL */
 
@@ -114,5 +123,7 @@
 
 __BEGIN_DECLS
+#ifndef __EMX__
 int semsys(int, ...);
+#endif
 int semctl(int, int, int, ...);
 int semget(key_t, int, int);
Index: trunk/src/emx/src/lib/libc.def
===================================================================
--- trunk/src/emx/src/lib/libc.def	(revision 1983)
+++ trunk/src/emx/src/lib/libc.def	(revision 1984)
@@ -1526,2 +1526,12 @@
     "___nullstub_function" @1531
     "___nullstub_data" @1532
+    "__getenv_int" @1533
+    "__getenv_long" @1534
+    "__getenv_longlong" @1535
+    "___libc_Back_sysvSemCtl" @1536
+    "___libc_Back_sysvSemGet" @1537
+    "___libc_Back_sysvSemOp" @1538
+    "__std_ftok" @1539
+    "__std_semctl" @1540
+    "__std_semget" @1541
+    "__std_semop" @1542
Index: trunk/src/emx/src/lib/process/semctl.c
===================================================================
--- trunk/src/emx/src/lib/process/semctl.c	(revision 1984)
+++ trunk/src/emx/src/lib/process/semctl.c	(revision 1984)
@@ -0,0 +1,73 @@
+/* $Id$ */
+/** @file
+ *
+ * LIBC - semctl.
+ *
+ * Copyright (c) 2005 knut st. osmundsen <bird@anduin.net>
+ *
+ *
+ * This file is part of kBuild.
+ *
+ * kBuild is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kBuild is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kBuild; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include "libc-alias.h"
+#include <sys/types.h>
+#include <sys/sem.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <InnoTekLIBC/backend.h>
+#define __LIBC_LOG_GROUP __LIBC_LOG_GRP_PROCESS
+#include <InnoTekLIBC/logstrict.h>
+
+
+/**
+ * semop.
+ */
+int _STD(semctl)(int semid, int semnum, int cmd, ...)
+{
+    LIBCLOG_ENTER("semid=%d semnum=%d cmd=%d\n", semid, semnum, cmd);
+
+    /*
+     * Get optional argument.
+     */
+    union semun arg = {0};
+    va_list args;
+    va_start(args, cmd);
+    switch (cmd)
+    {
+        case IPC_SET:
+        case IPC_STAT:
+        case GETALL:
+        case SETVAL:
+        case SETALL:
+            arg = va_arg(args, union semun);
+            break;
+    }
+    va_end(args);
+
+    /* do syscall */
+    int rc = __libc_Back_sysvSemCtl(semid, semnum, cmd, arg);
+    if (rc >= 0)
+        LIBCLOG_RETURN_INT(rc);
+    errno = -rc;
+    LIBCLOG_RETURN_INT(-1);
+}
+
Index: trunk/src/emx/src/lib/process/semget.c
===================================================================
--- trunk/src/emx/src/lib/process/semget.c	(revision 1984)
+++ trunk/src/emx/src/lib/process/semget.c	(revision 1984)
@@ -0,0 +1,52 @@
+/* $Id$ */
+/** @file
+ *
+ * LIBC - semget.
+ *
+ * Copyright (c) 2005 knut st. osmundsen <bird@anduin.net>
+ *
+ *
+ * This file is part of kBuild.
+ *
+ * kBuild is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kBuild is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kBuild; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include "libc-alias.h"
+#include <sys/types.h>
+#include <sys/sem.h>
+#include <errno.h>
+#include <InnoTekLIBC/backend.h>
+#define __LIBC_LOG_GROUP __LIBC_LOG_GRP_PROCESS
+#include <InnoTekLIBC/logstrict.h>
+
+
+/**
+ * sysget.
+ */
+int _STD(semget)(key_t key, int nsems, int semflg)
+{
+    LIBCLOG_ENTER("key=%#lx nsems=%d semflg=%#x\n", key, nsems, semflg);
+    int rc = __libc_Back_sysvSemGet(key, nsems, semflg);
+    if (rc >= 0)
+        LIBCLOG_RETURN_INT(rc);
+    errno = -rc;
+    LIBCLOG_RETURN_INT(-1);
+}
+
Index: trunk/src/emx/src/lib/process/semop.c
===================================================================
--- trunk/src/emx/src/lib/process/semop.c	(revision 1984)
+++ trunk/src/emx/src/lib/process/semop.c	(revision 1984)
@@ -0,0 +1,52 @@
+/* $Id$ */
+/** @file
+ *
+ * LIBC - semop.
+ *
+ * Copyright (c) 2005 knut st. osmundsen <bird@anduin.net>
+ *
+ *
+ * This file is part of kBuild.
+ *
+ * kBuild is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kBuild is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kBuild; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include "libc-alias.h"
+#include <sys/types.h>
+#include <sys/sem.h>
+#include <errno.h>
+#include <InnoTekLIBC/backend.h>
+#define __LIBC_LOG_GROUP __LIBC_LOG_GRP_PROCESS
+#include <InnoTekLIBC/logstrict.h>
+
+
+/**
+ * semop syscall.
+ */
+int _STD(semop)(int semid, struct sembuf *sops_user, size_t nsops)
+{
+    LIBCLOG_ENTER("semid=%#x sops_user=%p nsops=%d\n", semid, (void *)sops_user, nsops);
+    int rc = __libc_Back_sysvSemOp(semid, sops_user, nsops);
+    if (rc >= 0)
+        LIBCLOG_RETURN_INT(rc);
+    errno = -rc;
+    LIBCLOG_RETURN_INT(-1);
+}
+
Index: trunk/src/emx/src/lib/sys/sharedpm.c
===================================================================
--- trunk/src/emx/src/lib/sys/sharedpm.c	(revision 1983)
+++ trunk/src/emx/src/lib/sys/sharedpm.c	(revision 1984)
@@ -67,4 +67,5 @@
 #include <stdio.h>
 #include <sys/types.h>
+#include <sys/ipc.h>
 #include <emx/startup.h>
 #include <386/builtin.h>
@@ -103,6 +104,6 @@
 /** Checking for nested access to the shared memory. */
 static unsigned                 gcNesting;
-/** Pointer to tcpip termination handler. */
-static void                   (*gapfnSocketTerm[2])(void);
+/** Pointer to termination handlers. */
+static void                   (*gapfnExitList[4])(void);
 
 
@@ -925,4 +926,73 @@
 
 /**
+ * Checks if the calling process is a member of the specified group.
+ *
+ * @returns 0 if member.
+ * @returns -EPERM if not member.
+ * @param   gid     The group id in question.
+ */
+int __libc_spmIsGroupMember(gid_t gid)
+{
+    if (gpSPMSelf->egid == gid)
+        return 0;
+    int c = sizeof(gpSPMSelf->agidGroups) / sizeof(gpSPMSelf->agidGroups[0]);
+    gid_t *pGid = &gpSPMSelf->agidGroups[0];
+    while (c-- > 0)
+        if (*pGid++ == gid)
+            return 0;
+    return -EPERM;
+}
+
+
+/**
+ * Check if the caller can access the SysV IPC object as requested.
+ *
+ * @returns 0 if we can.
+ * @returns -EPERM if we cannot.
+ * @param   pPerm       The IPC permission structure.
+ * @param   Mode        The access request. IPC_M, IPC_W or IPC_R.
+ */
+int __libc_spmCanIPC(struct ipc_perm *pPerm, mode_t Mode)
+{
+    uid_t euid = gpSPMSelf->euid;
+    if (    euid != pPerm->cuid
+        &&  euid != pPerm->uid)
+    {
+        /*
+         * For a non-create/owner, we require privilege to
+         * modify the object protections.  Note: some other
+         * implementations permit IPC_M to be delegated to
+         * unprivileged non-creator/owner uids/gids.
+         */
+        if (Mode & IPC_M)
+            return __libc_spmIsSuperUser();
+
+        /*
+         * Try to match against creator/owner group; if not, fall
+         * back on other.
+         */
+        Mode >>= 3;
+        if (    __libc_spmIsGroupMember(pPerm->gid) != 0
+            &&  __libc_spmIsGroupMember(pPerm->cgid) != 0)
+                Mode >>= 3;
+    }
+    else
+    {
+        /*
+         * Always permit the creator/owner to update the object
+         * protections regardless of whether the object mode
+         * permits it.
+         */
+        if (Mode & IPC_M)
+            return 0;
+    }
+
+    if ((Mode & pPerm->mode) != Mode)
+        return __libc_spmIsSuperUser();
+    return 0;
+}
+
+
+/**
  * Gets the specified Id.
  *
@@ -1159,16 +1229,18 @@
  *
  * @returns 0 on success.
- * @returns -1 and errno on failure.
+ * @returns Negative error code (errno.h) on failure.
+ *
  * @param   pRegRec     Pointer to the exception handler registration record.
+ * @param   ppSPMHdr    Where to store the pointer to the SPM header. Can be NULL.
+ *
  * @remark  Don't even think of calling this if you're not LIBC!
  */
-int __libc_spmLock(__LIBC_PSPMXCPTREGREC pRegRec)
-{
-    LIBCLOG_ENTER("\n");
-    int rc = spmRequestMutexErrno(pRegRec);
-    if (!rc)
-        LIBCLOG_RETURN_INT(0);
-    errno = rc;
-    LIBCLOG_RETURN_INT(-1);
+int __libc_spmLock(__LIBC_PSPMXCPTREGREC pRegRec, __LIBC_PSPMHEADER *ppSPMHdr)
+{
+    LIBCLOG_ENTER("pRegRec=%p ppSPMHdr=%p\n", (void *)pRegRec, (void *)ppSPMHdr);
+    int rc = spmRequestMutex(pRegRec);
+    if (!rc && ppSPMHdr)
+        *ppSPMHdr = gpSPMHdr;
+    LIBCLOG_RETURN_INT(rc);
 }
 
@@ -1178,16 +1250,15 @@
  *
  * @returns 0 on success.
- * @returns -1 on and errno failure.
+ * @returns Negative error code (errno.h) on failure.
+ *
  * @param   pRegRec     Pointer to the exception handler registration record.
+ *
  * @remark  Don't even think of calling this if you're not LIBC!
  */
 int __libc_spmUnlock(__LIBC_PSPMXCPTREGREC pRegRec)
 {
-    LIBCLOG_ENTER("\n");
+    LIBCLOG_ENTER("pRegRec=%p\n", (void *)pRegRec);
     int rc = spmReleaseMutex(pRegRec);
-    if (!rc)
-        LIBCLOG_RETURN_INT(0);
-    errno = -rc;
-    LIBCLOG_RETURN_INT(-1);
+    LIBCLOG_RETURN_INT(rc);
 }
 
@@ -1195,8 +1266,50 @@
 /**
  * Allocate memory from the LIBC shared memory.
+ *
+ * The SPM must be locked using __libc_spmLock() prior to calling this function!
  *
  * @returns Pointer to allocated memory on success.
  * @returns NULL on failure.
+ *
  * @param   cbSize 	Size of memory to allocate.
+ *
+ * @remark  Don't think of calling this if you're not LIBC!
+ */
+void * __libc_spmAllocLocked(size_t cbSize)
+{
+    LIBCLOG_ENTER("cbSize=%d\n", cbSize);
+    void *pvRet = spmAlloc(cbSize);
+    LIBCLOG_RETURN_P(pvRet);
+}
+
+
+/**
+ * Free memory allocated by __libc_spmAllocLocked() or __libc_spmAlloc().
+ *
+ * The SPM must be locked using __libc_spmLock() prior to calling this function!
+ *
+ * @returns 0 on success.
+ * @returns -1 and errno on failure.
+ *
+ * @param   pv      Pointer to memory block returned by __libc_SpmAlloc().
+ *                  NULL is allowed.
+ * @remark  Don't think of calling this if you're not LIBC!
+ */
+int __libc_spmFreeLocked(void *pv)
+{
+    LIBCLOG_ENTER("pv=%p\n", pv);
+    int rc = spmFree(pv);
+    LIBCLOG_RETURN_INT(rc);
+}
+
+
+/**
+ * Allocate memory from the LIBC shared memory.
+ *
+ * @returns Pointer to allocated memory on success.
+ * @returns NULL on failure.
+ *
+ * @param   cbSize 	Size of memory to allocate.
+ *
  * @remark  Don't think of calling this if you're not LIBC!
  */
@@ -1234,5 +1347,5 @@
 
 /**
- * Free memory allocated by __libc_SpmAlloc().
+ * Free memory allocated by __libc_spmAlloc().
  *
  * @returns 0 on success.
@@ -1280,21 +1393,27 @@
 
 /**
- * Register TCPIP termination handler.
+ * Register termination handler.
  *
  * This is a manual way of by passing a.out's broken weak symbols.
  */
-void    __libc_spmSocketRegTerm(void (*pfnTerm)(void))
+void    __libc_spmRegTerm(void (*pfnTerm)(void))
 {
     LIBCLOG_ENTER("pfnTerm=%p\n", (void *)pfnTerm);
-    if (    pfnTerm == gapfnSocketTerm[0]
-        ||  pfnTerm == gapfnSocketTerm[1])
+    if (    pfnTerm == gapfnExitList[0]
+        ||  pfnTerm == gapfnExitList[1]
+        ||  pfnTerm == gapfnExitList[2]
+        ||  pfnTerm == gapfnExitList[3])
         LIBCLOG_RETURN_VOID();
 
-    if (!gapfnSocketTerm[0])
-        gapfnSocketTerm[0] = pfnTerm;
-    else if (!gapfnSocketTerm[1])
-        gapfnSocketTerm[1] = pfnTerm;
+    if (!gapfnExitList[0])
+        gapfnExitList[0] = pfnTerm;
+    else if (!gapfnExitList[1])
+        gapfnExitList[1] = pfnTerm;
+    else if (!gapfnExitList[2])
+        gapfnExitList[2] = pfnTerm;
+    else if (!gapfnExitList[3])
+        gapfnExitList[3] = pfnTerm;
     else
-        LIBC_ASSERTM_FAILED("There can only be two TCP/IP termination routines!!!!\n");
+        LIBC_ASSERTM_FAILED("There can only be 4 exit list routines!!!!\n");
 
     LIBCLOG_RETURN_VOID();
@@ -1932,5 +2051,5 @@
 /**
  * This function checks that there is at least 2k of writable
- * stack available. If there isn't a crash is usually the
+ * stack available. If there isn't, a crash is usually the
  * result.
  * @internal
@@ -2290,10 +2409,14 @@
     {
         /*
-         * Terminate tcpip.
+         * Exist list callbacks .
          */
-        if (gapfnSocketTerm[0])
-            gapfnSocketTerm[0]();
-        if (gapfnSocketTerm[1])
-            gapfnSocketTerm[1]();
+        if (gapfnExitList[0])
+            gapfnExitList[0]();
+        if (gapfnExitList[1])
+            gapfnExitList[1]();
+        if (gapfnExitList[2])
+            gapfnExitList[2]();
+        if (gapfnExitList[3])
+            gapfnExitList[3]();
 
         /*
Index: trunk/src/emx/src/lib/sys/signals.c
===================================================================
--- trunk/src/emx/src/lib/sys/signals.c	(revision 1983)
+++ trunk/src/emx/src/lib/sys/signals.c	(revision 1984)
@@ -1027,4 +1027,10 @@
 
     /*
+     * Does this thread have a notification callback?
+     */
+    if (pThrd->pfnSigCallback)
+        pThrd->pfnSigCallback(iSignalNo, pThrd->pvSigCallbackUser);
+
+    /*
      * Schedule the signal.
      */
@@ -2624,4 +2630,10 @@
 
     /*
+     * Does this thread have a notification callback?
+     */
+    if (pThrd->pfnSigCallback)
+        pThrd->pfnSigCallback(0, pThrd->pvSigCallbackUser);
+
+    /*
      * Try schedule signals pending on 1st (SPM) and 2nd level (private).
      */
Index: trunk/src/emx/src/lib/sys/sysv_sem.c
===================================================================
--- trunk/src/emx/src/lib/sys/sysv_sem.c	(revision 1984)
+++ trunk/src/emx/src/lib/sys/sysv_sem.c	(revision 1984)
@@ -0,0 +1,1538 @@
+/*-
+ * Implementation of SVID semaphores
+ *
+ * Author:  Daniel Boulet
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ */
+
+#include "libc-alias.h"
+//__FBSDID("$FreeBSD: src/sys/kern/sysv_sem.c,v 1.70.2.3 2005/03/22 17:20:07 sam Exp $");
+#include <sys/cdefs.h>
+
+#include <sys/types.h>
+#define _KERNEL
+#include <sys/sem.h>
+#undef _KERNEL
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSEXCEPTIONS
+#define INCL_DOSERRORS
+#define INCL_FSMACROS
+#define INCL_EXAPIS
+#include <os2emx.h>
+#include "syscalls.h"
+#include <string.h>
+#include <errno.h>
+#include <emx/umalloc.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <386/builtin.h>
+#include <InnoTekLIBC/sharedpm.h>
+#include <InnoTekLIBC/thread.h>
+#include <InnoTekLIBC/FastInfoBlocks.h>
+#include <InnoTekLIBC/libc.h>
+#include <InnoTekLIBC/backend.h>
+#define __LIBC_LOG_GROUP    __LIBC_LOG_GRP_BACK_FS
+#include <InnoTekLIBC/logstrict.h>
+
+/* debug printf */
+#define DPRINTF(a)	LIBCLOG_MSG a
+
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/**
+ * Representation of a semaphore id.
+ */
+struct semid_kernel
+{
+    /** Semaphore id. */
+    struct semid_ds u;
+    /** Event semaphore to wait on. */
+    HEV             hev;
+    /** Mutex protecting the semid_ds and HEV members. */
+    HMTX            hmtx;
+};
+
+/**
+ * SysV Sempahore globals residing in the shared process manager heap.
+ */
+struct __libc_SysV_Sem
+{
+    /** Structure version. */
+    unsigned            uVersion;
+    /** Number of users.
+     * This is primarily used for knowing when to re-create the semaphores.
+     */
+    volatile uint32_t	cUsers;
+    /** Number of active semaphores. */
+    int                 semtot;
+    /** Sempahores id pool. */
+    struct semid_kernel *sema;
+    /** Semaphore pool. */
+    struct sem         *sem;
+    /** Mutex protecting the undo list and other global sem stuff. */
+    HMTX                hmtx;
+    /** list of active undo structures. */
+    SLIST_HEAD(, sem_undo) semu_list;
+    /** undo structure pool. */
+    int	               *semu;
+
+    /** SysV Semaphore info. (Contains all the array sizes and such.) */
+    struct seminfo      seminfo;
+};
+
+/** Pointer to the globals. */
+static struct __libc_SysV_Sem *gpGlobals;
+
+//static struct mtx	sem_mtx;	/* semaphore global lock */ == spm mutex
+//static int	semtot = 0;
+//static struct semid_ds *sema;	/* semaphore id pool */
+//static struct mtx *sema_mtx;	/* semaphore id pool mutexes*/
+//static struct sem *sem;		/* semaphore pool */
+//SLIST_HEAD(, sem_undo) semu_list;	/* list of active undo structures */
+//static int	*semu;		/* undo structure pool */
+
+//#define SEMUNDO_MTX		gpGlobals->hmtx
+#define SEMUNDO_LOCK()		semMtxLock(gpGlobals->hmtx);
+#define SEMUNDO_UNLOCK()	semMtxUnlock(gpGlobals->hmtx);
+#define SEMUNDO_LOCKASSERT(how)	do {} while (0) //mtx_assert(gpGlobals->hmtx, (how));
+
+struct sem {
+	u_short	semval;		/* semaphore value */
+	pid_t	sempid;		/* pid of last operation */
+	u_short	volatile semncnt;	/* # awaiting semval > cval */
+	u_short	volatile semzcnt;	/* # awaiting semval = 0 */
+};
+
+/*
+ * Undo structure (one per process)
+ */
+struct sem_undo {
+	SLIST_ENTRY(sem_undo) un_next;	/* ptr to next active undo structure */
+	pid_t   un_proc;		/* owner of this structure */
+	short	un_cnt;			/* # of active entries */
+	struct undo {
+		short	un_adjval;	/* adjust on exit values */
+		short	un_num;		/* semaphore # */
+		int	un_id;		/* semid */
+	} un_ent[1];			/* undo entries */
+};
+
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+static int seminit(void);
+static void semexit_myhook(void);
+//later static int sysctl_sema(SYSCTL_HANDLER_ARGS);
+static int semvalid(int semid, struct semid_kernel *semaptr);
+static struct sem_undo *semu_alloc(void);
+static int semundo_adjust(struct sem_undo **supptr,
+		int semid, int semnum, int adjval);
+static void semundo_clear(int semid, int semnum);
+
+
+/*
+ * Configuration parameters
+ */
+#ifndef SEMMNI
+#define SEMMNI	10		/* # of semaphore identifiers */
+#endif
+#ifndef SEMMNS
+#define SEMMNS	60		/* # of semaphores in system */
+#endif
+#ifndef SEMUME
+#define SEMUME	10		/* max # of undo entries per process */
+#endif
+#ifndef SEMMNU
+#define SEMMNU	30		/* # of undo structures in system */
+#endif
+
+/* shouldn't need tuning */
+#ifndef SEMMAP
+#define SEMMAP	30		/* # of entries in semaphore map */
+#endif
+#ifndef SEMMSL
+#define SEMMSL	SEMMNS		/* max # of semaphores per id */
+#endif
+#ifndef SEMOPM
+#define SEMOPM	100		/* max # of operations per semop call */
+#endif
+
+#define SEMVMX	32767		/* semaphore maximum value */
+#define SEMAEM	16384		/* adjust on exit max value */
+
+/*
+ * Due to the way semaphore memory is allocated, we have to ensure that
+ * SEMUSZ is properly aligned.
+ */
+
+#define SEM_ALIGN(bytes) (((bytes) + (sizeof(long) - 1)) & ~(sizeof(long) - 1))
+
+/* actual size of an undo structure */
+#define SEMUSZ	SEM_ALIGN(offsetof(struct sem_undo, un_ent[SEMUME]))
+
+/*
+ * Macro to find a particular sem_undo vector
+ */
+#define SEMU(ix) \
+	((struct sem_undo *)(((intptr_t)gpGlobals->semu)+ix * seminfo.semusz))
+
+/*
+ * semaphore info struct
+ */
+static struct seminfo seminfo = {
+                SEMMAP,         /* # of entries in semaphore map */
+                SEMMNI,         /* # of semaphore identifiers */
+                SEMMNS,         /* # of semaphores in system */
+                SEMMNU,         /* # of undo structures in system */
+                SEMMSL,         /* max # of semaphores per id */
+                SEMOPM,         /* max # of operations per semop call */
+                SEMUME,         /* max # of undo entries per process */
+                SEMUSZ,         /* size in bytes of undo structure */
+                SEMVMX,         /* semaphore maximum value */
+                SEMAEM          /* adjust on exit max value */
+};
+
+#if 0
+SYSCTL_DECL(_kern_ipc);
+SYSCTL_INT(_kern_ipc, OID_AUTO, semmap, CTLFLAG_RD/*W*/, &seminfo.semmap, 0,
+    "Number of entries in the semaphore map");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semmni, CTLFLAG_RDTUN, &seminfo.semmni, 0,
+    "Number of semaphore identifiers");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semmns, CTLFLAG_RDTUN, &seminfo.semmns, 0,
+    "Maximum number of semaphores in the system");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semmnu, CTLFLAG_RDTUN, &seminfo.semmnu, 0,
+    "Maximum number of undo structures in the system");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semmsl, CTLFLAG_RD/*W*/, &seminfo.semmsl, 0,
+    "Max semaphores per id");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semopm, CTLFLAG_RDTUN, &seminfo.semopm, 0,
+    "Max operations per semop call");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semume, CTLFLAG_RDTUN, &seminfo.semume, 0,
+    "Max undo entries per process");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semusz, CTLFLAG_RDTUN, &seminfo.semusz, 0,
+    "Size in bytes of undo structure");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semvmx, CTLFLAG_RD/*W*/, &seminfo.semvmx, 0,
+    "Semaphore maximum value");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semaem, CTLFLAG_RD/*W*/, &seminfo.semaem, 0,
+    "Adjust on exit max value");
+SYSCTL_PROC(_kern_ipc, OID_AUTO, sema, CTLFLAG_RD,
+    NULL, 0, sysctl_sema, "", "");
+#endif
+
+
+
+/* wrappers */
+/* wrappers */
+/* wrappers */
+/* wrappers */
+/* wrappers */
+
+#define panic LIBC_ASSERTM_FAILED
+#undef copyout
+#define copyout(x,y,z)  !memcpy((y),(x),(z))
+#undef copyin
+#define copyin(x,y,z)   !memcpy((y),(x),(z))
+#define TUNABLE_INT_FETCH(path, var) _getenv_int("LIBC_" path, (var))
+
+//static int  spmRequestMutexErrno(__LIBC_PSPMXCPTREGREC pRegRec);
+//static int  spmRequestMutex(__LIBC_PSPMXCPTREGREC pRegRec);
+//static int  spmReleaseMutex(__LIBC_PSPMXCPTREGREC pRegRec);
+
+//int __libc_spmLock(__LIBC_PSPMXCPTREGREC pRegRec);
+
+
+static int seminit(void)
+{
+    LIBCLOG_ENTER("\n");
+
+    /*
+     * Try open it first.
+     */
+    __LIBC_SPMXCPTREGREC    RegRec;
+    __LIBC_PSPMHEADER       pSPMHdr;
+    int rc = __libc_spmLock(&RegRec, &pSPMHdr);
+    if (rc)
+        LIBCLOG_RETURN_INT(rc);
+    if (gpGlobals)
+    {
+        __libc_spmUnlock(&RegRec);
+        LIBCLOG_RETURN_INT(0);
+    }
+    struct __libc_SysV_Sem *pGlobals = gpGlobals = pSPMHdr->pSysVSem;
+    if (!pGlobals)
+    {
+        /*
+         * We will have to initialize the semaphore stuff.
+         * Since we're gonna parse environment first, release the SPM lock.
+         */
+        __libc_spmUnlock(&RegRec);
+
+        /*
+         * Get configuration options.
+         */
+        struct seminfo si = seminfo;
+        TUNABLE_INT_FETCH("kern.ipc.semmap", &si.semmap);
+        TUNABLE_INT_FETCH("kern.ipc.semmni", &si.semmni);
+        TUNABLE_INT_FETCH("kern.ipc.semmns", &si.semmns);
+        TUNABLE_INT_FETCH("kern.ipc.semmnu", &si.semmnu);
+        TUNABLE_INT_FETCH("kern.ipc.semmsl", &si.semmsl);
+        TUNABLE_INT_FETCH("kern.ipc.semopm", &si.semopm);
+        TUNABLE_INT_FETCH("kern.ipc.semume", &si.semume);
+        TUNABLE_INT_FETCH("kern.ipc.semusz", &si.semusz);
+        TUNABLE_INT_FETCH("kern.ipc.semvmx", &si.semvmx);
+        TUNABLE_INT_FETCH("kern.ipc.semaem", &si.semaem);
+
+        /*
+         * Calc the size of the stuff we need.
+         */
+        size_t cb = sizeof(*gpGlobals);
+        cb += sizeof(struct semid_kernel) * seminfo.semmni;
+        cb += sizeof(struct sem) * si.semmns;
+        cb += seminfo.semmnu * seminfo.semusz;
+
+        /*
+         * Take the sem and allocate the memory.
+         */
+        rc = __libc_spmLock(&RegRec, &pSPMHdr);
+        if (rc)
+            LIBCLOG_RETURN_INT(rc);
+        if (!pSPMHdr->pSysVSem)
+        {
+            pGlobals = (struct __libc_SysV_Sem *)__libc_spmAllocLocked(cb);
+            if (!pGlobals)
+            {
+                __libc_spmUnlock(&RegRec);
+                LIBCLOG_RETURN_INT(-ENOMEM);
+            }
+
+            /* init globals */
+            bzero(pGlobals, cb);
+            //pGlobals->cUsers    = 0;
+            pGlobals->uVersion  = 1;
+            //pGlobals->semtot    = 0;
+            uint8_t *pu8  = (uint8_t *)(pGlobals + 1);
+            pGlobals->sema      = (struct semid_kernel *)pu8;
+            pu8          += sizeof(struct semid_kernel) * si.semmni;
+            pGlobals->sem       = (struct sem *)pu8;
+            pu8          += sizeof(struct sem) * si.semmns;
+            SLIST_INIT(&pGlobals->semu_list);
+            pGlobals->semu       = (int *)pu8;
+            pGlobals->seminfo  = si;
+
+            /* Set globals. */
+            pSPMHdr->pSysVSem = pGlobals;
+            gpGlobals = pGlobals;
+        }
+    }
+
+    /*
+     * Open or create the semaphores.
+     */
+    int i;
+    FS_VAR_SAVE_LOAD();
+    if (!pGlobals->cUsers)
+    {
+        /* create */
+
+        rc = DosCreateMutexSemEx(NULL, &pGlobals->hmtx, DC_SEM_SHARED, FALSE);
+        for (i = 0; i < pGlobals->seminfo.semmni && !rc; i++)
+            rc = DosCreateMutexSemEx(NULL, &pGlobals->sema[i].hmtx, DC_SEM_SHARED, FALSE);
+        for (i = 0; i < pGlobals->seminfo.semmni && !rc; i++)
+            rc = DosCreateEventSemEx(NULL, &pGlobals->sema[i].hev, DC_SEM_SHARED, FALSE);
+    }
+    else
+    {
+        /* open */
+        rc = DosOpenMutexSemEx(NULL, &pGlobals->hmtx);
+        for (i = 0; i < pGlobals->seminfo.semmni && !rc; i++)
+            rc = DosOpenMutexSemEx(NULL, &pGlobals->sema[i].hmtx);
+        for (i = 0; i < pGlobals->seminfo.semmni && !rc; i++)
+            rc = DosOpenEventSemEx(NULL, &pGlobals->sema[i].hev);
+    }
+    __atomic_increment_u32(&pGlobals->cUsers);
+    FS_RESTORE();
+
+    /*
+     * Register exit list handler and release the SPM lock.
+     */
+    __libc_spmRegTerm(semexit_myhook);
+    __libc_spmUnlock(&RegRec);
+
+
+    /*
+     * Update seminfo and return.
+     */
+    seminfo = pGlobals->seminfo;
+    if (rc)
+        rc = -__libc_native2errno(rc);
+    LIBCLOG_RETURN_INT(rc);
+}
+
+
+/**
+ * This function checks that there is at least 2k of writable
+ * stack available. If there isn't, a crash is usually the
+ * result.
+ * @internal
+ */
+static int semMtxLockStackChecker(void)
+{
+    char volatile *pch = alloca(2048);
+    if (!pch)
+        return -1;
+    /* With any luck the compiler doesn't optimize this away. */
+    pch[0] = pch[1024] = pch[2044] = 0x7f;
+    return 0;
+}
+
+
+/**
+ * Locks a mutex semaphore.
+ *
+ * @returns 0 on success.
+ * @returns Negative errno on failure.
+ * @param   hmtx    Handle to the mutex.
+ */
+static int semMtxLock(HMTX hmtx)
+{
+    LIBCLOG_ENTER("hmtx=%#lx\n", hmtx);
+    ULONG       ul;
+    int         rc;
+    FS_VAR();
+
+    /*
+     * Check stack.
+     */
+    if (semMtxLockStackChecker())
+    {
+        LIBC_ASSERTM_FAILED("Too little stack left!\n");
+        LIBCLOG_RETURN_INT(-EFAULT);
+    }
+
+    /*
+     * Request semaphore and enter "must complete section" to avoid signal trouble.
+     */
+    FS_SAVE_LOAD();
+    rc = DosRequestMutexSem(hmtx, 30*1000);
+    DosEnterMustComplete(&ul);
+    if (!rc)
+    {
+        FS_RESTORE();
+        LIBCLOG_RETURN_INT(0);
+    }
+
+    /* failure out */
+    DosExitMustComplete(&ul);
+    LIBC_ASSERTM_FAILED("DosRequestMutexSem(%lu) failed with rc=%d!\n", hmtx, rc);
+    rc = -__libc_native2errno(rc);
+    FS_RESTORE();
+    LIBCLOG_RETURN_INT(rc);
+}
+
+
+/**
+ * Unlocks a mutex semaphore.
+ *
+ * @returns 0 on success.
+ * @returns Negative errno on failure.
+ * @param   hmtx    Handle to the mutex.
+ */
+static int semMtxUnlock(HMTX hmtx)
+{
+    LIBCLOG_ENTER("hmtx=%#lx\n", hmtx);
+    ULONG   ul = 0;
+    int     rc;
+    FS_VAR();
+
+    /*
+     * Release the semaphore.
+     */
+    FS_SAVE_LOAD();
+    rc = DosReleaseMutexSem(hmtx);
+    if (rc)
+    {
+        FS_RESTORE();
+        LIBC_ASSERTM_FAILED("DosReleaseMutexSem(%lu) -> %d\n", hmtx, rc);
+        rc = -__libc_native2errno(rc);
+        LIBCLOG_RETURN_INT(rc);
+    }
+
+    DosExitMustComplete(&ul);
+    FS_RESTORE();
+    LIBCLOG_RETURN_INT(0);
+}
+
+
+/**
+ * Arguments to semEvSleepSignalCallback().
+ */
+struct semEvSleepSignalCallback_args
+{
+    /** Set if pfnComplete have already been executed. */
+    volatile int            fDone;
+    /** Callback to execute. */
+    void                  (*pfnComplete)(int semid, struct semid_kernel *semaptr, uint16_t volatile *pu16);
+    /** Semaphore id. */
+    int	                    semid;
+    /** Semaphore id we was sleeping on. */
+    struct semid_kernel    *semaptr;
+    /** Pointer to the value to decrement. */
+    uint16_t volatile      *pu16;
+};
+
+/**
+ * Signal notification callback.
+ */
+static void semEvSleepSignalCallback(int iSignal, void *pvUser)
+{
+    struct semEvSleepSignalCallback_args *pArgs = (struct semEvSleepSignalCallback_args *)pvUser;
+    if (!pArgs->fDone)
+    {
+        pArgs->pfnComplete(pArgs->semid, pArgs->semaptr, pArgs->pu16);
+        pArgs->fDone = 1;
+    }
+}
+
+/**
+ * Sleep on a semaphore.
+ *
+ * This is the most difficult thing we're doing in this file.
+ *
+ * @returns 0 on success.
+ * @returns Negative error code (errno.h) on failure.
+ * @param   semaptr     Semaphore to wakeup.
+ * @param   pfnComplete Function to execute on signal and on wait completion.
+ */
+static int semEvSleep(struct semid_kernel *semaptr, void (*pfnComplete)(int semid, struct semid_kernel *semaptr, uint16_t volatile *pu16), int semid, uint16_t volatile *pu16)
+{
+    LIBCLOG_ENTER("semaptr=%p pfnComplete=%p semid=%#x pu16=%p:{%d}\n", (void *)semaptr, (void *)pfnComplete, semid, (void *)pu16, *pu16);
+
+    /*
+     * Setup signal notification.
+     */
+    __LIBC_PTHREAD pThrd = __libc_threadCurrentNoAuto();
+    int rc;
+    if (pThrd)
+    {
+        struct semEvSleepSignalCallback_args Args;
+        Args.fDone               = 0;
+        Args.pfnComplete         = pfnComplete;
+        Args.semid               = semid;
+        Args.semaptr             = semaptr;
+        Args.pu16                = pu16;
+        pThrd->pvSigCallbackUser = &Args;
+        pThrd->pfnSigCallback    = semEvSleepSignalCallback;
+
+        /*
+         * Reset the event semaphore.
+         */
+        ULONG ulIgnore;
+        FS_VAR_SAVE_LOAD();
+        rc = DosResetEventSem(semaptr->hev, &ulIgnore);
+        if (!rc || rc == ERROR_ALREADY_RESET)
+        {
+            /*
+             * Release the sempahore and exit the must complete section.
+             */
+            rc = semMtxUnlock(semaptr->hev);
+            if (!rc)
+            {
+                /*
+                 * Now, lets wait.
+                 */
+                DosWaitEventSem(semaptr->hev, SEM_INDEFINITE_WAIT);
+
+                /*
+                 * Regain access to the semaphore and must complete section
+                 * before checking if we got an interrupt or not.
+                 */
+                rc = semMtxLock(semaptr->hmtx);
+                if (!Args.fDone)
+		    Args.pfnComplete(Args.semid, Args.semaptr, Args.pu16);
+                else if (!rc)
+                    rc = -EINTR;
+            }
+        }
+        else
+            rc = -__libc_native2errno(rc);
+        FS_RESTORE();
+    }
+    else
+        rc = -ENOSYS;
+
+    LIBCLOG_RETURN_INT(rc);
+}
+
+
+/**
+ * Wakes up all threads sleeping on a given event semaphore.
+ *
+ * @returns 0 on success.
+ * @returns Negative error code (errno.h) on failure.
+ * @param   semaptr     Semaphore to wakeup.
+ */
+static int semEvWakeup(struct semid_kernel *semaptr)
+{
+    LIBCLOG_ENTER("semaptr=%p\n", (void *)semaptr);
+    FS_VAR_SAVE_LOAD();
+    int rc = DosPostEventSem(semaptr->hev);
+    FS_RESTORE();
+    if (rc)
+    {
+        if (rc == ERROR_ALREADY_POSTED || rc == ERROR_TOO_MANY_POSTS)
+            rc = 0;
+        else
+            rc = -__libc_native2errno(rc);
+    }
+    LIBCLOG_RETURN_INT(rc);
+}
+
+
+/*
+ * Allocate a new sem_undo structure for a process
+ * (returns ptr to structure or NULL if no more room)
+ */
+
+static struct sem_undo *
+semu_alloc(void)
+{
+	int i;
+	struct sem_undo *suptr;
+	struct sem_undo **supptr;
+	int attempt;
+
+	SEMUNDO_LOCKASSERT(MA_OWNED);
+	/*
+	 * Try twice to allocate something.
+	 * (we'll purge an empty structure after the first pass so
+	 * two passes are always enough)
+	 */
+
+	for (attempt = 0; attempt < 2; attempt++) {
+		/*
+		 * Look for a free structure.
+		 * Fill it in and return it if we find one.
+		 */
+
+		for (i = 0; i < seminfo.semmnu; i++) {
+			suptr = SEMU(i);
+			if (suptr->un_proc == 0) {
+				SLIST_INSERT_HEAD(&gpGlobals->semu_list, suptr, un_next);
+				suptr->un_cnt = 0;
+				suptr->un_proc = fibGetPid();
+				return(suptr);
+			}
+		}
+
+		/*
+		 * We didn't find a free one, if this is the first attempt
+		 * then try to free a structure.
+		 */
+
+		if (attempt == 0) {
+			/* All the structures are in use - try to free one */
+			int did_something = 0;
+
+			SLIST_FOREACH_PREVPTR(suptr, supptr, &gpGlobals->semu_list,
+			    un_next) {
+				if (suptr->un_cnt == 0) {
+					suptr->un_proc = 0;
+					did_something = 1;
+					*supptr = SLIST_NEXT(suptr, un_next);
+					break;
+				}
+			}
+
+			/* If we didn't free anything then just give-up */
+			if (!did_something)
+				return(NULL);
+		} else {
+			/*
+			 * The second pass failed even though we freed
+			 * something after the first pass!
+			 * This is IMPOSSIBLE!
+			 */
+			panic("semu_alloc - second attempt failed");
+		}
+	}
+	return (NULL);
+}
+
+/*
+ * Adjust a particular entry for a particular proc
+ */
+
+static int
+semundo_adjust(supptr, semid, semnum, adjval)
+	struct sem_undo **supptr;
+	int semid, semnum;
+	int adjval;
+{
+	pid_t  pid = fibGetPid();
+	struct sem_undo *suptr;
+	struct undo *sunptr;
+	int i;
+
+	SEMUNDO_LOCKASSERT(MA_OWNED);
+	/* Look for and remember the sem_undo if the caller doesn't provide
+	   it */
+
+	suptr = *supptr;
+	if (suptr == NULL) {
+		SLIST_FOREACH(suptr, &gpGlobals->semu_list, un_next) {
+			if (suptr->un_proc == pid) {
+				*supptr = suptr;
+				break;
+			}
+		}
+		if (suptr == NULL) {
+			if (adjval == 0)
+				return(0);
+			suptr = semu_alloc();
+			if (suptr == NULL)
+				return(ENOSPC);
+			*supptr = suptr;
+		}
+	}
+
+	/*
+	 * Look for the requested entry and adjust it (delete if adjval becomes
+	 * 0).
+	 */
+	sunptr = &suptr->un_ent[0];
+	for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
+		if (sunptr->un_id != semid || sunptr->un_num != semnum)
+			continue;
+		if (adjval != 0) {
+			adjval += sunptr->un_adjval;
+			if (adjval > seminfo.semaem || adjval < -seminfo.semaem)
+				return (ERANGE);
+		}
+		sunptr->un_adjval = adjval;
+		if (sunptr->un_adjval == 0) {
+			suptr->un_cnt--;
+			if (i < suptr->un_cnt)
+				suptr->un_ent[i] =
+				    suptr->un_ent[suptr->un_cnt];
+		}
+		return(0);
+	}
+
+	/* Didn't find the right entry - create it */
+	if (adjval == 0)
+		return(0);
+	if (adjval > seminfo.semaem || adjval < -seminfo.semaem)
+		return (ERANGE);
+	if (suptr->un_cnt != seminfo.semume) {
+		sunptr = &suptr->un_ent[suptr->un_cnt];
+		suptr->un_cnt++;
+		sunptr->un_adjval = adjval;
+		sunptr->un_id = semid; sunptr->un_num = semnum;
+	} else
+		return(EINVAL);
+	return(0);
+}
+
+static void
+semundo_clear(semid, semnum)
+	int semid, semnum;
+{
+	struct sem_undo *suptr;
+
+	SEMUNDO_LOCKASSERT(MA_OWNED);
+	SLIST_FOREACH(suptr, &gpGlobals->semu_list, un_next) {
+		struct undo *sunptr = &suptr->un_ent[0];
+		int i = 0;
+
+		while (i < suptr->un_cnt) {
+			if (sunptr->un_id == semid) {
+				if (semnum == -1 || sunptr->un_num == semnum) {
+					suptr->un_cnt--;
+					if (i < suptr->un_cnt) {
+						suptr->un_ent[i] =
+						  suptr->un_ent[suptr->un_cnt];
+						continue;
+					}
+				}
+				if (semnum != -1)
+					break;
+			}
+			i++, sunptr++;
+		}
+	}
+}
+
+static int
+semvalid(semid, semaptr)
+	int semid;
+	struct semid_kernel *semaptr;
+{
+
+	return ((semaptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
+	    semaptr->u.sem_perm.seq != IPCID_TO_SEQ(semid) ? EINVAL : 0);
+}
+
+
+/**
+ * semctl syscall
+ */
+int __libc_Back_sysvSemCtl(int semid, int semnum, int cmd, union semun real_arg)
+{
+	LIBCLOG_ENTER("semid=%d semnum=%d cmd=%d real_arg=%p\n", semid, semnum, cmd, (void *)real_arg.buf);
+	u_short *array;
+	int i, rval, error;
+	struct semid_ds sbuf;
+	struct semid_kernel *semaptr;
+	u_short usval, count;
+        uid_t uid;
+        HMTX hmtx;
+
+
+        /*
+         * Lazy init.
+         */
+        if (!gpGlobals)
+        {
+            error = seminit();
+            if (error)
+                LIBCLOG_RETURN_INT(error);
+        }
+	struct sem * sem = gpGlobals->sem;
+	struct semid_kernel * sema = gpGlobals->sema;
+
+	array = NULL;
+	error = 0;
+	rval = 0;
+        hmtx = NULLHANDLE;
+
+	switch(cmd) {
+	case SEM_STAT:
+		if (semid < 0 || semid >= seminfo.semmni)
+			LIBCLOG_RETURN_INT(-EINVAL);
+		semaptr = &sema[semid];
+                semMtxLock(hmtx = semaptr->hmtx);
+		if ((semaptr->u.sem_perm.mode & SEM_ALLOC) == 0) {
+			error = EINVAL;
+			goto done2;
+		}
+		if ((error = __libc_spmCanIPC(&semaptr->u.sem_perm, IPC_R)))
+			goto done2;
+                semMtxUnlock(hmtx); hmtx = NULLHANDLE;
+		memcpy(real_arg.buf, &semaptr->u, sizeof(struct semid_ds));
+		rval = IXSEQ_TO_IPCID(semid, semaptr->u.sem_perm);
+		LIBCLOG_RETURN_INT(rval);
+	}
+
+	semid = IPCID_TO_IX(semid);
+	if (semid < 0 || semid >= seminfo.semmni)
+		LIBCLOG_RETURN_INT(-EINVAL);
+
+	semaptr = &sema[semid];
+
+	switch (cmd) {
+	case IPC_RMID:
+		semMtxLock(hmtx = semaptr->hmtx);
+		if ((error = semvalid(semid, semaptr)) != 0)
+			goto done2;
+		if ((error = __libc_spmCanIPC(&semaptr->u.sem_perm, IPC_M)))
+			goto done2;
+                uid = __libc_spmGetId(__LIBC_SPMID_EUID);
+		semaptr->u.sem_perm.cuid = uid;
+		semaptr->u.sem_perm.uid = uid;
+		gpGlobals->semtot -= semaptr->u.sem_nsems;
+		for (i = semaptr->u.sem_base - sem; i < gpGlobals->semtot; i++)
+			sem[i] = sem[i + semaptr->u.sem_nsems];
+		for (i = 0; i < seminfo.semmni; i++) {
+			if ((sema[i].u.sem_perm.mode & SEM_ALLOC) &&
+			    sema[i].u.sem_base > semaptr->u.sem_base)
+				sema[i].u.sem_base -= semaptr->u.sem_nsems;
+		}
+		semaptr->u.sem_perm.mode = 0;
+		SEMUNDO_LOCK();
+		semundo_clear(semid, -1);
+		SEMUNDO_UNLOCK();
+		semEvWakeup(semaptr);
+		break;
+
+	case IPC_SET:
+		if ((error = copyin(real_arg.buf, &sbuf, sizeof(sbuf))) != 0)
+			goto done2;
+		semMtxLock(hmtx = semaptr->hmtx);
+		if ((error = semvalid(semid, semaptr)) != 0)
+			goto done2;
+		if ((error = __libc_spmCanIPC(&semaptr->u.sem_perm, IPC_M)))
+			goto done2;
+		semaptr->u.sem_perm.uid = sbuf.sem_perm.uid;
+		semaptr->u.sem_perm.gid = sbuf.sem_perm.gid;
+		semaptr->u.sem_perm.mode = (semaptr->u.sem_perm.mode & ~0777) |
+		    (sbuf.sem_perm.mode & 0777);
+		semaptr->u.sem_ctime = fibGetUnixSeconds();
+		break;
+
+	case IPC_STAT:
+		semMtxLock(hmtx = semaptr->hmtx);
+		if ((error = semvalid(semid, semaptr)) != 0)
+			goto done2;
+		if ((error = __libc_spmCanIPC(&semaptr->u.sem_perm, IPC_R)))
+			goto done2;
+		sbuf = semaptr->u;
+		semMtxUnlock(hmtx); hmtx = NULLHANDLE;
+		error = copyout(semaptr, real_arg.buf,
+				sizeof(struct semid_ds));
+		break;
+
+	case GETNCNT:
+		semMtxLock(hmtx = semaptr->hmtx);
+		if ((error = semvalid(semid, semaptr)) != 0)
+			goto done2;
+		if ((error = __libc_spmCanIPC(&semaptr->u.sem_perm, IPC_R)))
+			goto done2;
+		if (semnum < 0 || semnum >= semaptr->u.sem_nsems) {
+			error = EINVAL;
+			goto done2;
+		}
+		rval = semaptr->u.sem_base[semnum].semncnt;
+		break;
+
+	case GETPID:
+		semMtxLock(hmtx = semaptr->hmtx);
+		if ((error = semvalid(semid, semaptr)) != 0)
+			goto done2;
+		if ((error = __libc_spmCanIPC(&semaptr->u.sem_perm, IPC_R)))
+			goto done2;
+		if (semnum < 0 || semnum >= semaptr->u.sem_nsems) {
+			error = EINVAL;
+			goto done2;
+		}
+		rval = semaptr->u.sem_base[semnum].sempid;
+		break;
+
+	case GETVAL:
+		semMtxLock(hmtx = semaptr->hmtx);
+		if ((error = semvalid(semid, semaptr)) != 0)
+			goto done2;
+		if ((error = __libc_spmCanIPC(&semaptr->u.sem_perm, IPC_R)))
+			goto done2;
+		if (semnum < 0 || semnum >= semaptr->u.sem_nsems) {
+			error = EINVAL;
+			goto done2;
+		}
+		rval = semaptr->u.sem_base[semnum].semval;
+		break;
+
+	case GETALL:
+		array = _hmalloc(sizeof(*array) * semaptr->u.sem_nsems);
+		semMtxLock(hmtx = semaptr->hmtx);
+		if ((error = semvalid(semid, semaptr)) != 0)
+			goto done2;
+		if ((error = __libc_spmCanIPC(&semaptr->u.sem_perm, IPC_R)))
+			goto done2;
+		for (i = 0; i < semaptr->u.sem_nsems; i++)
+			array[i] = semaptr->u.sem_base[i].semval;
+		semMtxUnlock(hmtx); hmtx = NULLHANDLE;
+		error = copyout(array, real_arg.array,
+		    i * sizeof(real_arg.array[0]));
+		break;
+
+	case GETZCNT:
+		semMtxLock(hmtx = semaptr->hmtx);
+		if ((error = semvalid(semid, semaptr)) != 0)
+			goto done2;
+		if ((error = __libc_spmCanIPC(&semaptr->u.sem_perm, IPC_R)))
+			goto done2;
+		if (semnum < 0 || semnum >= semaptr->u.sem_nsems) {
+			error = EINVAL;
+			goto done2;
+		}
+		rval = semaptr->u.sem_base[semnum].semzcnt;
+		break;
+
+	case SETVAL:
+		semMtxLock(hmtx = semaptr->hmtx);
+		if ((error = semvalid(semid, semaptr)) != 0)
+			goto done2;
+		if ((error = __libc_spmCanIPC(&semaptr->u.sem_perm, IPC_W)))
+			goto done2;
+		if (semnum < 0 || semnum >= semaptr->u.sem_nsems) {
+			error = EINVAL;
+			goto done2;
+		}
+		if (real_arg.val < 0 || real_arg.val > seminfo.semvmx) {
+			error = ERANGE;
+			goto done2;
+		}
+		semaptr->u.sem_base[semnum].semval = real_arg.val;
+		SEMUNDO_LOCK();
+		semundo_clear(semid, semnum);
+		SEMUNDO_UNLOCK();
+		semEvWakeup(semaptr);
+		break;
+
+	case SETALL:
+		semMtxLock(hmtx = semaptr->hmtx);
+raced:
+		if ((error = semvalid(semid, semaptr)) != 0)
+			goto done2;
+		count = semaptr->u.sem_nsems;
+		semMtxUnlock(hmtx); hmtx = NULLHANDLE;
+		array = _hmalloc(sizeof(*array) * count);
+		error = copyin(real_arg.array, array, count * sizeof(*array));
+		if (error)
+			break;
+		semMtxLock(hmtx = semaptr->hmtx);
+		if ((error = semvalid(semid, semaptr)) != 0)
+			goto done2;
+		/* we could have raced? */
+		if (count != semaptr->u.sem_nsems) {
+			free(array);
+			array = NULL;
+			goto raced;
+		}
+		if ((error = __libc_spmCanIPC(&semaptr->u.sem_perm, IPC_W)))
+			goto done2;
+		for (i = 0; i < semaptr->u.sem_nsems; i++) {
+			usval = array[i];
+			if (usval > seminfo.semvmx) {
+				error = ERANGE;
+				break;
+			}
+			semaptr->u.sem_base[i].semval = usval;
+		}
+		SEMUNDO_LOCK();
+		semundo_clear(semid, -1);
+		SEMUNDO_UNLOCK();
+		semEvWakeup(semaptr);
+		break;
+
+	default:
+		error = EINVAL;
+		break;
+	}
+
+done2:
+	if (hmtx)
+		semMtxUnlock(hmtx);
+
+	if (array != NULL)
+		free(array);
+        if (error)
+            rval = -error;
+	LIBCLOG_RETURN_INT(rval);
+}
+
+
+/**
+ * sysget syscall.
+ */
+int __libc_Back_sysvSemGet(key_t key, int nsems, int semflg)
+{
+	LIBCLOG_ENTER("key=%#lx nsems=%d semflg=%#x\n", key, nsems, semflg);
+	int error = 0;
+        int semid;
+
+        /*
+         * Lazy init.
+         */
+        if (!gpGlobals)
+        {
+            error = seminit();
+            if (error)
+                LIBCLOG_RETURN_INT(error);
+        }
+	struct sem * sem = gpGlobals->sem;
+	struct semid_kernel * sema = gpGlobals->sema;
+
+	error = semMtxLock(gpGlobals->hmtx);
+        if (error)
+            LIBCLOG_RETURN_INT(error);
+
+	if (key != IPC_PRIVATE) {
+		for (semid = 0; semid < seminfo.semmni; semid++) {
+			if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) &&
+			    sema[semid].u.sem_perm.key == key)
+				break;
+		}
+		if (semid < seminfo.semmni) {
+			DPRINTF(("found public key\n"));
+			if ((error = __libc_spmCanIPC(&sema[semid].u.sem_perm,
+			    semflg & 0700))) {
+				goto done2;
+			}
+			if (nsems > 0 && sema[semid].u.sem_nsems < nsems) {
+				DPRINTF(("too small\n"));
+				error = EINVAL;
+				goto done2;
+			}
+			if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
+				DPRINTF(("not exclusive\n"));
+				error = EEXIST;
+				goto done2;
+			}
+			goto found;
+		}
+	}
+
+	DPRINTF(("need to allocate the semid_ds\n"));
+	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
+		if (nsems <= 0 || nsems > seminfo.semmsl) {
+			DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
+			    seminfo.semmsl));
+			error = EINVAL;
+			goto done2;
+		}
+		if (nsems > seminfo.semmns - gpGlobals->semtot) {
+			DPRINTF((
+			    "not enough semaphores left (need %d, got %d)\n",
+			    nsems, seminfo.semmns - gpGlobals->semtot));
+			error = ENOSPC;
+			goto done2;
+		}
+		for (semid = 0; semid < seminfo.semmni; semid++) {
+			if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) == 0)
+				break;
+		}
+		if (semid == seminfo.semmni) {
+			DPRINTF(("no more semid_ds's available\n"));
+			error = ENOSPC;
+			goto done2;
+		}
+		DPRINTF(("semid %d is available\n", semid));
+		sema[semid].u.sem_perm.key = key;
+		sema[semid].u.sem_perm.cuid = sema[semid].u.sem_perm.uid = __libc_spmGetId(__LIBC_SPMID_EUID);
+		sema[semid].u.sem_perm.cgid = sema[semid].u.sem_perm.gid = __libc_spmGetId(__LIBC_SPMID_EGID);
+		sema[semid].u.sem_perm.mode = (semflg & 0777) | SEM_ALLOC;
+		sema[semid].u.sem_perm.seq =
+		    (sema[semid].u.sem_perm.seq + 1) & 0x7fff;
+		sema[semid].u.sem_nsems = nsems;
+		sema[semid].u.sem_otime = 0;
+		sema[semid].u.sem_ctime = fibGetUnixSeconds();
+		sema[semid].u.sem_base = &sem[gpGlobals->semtot];
+		gpGlobals->semtot += nsems;
+		bzero(sema[semid].u.sem_base,
+		    sizeof(sema[semid].u.sem_base[0])*nsems);
+		DPRINTF(("sembase = %p, next = %p\n", (void *)sema[semid].u.sem_base, (void *)&sem[gpGlobals->semtot]));
+	} else {
+		DPRINTF(("didn't find it and wasn't asked to create it\n"));
+		error = ENOENT;
+		goto done2;
+	}
+
+found:
+        errno = -IXSEQ_TO_IPCID(semid, sema[semid].u.sem_perm);
+done2:
+	semMtxUnlock(gpGlobals->hmtx);
+	LIBCLOG_RETURN_INT(-error);
+}
+
+
+static void __libc_Back_sysvSemSleepDone(int semid, struct semid_kernel *semaptr, uint16_t volatile *pu16)
+{
+    /*
+     * Make sure that the semaphore still exists
+     */
+    if ((semaptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
+	semaptr->u.sem_perm.seq != IPCID_TO_SEQ(semid)) {
+	    return;
+    }
+
+    /*
+     * The semaphore is still alive.  Readjust the count of
+     * waiting processes.
+     */
+    __atomic_decrement_u16(pu16);
+}
+
+/**
+ * semop syscall.
+ */
+int __libc_Back_sysvSemOp(int semid, struct sembuf *sops_user, size_t nsops)
+{
+	LIBCLOG_ENTER("semid=%d sops_user=%p nsops=%d\n", semid, (void *)sops_user, nsops);
+#define SMALL_SOPS	8
+        struct sembuf *sops;
+	struct sembuf small_sops[SMALL_SOPS];
+	struct semid_kernel *semaptr;
+	struct sembuf *sopptr = 0;
+	struct sem *semptr = 0;
+	struct sem_undo *suptr;
+	size_t i, j, k;
+	int error;
+	int do_wakeup, do_undos;
+
+        /*
+         * Lazy init
+         */
+        if (!gpGlobals)
+        {
+            error = seminit();
+            if (error)
+                LIBCLOG_RETURN_INT(error);
+        }
+	struct semid_kernel * sema = gpGlobals->sema;
+
+
+	semid = IPCID_TO_IX(semid);	/* Convert back to zero origin */
+
+	if (semid < 0 || semid >= seminfo.semmni)
+		LIBCLOG_RETURN_INT(-EINVAL);
+
+	/* Allocate memory for sem_ops */
+	if (nsops <= SMALL_SOPS)
+		sops = small_sops;
+	else if (nsops <= seminfo.semopm)
+		sops = _hmalloc(nsops * sizeof(*sops));
+	else {
+		DPRINTF(("too many sops (max=%d, nsops=%d)\n", seminfo.semopm,
+		    nsops));
+		LIBCLOG_RETURN_INT(-E2BIG);
+	}
+	if ((error = copyin(sops_user, sops, nsops * sizeof(sops[0]))) != 0) {
+		DPRINTF(("error = %d from copyin(%p, %p, %d)\n", error,
+		    (void *)sops_user, (void *)sops, nsops * sizeof(sops[0])));
+		if (sops != small_sops)
+			free(sops);
+		LIBCLOG_RETURN_INT(error);
+	}
+
+	semaptr = &sema[semid];
+	error = semMtxLock(semaptr->hmtx);
+        if (error) {
+                if (sops != small_sops)
+                        free(sops);
+                LIBCLOG_RETURN_INT(error);
+        }
+
+	if ((semaptr->u.sem_perm.mode & SEM_ALLOC) == 0) {
+		error = EINVAL;
+		goto done2;
+	}
+	if (semaptr->u.sem_perm.seq != IPCID_TO_SEQ(semid)) {
+		error = EINVAL;
+		goto done2;
+	}
+	/*
+	 * Initial pass thru sops to see what permissions are needed.
+	 * Also perform any checks that don't need repeating on each
+	 * attempt to satisfy the request vector.
+	 */
+	j = 0;		/* permission needed */
+	do_undos = 0;
+	for (i = 0; i < nsops; i++) {
+		sopptr = &sops[i];
+		if (sopptr->sem_num >= semaptr->u.sem_nsems) {
+			error = EFBIG;
+			goto done2;
+		}
+		if (sopptr->sem_flg & SEM_UNDO && sopptr->sem_op != 0)
+			do_undos = 1;
+		j |= (sopptr->sem_op == 0) ? SEM_R : SEM_A;
+	}
+
+	if ((error = __libc_spmCanIPC(&semaptr->u.sem_perm, j))) {
+		DPRINTF(("error = %d from ipaccess\n", error));
+		goto done2;
+	}
+
+	/*
+	 * Loop trying to satisfy the vector of requests.
+	 * If we reach a point where we must wait, any requests already
+	 * performed are rolled back and we go to sleep until some other
+	 * process wakes us up.  At this point, we start all over again.
+	 *
+	 * This ensures that from the perspective of other tasks, a set
+	 * of requests is atomic (never partially satisfied).
+	 */
+	for (;;) {
+		do_wakeup = 0;
+		error = 0;	/* error return if necessary */
+
+		for (i = 0; i < nsops; i++) {
+			sopptr = &sops[i];
+			semptr = &semaptr->u.sem_base[sopptr->sem_num];
+
+			DPRINTF((
+			    "semop:  semaptr=%p, sem_base=%p, "
+			    "semptr=%p, sem[%d]=%d : op=%d, flag=%s\n",
+			    (void *)semaptr, (void *)semaptr->u.sem_base, (void *)semptr,
+			    sopptr->sem_num, semptr->semval, sopptr->sem_op,
+			    (sopptr->sem_flg & IPC_NOWAIT) ?
+			    "nowait" : "wait"));
+
+			if (sopptr->sem_op < 0) {
+				if (semptr->semval + sopptr->sem_op < 0) {
+					DPRINTF(("semop:  can't do it now\n"));
+					break;
+				} else {
+					semptr->semval += sopptr->sem_op;
+					if (semptr->semval == 0 &&
+					    semptr->semzcnt > 0)
+						do_wakeup = 1;
+				}
+			} else if (sopptr->sem_op == 0) {
+				if (semptr->semval != 0) {
+					DPRINTF(("semop:  not zero now\n"));
+					break;
+				}
+			} else if (semptr->semval + sopptr->sem_op >
+			    seminfo.semvmx) {
+				error = ERANGE;
+				break;
+			} else {
+				if (semptr->semncnt > 0)
+					do_wakeup = 1;
+				semptr->semval += sopptr->sem_op;
+			}
+		}
+
+		/*
+		 * Did we get through the entire vector?
+		 */
+		if (i >= nsops)
+			goto done;
+
+		/*
+		 * No ... rollback anything that we've already done
+		 */
+		DPRINTF(("semop:  rollback 0 through %d\n", i-1));
+		for (j = 0; j < i; j++)
+			semaptr->u.sem_base[sops[j].sem_num].semval -=
+			    sops[j].sem_op;
+
+		/* If we detected an error, return it */
+		if (error != 0)
+			goto done2;
+
+		/*
+		 * If the request that we couldn't satisfy has the
+		 * NOWAIT flag set then return with EAGAIN.
+		 */
+		if (sopptr->sem_flg & IPC_NOWAIT) {
+			error = EAGAIN;
+			goto done2;
+		}
+
+		uint16_t volatile *pu16 = sopptr->sem_op == 0 ? &semptr->semzcnt : &semptr->semncnt;
+		__atomic_increment_u16(pu16);
+		DPRINTF(("semop:  good night (%d)!\n", sopptr->sem_op));
+		error = -semEvSleep(semaptr, __libc_Back_sysvSemSleepDone, semid, pu16);
+		DPRINTF(("semop:  good morning (error=%d)!\n", error));
+		/* return code is checked below, after sem[nz]cnt-- */
+
+		/*
+		 * Make sure that the semaphore still exists
+		 */
+		if ((semaptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
+		    semaptr->u.sem_perm.seq != IPCID_TO_SEQ(semid)) {
+			error = EIDRM;
+			goto done2;
+		}
+
+		/*
+		 * Is it really morning, or was our sleep interrupted?
+		 * (Delayed check of msleep() return code because we
+		 * need to decrement sem[nz]cnt either way.)
+		 */
+		if (error != 0) {
+			error = EINTR;
+			goto done2;
+		}
+		DPRINTF(("semop:  good morning!\n"));
+	}
+
+done:
+	/*
+	 * Process any SEM_UNDO requests.
+	 */
+	if (do_undos) {
+		SEMUNDO_LOCK();
+		suptr = NULL;
+		for (i = 0; i < nsops; i++) {
+			/*
+			 * We only need to deal with SEM_UNDO's for non-zero
+			 * op's.
+			 */
+			int adjval;
+
+			if ((sops[i].sem_flg & SEM_UNDO) == 0)
+				continue;
+			adjval = sops[i].sem_op;
+			if (adjval == 0)
+				continue;
+			error = semundo_adjust(&suptr, semid,
+			    sops[i].sem_num, -adjval);
+			if (error == 0)
+				continue;
+
+			/*
+			 * Oh-Oh!  We ran out of either sem_undo's or undo's.
+			 * Rollback the adjustments to this point and then
+			 * rollback the semaphore ups and down so we can return
+			 * with an error with all structures restored.  We
+			 * rollback the undo's in the exact reverse order that
+			 * we applied them.  This guarantees that we won't run
+			 * out of space as we roll things back out.
+			 */
+			for (j = 0; j < i; j++) {
+				k = i - j - 1;
+				if ((sops[k].sem_flg & SEM_UNDO) == 0)
+					continue;
+				adjval = sops[k].sem_op;
+				if (adjval == 0)
+					continue;
+				if (semundo_adjust(&suptr, semid,
+				    sops[k].sem_num, adjval) != 0)
+					panic("semop - can't undo undos");
+			}
+
+			for (j = 0; j < nsops; j++)
+				semaptr->u.sem_base[sops[j].sem_num].semval -=
+				    sops[j].sem_op;
+
+			DPRINTF(("error = %d from semundo_adjust\n", error));
+			SEMUNDO_UNLOCK();
+			goto done2;
+		} /* loop through the sops */
+		SEMUNDO_UNLOCK();
+	} /* if (do_undos) */
+
+	/* We're definitely done - set the sempid's and time */
+	for (i = 0; i < nsops; i++) {
+		sopptr = &sops[i];
+		semptr = &semaptr->u.sem_base[sopptr->sem_num];
+		semptr->sempid = fibGetPid();
+	}
+	semaptr->u.sem_otime = fibGetUnixSeconds();
+
+	/*
+	 * Do a wakeup if any semaphore was up'd whilst something was
+	 * sleeping on it.
+	 */
+	if (do_wakeup) {
+		DPRINTF(("semop:  doing wakeup\n"));
+		semEvWakeup(semaptr);
+		DPRINTF(("semop:  back from wakeup\n"));
+	}
+	DPRINTF(("semop:  done\n"));
+done2:
+	semMtxUnlock(semaptr->hmtx);
+	if (sops != small_sops)
+		free(sops);
+	LIBCLOG_RETURN_INT(-error);
+}
+
+/*
+ * Go through the undo structures for this process and apply the adjustments to
+ * semaphores.
+ */
+static void
+semexit_myhook(void)
+{
+    	LIBCLOG_ENTER("\n");
+	struct sem_undo *suptr;
+	struct sem_undo **supptr;
+
+        /*
+         * If no pointer to globals then there is nothing to do (nearly impossible).
+         */
+        if (!gpGlobals)
+	    LIBCLOG_RETURN_VOID();
+
+	/*
+	 * Go through the chain of undo vectors looking for one
+	 * associated with this process.
+	 */
+        pid_t  pid = fibGetPid();
+	SEMUNDO_LOCK();
+	SLIST_FOREACH_PREVPTR(suptr, supptr, &gpGlobals->semu_list, un_next) {
+		if (suptr->un_proc == pid)
+			break;
+	}
+	SEMUNDO_UNLOCK();
+
+	if (suptr == NULL)
+		goto dereference;
+
+	DPRINTF(("proc %d has undo structure with %d entries\n", pid,
+	    suptr->un_cnt));
+
+	/*
+	 * If there are any active undo elements then process them.
+	 */
+	if (suptr->un_cnt > 0) {
+		int ix;
+
+		for (ix = 0; ix < suptr->un_cnt; ix++) {
+			int semid = suptr->un_ent[ix].un_id;
+			int semnum = suptr->un_ent[ix].un_num;
+			int adjval = suptr->un_ent[ix].un_adjval;
+			struct semid_kernel *semaptr;
+
+			semaptr = &gpGlobals->sema[semid];
+			SEMUNDO_LOCK();
+			semMtxUnlock(semaptr->hmtx);
+			if ((semaptr->u.sem_perm.mode & SEM_ALLOC) == 0) {
+				panic("semexit - semid not allocated");
+				goto skip;
+			}
+			if (semnum >= semaptr->u.sem_nsems) {
+				panic("semexit - semnum out of range");
+				goto skip;
+			}
+
+			DPRINTF((
+			    "semexit:  %d id=%d num=%d(adj=%d) ; sem=%d\n",
+			    suptr->un_proc, suptr->un_ent[ix].un_id,
+			    suptr->un_ent[ix].un_num,
+			    suptr->un_ent[ix].un_adjval,
+			    semaptr->u.sem_base[semnum].semval));
+
+			if (adjval < 0) {
+				if (semaptr->u.sem_base[semnum].semval < -adjval)
+					semaptr->u.sem_base[semnum].semval = 0;
+				else
+					semaptr->u.sem_base[semnum].semval +=
+					    adjval;
+			} else
+				semaptr->u.sem_base[semnum].semval += adjval;
+
+			semEvWakeup(semaptr);
+			DPRINTF(("semexit:  back from wakeup\n"));
+		    skip:
+			semMtxUnlock(semaptr->hmtx);
+			SEMUNDO_UNLOCK();
+		}
+	}
+
+	/*
+	 * Deallocate the undo vector.
+	 */
+	DPRINTF(("removing vector\n"));
+	suptr->un_proc = 0;
+	*supptr = SLIST_NEXT(suptr, un_next);
+
+
+	/*
+	 * Dereference the globals.
+	 *
+	 * This means zombies may waste global sems, but assuming zombies are not happening it's faster
+	 * having OS/2 cleanup semaphores than doing it here.
+	 */
+dereference:			
+	__atomic_decrement_u32(&gpGlobals->cUsers);
+	gpGlobals = NULL;
+        LIBCLOG_RETURN_VOID();
+}
+
+#if 0
+static int
+sysctl_sema(SYSCTL_HANDLER_ARGS)
+{
+
+	return (SYSCTL_OUT(req, sema,
+	    sizeof(struct semid_ds) * seminfo.semmni));
+}
+#endif
+
Index: trunk/src/emx/src/lib/sys/tcpipver.c
===================================================================
--- trunk/src/emx/src/lib/sys/tcpipver.c	(revision 1983)
+++ trunk/src/emx/src/lib/sys/tcpipver.c	(revision 1984)
@@ -1339,5 +1339,5 @@
     static void *pfn = (void*)tcpipInit; /* reference hack */
     pfn = pfn;
-    __libc_spmSocketRegTerm(TCPNAME(Cleanup));
+    __libc_spmRegTerm(TCPNAME(Cleanup));
     LIBCLOG_RETURN_VOID();
 }
