module System.LXC.Internal.Container where
import Bindings.LXC.AttachOptions
import Bindings.LXC.Container
import Bindings.LXC.Sys.Types
import Control.Applicative
import Control.Monad
import Data.Maybe
import Data.Word
import Foreign.C.Types
import Foreign.C.String
import Foreign.Marshal.Alloc
import Foreign.Marshal.Array
import Foreign.Marshal.Utils
import Foreign.Ptr
import Foreign.Storable
import System.LXC.Internal.AttachOptions
import System.LXC.Internal.Utils
import System.Exit
import System.Posix.Types (ProcessID, Fd)
type ContainerCreateFn = Ptr C'lxc_container -> CString -> CString -> Ptr C'bdev_specs -> CInt -> Ptr CString -> IO CBool
foreign import ccall "dynamic"
mkCreateFn :: FunPtr ContainerCreateFn -> ContainerCreateFn
type ContainerCloneFn = Ptr C'lxc_container -> CString -> CString -> CInt -> CString -> CString -> C'uint64_t -> Ptr CString -> IO (Ptr C'lxc_container)
foreign import ccall "dynamic"
mkCloneFn :: FunPtr ContainerCloneFn -> ContainerCloneFn
type ContainerBoolFn = Ptr C'lxc_container -> IO CBool
foreign import ccall "dynamic"
mkBoolFn :: FunPtr ContainerBoolFn -> ContainerBoolFn
type ContainerStringFn = Ptr C'lxc_container -> IO CString
foreign import ccall "dynamic"
mkStringFn :: FunPtr ContainerStringFn -> ContainerStringFn
type ContainerProcessIDFn = Ptr C'lxc_container -> IO C'pid_t
foreign import ccall "dynamic"
mkProcessIDFn :: FunPtr ContainerProcessIDFn -> ContainerProcessIDFn
type ContainerStringBoolFn = Ptr C'lxc_container -> CString -> IO CBool
foreign import ccall "dynamic"
mkStringBoolFn :: FunPtr ContainerStringBoolFn -> ContainerStringBoolFn
type ContainerBoolBoolFn = Ptr C'lxc_container -> CBool -> IO CBool
foreign import ccall "dynamic"
mkBoolBoolFn :: FunPtr ContainerBoolBoolFn -> ContainerBoolBoolFn
type ContainerStartFn = Ptr C'lxc_container -> CInt -> Ptr CString -> IO CBool
foreign import ccall "dynamic"
mkStartFn :: FunPtr ContainerStartFn -> ContainerStartFn
type ContainerShutdownFn = Ptr C'lxc_container -> CInt -> IO CBool
foreign import ccall "dynamic"
mkShutdownFn :: FunPtr ContainerShutdownFn -> ContainerShutdownFn
type ContainerClearConfigFn = Ptr C'lxc_container -> IO ()
foreign import ccall "dynamic"
mkClearConfigFn :: FunPtr ContainerClearConfigFn -> ContainerClearConfigFn
type ContainerGetRunningConfigItemFn = Ptr C'lxc_container -> CString -> IO CString
foreign import ccall "dynamic"
mkGetRunningConfigItemFn :: FunPtr ContainerGetRunningConfigItemFn -> ContainerGetRunningConfigItemFn
type ContainerGetItemFn = Ptr C'lxc_container -> CString -> CString -> CInt -> IO CInt
foreign import ccall "dynamic"
mkGetItemFn :: FunPtr ContainerGetItemFn -> ContainerGetItemFn
type ContainerSetItemFn = Ptr C'lxc_container -> CString -> CString -> IO CBool
foreign import ccall "dynamic"
mkSetItemFn :: FunPtr ContainerSetItemFn -> ContainerSetItemFn
type ContainerGetInterfacesFn = Ptr C'lxc_container -> IO (Ptr CString)
foreign import ccall "dynamic"
mkGetInterfacesFn :: FunPtr ContainerGetInterfacesFn -> ContainerGetInterfacesFn
type ContainerGetIPsFn = Ptr C'lxc_container -> CString -> CString -> CInt -> IO (Ptr CString)
foreign import ccall "dynamic"
mkGetIPsFn :: FunPtr ContainerGetIPsFn -> ContainerGetIPsFn
type ContainerWaitFn = Ptr C'lxc_container -> CString -> CInt -> IO CBool
foreign import ccall "dynamic"
mkWaitFn :: FunPtr ContainerWaitFn -> ContainerWaitFn
type ContainerSnapshotFn = Ptr C'lxc_container -> CString -> IO CInt
foreign import ccall "dynamic"
mkSnapshotFn :: FunPtr ContainerSnapshotFn -> ContainerSnapshotFn
type ContainerSnapshotListFn = Ptr C'lxc_container -> Ptr (Ptr C'lxc_snapshot) -> IO CInt
foreign import ccall "dynamic"
mkSnapshotListFn :: FunPtr ContainerSnapshotListFn -> ContainerSnapshotListFn
type ContainerConsoleGetFDFn = Ptr C'lxc_container -> Ptr CInt -> Ptr CInt -> IO CInt
foreign import ccall "dynamic"
mkConsoleGetFDFn :: FunPtr ContainerConsoleGetFDFn -> ContainerConsoleGetFDFn
type ContainerConsoleFn = Ptr C'lxc_container -> CInt -> CInt -> CInt -> CInt -> CInt -> IO CInt
foreign import ccall "dynamic"
mkConsoleFn :: FunPtr ContainerConsoleFn -> ContainerConsoleFn
type ContainerAttachFn = Ptr C'lxc_container -> C_lxc_attach_exec_t -> Ptr () -> Ptr C'lxc_attach_options_t -> Ptr C'pid_t -> IO CInt
foreign import ccall "dynamic"
mkAttachFn :: FunPtr ContainerAttachFn -> ContainerAttachFn
type ContainerAttachRunWaitFn = Ptr C'lxc_container -> Ptr C'lxc_attach_options_t -> CString -> Ptr CString -> IO CInt
foreign import ccall "dynamic"
mkAttachRunWaitFn :: FunPtr ContainerAttachRunWaitFn -> ContainerAttachRunWaitFn
type SnapshotFreeFn = Ptr C'lxc_snapshot -> IO ()
foreign import ccall "dynamic"
mkFreeFn :: FunPtr SnapshotFreeFn -> SnapshotFreeFn
data CloneOption
= CloneKeepName
| CloneKeepMacAddr
| CloneSnapshot
| CloneKeepBDevType
| CloneMaybeSnapshot
| CloneMaxFlags
deriving (Eq, Ord)
data CreateOption
= CreateQuiet
| CreateMaxFlags
deriving (Eq, Ord)
cloneFlag :: Num a => CloneOption -> a
cloneFlag CloneKeepName = c'LXC_CLONE_KEEPNAME
cloneFlag CloneKeepMacAddr = c'LXC_CLONE_KEEPMACADDR
cloneFlag CloneSnapshot = c'LXC_CLONE_SNAPSHOT
cloneFlag CloneKeepBDevType = c'LXC_CLONE_KEEPBDEVTYPE
cloneFlag CloneMaybeSnapshot = c'LXC_CLONE_MAYBE_SNAPSHOT
cloneFlag CloneMaxFlags = c'LXC_CLONE_MAXFLAGS
createFlag :: Num a => CreateOption -> a
createFlag CreateQuiet = c'LXC_CREATE_QUIET
createFlag CreateMaxFlags = c'LXC_CREATE_MAXFLAGS
data Snapshot = Snapshot
{ snapshotName :: String
, snapshotCommentPathname :: Maybe FilePath
, snapshotTimestamp :: String
, snapshotLXCPath :: FilePath
}
deriving (Show)
newtype Container = Container {
getContainer :: Ptr C'lxc_container
}
data ContainerState
= ContainerStopped
| ContainerStarting
| ContainerRunning
| ContainerStopping
| ContainerAborting
| ContainerFreezing
| ContainerFrozen
| ContainerThawed
| ContainerOtherState String
deriving (Eq, Show)
parseState :: String -> ContainerState
parseState "STOPPED" = ContainerStopped
parseState "STARTING" = ContainerStarting
parseState "RUNNING" = ContainerRunning
parseState "STOPPING" = ContainerStopping
parseState "ABORTING" = ContainerAborting
parseState "FREEZING" = ContainerFreezing
parseState "FROZEN" = ContainerFrozen
parseState "THAWED" = ContainerThawed
parseState s = ContainerOtherState s
printState :: ContainerState -> String
printState ContainerStopped = "STOPPED"
printState ContainerStarting = "STARTING"
printState ContainerRunning = "RUNNING"
printState ContainerStopping = "STOPPING"
printState ContainerAborting = "ABORTING"
printState ContainerFreezing = "FREEZING"
printState ContainerFrozen = "FROZEN"
printState ContainerThawed = "THAWED"
printState (ContainerOtherState s) = s
data BDevSpecs = BDevSpecs
{ bdevFSType :: String
, bdevFSSize :: Word64
, bdevZFSRootPath :: FilePath
, bdevLVMVolumeGroupName :: String
, bdevLVMLogicalVolumeName :: String
, bdevLVMThinPool :: Maybe String
, bdevDirectory :: FilePath
}
withC'bdev_specs :: BDevSpecs -> (Ptr C'bdev_specs -> IO a) -> IO a
withC'bdev_specs specs f = do
withCString (bdevFSType specs) $ \cFSType ->
withCString (bdevZFSRootPath specs) $ \cZFSRootPath ->
withCString (bdevLVMVolumeGroupName specs) $ \cLVMVolumeGroupName ->
withCString (bdevLVMLogicalVolumeName specs) $ \cLVMLogicalVolumeName ->
maybeWith withCString (bdevLVMThinPool specs) $ \cLVMThinPool ->
withCString (bdevDirectory specs) $ \cDirectory -> do
let cspecs = C'bdev_specs
cFSType
(bdevFSSize specs)
(C'zfs_t cZFSRootPath)
(C'lvm_t
cLVMVolumeGroupName
cLVMLogicalVolumeName
cLVMThinPool)
cDirectory
with cspecs f
mkContainer :: String
-> Maybe FilePath
-> IO Container
mkContainer name configPath = do
c <- withCString name $ \cname ->
case configPath of
Nothing -> c'lxc_container_new cname nullPtr
Just s -> withCString s $ c'lxc_container_new cname
when (c == nullPtr) $ error "failed to allocate new container"
return $ Container c
type Field s a = Ptr s -> Ptr a
mkFn :: (t -> Ptr s) -> (FunPtr (Ptr s -> a) -> (Ptr s -> a)) -> Field s (FunPtr (Ptr s -> a)) -> t -> IO a
mkFn unwrap mk g t = do
let s = unwrap t
fn <- peek (g s)
return $ mk fn s
boolFn :: Field C'lxc_container (FunPtr ContainerBoolFn) -> Container -> IO Bool
boolFn g c = do
fn <- mkFn getContainer mkBoolFn g c
toBool <$> fn
stringBoolFn :: Field C'lxc_container (FunPtr ContainerStringBoolFn) -> Container -> Maybe String -> IO Bool
stringBoolFn g c s = do
fn <- mkFn getContainer mkStringBoolFn g c
maybeWith withCString s $ \cs ->
toBool <$> fn cs
boolBoolFn :: Field C'lxc_container (FunPtr ContainerBoolBoolFn) -> Container -> Bool -> IO Bool
boolBoolFn g c b = do
fn <- mkFn getContainer mkBoolBoolFn g c
toBool <$> fn (if b then 1 else 0)
getItemFn :: Field C'lxc_container (FunPtr ContainerGetItemFn) -> Container -> String -> IO (Maybe String)
getItemFn g c s = do
fn <- mkFn getContainer mkGetItemFn g c
withCString s $ \cs -> do
sz <- fn cs nullPtr 0
if (sz < 0)
then return Nothing
else allocaBytes (fromIntegral sz) $ \cretv -> do
fn cs cretv sz
Just <$> peekCString cretv
setItemFn :: Field C'lxc_container (FunPtr ContainerSetItemFn) -> Container -> String -> Maybe String -> IO Bool
setItemFn g c k v = do
fn <- mkFn getContainer mkSetItemFn g c
withCString k $ \ck ->
maybeWith withCString v $ \cv ->
toBool <$> fn ck cv
setItemFn' :: Field C'lxc_container (FunPtr ContainerSetItemFn) -> Container -> String -> String -> IO Bool
setItemFn' g c k v = setItemFn g c k (Just v)
isDefined :: Container -> IO Bool
isDefined = boolFn p'lxc_container'is_defined
isRunning :: Container -> IO Bool
isRunning = boolFn p'lxc_container'is_running
state :: Container -> IO ContainerState
state (Container c) = do
fn <- peek (p'lxc_container'state c)
cs <- mkStringFn fn c
parseState <$> peekCString cs
freeze :: Container -> IO Bool
freeze = boolFn p'lxc_container'freeze
unfreeze :: Container -> IO Bool
unfreeze = boolFn p'lxc_container'unfreeze
initPID :: Container -> IO (Maybe ProcessID)
initPID c = do
fn <- mkFn getContainer mkProcessIDFn p'lxc_container'init_pid c
pid <- fromIntegral <$> fn
if (pid < 0)
then return Nothing
else return (Just pid)
loadConfig :: Container
-> Maybe FilePath
-> IO Bool
loadConfig = stringBoolFn p'lxc_container'load_config
start :: Container
-> Int
-> [String]
-> IO Bool
start c n argv = do
fn <- mkFn getContainer mkStartFn p'lxc_container'start c
withMany withCString argv $ \cargv ->
withArray0 nullPtr cargv $ \cargv' ->
toBool <$> fn (fromIntegral n) cargv'
stop :: Container -> IO Bool
stop = boolFn p'lxc_container'stop
wantDaemonize :: Container
-> Bool
-> IO Bool
wantDaemonize = boolBoolFn p'lxc_container'want_daemonize
wantCloseAllFDs :: Container
-> Bool
-> IO Bool
wantCloseAllFDs = boolBoolFn p'lxc_container'want_close_all_fds
configFileName :: Container -> IO (Maybe FilePath)
configFileName (Container c) = do
fn <- peek (p'lxc_container'config_file_name c)
cs <- mkStringFn fn c
s <- maybePeek peekCString cs
when (isJust s) $ free cs
return s
wait :: Container
-> ContainerState
-> Int
-> IO Bool
wait c s t = do
fn <- mkFn getContainer mkWaitFn p'lxc_container'wait c
withCString (printState s) $ \cs ->
toBool <$> fn cs (fromIntegral t)
setConfigItem :: Container
-> String
-> String
-> IO Bool
setConfigItem = setItemFn' p'lxc_container'set_config_item
destroy :: Container -> IO Bool
destroy = boolFn p'lxc_container'destroy
saveConfig :: Container
-> FilePath
-> IO Bool
saveConfig c s = stringBoolFn p'lxc_container'save_config c (Just s)
rename :: Container
-> String
-> IO Bool
rename c s = stringBoolFn p'lxc_container'rename c (Just s)
reboot :: Container -> IO Bool
reboot = boolFn p'lxc_container'reboot
shutdown :: Container
-> Int
-> IO Bool
shutdown c n = do
fn <- mkFn getContainer mkShutdownFn p'lxc_container'shutdown c
toBool <$> fn (fromIntegral n)
clearConfig :: Container -> IO ()
clearConfig = join . mkFn getContainer mkClearConfigFn p'lxc_container'clear_config
getConfigItem :: Container
-> String
-> IO (Maybe String)
getConfigItem = getItemFn p'lxc_container'get_config_item
getRunningConfigItem :: Container
-> String
-> IO (Maybe String)
getRunningConfigItem c k = do
fn <- mkFn getContainer mkGetRunningConfigItemFn p'lxc_container'get_running_config_item c
withCString k $ \ck -> do
cv <- fn ck
v <- maybePeek peekCString cv
when (isJust v) $ free cv
return v
getKeys :: Container
-> String
-> IO [String]
getKeys c kp = concatMap lines . maybeToList <$> getItemFn p'lxc_container'get_keys c kp
getInterfaces :: Container -> IO [String]
getInterfaces c = do
cifs <- join $ mkFn getContainer mkGetInterfacesFn p'lxc_container'get_interfaces c
if (cifs == nullPtr)
then return []
else do
cifs' <- peekArray0 nullPtr cifs
ifs <- mapM peekCString cifs'
mapM_ free cifs'
free cifs
return ifs
getIPs :: Container
-> String
-> String
-> Word32
-> IO [String]
getIPs c iface fam sid = do
fn <- mkFn getContainer mkGetIPsFn p'lxc_container'get_ips c
withCString iface $ \ciface ->
withCString fam $ \cfam -> do
cips <- fn ciface cfam (fromIntegral sid)
if (cips == nullPtr)
then return []
else do
cips' <- peekArray0 nullPtr cips
ips <- mapM peekCString cips'
mapM_ free cips'
free cips
return ips
getCGroupItem :: Container
-> String
-> IO (Maybe String)
getCGroupItem = getItemFn p'lxc_container'get_cgroup_item
setCGroupItem :: Container
-> String
-> String
-> IO Bool
setCGroupItem = setItemFn' p'lxc_container'set_cgroup_item
clearConfigItem :: Container
-> String
-> IO Bool
clearConfigItem c s = stringBoolFn p'lxc_container'clear_config_item c (Just s)
getConfigPath :: Container -> IO FilePath
getConfigPath c = do
cs <- join $ mkFn getContainer mkStringFn p'lxc_container'get_config_path c
s <- peekCString cs
free cs
return s
setConfigPath :: Container
-> FilePath
-> IO Bool
setConfigPath c s = stringBoolFn p'lxc_container'set_config_path c (Just s)
clone :: Container
-> Maybe String
-> Maybe FilePath
-> [CloneOption]
-> Maybe String
-> Maybe String
-> Maybe Word64
-> [String]
-> IO Container
clone c newname lxcpath flags bdevtype bdevdata newsize hookargs = do
c' <- maybeWith withCString newname $ \cnewname ->
maybeWith withCString lxcpath $ \clxcpath ->
maybeWith withCString bdevtype $ \cbdevtype ->
maybeWith withCString bdevdata $ \cbdevdata ->
withMany withCString hookargs $ \chookargs ->
withArray0 nullPtr chookargs $ \chookargs' -> do
fn <- peek $ p'lxc_container'clone $ getContainer c
mkCloneFn fn
(getContainer c)
cnewname
clxcpath
(mkFlags cloneFlag flags)
cbdevtype
cbdevdata
(fromMaybe 0 newsize)
chookargs'
when (c' == nullPtr) $ error "failed to clone a container"
return $ Container c'
consoleGetFD :: Container
-> Maybe Int
-> IO (Maybe (Int, Int, Int))
consoleGetFD c ttynum = do
fn <- mkFn getContainer mkConsoleGetFDFn p'lxc_container'console_getfd c
alloca $ \cttynum ->
alloca $ \cmasterfd -> do
poke cttynum (fromIntegral $ fromMaybe (1) ttynum)
fd <- fromIntegral <$> fn cttynum cmasterfd
ttynum' <- fromIntegral <$> peek cttynum
masterfd <- fromIntegral <$> peek cmasterfd
if (fd < 0)
then return Nothing
else return $ Just (fd, ttynum', masterfd)
console :: Container
-> Maybe Int
-> Fd
-> Fd
-> Fd
-> Int
-> IO Bool
console c ttynum stdin stdout stderr escape = do
fn <- mkFn getContainer mkConsoleFn p'lxc_container'console c
toBool <$> fn (fromIntegral $ fromMaybe (1) ttynum)
(fromIntegral stdin)
(fromIntegral stdout)
(fromIntegral stderr)
(fromIntegral escape)
attach :: Container
-> AttachExecFn
-> AttachCommand
-> AttachOptions
-> IO (Maybe ProcessID)
attach c exec cmd opts = do
fn <- mkFn getContainer mkAttachFn p'lxc_container'attach c
withC'lxc_attach_command_t cmd $ \ccmd ->
withC'lxc_attach_options_t opts $ \copts ->
alloca $ \cpid -> do
ret <- fn (getAttachExecFn exec) (castPtr ccmd) copts cpid
if (ret < 0)
then return Nothing
else Just . fromIntegral <$> peek cpid
attachRunWait :: Container
-> AttachOptions
-> String
-> [String]
-> IO (Maybe ExitCode)
attachRunWait c opts prg argv = do
fn <- mkFn getContainer mkAttachRunWaitFn p'lxc_container'attach_run_wait c
withCString prg $ \cprg ->
withMany withCString argv $ \cargv ->
withArray0 nullPtr cargv $ \cargv' ->
withC'lxc_attach_options_t opts $ \copts -> do
ret <- fromIntegral <$> fn copts cprg cargv'
case ret of
_ | ret < 0 -> return Nothing
0 -> return $ Just ExitSuccess
_ -> return $ Just (ExitFailure ret)
snapshot :: Container
-> FilePath
-> IO (Maybe Int)
snapshot c path = do
fn <- mkFn getContainer mkSnapshotFn p'lxc_container'snapshot c
withCString path $ \cpath -> do
n <- fn cpath
if (n == 1)
then return Nothing
else return (Just $ fromIntegral n)
peekC'lxc_snapshot :: Ptr C'lxc_snapshot -> IO Snapshot
peekC'lxc_snapshot ptr = Snapshot
<$> peekField peekCString p'lxc_snapshot'name
<*> peekField (maybePeek peekCString) p'lxc_snapshot'comment_pathname
<*> peekField peekCString p'lxc_snapshot'timestamp
<*> peekField peekCString p'lxc_snapshot'lxcpath
where
peekField g f = peek (f ptr) >>= g
snapshotList :: Container
-> IO [Snapshot]
snapshotList c = do
alloca $ \css -> do
fn <- mkFn getContainer mkSnapshotListFn p'lxc_container'snapshot_list c
n <- fromIntegral <$> fn css
if (n < 0)
then return []
else do
css' <- peek css
let css'' = take n $ iterate (flip advancePtr 1) css'
css <- mapM peekC'lxc_snapshot css''
forM_ css'' $ join . mkFn id mkFreeFn p'lxc_snapshot'free
free css'
return css
snapshotRestore :: Container
-> String
-> String
-> IO Bool
snapshotRestore = setItemFn' p'lxc_container'snapshot_restore
snapshotDestroy :: Container
-> String
-> IO Bool
snapshotDestroy c n = stringBoolFn p'lxc_container'snapshot_destroy c (Just n)
mayControl :: Container -> IO Bool
mayControl = boolFn p'lxc_container'may_control
addDeviceNode :: Container
-> FilePath
-> Maybe FilePath
-> IO Bool
addDeviceNode = setItemFn p'lxc_container'add_device_node
removeDeviceNode :: Container
-> FilePath
-> Maybe FilePath
-> IO Bool
removeDeviceNode = setItemFn p'lxc_container'remove_device_node
create :: Container
-> String
-> Maybe String
-> Maybe BDevSpecs
-> [CreateOption]
-> [String]
-> IO Bool
create c t bdevtype bdevspecs flags argv = toBool <$> do
withMany withCString argv $ \cargv ->
withArray0 nullPtr cargv $ \cargv' ->
withCString t $ \ct ->
maybeWith withCString bdevtype $ \cbdevtype ->
maybeWith withC'bdev_specs bdevspecs $ \cbdevspecs -> do
fn <- peek $ p'lxc_container'create $ getContainer c
mkCreateFn fn
(getContainer c)
ct
cbdevtype
nullPtr
(mkFlags createFlag flags)
cargv'
getRef :: Container -> IO Bool
getRef (Container c) = toBool <$> c'lxc_container_get c
dropRef :: Container
-> IO (Maybe Bool)
dropRef (Container c) = do
n <- c'lxc_container_put c
return $ case n of
0 -> Just False
1 -> Just True
_ -> Nothing
getWaitStates :: IO [ContainerState]
getWaitStates = do
sz <- fromIntegral <$> c'lxc_get_wait_states nullPtr
allocaArray sz $ \cstates -> do
c'lxc_get_wait_states cstates
cstates' <- peekArray sz cstates
map parseState <$> mapM peekCString cstates'
getGlobalConfigItem :: String
-> IO (Maybe String)
getGlobalConfigItem k = do
withCString k $ \ck -> do
cv <- c'lxc_get_global_config_item ck
maybePeek peekCString cv
getVersion :: IO String
getVersion = c'lxc_get_version >>= peekCString
listContainersFn :: (CString -> Ptr (Ptr CString) -> Ptr (Ptr (Ptr C'lxc_container)) -> IO CInt)
-> Maybe String
-> IO [(String, Container)]
listContainersFn f lxcpath = do
maybeWith withCString lxcpath $ \clxcpath ->
alloca $ \cnames ->
alloca $ \ccontainers -> do
n <- fromIntegral <$> f clxcpath cnames ccontainers
if (n < 0)
then return []
else do
cnames' <- peek cnames
cnames'' <- peekArray n cnames'
names <- mapM peekCString cnames''
mapM_ free cnames''
free cnames'
ccontainers' <- peek ccontainers
containers <- map Container <$> peekArray n ccontainers'
free ccontainers'
return $ zip names containers
listDefinedContainers :: Maybe String
-> IO [(String, Container)]
listDefinedContainers = listContainersFn c'list_defined_containers
listActiveContainers :: Maybe String
-> IO [(String, Container)]
listActiveContainers = listContainersFn c'list_active_containers
listAllContainers :: Maybe String
-> IO [(String, Container)]
listAllContainers = listContainersFn c'list_all_containers
logClose :: IO ()
logClose = c'lxc_log_close