1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
package main
import (
"flag"
"fmt"
"os"
"os/signal"
"path/filepath"
"syscall"
)
// =============================================================================
// hosty fs-serve — mount _hosty_fs as a FUSE filesystem (companion unit)
// =============================================================================
func cmdFSServe(args []string) error {
fset := flag.NewFlagSet("fs-serve", flag.ExitOnError)
fset.Usage = func() {
fmt.Print(`Usage: hosty fs-serve -name NAME [-state-dir DIR] [-fs-dir DIR] [-allow-other]
Mount the _hosty_fs table from data.hosty as a FUSE filesystem at
<stateDir>/<name>/fs/ (or -fs-dir, if given). Blocks until unmounted or killed.
This command is intended to be run as a companion systemd unit generated
by 'hosty start'. The HOSTY_FS environment variable is set to the mount
point by the app service unit.
-fs-dir overrides the mount point. System mode uses a RuntimeDirectory= under
/run so the path is stable for DynamicUser services (StateDirectory= would be
relocated to /var/lib/private and break the shared path).
-allow-other lifts FUSE's default same-uid-only access restriction, needed when
the app runs as a different user (DynamicUser) than this mount. Requires
'user_allow_other' in /etc/fuse.conf.
Options:
`)
fset.PrintDefaults()
}
name := fset.String("name", "", "App name (required)")
stateDir := fset.String("state-dir", "", "State directory (default: $XDG_STATE_HOME/hosty)")
fsDirFlag := fset.String("fs-dir", "", "Mount point (default: <state-dir>/<name>/fs)")
allowOther := fset.Bool("allow-other", false, "Allow other users to access the mount (FUSE allow_other)")
if err := fset.Parse(args); err != nil {
return err
}
if *name == "" {
fset.Usage()
return fmt.Errorf("-name is required")
}
// -state-dir is supplied by the generated unit, so a relative value here
// means hosty produced a bad unit; fail loudly rather than mounting the
// filesystem at a path that depends on the process's working directory.
sd, err := resolveStateDir(*stateDir, false)
if err != nil {
return err
}
dataPath := filepath.Join(sd, *name, "data.hosty")
fsDir := *fsDirFlag
if fsDir == "" {
fsDir = filepath.Join(sd, *name, "fs")
}
if !fileExists(dataPath) {
return fmt.Errorf("no data.hosty found at %s — is %q installed?", dataPath, *name)
}
if err := os.MkdirAll(fsDir, 0755); err != nil {
return fmt.Errorf("creating fs mount dir: %w", err)
}
fmt.Fprintf(os.Stderr, "hosty fs-serve: mounting _hosty_fs at %s\n", fsDir)
server, err := mountHostyFSOpts(dataPath, fsDir, *allowOther)
if err != nil {
return fmt.Errorf("mounting hosty fs: %w", err)
}
// Handle signals for clean unmount.
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigs
fmt.Fprintf(os.Stderr, "hosty fs-serve: unmounting %s\n", fsDir)
_ = server.Unmount()
}()
server.Wait()
fmt.Fprintf(os.Stderr, "hosty fs-serve: unmounted\n")
return nil
}
|