{-# LINE 1 "System/Posix/User/ByteString.hsc" #-}{-# LANGUAGE Trustworthy, CApiFFI, PatternSynonyms, ViewPatterns #-}------------------------------------------------------------------------------- |-- Module : System.Posix.User.ByteString-- Copyright : (c) The University of Glasgow 2002-- License : BSD-style (see the file libraries/base/LICENSE)---- Maintainer : libraries@haskell.org-- Stability : provisional-- Portability : non-portable (requires POSIX)---- POSIX user\/group support-------------------------------------------------------------------------------moduleSystem.Posix.User.ByteString(-- * User environment-- ** Querying the user environmentgetRealUserID ,getRealGroupID ,getEffectiveUserID ,getEffectiveGroupID ,getGroups ,getLoginName ,getEffectiveUserName ,-- *** The group databaseGroupEntry (..),getGroupEntryForID ,getGroupEntryForName ,getAllGroupEntries ,-- *** The user databaseUserEntry (..),getUserEntryForID ,getUserEntryForName ,getAllUserEntries ,-- ** Modifying the user environmentsetUserID ,setGroupID ,setEffectiveUserID ,setEffectiveGroupID ,setGroups )whereimportSystem.Posix.TypesimportSystem.IO.Unsafe(unsafePerformIO)importForeign.C(CSize(..),CInt(..),CString,CLong(..),getErrno,throwErrno,eOK,throwErrnoIfMinus1_,throwErrnoIfNull,resetErrno,Errno(..),eRANGE,errnoToIOError)importForeign.PtrimportForeign.MarshalimportForeign.StorableimportSystem.Posix.User.Common {-# LINE 61 "System/Posix/User/ByteString.hsc" #-}{-# LINE 64 "System/Posix/User/ByteString.hsc" #-}importControl.Concurrent.MVar(MVar,newMVar,withMVar)importControl.Exception{-# LINE 67 "System/Posix/User/ByteString.hsc" #-}importControl.MonadimportSystem.IO.ErrorimportData.ByteString(ByteString,packCString,useAsCString){-# LINE 75 "System/Posix/User/ByteString.hsc" #-}{-# LINE 140 "System/Posix/User/ByteString.hsc" #-}-- ------------------------------------------------------------------------------- Thread safety of passwd/group database access APIs:---- All supported unix platforms have @get(pw|gr)(nam|[ug]id)_r(3)@, which-- store the result in a caller provided buffer, which solves the most-- immediate thread-safety issues.---- Things are more complicated for getpwent(3) and getgrent(3).---- * On Linux systems, these read a global open file, opened via-- setpwent(3) and closed via endpwent(3). Only one thread at-- a time can safely iterate through the file.---- * On macOS (through Catalina 10.15), there is no getpwent_r(3) or-- getgrent_r(3), so a lock is also required for safe buffer sharing.---- * On FreeBSD, in the default configuration with passwd lookups configured-- in nsswitch.conf to use "compat" rather than "files", the getpwnam_r(3)-- and getpwuid_r(3) functions reset the iterator index used by getpwent(3).-- A bug [report](https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=252094)-- has been filed to track this long-standing issue. A similar issue affects-- getgrent(3), this time regardless of the nsswitch.conf setting. This too-- should be fixed at some point in the future. The state in question is-- thread-specific, so both issues only affect overlapping use of the @*ent@-- and @*(nam|[ug]id)_r(3)@ functions in the /same/ thread.---- * Despite rather similar manpages for getpwent(3) and getpwnam(3), ... as-- on FreeBSD, the above issue is not seen on NetBSD or macOS.---- This is not an issue with 1-to-1 thread models, where the code executing-- @get(pw|gr)ent@ has exclusive use of its thread, but it is a real issue-- for Haskell with its many-to-1 green threads, because multiple `forkIO`-- threads may take turns using the same underlying OS thread, breaking the-- thread-safety of the @*_r@ functions, which mutate the file-offset of the-- open file shared with any overlapping execution of @*ent(3)@ in the same-- thread.---- Consequently, correct portable support for @get(pw|gr)ent(3)@ is rather-- non-trivial. In the threaded runtime, we can run these functions in a-- /bound thread/ (via 'forkOS'), thereby avoiding the FreeBSD issues. We-- still need a lock to serialise multiple threads calling these functions-- on at least macOS for lack of @_r@ equivalents. While on FreeBSD we could-- use @getpwent_r(3)@ and @getgrent_r(3)@ in a bound thread without any-- locks, implementing this special case is likely not worthwhile.---- In the non-threaded runtime, `forkOS` is not available, and so on FreeBSD-- systems we have to also lock the @*(nam|[ug]id)_r(3)@ functions to avoid-- concurrent use with @*ent(3)@.---- FWIW, the below Perl one-liners will quickly show whether interleaved calls-- of getpwuid() or getgrgid() disturb iteration through all the entries. If-- each line of output is distinct, there is likely no issue. If the same-- passwd or group entry repeats multiple times, the system is affected.---- > for ($i=0;$i<3;++$i) {getpwuid(0); print join(":",getpwent()),"\n"}-- > for ($i=0;$i<3;++$i) {getgrgid(0); print join(":",getgrent()),"\n"}---- XXX: It has been suggested, not without some merit, that attempts to-- enumerate /all/ users or /all/ groups are fundamentally flawed. Modern-- unix systems have a variety nsswitch backends, some of which instantiate-- users on demand or may enumerate slowly or not at all. We could shed a-- lot of complexity by deprecating the "get all" functions and simply-- always returning an empty list.--{-# LINE 206 "System/Posix/User/ByteString.hsc" #-}pwlock::MVar()pwlock :: MVar ()
pwlock =IO (MVar ()) -> MVar ()
forall a. IO a -> a
unsafePerformIO(IO (MVar ()) -> MVar ()) -> IO (MVar ()) -> MVar ()
forall a b. (a -> b) -> a -> b
$() -> IO (MVar ())
forall a. a -> IO (MVar a)
newMVar(){-# NOINLINEpwlock #-}lockpw ::LKUPTYPE ->IOa ->IOa {-# LINE 219 "System/Posix/User/ByteString.hsc" #-}lockpw :: forall a. LKUPTYPE -> IO a -> IO a
lockpw LKUPTYPE
GETONE =IO a -> IO a
forall a. a -> a
idlockpw LKUPTYPE
GETALL =MVar () -> (() -> IO a) -> IO a
forall a b. MVar a -> (a -> IO b) -> IO b
withMVarMVar ()
pwlock ((() -> IO a) -> IO a) -> (IO a -> () -> IO a) -> IO a -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
.IO a -> () -> IO a
forall a b. a -> b -> a
const{-# LINE 222 "System/Posix/User/ByteString.hsc" #-}{-# LINE 225 "System/Posix/User/ByteString.hsc" #-}{-# LINE 227 "System/Posix/User/ByteString.hsc" #-}grlock::MVar()grlock :: MVar ()
grlock =IO (MVar ()) -> MVar ()
forall a. IO a -> a
unsafePerformIO(IO (MVar ()) -> MVar ()) -> IO (MVar ()) -> MVar ()
forall a b. (a -> b) -> a -> b
$() -> IO (MVar ())
forall a. a -> IO (MVar a)
newMVar(){-# NOINLINEgrlock #-}lockgr ::LKUPTYPE ->IOa ->IOa {-# LINE 240 "System/Posix/User/ByteString.hsc" #-}lockgr :: forall a. LKUPTYPE -> IO a -> IO a
lockgr LKUPTYPE
GETONE =IO a -> IO a
forall a. a -> a
idlockgr LKUPTYPE
GETALL =MVar () -> (() -> IO a) -> IO a
forall a b. MVar a -> (a -> IO b) -> IO b
withMVarMVar ()
grlock ((() -> IO a) -> IO a) -> (IO a -> () -> IO a) -> IO a -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
.IO a -> () -> IO a
forall a b. a -> b -> a
const{-# LINE 243 "System/Posix/User/ByteString.hsc" #-}{-# LINE 246 "System/Posix/User/ByteString.hsc" #-}-- ------------------------------------------------------------------------------- user environment-- | @getRealUserID@ calls @getuid@ to obtain the real @UserID@-- associated with the current process.getRealUserID ::IOUserIDgetRealUserID :: IO UserID
getRealUserID =IO UserID
c_getuid foreignimportccallunsafe"getuid"c_getuid ::IOCUid-- | @getRealGroupID@ calls @getgid@ to obtain the real @GroupID@-- associated with the current process.getRealGroupID ::IOGroupIDgetRealGroupID :: IO GroupID
getRealGroupID =IO GroupID
c_getgid foreignimportccallunsafe"getgid"c_getgid ::IOCGid-- | @getEffectiveUserID@ calls @geteuid@ to obtain the effective-- @UserID@ associated with the current process.getEffectiveUserID ::IOUserIDgetEffectiveUserID :: IO UserID
getEffectiveUserID =IO UserID
c_geteuid foreignimportccallunsafe"geteuid"c_geteuid ::IOCUid-- | @getEffectiveGroupID@ calls @getegid@ to obtain the effective-- @GroupID@ associated with the current process.getEffectiveGroupID ::IOGroupIDgetEffectiveGroupID :: IO GroupID
getEffectiveGroupID =IO GroupID
c_getegid foreignimportccallunsafe"getegid"c_getegid ::IOCGid-- | @getGroups@ calls @getgroups@ to obtain the list of-- supplementary @GroupID@s associated with the current process.getGroups ::IO[GroupID]getGroups :: IO [GroupID]
getGroups =doCInt
ngroups <-CInt -> Ptr GroupID -> IO CInt
c_getgroups CInt
0Ptr GroupID
forall a. Ptr a
nullPtrInt -> (Ptr GroupID -> IO [GroupID]) -> IO [GroupID]
forall a b. Storable a => Int -> (Ptr a -> IO b) -> IO b
allocaArray(CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegralCInt
ngroups )((Ptr GroupID -> IO [GroupID]) -> IO [GroupID])
-> (Ptr GroupID -> IO [GroupID]) -> IO [GroupID]
forall a b. (a -> b) -> a -> b
$\Ptr GroupID
arr ->doString -> IO CInt -> IO ()
forall a. (Eq a, Num a) => String -> IO a -> IO ()
throwErrnoIfMinus1_String
"getGroups"(CInt -> Ptr GroupID -> IO CInt
c_getgroups CInt
ngroups Ptr GroupID
arr )[GroupID]
groups <-Int -> Ptr GroupID -> IO [GroupID]
forall a. Storable a => Int -> Ptr a -> IO [a]
peekArray(CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegralCInt
ngroups )Ptr GroupID
arr [GroupID] -> IO [GroupID]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return[GroupID]
groups foreignimportccallunsafe"getgroups"c_getgroups ::CInt->PtrCGid->IOCInt-- | @setGroups@ calls @setgroups@ to set the list of-- supplementary @GroupID@s associated with the current process.setGroups ::[GroupID]->IO()setGroups :: [GroupID] -> IO ()
setGroups [GroupID]
groups =do[GroupID] -> (Int -> Ptr GroupID -> IO ()) -> IO ()
forall a b. Storable a => [a] -> (Int -> Ptr a -> IO b) -> IO b
withArrayLen[GroupID]
groups ((Int -> Ptr GroupID -> IO ()) -> IO ())
-> (Int -> Ptr GroupID -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$\Int
ngroups Ptr GroupID
arr ->String -> IO CInt -> IO ()
forall a. (Eq a, Num a) => String -> IO a -> IO ()
throwErrnoIfMinus1_String
"setGroups"(CInt -> Ptr GroupID -> IO CInt
c_setgroups (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegralInt
ngroups )Ptr GroupID
arr )foreignimportccallunsafe"setgroups"c_setgroups ::CInt->PtrCGid->IOCInt-- | @getLoginName@ calls @getlogin@ to obtain the login name-- associated with the current process.getLoginName ::IOByteStringgetLoginName :: IO ByteString
getLoginName =do-- ToDo: use getlogin_rPtr CChar
str <-String -> IO (Ptr CChar) -> IO (Ptr CChar)
forall a. String -> IO (Ptr a) -> IO (Ptr a)
throwErrnoIfNullString
"getLoginName"IO (Ptr CChar)
c_getlogin Ptr CChar -> IO ByteString
packCStringPtr CChar
str foreignimportccallunsafe"getlogin"c_getlogin ::IOCString-- | @setUserID uid@ calls @setuid@ to set the real, effective, and-- saved set-user-id associated with the current process to @uid@.setUserID ::UserID->IO()setUserID :: UserID -> IO ()
setUserID UserID
uid =String -> IO CInt -> IO ()
forall a. (Eq a, Num a) => String -> IO a -> IO ()
throwErrnoIfMinus1_String
"setUserID"(UserID -> IO CInt
c_setuid UserID
uid )foreignimportccallunsafe"setuid"c_setuid ::CUid->IOCInt-- | @setEffectiveUserID uid@ calls @seteuid@ to set the effective-- user-id associated with the current process to @uid@. This-- does not update the real user-id or set-user-id.setEffectiveUserID ::UserID->IO()setEffectiveUserID :: UserID -> IO ()
setEffectiveUserID UserID
uid =String -> IO CInt -> IO ()
forall a. (Eq a, Num a) => String -> IO a -> IO ()
throwErrnoIfMinus1_String
"setEffectiveUserID"(UserID -> IO CInt
c_seteuid UserID
uid )foreignimportccallunsafe"seteuid"c_seteuid ::CUid->IOCInt-- | @setGroupID gid@ calls @setgid@ to set the real, effective, and-- saved set-group-id associated with the current process to @gid@.setGroupID ::GroupID->IO()setGroupID :: GroupID -> IO ()
setGroupID GroupID
gid =String -> IO CInt -> IO ()
forall a. (Eq a, Num a) => String -> IO a -> IO ()
throwErrnoIfMinus1_String
"setGroupID"(GroupID -> IO CInt
c_setgid GroupID
gid )foreignimportccallunsafe"setgid"c_setgid ::CGid->IOCInt-- | @setEffectiveGroupID uid@ calls @setegid@ to set the effective-- group-id associated with the current process to @gid@. This-- does not update the real group-id or set-group-id.setEffectiveGroupID ::GroupID->IO()setEffectiveGroupID :: GroupID -> IO ()
setEffectiveGroupID GroupID
gid =String -> IO CInt -> IO ()
forall a. (Eq a, Num a) => String -> IO a -> IO ()
throwErrnoIfMinus1_String
"setEffectiveGroupID"(GroupID -> IO CInt
c_setegid GroupID
gid )foreignimportccallunsafe"setegid"c_setegid ::CGid->IOCInt-- ------------------------------------------------------------------------------- User names-- | @getEffectiveUserName@ gets the name-- associated with the effective @UserID@ of the process.getEffectiveUserName ::IOByteStringgetEffectiveUserName :: IO ByteString
getEffectiveUserName =doUserID
euid <-IO UserID
getEffectiveUserID UserEntry
pw <-UserID -> IO UserEntry
getUserEntryForID UserID
euid ByteString -> IO ByteString
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return(UserEntry -> ByteString
userName UserEntry
pw ){-# LINE 366 "System/Posix/User/ByteString.hsc" #-}-- ------------------------------------------------------------------------------- The group database (grp.h){-# LINE 389 "System/Posix/User/ByteString.hsc" #-}-- | @getGroupEntryForID gid@ calls @getgrgid_r@ to obtain-- the @GroupEntry@ information associated with @GroupID@-- @gid@. This operation may fail with 'isDoesNotExistError'-- if no such group exists.getGroupEntryForID ::GroupID->IOGroupEntry {-# LINE 396 "System/Posix/User/ByteString.hsc" #-}getGroupEntryForIDgid=lockgrGETONE$allocaBytes(32)$\pgr->{-# LINE 398 "System/Posix/User/ByteString.hsc" #-}doubleAllocWhileERANGE"getGroupEntryForID""group"grBufSizeunpackGroupEntry$c_getgrgid_rgidpgrforeignimportcapisafe"HsUnix.h getgrgid_r"c_getgrgid_r ::CGid->PtrCGroup ->CString->CSize->Ptr(PtrCGroup )->IOCInt{-# LINE 408 "System/Posix/User/ByteString.hsc" #-}-- | @getGroupEntryForName name@ calls @getgrnam_r@ to obtain-- the @GroupEntry@ information associated with the group called-- @name@. This operation may fail with 'isDoesNotExistError'-- if no such group exists.getGroupEntryForName ::ByteString->IOGroupEntry {-# LINE 415 "System/Posix/User/ByteString.hsc" #-}getGroupEntryForNamename=lockgrGETONE$allocaBytes(32)$\pgr->{-# LINE 417 "System/Posix/User/ByteString.hsc" #-}useAsCStringname$\pstr->doubleAllocWhileERANGE"getGroupEntryForName""group"grBufSizeunpackGroupEntry$c_getgrnam_rpstrpgrforeignimportcapisafe"HsUnix.h getgrnam_r"c_getgrnam_r ::CString->PtrCGroup ->CString->CSize->Ptr(PtrCGroup )->IOCInt{-# LINE 428 "System/Posix/User/ByteString.hsc" #-}-- | @getAllGroupEntries@ returns all group entries on the system by-- repeatedly calling @getgrent@---- getAllGroupEntries may fail with isDoesNotExistError on Linux due to-- this bug in glibc:-- http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=466647--getAllGroupEntries ::IO[GroupEntry ]{-# LINE 439 "System/Posix/User/ByteString.hsc" #-}getAllGroupEntries=lockgrGETALL$bracket_c_setgrentc_endgrent$worker[]whereworkeraccum=doresetErrnoppw<-throwErrnoIfNullAndError"getAllGroupEntries"$c_getgrentifppw==nullPtrthenreturn(reverseaccum)elsedothisentry<-unpackGroupEntryppwworker(thisentry:accum)foreignimportccallsafe"getgrent"c_getgrent ::IO(PtrCGroup )foreignimportccallsafe"setgrent"c_setgrent ::IO()foreignimportccallsafe"endgrent"c_endgrent ::IO(){-# LINE 456 "System/Posix/User/ByteString.hsc" #-}{-# LINE 458 "System/Posix/User/ByteString.hsc" #-}grBufSize::Int{-# LINE 460 "System/Posix/User/ByteString.hsc" #-}grBufSize=sysconfWithDefault1024(69){-# LINE 461 "System/Posix/User/ByteString.hsc" #-}{-# LINE 464 "System/Posix/User/ByteString.hsc" #-}{-# LINE 465 "System/Posix/User/ByteString.hsc" #-}{-# LINE 467 "System/Posix/User/ByteString.hsc" #-}-- ------------------------------------------------------------------------------- The user database (pwd.h)-- | @getUserEntryForID uid@ calls @getpwuid_r@ to obtain-- the @UserEntry@ information associated with @UserID@-- @uid@. This operation may fail with 'isDoesNotExistError'-- if no such user exists.getUserEntryForID ::UserID->IOUserEntry {-# LINE 478 "System/Posix/User/ByteString.hsc" #-}getUserEntryForIDuid=lockpwGETONE$allocaBytes(48)$\ppw->{-# LINE 480 "System/Posix/User/ByteString.hsc" #-}doubleAllocWhileERANGE"getUserEntryForID""user"pwBufSizeunpackUserEntry$c_getpwuid_ruidppwforeignimportcapisafe"HsUnix.h getpwuid_r"c_getpwuid_r ::CUid->PtrCPasswd ->CString->CSize->Ptr(PtrCPasswd )->IOCInt{-# LINE 490 "System/Posix/User/ByteString.hsc" #-}-- | @getUserEntryForName name@ calls @getpwnam_r@ to obtain-- the @UserEntry@ information associated with the user login-- @name@. This operation may fail with 'isDoesNotExistError'-- if no such user exists.getUserEntryForName ::ByteString->IOUserEntry {-# LINE 497 "System/Posix/User/ByteString.hsc" #-}getUserEntryForNamename=lockpwGETONE$allocaBytes(48)$\ppw->{-# LINE 499 "System/Posix/User/ByteString.hsc" #-}useAsCStringname$\pstr->doubleAllocWhileERANGE"getUserEntryForName""user"pwBufSizeunpackUserEntry$c_getpwnam_rpstrppwforeignimportcapisafe"HsUnix.h getpwnam_r"c_getpwnam_r ::CString->PtrCPasswd ->CString->CSize->Ptr(PtrCPasswd )->IOCInt{-# LINE 510 "System/Posix/User/ByteString.hsc" #-}-- | @getAllUserEntries@ returns all user entries on the system by-- repeatedly calling @getpwent@getAllUserEntries ::IO[UserEntry ]{-# LINE 515 "System/Posix/User/ByteString.hsc" #-}getAllUserEntries=lockpwGETALL$bracket_c_setpwentc_endpwent$worker[]whereworkeraccum=doresetErrnoppw<-throwErrnoIfNullAndError"getAllUserEntries"$c_getpwentifppw==nullPtrthenreturn(reverseaccum)elsedothisentry<-unpackUserEntryppwworker(thisentry:accum)foreignimportccallsafe"getpwent"c_getpwent ::IO(PtrCPasswd )foreignimportccallsafe"setpwent"c_setpwent ::IO()foreignimportccallsafe"endpwent"c_endpwent ::IO(){-# LINE 532 "System/Posix/User/ByteString.hsc" #-}{-# LINE 534 "System/Posix/User/ByteString.hsc" #-}pwBufSize::Int{-# LINE 536 "System/Posix/User/ByteString.hsc" #-}pwBufSize=sysconfWithDefault1024(70){-# LINE 537 "System/Posix/User/ByteString.hsc" #-}{-# LINE 540 "System/Posix/User/ByteString.hsc" #-}{-# LINE 541 "System/Posix/User/ByteString.hsc" #-}{-# LINE 543 "System/Posix/User/ByteString.hsc" #-}foreignimportccallunsafe"sysconf"c_sysconf::CInt->IOCLong-- We need a default value since sysconf can fail and return -1-- even when the parameter name is defined in unistd.h.-- One example of this is _SC_GETPW_R_SIZE_MAX under-- Mac OS X 10.4.9 on i386.sysconfWithDefault ::Int->CInt->IntsysconfWithDefault :: Int -> CInt -> Int
sysconfWithDefault Int
def CInt
sc =IO Int -> Int
forall a. IO a -> a
unsafePerformIO(IO Int -> Int) -> IO Int -> Int
forall a b. (a -> b) -> a -> b
$doInt
v <-(CLong -> Int) -> IO CLong -> IO Int
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmapCLong -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral(IO CLong -> IO Int) -> IO CLong -> IO Int
forall a b. (a -> b) -> a -> b
$CInt -> IO CLong
c_sysconf CInt
sc Int -> IO Int
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return(Int -> IO Int) -> Int -> IO Int
forall a b. (a -> b) -> a -> b
$ifInt
v Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
==(-Int
1)thenInt
def elseInt
v {-# LINE 555 "System/Posix/User/ByteString.hsc" #-}{-# LINE 557 "System/Posix/User/ByteString.hsc" #-}-- The following function is used by the getgr*_r, c_getpw*_r-- families of functions. These functions return their result-- in a struct that contains strings and they need a buffer-- that they can use to store those strings. We have to be-- careful to unpack the struct containing the result before-- the buffer is deallocated.doubleAllocWhileERANGE ::String->String-- entry type: "user" or "group"->Int->(Ptrr ->IOa )->(Ptrb ->CSize->Ptr(Ptrr )->IOCInt)->IOa doubleAllocWhileERANGE :: forall r a b.
String
-> String
-> Int
-> (Ptr r -> IO a)
-> (Ptr b -> CSize -> Ptr (Ptr r) -> IO CInt)
-> IO a
doubleAllocWhileERANGE String
loc String
enttype Int
initlen Ptr r -> IO a
unpack Ptr b -> CSize -> Ptr (Ptr r) -> IO CInt
action =(Ptr (Ptr r) -> IO a) -> IO a
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca((Ptr (Ptr r) -> IO a) -> IO a) -> (Ptr (Ptr r) -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$Int -> Ptr (Ptr r) -> IO a
go Int
initlen wherego :: Int -> Ptr (Ptr r) -> IO a
go Int
len Ptr (Ptr r)
res =doEither CInt a
r <-Int -> (Ptr b -> IO (Either CInt a)) -> IO (Either CInt a)
forall a b. Int -> (Ptr a -> IO b) -> IO b
allocaBytesInt
len ((Ptr b -> IO (Either CInt a)) -> IO (Either CInt a))
-> (Ptr b -> IO (Either CInt a)) -> IO (Either CInt a)
forall a b. (a -> b) -> a -> b
$\Ptr b
buf ->doCInt
rc <-Ptr b -> CSize -> Ptr (Ptr r) -> IO CInt
action Ptr b
buf (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegralInt
len )Ptr (Ptr r)
res ifCInt
rc CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
/=CInt
0thenEither CInt a -> IO (Either CInt a)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return(CInt -> Either CInt a
forall a b. a -> Either a b
LeftCInt
rc )elsedoPtr r
p <-Ptr (Ptr r) -> IO (Ptr r)
forall a. Storable a => Ptr a -> IO a
peekPtr (Ptr r)
res Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when(Ptr r
p Ptr r -> Ptr r -> Bool
forall a. Eq a => a -> a -> Bool
==Ptr r
forall a. Ptr a
nullPtr)(IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$IO ()
forall {a}. IO a
notFoundErr (a -> Either CInt a) -> IO a -> IO (Either CInt a)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmapa -> Either CInt a
forall a b. b -> Either a b
Right(Ptr r -> IO a
unpack Ptr r
p )caseEither CInt a
r ofRighta
x ->a -> IO a
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
returna
x LeftCInt
rc |CInt -> Errno
ErrnoCInt
rc Errno -> Errno -> Bool
forall a. Eq a => a -> a -> Bool
==Errno
eRANGE->-- ERANGE means this is not an error-- we just have to try again with a larger bufferInt -> Ptr (Ptr r) -> IO a
go (Int
2Int -> Int -> Int
forall a. Num a => a -> a -> a
*Int
len )Ptr (Ptr r)
res LeftCInt
rc ->IOError -> IO a
forall a. IOError -> IO a
ioError(String -> Errno -> Maybe Handle -> Maybe String -> IOError
errnoToIOErrorString
loc (CInt -> Errno
ErrnoCInt
rc )Maybe Handle
forall a. Maybe a
NothingMaybe String
forall a. Maybe a
Nothing)notFoundErr :: IO a
notFoundErr =IOError -> IO a
forall a. IOError -> IO a
ioError(IOError -> IO a) -> IOError -> IO a
forall a b. (a -> b) -> a -> b
$(IOError -> String -> IOError) -> String -> IOError -> IOError
forall a b c. (a -> b -> c) -> b -> a -> c
flipIOError -> String -> IOError
ioeSetErrorString(String
"no such "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
enttype )(IOError -> IOError) -> IOError -> IOError
forall a b. (a -> b) -> a -> b
$IOErrorType -> String -> Maybe Handle -> Maybe String -> IOError
mkIOErrorIOErrorType
doesNotExistErrorTypeString
loc Maybe Handle
forall a. Maybe a
NothingMaybe String
forall a. Maybe a
Nothing-- Used when a function returns NULL to indicate either an error or-- EOF, depending on whether the global errno is nonzero.throwErrnoIfNullAndError ::String->IO(Ptra )->IO(Ptra )throwErrnoIfNullAndError :: forall a. String -> IO (Ptr a) -> IO (Ptr a)
throwErrnoIfNullAndError String
loc IO (Ptr a)
act =doPtr a
rc <-IO (Ptr a)
act Errno
errno <-IO Errno
getErrnoifPtr a
rc Ptr a -> Ptr a -> Bool
forall a. Eq a => a -> a -> Bool
==Ptr a
forall a. Ptr a
nullPtrBool -> Bool -> Bool
&&Errno
errno Errno -> Errno -> Bool
forall a. Eq a => a -> a -> Bool
/=Errno
eOKthenString -> IO (Ptr a)
forall a. String -> IO a
throwErrnoString
loc elsePtr a -> IO (Ptr a)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
returnPtr a
rc {-# LINE 605 "System/Posix/User/ByteString.hsc" #-}

AltStyle によって変換されたページ (->オリジナル) /