@cryptotaxi247 / kubo / commits / 7681b66fe

initial support for commands to use external binaries

License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>

Jeromy committed Nov 11, 2015 at 12:47 UTC 7681b66fee82b4a420d02c4a40fc7d8daf1aff7e
3 files changed +79 -1
commands/cli/parse.go
+7
@@ -204,6 +204,13 @@ func parseOpts(args []string, root *cmds.Command) (
204 if err != nil {
205 return
206 }
207 +
208 + // If we've come across an external binary call, pass all the remaining
209 + // arguments on to it
210 + if cmd.External {
211 + stringVals = append(stringVals, args[i+1:]...)
212 + return
213 + }
214 } else {
215 stringVals = append(stringVals, arg)
216 }
commands/command.go
+71
@@ -9,9 +9,12 @@ output to the user, including text, JSON, and XML marshallers.
9 package commands
10
11 import (
12 + "bytes"
13 "errors"
14 "fmt"
15 "io"
16 + "os"
17 + "os/exec"
18 "reflect"
19 "strings"
20
@@ -59,6 +62,10 @@ type Command struct {
62 Marshalers map[EncodingType]Marshaler
63 Helptext HelpText
64
65 + // External denotes that a command is actually an external binary.
66 + // fewer checks and validations will be performed on such commands.
67 + External bool
68 +
69 // Type describes the type of the output of the Command's Run Function.
70 // In precise terms, the value of Type is an instance of the return type of
71 // the Run Function.
@@ -262,3 +269,67 @@ func checkArgValue(v string, found bool, def Argument) error {
269 func ClientError(msg string) error {
270 return &Error{Code: ErrClient, Message: msg}
271 }
272 +
273 +func ExternalBinary() *Command {
274 + return &Command{
275 + Arguments: []Argument{
276 + StringArg("args", false, true, "arguments for subcommand"),
277 + },
278 + External: true,
279 + Run: func(req Request, res Response) {
280 + binname := strings.Join(append([]string{"ipfs"}, req.Path()...), "-")
281 + _, err := exec.LookPath(binname)
282 + if err != nil {
283 + // special case for '--help' on uninstalled binaries.
284 + if req.Arguments()[0] == "--help" {
285 + buf := new(bytes.Buffer)
286 + fmt.Fprintf(buf, "%s is an 'external' command.\n", binname)
287 + fmt.Fprintf(buf, "it does not currently appear to be installated.\n")
288 + fmt.Fprintf(buf, "please refer to the ipfs documentation for instructions\n")
289 + res.SetOutput(buf)
290 + return
291 + }
292 +
293 + res.SetError(fmt.Errorf("%s not installed."), ErrNormal)
294 + return
295 + }
296 +
297 + r, w := io.Pipe()
298 +
299 + cmd := exec.Command(binname, req.Arguments()...)
300 +
301 + // TODO: make commands lib be able to pass stdin through daemon
302 + //cmd.Stdin = req.Stdin()
303 + cmd.Stdin = io.LimitReader(nil, 0)
304 + cmd.Stdout = w
305 + cmd.Stderr = w
306 +
307 + // setup env of child program
308 + env := os.Environ()
309 +
310 + nd, err := req.InvocContext().GetNode()
311 + if err == nil {
312 + env = append(env, fmt.Sprintf("IPFS_ONLINE=%t", nd.OnlineMode()))
313 + }
314 +
315 + cmd.Env = env
316 +
317 + err = cmd.Start()
318 + if err != nil {
319 + res.SetError(fmt.Errorf("failed to start subcommand: %s", err), ErrNormal)
320 + return
321 + }
322 +
323 + res.SetOutput(r)
324 +
325 + go func() {
326 + err = cmd.Wait()
327 + if err != nil {
328 + res.SetError(err, ErrNormal)
329 + }
330 +
331 + w.Close()
332 + }()
333 + },
334 + }
335 +}
core/commands/root.go
+1 -1
@@ -111,7 +111,7 @@ var rootSubcommands = map[string]*cmds.Command{
111 "tar": TarCmd,
112 "tour": tourCmd,
113 "file": unixfs.UnixFSCmd,
114 - "update": UpdateCmd,
114 + "update": cmds.ExternalBinary(),
115 "version": VersionCmd,
116 "bitswap": BitswapCmd,
117 }