Skip to content

Commit 2bdd171

Browse files
committed
wip/remote: add daemon runners, simplify server using StoreReply
1 parent 8078f0f commit 2bdd171

File tree

3 files changed

+147
-85
lines changed

3 files changed

+147
-85
lines changed

hnix-store-remote/hnix-store-remote.cabal

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,16 @@ library
114114
, data-default-class
115115
, dependent-sum > 0.7
116116
, dependent-sum-template >= 0.2.0.1 && < 0.3
117+
, directory
117118
, dlist >= 1.0
119+
, exceptions
118120
, generic-arbitrary < 1.1
119121
, hashable
120122
, text
121123
, time
122124
, transformers
123125
, network
126+
, monad-control
124127
, mtl
125128
, QuickCheck
126129
, unordered-containers

hnix-store-remote/src/System/Nix/Store/Remote.hs

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
{-# LANGUAGE Rank2Types #-}
3+
14
module System.Nix.Store.Remote
2-
(
3-
module System.Nix.Store.Types
5+
( module System.Nix.Store.Types
46
, module System.Nix.Store.Remote.Client
57
, module System.Nix.Store.Remote.MonadStore
68
, module System.Nix.Store.Remote.Types
@@ -10,6 +12,10 @@ module System.Nix.Store.Remote
1012
, runStore
1113
, runStoreOpts
1214
, runStoreOptsTCP
15+
-- ** Daemon
16+
, runDaemon
17+
, runDaemonOpts
18+
, justdoit
1319
) where
1420

1521
import Data.Default.Class (Default(def))
@@ -23,6 +29,16 @@ import System.Nix.Store.Remote.Types
2329
import qualified Control.Exception
2430
import qualified Network.Socket
2531

32+
-- wip daemon
33+
import Control.Monad.Conc.Class (MonadConc)
34+
import Control.Monad.IO.Class (MonadIO, liftIO)
35+
import Control.Monad.Trans.Control (MonadBaseControl)
36+
import System.Nix.StorePath (StorePath)
37+
import System.Nix.Store.Remote.Server (WorkerHelper, runDaemonSocket)
38+
import qualified System.Directory
39+
import qualified System.Nix.StorePath
40+
import qualified Control.Monad.Catch
41+
2642
-- * Compat
2743

2844
type MonadStore = RemoteStoreT StoreConfig IO
@@ -31,9 +47,9 @@ type MonadStore = RemoteStoreT StoreConfig IO
3147

3248
runStore :: MonadStore a -> Run IO a
3349
runStore = runStoreOpts defaultSockPath def
34-
where
35-
defaultSockPath :: String
36-
defaultSockPath = "/nix/var/nix/daemon-socket/socket"
50+
51+
defaultSockPath :: String
52+
defaultSockPath = "/nix/var/nix/daemon-socket/socket"
3753

3854
runStoreOpts
3955
:: FilePath
@@ -84,3 +100,71 @@ runStoreOpts' sockFamily sockAddr storeRootDir code =
84100
{ preStoreConfig_socket = soc
85101
, preStoreConfig_dir = storeRootDir
86102
}
103+
104+
justdoit :: Run IO (Bool, Bool)
105+
justdoit = do
106+
runDaemonOpts def handler "/tmp/dsock" $
107+
runStoreOpts "/tmp/dsock" def
108+
$ do
109+
a <- isValidPath pth
110+
b <- isValidPath pth
111+
pure (a, b)
112+
where
113+
pth :: StorePath
114+
pth =
115+
either (error . show) id
116+
$ System.Nix.StorePath.parsePathFromText
117+
def
118+
"/nix/store/yyznqbwam67cmp7zfwk0rkgmi9yqsdsm-hnix-store-core-0.8.0.0"
119+
120+
handler :: MonadIO m => WorkerHelper m
121+
handler k = do
122+
x <- liftIO $ runStore $ doReq k
123+
either (error . show) pure (fst x)
124+
125+
runDaemon
126+
:: forall m a
127+
. ( MonadIO m
128+
, MonadBaseControl IO m
129+
, MonadConc m
130+
)
131+
=> WorkerHelper m
132+
-> m a
133+
-> m a
134+
runDaemon workerHelper k = runDaemonOpts def workerHelper defaultSockPath k
135+
136+
-- | Run an emulated nix daemon on given socket address.
137+
-- the deamon will close when the continuation returns.
138+
runDaemonOpts
139+
:: forall m a
140+
. ( MonadIO m
141+
, MonadBaseControl IO m
142+
, MonadConc m
143+
)
144+
=> StoreDir
145+
-> WorkerHelper m
146+
-> FilePath
147+
-> m a
148+
-> m a
149+
runDaemonOpts sd workerHelper f k = Control.Monad.Catch.bracket
150+
(liftIO
151+
$ Network.Socket.socket
152+
Network.Socket.AF_UNIX
153+
Network.Socket.Stream
154+
Network.Socket.defaultProtocol
155+
)
156+
(\lsock -> liftIO $ Network.Socket.close lsock *> System.Directory.removeFile f)
157+
$ \lsock -> do
158+
-- ^^^^^^^^^^^^
159+
-- TODO: this: --------------------------------------------------////////////
160+
-- should really be
161+
-- a file lock followed by unlink *before* bind rather than after close. If
162+
-- the program crashes (or loses power or something) then a stale unix
163+
-- socket will stick around and prevent the daemon from starting. using a
164+
-- lock file instead means only one "copy" of the daemon can hold the lock,
165+
-- and can safely unlink the socket before binding no matter how shutdown
166+
-- occured.
167+
168+
-- set up the listening socket
169+
liftIO $ Network.Socket.bind lsock (SockAddrUnix f)
170+
runDaemonSocket sd workerHelper lsock k

hnix-store-remote/src/System/Nix/Store/Remote/Server.hs

Lines changed: 55 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,32 @@ import System.Nix.StorePath (StoreDir)
2222
import System.Nix.Store.Remote.Serializer as RB
2323
import System.Nix.Store.Remote.Socket
2424
import System.Nix.Store.Remote.Types.StoreRequest as R
25+
import System.Nix.Store.Remote.Types.StoreReply
2526
import System.Nix.Store.Remote.Types.StoreConfig (HasStoreSocket(..), StoreConfig(..), PreStoreConfig(..), preStoreConfigToStoreConfig)
2627
import System.Nix.Store.Remote.Types.ProtoVersion (HasProtoVersion(..), ProtoVersion(..))
2728
import System.Nix.Store.Remote.Types.Logger (BasicError(..), ErrorInfo, Logger(..))
28-
2929
import System.Nix.Store.Remote.MonadStore (WorkerError(..), WorkerException(..), RemoteStoreError(..), RemoteStoreT, runRemoteStoreT, mapStoreConfig)
3030
import System.Nix.Store.Remote.Types.Handshake (ServerHandshakeInput(..), ServerHandshakeOutput(..))
3131
import System.Nix.Store.Remote.Types.ProtoVersion (ourProtoVersion)
3232
import System.Nix.Store.Remote.Types.WorkerMagic (WorkerMagic(..))
3333

34-
type WorkerHelper m = forall a. StoreRequest a -> m a
34+
-- wip
35+
-- import Data.Some (traverseSome)
36+
import Data.Functor.Identity
37+
38+
type WorkerHelper m
39+
= forall a
40+
. ( Show a
41+
, StoreReply a
42+
)
43+
=> StoreRequest a -> m a
3544

3645
-- | Run an emulated nix daemon on given socket address.
3746
-- The deamon will close when the continuation returns.
3847
runDaemonSocket
3948
:: forall m a
4049
. ( MonadIO m
4150
, MonadConc m
42-
, MonadError RemoteStoreError m
43-
, MonadReader StoreConfig m
4451
)
4552
=> StoreDir
4653
-> WorkerHelper m
@@ -63,18 +70,18 @@ runDaemonSocket sd workerHelper lsock k = do
6370
}
6471

6572
-- TODO: this, but without the space leak
66-
fmap fst $ concurrently listener $ processConnection workerHelper preStoreConfig
73+
fmap fst
74+
$ concurrently listener
75+
$ processConnection workerHelper preStoreConfig
6776

6877
either absurd id <$> race listener k
6978

7079
-- | "main loop" of the daemon for a single connection.
7180
--
7281
-- this function should take care to not throw errors from client connections.
7382
processConnection
74-
:: ( MonadIO m
75-
, MonadError RemoteStoreError m
76-
, MonadReader StoreConfig m
77-
)
83+
:: forall m
84+
. MonadIO m
7885
=> WorkerHelper m
7986
-> PreStoreConfig
8087
-> m ()
@@ -103,6 +110,22 @@ processConnection workerHelper preStoreConfig = do
103110
--authHook(*store);
104111
stopWork tunnelLogger
105112

113+
let perform
114+
:: ( Show a
115+
, StoreReply a
116+
)
117+
=> StoreRequest a
118+
-> RemoteStoreT StoreConfig m (Identity a)
119+
perform req = do
120+
resp <- bracketLogger tunnelLogger $ lift $ workerHelper req
121+
sockPutS
122+
(mapErrorS
123+
RemoteStoreError_SerializerReply
124+
$ getReplyS
125+
)
126+
resp
127+
pure (Identity resp)
128+
106129
-- Process client requests.
107130
let loop = do
108131
someReq <-
@@ -111,7 +134,26 @@ processConnection workerHelper preStoreConfig = do
111134
RemoteStoreError_SerializerRequest
112135
storeRequest
113136

114-
lift $ performOp' workerHelper tunnelLogger someReq
137+
-- • Could not deduce (Show a) arising from a use of ‘perform’
138+
-- and also (StoreReply a)
139+
-- traverseSome perform someReq
140+
void $ do
141+
case someReq of
142+
Some req@(IsValidPath {}) -> do
143+
-- • Couldn't match type ‘a0’ with ‘Bool’
144+
-- Expected: StoreRequest a0
145+
-- Actual: StoreRequest a
146+
-- • ‘a0’ is untouchable
147+
-- inside the constraints: a ~ Bool
148+
-- bound by a pattern with constructor:
149+
-- IsValidPath :: StorePath -> StoreRequest Bool
150+
-- runIdentity <$> perform req
151+
152+
void $ perform req
153+
pure undefined
154+
155+
_ -> throwError unimplemented
156+
115157
loop
116158
loop
117159

@@ -189,48 +231,9 @@ processConnection workerHelper preStoreConfig = do
189231
, serverHandshakeOutputClientVersion = clientVersion
190232
}
191233

