aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--System/Posix/Terminal.hsc83
-rw-r--r--configure.ac31
-rw-r--r--include/HsUnix.h44
3 files changed, 154 insertions, 4 deletions
diff --git a/System/Posix/Terminal.hsc b/System/Posix/Terminal.hsc
index 693c8bb..8717c7d 100644
--- a/System/Posix/Terminal.hsc
+++ b/System/Posix/Terminal.hsc
@@ -60,22 +60,33 @@ module System.Posix.Terminal (
-- ** Testing a file descriptor
queryTerminal,
getTerminalName,
- getControllingTerminalName
+ getControllingTerminalName,
+ -- ** Pseudoterminal operations
+ openPseudoTerminal,
+ getSlaveTerminalName
) where
#include "HsUnix.h"
import Data.Bits
import Data.Char
-import Foreign.C.Error ( throwErrnoIfMinus1, throwErrnoIfMinus1_, throwErrnoIfNull )
-import Foreign.C.String ( CString, peekCString )
+import Foreign.C.Error ( errnoToIOError, throwErrnoIfMinus1,
+ throwErrnoIfMinus1_, throwErrnoIfNull )
+#ifndef HAVE_PTSNAME
+import Foreign.C.Error ( eNOSYS )
+#endif
+import Foreign.C.String ( CString, peekCString, withCString )
import Foreign.C.Types ( CInt )
import Foreign.ForeignPtr ( ForeignPtr, withForeignPtr, mallocForeignPtrBytes )
+import Foreign.Marshal.Alloc ( alloca )
import Foreign.Marshal.Utils ( copyBytes )
import Foreign.Ptr ( Ptr, nullPtr, plusPtr )
import Foreign.Storable ( Storable(..) )
+import System.IO.Error ( ioError )
import System.IO.Unsafe ( unsafePerformIO )
+import System.Posix.IO ( OpenFileFlags(..), OpenMode(..), defaultFileFlags,
+ openFd )
import System.Posix.Types
-- -----------------------------------------------------------------------------
@@ -515,6 +526,72 @@ getControllingTerminalName = do
foreign import ccall unsafe "ctermid"
c_ctermid :: CString -> IO CString
+-- | @getSlaveTerminalName@ calls @ptsname@ to obtain the name of the
+-- slave terminal associated with a pseudoterminal pair. The file
+-- descriptor to pass in must be that of the master.
+getSlaveTerminalName :: Fd -> IO FilePath
+
+#ifdef HAVE_PTSNAME
+getSlaveTerminalName (Fd fd) = do
+ s <- throwErrnoIfNull "getSlaveTerminalName" (c_ptsname fd)
+ peekCString s
+
+foreign import ccall unsafe "__hsunix_ptsname"
+ c_ptsname :: CInt -> IO CString
+#else
+getSlaveTerminalName _ =
+ ioError (errnoToIOError "getSlaveTerminalName" eNOSYS Nothing Nothing)
+#endif
+
+-- | @openPseudoTerminal@ creates a pseudoterminal (pty) pair, and
+-- returns the newly created pair as a (@master@, @slave@) tuple.
+openPseudoTerminal :: IO (Fd, Fd)
+
+#ifdef HAVE_OPENPTY
+openPseudoTerminal =
+ alloca $ \p_master ->
+ alloca $ \p_slave -> do
+ throwErrnoIfMinus1_ "openPty"
+ (c_openpty p_master p_slave nullPtr nullPtr nullPtr)
+ master <- peek p_master
+ slave <- peek p_slave
+ return (Fd master, Fd slave)
+
+foreign import ccall unsafe "openpty"
+ c_openpty :: Ptr CInt -> Ptr CInt -> CString -> Ptr CTermios -> Ptr a
+ -> IO CInt
+#else
+openPseudoTerminal = do
+ (Fd master) <- openFd "/dev/ptmx" ReadWrite Nothing
+ defaultFileFlags{noctty=True}
+ throwErrnoIfMinus1_ "openPseudoTerminal" (c_grantpt master)
+ throwErrnoIfMinus1_ "openPseudoTerminal" (c_unlockpt master)
+ slaveName <- getSlaveTerminalName (Fd master)
+ slave <- openFd slaveName ReadWrite Nothing defaultFileFlags{noctty=True}
+ pushModule slave "ptem"
+ pushModule slave "ldterm"
+# ifndef __hpux
+ pushModule slave "ttcompat"
+# endif /* __hpux */
+ return (Fd master, slave)
+
+-- Push a STREAMS module, for System V systems.
+pushModule :: Fd -> String -> IO ()
+pushModule (Fd fd) name =
+ withCString name $ \p_name ->
+ throwErrnoIfMinus1_ "openPseudoTerminal"
+ (c_push_module fd p_name)
+
+foreign import ccall unsafe "__hsunix_push_module"
+ c_push_module :: CInt -> CString -> IO CInt
+
+foreign import ccall unsafe "__hsunix_grantpt"
+ c_grantpt :: CInt -> IO CInt
+
+foreign import ccall unsafe "__hsunix_unlockpt"
+ c_unlockpt :: CInt -> IO CInt
+#endif /* !HAVE_OPENPTY */
+
-- -----------------------------------------------------------------------------
-- Local utility functions
diff --git a/configure.ac b/configure.ac
index 11e5133..3618122 100644
--- a/configure.ac
+++ b/configure.ac
@@ -14,12 +14,14 @@ AC_C_CONST
AC_CHECK_HEADERS([dirent.h fcntl.h grp.h limits.h pwd.h signal.h string.h])
AC_CHECK_HEADERS([sys/resource.h sys/stat.h sys/times.h sys/time.h])
AC_CHECK_HEADERS([sys/utsname.h sys/wait.h])
+AC_CHECK_HEADERS([libutil.h pty.h utmp.h])
AC_CHECK_HEADERS([termios.h time.h unistd.h utime.h])
AC_CHECK_FUNCS([getgrgid_r getgrnam_r getpwnam_r getpwuid_r getpwnam getpwuid])
AC_CHECK_FUNCS([getpwent getgrent])
AC_CHECK_FUNCS([lchown setenv sysconf unsetenv])
AC_CHECK_FUNCS([nanosleep])
+AC_CHECK_FUNCS([ptsname])
AC_CHECK_FUNCS([setitimer])
AC_CHECK_FUNCS([shm_open shm_unlink])
@@ -144,8 +146,35 @@ AC_EGREP_CPP(yes,
AC_MSG_RESULT(no)
])
+AC_CHECK_FUNCS(openpty,,
+ AC_CHECK_LIB(util,openpty,
+ [AC_DEFINE(HAVE_OPENPTY) EXTRA_LIBS="$EXTRA_LIBS util"],
+ AC_CHECK_LIB(bsd,openpty, [AC_DEFINE(HAVE_OPENPTY) EXTRA_LIBS="$EXTRA_LIBS bsd"])
+ )
+)
+
+AC_MSG_CHECKING(for /dev/ptmx)
+if test -r /dev/ptmx
+then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_DEV_PTMX, 1,
+ [Define if we have /dev/ptmx.])
+else
+ AC_MSG_RESULT(no)
+fi
+
+AC_MSG_CHECKING(for /dev/ptc)
+if test -r /dev/ptc
+then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_DEV_PTC, 1,
+ [Define if we have /dev/ptc.])
+else
+ AC_MSG_RESULT(no)
+fi
+
# Avoid adding dl if absent or unneeded
-AC_CHECK_LIB(dl, dlopen, [EXTRA_LIBS=dl], [EXTRA_LIBS=])
+AC_CHECK_LIB(dl, dlopen, [EXTRA_LIBS="$EXTRA_LIBS dl"])
AC_SUBST([EXTRA_LIBS])
AC_CONFIG_FILES([unix.buildinfo])
diff --git a/include/HsUnix.h b/include/HsUnix.h
index 9b2e697..2df3932 100644
--- a/include/HsUnix.h
+++ b/include/HsUnix.h
@@ -74,6 +74,16 @@
#include <dirent.h>
#endif
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#ifdef HAVE_PTY_H
+#include <pty.h>
+#endif
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#endif
+
#include <dlfcn.h>
#ifdef HAVE_SIGNAL_H
@@ -130,4 +140,38 @@ INLINE int __hsunix_mknod(const char *pathname, mode_t mode, dev_t dev)
return mknod(pathname,mode,dev);
}
+#ifdef HAVE_PTSNAME
+// I cannot figure out how to make the definitions of the following
+// functions visible in <stdlib.h> on Linux. But these definitions
+// follow the POSIX specs, and everything links and runs.
+
+INLINE char *__hsunix_ptsname(int fd)
+{
+ extern char *ptsname(int);
+ return ptsname(fd);
+}
+
+INLINE int __hsunix_grantpt(int fd)
+{
+ extern int grantpt(int);
+ return grantpt(fd);
+}
+
+INLINE int __hsunix_unlockpt(int fd)
+{
+ extern int unlockpt(int);
+ return unlockpt(fd);
+}
+#endif
+
+// push a SVR4 STREAMS module; do nothing if STREAMS not available
+INLINE int __hsunix_push_module(int fd, const char *module)
+{
+#if defined(I_PUSH) && !defined(__CYGWIN__) && !defined(HAVE_DEV_PTC)
+ return ioctl(fd, I_PUSH, module);
+#else
+ return 0;
+#endif
+}
+
#endif