17840: Deduplicate flag-parsing code.
[arvados.git] / lib / mount / command.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4
5 package mount
6
7 import (
8         "flag"
9         "io"
10         "log"
11         "net/http"
12
13         // pprof is only imported to register its HTTP handlers
14         _ "net/http/pprof"
15         "os"
16
17         "git.arvados.org/arvados.git/lib/cmd"
18         "git.arvados.org/arvados.git/sdk/go/arvados"
19         "git.arvados.org/arvados.git/sdk/go/arvadosclient"
20         "git.arvados.org/arvados.git/sdk/go/keepclient"
21         "github.com/arvados/cgofuse/fuse"
22 )
23
24 var Command = &mountCommand{}
25
26 type mountCommand struct {
27         // ready, if non-nil, will be closed when the mount is
28         // initialized.  If ready is non-nil, it RunCommand() should
29         // not be called more than once, or when ready is already
30         // closed.
31         ready chan struct{}
32         // It is safe to call Unmount only after ready has been
33         // closed.
34         Unmount func() (ok bool)
35 }
36
37 // RunCommand implements the subcommand "mount <path> [fuse options]".
38 //
39 // The "-d" fuse option (and perhaps other features) ignores the
40 // stderr argument and prints to os.Stderr instead.
41 func (c *mountCommand) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
42         logger := log.New(stderr, prog+" ", 0)
43         flags := flag.NewFlagSet(prog, flag.ContinueOnError)
44         ro := flags.Bool("ro", false, "read-only")
45         experimental := flags.Bool("experimental", false, "acknowledge this is an experimental command, and should not be used in production (required)")
46         blockCache := flags.Int("block-cache", 4, "read cache size (number of 64MiB blocks)")
47         pprof := flags.String("pprof", "", "serve Go profile data at `[addr]:port`")
48         if ok, code := cmd.ParseFlags(flags, prog, args, "[FUSE mount options]", stderr); !ok {
49                 return code
50         }
51         if !*experimental {
52                 logger.Printf("error: experimental command %q used without --experimental flag", prog)
53                 return 2
54         }
55         if *pprof != "" {
56                 go func() {
57                         log.Println(http.ListenAndServe(*pprof, nil))
58                 }()
59         }
60
61         client := arvados.NewClientFromEnv()
62         ac, err := arvadosclient.New(client)
63         if err != nil {
64                 logger.Print(err)
65                 return 1
66         }
67         kc, err := keepclient.MakeKeepClient(ac)
68         if err != nil {
69                 logger.Print(err)
70                 return 1
71         }
72         kc.BlockCache = &keepclient.BlockCache{MaxBlocks: *blockCache}
73         host := fuse.NewFileSystemHost(&keepFS{
74                 Client:     client,
75                 KeepClient: kc,
76                 ReadOnly:   *ro,
77                 Uid:        os.Getuid(),
78                 Gid:        os.Getgid(),
79                 ready:      c.ready,
80         })
81         c.Unmount = host.Unmount
82         ok := host.Mount("", flags.Args())
83         if !ok {
84                 return 1
85         }
86         return 0
87 }