%
\section[BSD]{Misc BSD bindings}
-The @BSD@ module defines Haskell bindings to network programming
-functionality that is only provided by BSD-style APIs.
+The @BSD@ module defines Haskell bindings to functionality
+provided by BSD Unix derivatives. Currently this covers
+network programming functionality and symbolic links.
+(OK, so the latter is pretty much supported by most *nixes
+today, but it was BSD that introduced them.)
\begin{code}
-{-# OPTIONS -#include "cbits/ghcSockets.h" #-}
+{-# OPTIONS -#include "cbits/ghcSockets.h" -#include "stgio.h" #-}
+
+#include "config.h"
module BSD (
HostName,
getHostName, -- :: IO HostName
+ ServiceEntry(..),
ServiceName,
- getServiceByName, -- :: ServiceName -> IO ServiceEntry
+ getServiceByName, -- :: ServiceName -> ProtocolName -> IO ServiceEntry
+ getServiceByPort, -- :: PortNumber -> ProtocolName -> IO ServiceEntry
getServicePortNumber, -- :: ServiceName -> IO PortNumber
- ServiceEntry(..),
+#ifndef _WIN32
getServiceEntry, -- :: IO ServiceEntry
setServiceEntry, -- :: Bool -> IO ()
endServiceEntry, -- :: IO ()
getServiceEntries, -- :: Bool -> IO [ServiceEntry]
+#endif
ProtocolName,
+ ProtocolNumber,
ProtocolEntry(..),
getProtocolByName, -- :: ProtocolName -> IO ProtocolEntry
getProtocolByNumber, -- :: ProtocolNumber -> IO ProtcolEntry
+ getProtocolNumber, -- :: ProtocolName -> ProtocolNumber
+#ifndef _WIN32
setProtocolEntry, -- :: Bool -> IO ()
getProtocolEntry, -- :: IO ProtocolEntry
endProtocolEntry, -- :: IO ()
getProtocolEntries, -- :: Bool -> IO [ProtocolEntry]
+#endif
PortNumber,
- getProtocolNumber, -- :: ProtocolName -> ProtocolNumber
+ mkPortNumber, -- :: Int -> PortNumber
HostEntry(..),
getHostByName, -- :: HostName -> IO HostEntry
getHostByAddr, -- :: HostAddress -> Family -> IO HostEntry
+ hostAddress, -- :: HostEntry -> HostAddress
+#ifndef _WIN32
setHostEntry, -- :: Bool -> IO ()
getHostEntry, -- :: IO HostEntry
endHostEntry, -- :: IO ()
getHostEntries, -- :: Bool -> IO [HostEntry]
+#endif
NetworkName,
NetworkAddr,
- NetworkEntry(..),
- getNetworkByName, -- :: NetworkName -> IO NetworkEntry
- getNetworkByAddr, -- :: NetworkAddr -> Family -> IO NetworkEntry
- setNetworkEntry, -- :: Bool -> IO ()
- getNetworkEntry, -- :: IO NetworkEntry
- endNetworkEntry, -- :: IO ()
- getNetworkEntries -- :: Bool -> IO [NetworkEntry]
-
-) where
+ NetworkEntry(..)
+#ifndef _WIN32
+ , getNetworkByName -- :: NetworkName -> IO NetworkEntry
+ , getNetworkByAddr -- :: NetworkAddr -> Family -> IO NetworkEntry
+ , setNetworkEntry -- :: Bool -> IO ()
+ , getNetworkEntry -- :: IO NetworkEntry
+ , endNetworkEntry -- :: IO ()
+ , getNetworkEntries -- :: Bool -> IO [NetworkEntry]
+#endif
+
+#ifdef HAVE_SYMLINK
+ , symlink -- :: String -> String -> IO ()
+#endif
+#ifdef HAVE_READLINK
+ , readlink -- :: String -> IO String
+#endif
+
+ ) where
import GlaExts
+import PrelIOBase ( IOError (..), IOErrorType(..) )
-import PrelIOBase
-
-import Foreign -- Addr..
-import PackedString ( byteArrayToPS, unpackPS )
-
-import PosixUtil ( strcpy, unvectorize )
+import Foreign
+import Addr
+import CString ( unpackCStringIO, unpackCStringBA, unvectorize, unpackNBytesBA )
import SocketPrim
\end{code}
\begin{code}
type HostName = String
type ProtocolName = String
-type ProtocolNumber = Int
type ServiceName = String
-type PortNumber = Int
data ProtocolEntry =
- ProtocolEntry
- ProtocolName -- Official Name
- [ProtocolName] -- aliases
- Int -- Protocol Number
+ ProtocolEntry {
+ protoName :: ProtocolName, -- Official Name
+ protoAliases :: [ProtocolName], -- aliases
+ protoNumber :: ProtocolNumber -- Protocol Number
+ }
data ServiceEntry =
- ServiceEntry
- ServiceName -- Official Name
- [ServiceName] -- aliases
- PortNumber -- Port Number
- ProtocolName -- Protocol
-
+ ServiceEntry {
+ serviceName :: ServiceName, -- Official Name
+ serviceAliases :: [ServiceName], -- aliases
+ servicePort :: PortNumber, -- Port Number ( network byte order )
+ serviceProtocol :: ProtocolName -- Protocol
+ }
+
data HostEntry =
- HostEntry
- HostName -- Official Name
- [HostName] -- aliases
- Family -- Host Type (currently AF_INET)
- [HostAddress] -- Set of Network Addresses
+ HostEntry {
+ hostName :: HostName, -- Official Name
+ hostAliases :: [HostName], -- aliases
+ hostFamily :: Family, -- Host Type (currently AF_INET)
+ hostAddresses :: [HostAddress] -- Set of Network Addresses (in network byte order)
+ }
+
+-- convenience function:
+hostAddress :: HostEntry -> HostAddress
+hostAddress (HostEntry nm _ _ ls) =
+ case ls of
+ [] -> error ("BSD.hostAddress: empty network address list for " ++ nm)
+ (x:_) -> x
\end{code}
-> IO ServiceEntry -- Service Entry
getServiceByName name proto = do
ptr <- _ccall_ getservbyname name proto
- if ptr == ``NULL''
- then fail (IOError Nothing NoSuchThing "no such service entry")
+ if ptr == nullAddr
+ then ioError (IOError Nothing NoSuchThing "getServiceByName" "no such service entry")
else unpackServiceEntry ptr
-getServiceByPort :: PortNumber ->
- ProtocolName ->
- IO ServiceEntry
-getServiceByPort port proto = do
+getServiceByPort :: PortNumber
+ -> ProtocolName
+ -> IO ServiceEntry
+getServiceByPort (PNum port) proto = do
ptr <- _ccall_ getservbyport port proto
- if ptr == ``NULL''
- then fail (IOError Nothing NoSuchThing "no such service entry")
+ if ptr == nullAddr
+ then ioError (IOError Nothing NoSuchThing "getServiceByPort" "no such service entry")
else unpackServiceEntry ptr
getServicePortNumber :: ServiceName -> IO PortNumber
(ServiceEntry _ _ port _) <- getServiceByName name "tcp"
return port
+#ifndef _WIN32
getServiceEntry :: IO ServiceEntry
getServiceEntry = do
ptr <- _ccall_ getservent
- if ptr == ``NULL''
- then fail (IOError Nothing NoSuchThing "no such service entry")
+ if ptr == nullAddr
+ then ioError (IOError Nothing NoSuchThing "getServiceEntry" "no such service entry")
else unpackServiceEntry ptr
setServiceEntry :: Bool -> IO ()
setServiceEntry flg = _ccall_ setservent stayOpen
- where stayOpen = if flg then 1 else 0
+ where stayOpen = (if flg then 1 else 0) :: Int
endServiceEntry :: IO ()
endServiceEntry = _ccall_ endservent
getServiceEntries stayOpen = do
setServiceEntry stayOpen
getEntries (getServiceEntry) (endServiceEntry)
-
+#endif
\end{code}
The following relate directly to the corresponding \tr{UNIX} {C} calls for
@getProtocolEntry@. Similarly,
\begin{code}
-getProtocolByName :: ProtocolName -> IO ProtocolEntry
-getProtocolByNumber :: PortNumber -> IO ProtocolEntry
-getProtocolNumber :: ProtocolName -> IO ProtocolNumber
+getProtocolByName :: ProtocolName -> IO ProtocolEntry
+getProtocolByNumber :: ProtocolNumber -> IO ProtocolEntry
+getProtocolNumber :: ProtocolName -> IO ProtocolNumber
+#ifndef _WIN32
setProtocolEntry :: Bool -> IO () -- Keep DB Open ?
getProtocolEntry :: IO ProtocolEntry -- Next Protocol Entry from DB
endProtocolEntry :: IO ()
getProtocolEntries :: Bool -> IO [ProtocolEntry]
+#endif
\end{code}
\begin{code}
--getProtocolByName :: ProtocolName -> IO ProtocolEntry
getProtocolByName name = do
ptr <- _ccall_ getprotobyname name
- if (ptr == ``NULL'' )
- then fail (IOError Nothing NoSuchThing "no such protocol entry")
+ if ptr == nullAddr
+ then ioError (IOError Nothing NoSuchThing "getProtocolByName" "no such protocol entry")
else unpackProtocolEntry ptr
---getProtocolByNumber :: PortNumber -> IO ProtocolEntry
+--getProtocolByNumber :: ProtocolNumber -> IO ProtocolEntry
getProtocolByNumber num = do
ptr <- _ccall_ getprotobynumber num
- if ptr == ``NULL''
- then fail (IOError Nothing NoSuchThing "no such protocol entry")
+ if ptr == nullAddr
+ then ioError (IOError Nothing NoSuchThing "getProtocolByNumber" "no such protocol entry")
else unpackProtocolEntry ptr
--getProtocolNumber :: ProtocolName -> IO ProtocolNumber
(ProtocolEntry _ _ num) <- getProtocolByName proto
return num
+#ifndef _WIN32
--getProtocolEntry :: IO ProtocolEntry -- Next Protocol Entry from DB
getProtocolEntry = do
ptr <- _ccall_ getprotoent
- if ptr == ``NULL''
- then fail (IOError Nothing NoSuchThing "no such protocol entry")
+ if ptr == nullAddr
+ then ioError (IOError Nothing NoSuchThing "getProtocolEntry" "no such protocol entry")
else unpackProtocolEntry ptr
--setProtocolEntry :: Bool -> IO () -- Keep DB Open ?
setProtocolEntry flg = _ccall_ setprotoent v
- where v = if flg then 1 else 0
+ where v = (if flg then 1 else 0) :: Int
--endProtocolEntry :: IO ()
endProtocolEntry = _ccall_ endprotoent
getProtocolEntries stayOpen = do
setProtocolEntry stayOpen
getEntries (getProtocolEntry) (endProtocolEntry)
+#endif
\end{code}
getHostByName :: HostName -> IO HostEntry
getHostByName name = do
ptr <- _ccall_ gethostbyname name
- if ptr == ``NULL''
- then fail (IOError Nothing NoSuchThing "no such host entry")
+ if ptr == nullAddr
+ then ioError (IOError Nothing NoSuchThing "getHostByName" "no such host entry")
else unpackHostEntry ptr
getHostByAddr :: Family -> HostAddress -> IO HostEntry
getHostByAddr family addr = do
ptr <- _casm_ ``struct in_addr addr;
- addr.s_addr = htonl(%0);
+ addr.s_addr = %0;
%r = gethostbyaddr ((char*)&addr, sizeof(struct in_addr), %1);''
addr
(packFamily family)
- if ptr == ``NULL''
- then fail (IOError Nothing NoSuchThing "no such host entry")
+ if ptr == nullAddr
+ then ioError (IOError Nothing NoSuchThing "getHostByAddr" "no such host entry")
else unpackHostEntry ptr
+#ifndef _WIN32
getHostEntry :: IO HostEntry
getHostEntry = do
ptr <- _ccall_ gethostent
- if ptr == ``NULL''
- then fail (IOError Nothing NoSuchThing "unable to retrieve host entry")
+ if ptr == nullAddr
+ then ioError (IOError Nothing NoSuchThing "getHostEntry" "unable to retrieve host entry")
else unpackHostEntry ptr
setHostEntry :: Bool -> IO ()
setHostEntry flg = _ccall_ sethostent v
- where v = if flg then 1 else 0
+ where v = (if flg then 1 else 0) :: Int
endHostEntry :: IO ()
endHostEntry = _ccall_ endhostent
getHostEntries stayOpen = do
setHostEntry stayOpen
getEntries (getHostEntry) (endHostEntry)
-
+#endif
\end{code}
%***************************************************************************
system info, this time for the types of networks supported.
\begin{code}
+-- network addresses are represented in host byte order.
type NetworkAddr = Word
+
type NetworkName = String
data NetworkEntry =
- NetworkEntry
- NetworkName -- official name
- [NetworkName] -- aliases
- Family -- type
- NetworkAddr
-
+ NetworkEntry {
+ networkName :: NetworkName, -- official name
+ networkAliases :: [NetworkName], -- aliases
+ networkFamily :: Family, -- type
+ networkAddress :: NetworkAddr
+ }
+#ifndef _WIN32
getNetworkByName :: NetworkName -> IO NetworkEntry
getNetworkByName name = do
ptr <- _ccall_ getnetbyname name
- if ptr == ``NULL''
- then fail (IOError Nothing NoSuchThing "no such network entry")
+ if ptr == nullAddr
+ then ioError (IOError Nothing NoSuchThing "getNetworkByName" "no such network entry")
else unpackNetworkEntry ptr
getNetworkByAddr :: NetworkAddr -> Family -> IO NetworkEntry
getNetworkByAddr addr family = do
- ptr <- _casm_ ``long naddr = htonl(%0);
- %r = getnetbyaddr (naddr, (int)%1);''
- addr
- (packFamily family)
- if ptr == ``NULL''
- then fail (IOError Nothing NoSuchThing "no such network entry")
+ ptr <- _ccall_ getnetbyaddr addr (packFamily family)
+ if ptr == nullAddr
+ then ioError (IOError Nothing NoSuchThing "getNetworkByAddr" "no such network entry")
else unpackNetworkEntry ptr
getNetworkEntry :: IO NetworkEntry
getNetworkEntry = do
ptr <- _ccall_ getnetent
- if ptr == ``NULL''
- then fail (IOError Nothing NoSuchThing "no more network entries")
+ if ptr == nullAddr
+ then ioError (IOError Nothing NoSuchThing "getNetworkEntry" "no more network entries")
else unpackNetworkEntry ptr
setNetworkEntry :: Bool -> IO ()
setNetworkEntry flg = _ccall_ setnetent v
- where v = if flg then 1 else 0
+ where v = (if flg then 1 else 0) :: Int
endNetworkEntry :: IO ()
endNetworkEntry = _ccall_ endnetent
getNetworkEntries stayOpen = do
setNetworkEntry stayOpen
getEntries (getNetworkEntry) (endNetworkEntry)
+#endif
\end{code}
getHostName = do
ptr <- stToIO (newCharArray (0,256))
rc <- _casm_ ``%r=gethostname(%0, 256);'' ptr
- ba <- stToIO (unsafeFreezeByteArray ptr)
- if rc == -1
- then fail (userError "getHostName: unable to determine host name")
- else return (unpackPS (byteArrayToPS ba))
+ if rc == ((-1)::Int)
+ then ioError (userError "getHostName: unable to determine host name")
+ else do
+ ba <- stToIO (unsafeFreezeByteArray ptr)
+ return (unpackCStringBA ba)
\end{code}
Helper function used by the exported functions that provides a
\begin{code}
unpackServiceEntry :: Addr -> PrimIO ServiceEntry
unpackServiceEntry ptr = do
- str <- _casm_ ``%r = ((struct servent*)%0)->s_name;'' ptr
- name <- strcpy str
+ pname <- _casm_ ``%r = ((struct servent*)%0)->s_name;'' ptr
+ name <- unpackCStringIO pname
alias <- _casm_ ``%r = ((struct servent*)%0)->s_aliases;'' ptr
aliases <- unvectorize alias 0
- -- Note: port numbers are represented as ints in (struct servent), but
- -- inet port numbers are 16-bit, hence the use of ntohs() rather than ntohl()
- port <- _casm_ ``%r = (int)ntohs((int)(((struct servent*)%0)->s_port));'' ptr
+ port <- _casm_ ``%r = (int)(((struct servent*)%0)->s_port);'' ptr
str <- _casm_ ``%r = (char *)((struct servent*)%0)->s_proto;'' ptr
- proto <- strcpy str
- return (ServiceEntry name aliases port proto)
+ proto <- unpackCStringIO str
+ return (ServiceEntry name aliases (PNum port) proto)
-------------------------------------------------------------------------------
unpackProtocolEntry :: Addr -> IO ProtocolEntry
unpackProtocolEntry ptr = do
str <- _casm_ ``%r = ((struct protoent*)%0)->p_name;'' ptr
- name <- strcpy str
+ name <- unpackCStringIO str
alias <- _casm_ ``%r = ((struct protoent*)%0)->p_aliases;'' ptr
aliases <- unvectorize alias 0
proto <- _casm_ ``%r = ((struct protoent*)%0)->p_proto;'' ptr
unpackHostEntry :: Addr -> IO HostEntry
unpackHostEntry ptr = do
str <- _casm_ ``%r = ((struct hostent*)%0)->h_name;'' ptr
- name <- strcpy str
+ name <- unpackCStringIO str
alias <- _casm_ ``%r = ((struct hostent*)%0)->h_aliases;'' ptr
aliases <- unvectorize alias 0
addrList <- unvectorizeHostAddrs ptr 0
unpackNetworkEntry :: Addr -> IO NetworkEntry
unpackNetworkEntry ptr = do
str <- _casm_ ``%r = ((struct netent*)%0)->n_name;'' ptr
- name <- strcpy str
+ name <- unpackCStringIO str
alias <- _casm_ ``%r = ((struct netent*)%0)->n_aliases;'' ptr
aliases <- unvectorize alias 0
fam <- _casm_ ``%r = ((struct netent*)%0)->n_addrtype;'' ptr
-------------------------------------------------------------------------------
-unvectorizeHostAddrs :: Addr -> Int -> IO [Word]
-unvectorizeHostAddrs ptr n
- | str == ``NULL'' = return []
- | otherwise = do
- x <- _casm_ ``{ u_long tmp;
+unvectorizeHostAddrs :: Addr -> Int -> IO [HostAddress]
+unvectorizeHostAddrs ptr n = do
+ x <- _casm_ ``{ unsigned long tmp;
if ((((struct hostent*)%0)->h_addr_list[(int)%1]) == NULL)
tmp=(W_)0;
else
- tmp = (W_)ntohl(((struct in_addr *)(((struct hostent*)%0)->h_addr_list[(int)%1]))->s_addr);
+ tmp = (W_)((struct in_addr *)(((struct hostent*)%0)->h_addr_list[(int)%1]))->s_addr;
%r=(W_)tmp;} ''
ptr n
- xs <- unvectorizeHostAddrs ptr (n+1)
- return (x : xs)
- where str = indexAddrOffAddr ptr n
+ if x == (W# (int2Word# 0#))
+ then return []
+ else do
+ xs <- unvectorizeHostAddrs ptr (n+1)
+ return (x : xs)
+
+
+\end{code}
+
+%***************************************************************************
+%* *
+\subsection[BSD-symlink]{Symbolic links}
+%* *
+%***************************************************************************
+
+
+\begin{code}
+#ifdef HAVE_SYMLINK
+symlink :: String -> String -> IO ()
+symlink actual_path sym_path = do
+ rc <- _ccall_ symlink actual_path sym_path
+ if rc == (0::Int) then
+ return ()
+ else do
+ _ccall_ convertErrno
+ cstr <- _ccall_ getErrStr__
+ estr <- unpackCStringIO cstr
+ ioError (userError ("BSD.symlink: " ++ estr))
+#endif
+
+#ifdef HAVE_READLINK
+readlink :: String -> IO String
+readlink sym = do
+ mbuf <- stToIO (newCharArray (0, path_max))
+ buf <- stToIO (unsafeFreezeByteArray mbuf)
+ rc <- _ccall_ readlink sym buf (path_max + 1)
+ if rc /= -1 then
+ return (unpackNBytesBA buf rc)
+ else do
+ _ccall_ convertErrno
+ cstr <- _ccall_ getErrStr__
+ estr <- unpackCStringIO cstr
+ ioError (userError ("BSD.readlink: " ++ estr))
+ where
+ path_max = (``PATH_MAX''::Int)
+#endif
\end{code}