Run privileged command

11 views
Skip to first unread message

sgf...@gmail.com

unread,
Mar 12, 2017, 8:30:42 AM3/12/17
to Shake build system
Hi.

I need to run several commands via `sudo`. After reading Don Stewart's
["Practical Haskell Programming: Scripting With Types"][1], i think about
adding a wrapper type:

> {-# LANGUAGE RankNTypes #-}
>
> import Development.Shake
> import Development.Shake.Command
>
> data Priv = Priv (forall t. CmdArguments t => t)

which may be run using

> runPriv :: CmdArguments t => Priv -> t
> runPriv (Priv x) = cmdArguments (arg ("sudo" :: String) ++ x)

and used like

*Main> shakeArgs shakeOptions (action (unit . runPriv $ Priv (cmd "ls" ".")))
# sudo (for <unknown>)
[sudo] password for sgf:
1.cfg 1.lhs app extra-deps LICENSE Setup.hs set-wakeup.cabal src stack.yaml test
Build completed in 0:06m

but neither `arg`, nor `cmdArguments` are exported. Instead of `arg` i may
just create `Either` by hand, but i can't go ahead without `cmdArguments`,
because once i've converted `CmdArguments t => t` to `[Either CmdOption
String]` i can no longer apply `cmd` to a list (to convert back to
`CmdArguments t => t`), because (i think) ghc can't determine the number of
elements in a list at compile time.

So, what is the proper way to run privileged commands with shake?

[1]: https://donsbot.files.wordpress.com/2009/01/semicolon.pdf

--
Dmitriy Matrosov

Neil Mitchell

unread,
Mar 13, 2017, 4:42:53 PM3/13/17
to sgf...@gmail.com, Shake build system
Hi Dmitriy,

I will say that I've written a lot of command line execution stuff via Shake, and never used the techniques suggested in the talk - I just don't see them as being that valuable... Of course, that's not to say you shouldn't be able to use them, or that they aren't valuable to you.

Part of the problem here is that the variable argument stuff that Shake takes advantage of just isn't that compositional. You might get further calling `command` and getting a `CmdResult` out at the end. The set of things exposed also isn't really how it should be - there's a ticket to expose more things: https://github.com/ndmitchell/shake/issues/514.

Finally, if I was going to go this route, and still rely on the variable argument stuff, I'd be tempted to make Priv defined as:

data Priv a = Priv a

Not quite sure the implications of that, but it might let you get away without erasing the inner t and then having to reinvent it.

Thanks, Neil

sgf...@gmail.com

unread,
Mar 15, 2017, 5:38:56 AM3/15/17
to Shake build system, sgf...@gmail.com
Thanks for the answer, Neil!

понедельник, 13 марта 2017 г., 23:42:53 UTC+3 пользователь Neil Mitchell написал:
>
> .... You might get further calling `command` and getting a `CmdResult` out at the end.

Ah, you're right, `command` make it work!

{-# LANGUAGE RankNTypes #-}

import Data.Either
import Development.Shake
import Development.Shake.Command

data Priv = Priv (forall t. CmdArguments t => t)

runPriv :: CmdResult r => Priv -> Action r
runPriv (Priv x) = case partitionEithers x of
(opts, ys@(_ : _)) -> command opts "sudo" ys
_ -> error "Err"

понедельник, 13 марта 2017 г., 23:42:53 UTC+3 пользователь Neil Mitchell написал:
>
.....
> Finally, if I was going to go this route, and still rely on the variable argument stuff, I'd be tempted to make Priv defined as:
>
>
> data Priv a = Priv a
>
>
> Not quite sure the implications of that, but it might let you get away without erasing the inner t and then having to reinvent it.
>

If i understand you correctly, you suggest to define `Priv` like:

data Priv3 a = Priv3 a

runPriv3 :: CmdResult r => Priv3 [Either CmdOption String] -> Action r
runPriv3 (Priv3 x) = case partitionEithers x of
(opts, ys@(_ : _)) -> command opts "sudo" ys
_ -> error "Err"

Hm, indeed, `forall` looks redundant now. Thanks!

--
Dmitriy Matrosov
Reply all
Reply to author
Forward
0 new messages