From 00557e661aa4cecba322802e696203f64508e04b Mon Sep 17 00:00:00 2001 From: Bryan O'Sullivan Date: Tue, 25 Sep 2007 11:33:30 +0000 Subject: Add basic pseudoterminal support. --- System/Posix/Terminal.hsc | 83 +++++++++++++++++++++++++++++++++++++++++++++-- configure.ac | 31 +++++++++++++++++- include/HsUnix.h | 44 +++++++++++++++++++++++++ 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 #endif +#ifdef HAVE_LIBUTIL_H +#include +#endif +#ifdef HAVE_PTY_H +#include +#endif +#ifdef HAVE_UTMP_H +#include +#endif + #include #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 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 -- cgit v1.2.3