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