192-
simpleOp
193-
:: ( MonadIO m
194-
, HasStoreSocket r
195-
, HasProtoVersion r
196-
, MonadError RemoteStoreError m
197-
, MonadReader r m
198-
)
199-
=> (StoreRequest () -> m ())
200-
-> TunnelLogger r
201-
-> m (StoreRequest ())
202-
-> m ()
203-
simpleOp workerHelper tunnelLogger m = do
204-
req <- m
205-
bracketLogger tunnelLogger $ workerHelper req
206-
sockPutS
207-
(mapErrorS
208-
RemoteStoreError_SerializerPut
209-
bool
210-
)
211-
True
212-
213-
simpleOpRet
214-
:: ( MonadIO m
215-
, HasStoreSocket r
216-
, HasProtoVersion r
217-
, MonadError RemoteStoreError m
218-
, MonadReader r m
219-
)
220-
=> (StoreRequest a -> m a)
221-
-> TunnelLogger r
222-
-> NixSerializer r SError a
223-
-> m (StoreRequest a)
224-
-> m ()
225-
simpleOpRet workerHelper tunnelLogger s m = do
226-
req <- m
227-
resp <- bracketLogger tunnelLogger $ workerHelper req
228-
sockPutS
229-
(mapErrorS
230-
RemoteStoreError_SerializerPut
231-
s
232-
)
233-
resp
234+
{-# WARNING unimplemented "not yet implemented" #-}
235+
unimplemented :: RemoteStoreError
236+
unimplemented = RemoteStoreError_WorkerException $ WorkerException_Error $ WorkerError_NotYetImplemented
234237

235238
bracketLogger
236239
:: ( MonadIO m
@@ -248,34 +251,6 @@ bracketLogger tunnelLogger m = do
248251
stopWork tunnelLogger
249252
pure a
250253

251-
{-# WARNING unimplemented "not yet implemented" #-}
252-
unimplemented :: WorkerException
253-
unimplemented = WorkerException_Error $ WorkerError_NotYetImplemented
254-
255-
performOp'
256-
:: forall m
257-
. ( MonadIO m
258-
, MonadError RemoteStoreError m
259-
, MonadReader StoreConfig m
260-
)
261-
=> WorkerHelper m
262-
-> TunnelLogger StoreConfig
263-
-> Some StoreRequest
264-
-> m ()
265-
performOp' workerHelper tunnelLogger op = do
266-
let _simpleOp' = simpleOp workerHelper tunnelLogger
267-
let simpleOpRet'
268-
:: NixSerializer StoreConfig SError a
269-
-> m (StoreRequest a)
270-
-> m ()
271-
simpleOpRet' = simpleOpRet workerHelper tunnelLogger
272-
273-
case op of
274-
Some (IsValidPath path) -> simpleOpRet' bool $ do
275-
pure $ R.IsValidPath path
276-
277-
_ -> undefined
278-
279254
---
280255

281256
data TunnelLogger r = TunnelLogger

0 commit comments

Comments
 (0)