@cryptotaxi247 / kubo / commits / 7ae3706b6

fuse: no longer require fuse to compile ipfs

This commit removes the dependency on go-fuse-version, and thus the fuse headers. It also introduces an elaborate troubleshooting process that diagnoses whether fuse installed -- and which version -- with as little requirements as possible (attept to use sysctl, fall-back on the go-fuse-version binary, etc). It then nicely instructs the user what to do next.

Juan Batiz-Benet committed Jan 28, 2015 at 05:18 UTC 7ae3706b6d8c13e61b2f829e7296fc471401ce2d
16 files changed +326 -72
Godeps/Godeps.json
+1 -1
@@ -147,7 +147,7 @@
147 },
148 {
149 "ImportPath": "github.com/jbenet/go-fuse-version",
150 - "Rev": "c723f93ceeb1d1e21eb7fe6fd39aa21a9fe7db99"
150 + "Rev": "b733dfc0597e1f6780510ee7afad8b6e3c7af3eb"
151 },
152 {
153 "ImportPath": "github.com/jbenet/go-is-domain",
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/README.md
+5 -5
@@ -32,14 +32,14 @@ func main() {
32 }
33 ```
34
35 -## fuseprint
35 +## fuse-print
36
37 -If you dont use Go, you can also install the example as the silly util fuseprint:
37 +If you dont use Go, you can also install the example as the silly util `fuse-version`:
38
39 ```
40 -> go get github.com/jbenet/go-fuse-version/fuseprint
41 -> go install github.com/jbenet/go-fuse-version/fuseprint
42 -> fuseprint
40 +> go get github.com/jbenet/go-fuse-version/fuse-version
41 +> go install github.com/jbenet/go-fuse-version/fuse-version
42 +> fuse-version
43 FuseVersion, AgentVersion, Agent
44 27, 2.7.2, OSXFUSE
45 ```
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/fuse-version/.gitignore new
+1
@@ -0,0 +1 @@
1 +fuse-version
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/fuse-version/README.md renamed
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/fuse-version/index.go new
+106
@@ -0,0 +1,106 @@
1 +package main
2 +
3 +import (
4 + "bytes"
5 + "flag"
6 + "fmt"
7 + "os"
8 +
9 + fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
10 +)
11 +
12 +// flags
13 +var (
14 + flagSystem string
15 + flagOnly string
16 + flagQuiet bool
17 +)
18 +
19 +var usage = `usage: %s [flags]
20 +print fuse and fuse agent versions
21 +`
22 +
23 +func init() {
24 + flag.Usage = func() {
25 + fmt.Fprintf(os.Stderr, usage, os.Args[0])
26 + flag.PrintDefaults()
27 + }
28 +
29 + flag.StringVar(&flagSystem, "s", "", "show only one system (e.g. OSXFUSE)")
30 + flag.StringVar(&flagOnly, "only", "", "show one of {fuse, agent, agent-name}")
31 + flag.BoolVar(&flagQuiet, "q", false, "quiet output, no newline (use with --only)")
32 +}
33 +
34 +func main() {
35 + flag.Parse()
36 + sys, err := fuseversion.LocalFuseSystems()
37 + if err != nil {
38 + fmt.Fprintf(os.Stderr, "%s\n", err)
39 + os.Exit(1)
40 + }
41 +
42 + all := flagSystem == ""
43 +
44 + // if user specified a system and we dont have it, error out.
45 + if !all {
46 + checkExists(sys, flagSystem)
47 + }
48 +
49 + var buf bytes.Buffer
50 + for name, s := range sys {
51 + if !all && flagSystem != name {
52 + continue
53 + }
54 +
55 + switch flagOnly {
56 + case "fuse":
57 + fmt.Fprintf(&buf, PartString(name, "FuseVersion", s.FuseVersion))
58 + case "agent":
59 + fmt.Fprintf(&buf, PartString(name, "AgentVersion", s.AgentVersion))
60 + case "agent-name":
61 + fmt.Fprintf(&buf, PartString(name, "AgentName", s.AgentName))
62 + default:
63 + fmt.Fprintf(&buf, SystemString(name, s))
64 + }
65 +
66 + if all && flagQuiet { // if all & quiet, need to break between systems
67 + fmt.Fprintf(&buf, "\n")
68 + }
69 + }
70 +
71 + out := buf.Bytes()
72 + if flagQuiet {
73 + out = bytes.TrimSpace(out)
74 + }
75 + os.Stdout.Write(out)
76 +}
77 +
78 +func checkExists(all fuseversion.Systems, name string) {
79 +
80 + if _, found := all[name]; found {
81 + return
82 + }
83 +
84 + if !flagQuiet {
85 + fmt.Fprintf(os.Stderr, "error: %s system not found.\nHave: ")
86 + for name := range all {
87 + fmt.Fprintf(os.Stderr, "%s ", name)
88 + }
89 + fmt.Fprintf(os.Stderr, "\n")
90 + }
91 + os.Exit(1)
92 +}
93 +
94 +func SystemString(name string, sys fuseversion.FuseSystem) (s string) {
95 + s += PartString(name, "FuseVersion", sys.FuseVersion)
96 + s += PartString(name, "AgentVersion", sys.AgentVersion)
97 + s += PartString(name, "AgentName", sys.AgentName)
98 + return s
99 +}
100 +
101 +func PartString(sysname, partname, part string) string {
102 + if !flagQuiet {
103 + return fmt.Sprintf("%s.%s: %s\n", sysname, partname, part)
104 + }
105 + return part + "\t"
106 +}
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/fuseprint/.gitignore deleted
-1
@@ -1 +0,0 @@
1 -fuseprint
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/fuseprint/index.go deleted
-21
@@ -1,21 +0,0 @@
1 -package main
2 -
3 -import (
4 - "fmt"
5 - "os"
6 -
7 - fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
8 -)
9 -
10 -func main() {
11 - sys, err := fuseversion.LocalFuseSystems()
12 - if err != nil {
13 - fmt.Fprintf(os.Stderr, "%s\n", err)
14 - os.Exit(1)
15 - }
16 -
17 - fmt.Printf("FuseVersion, AgentVersion, Agent\n")
18 - for _, s := range *sys {
19 - fmt.Printf("%s, %s, %s\n", s.FuseVersion, s.AgentVersion, s.AgentName)
20 - }
21 -}
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/version.go
+1 -1
@@ -29,7 +29,7 @@ type FuseSystem struct {
29 // // Outputs:
30 // // OSXFUSE, , 2.7.2
31 //
32 -func LocalFuseSystems() (*Systems, error) {
32 +func LocalFuseSystems() (Systems, error) {
33 return getLocalFuseSystems() // implemented by each platform
34 }
35
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/version_bsd.go
+1 -1
@@ -7,6 +7,6 @@ import (
7 "runtime"
8 )
9
10 -func getLocalFuseSystems() (*Systems, error) {
10 +func getLocalFuseSystems() (Systems, error) {
11 return nil, fmt.Errorf(notImplYet, runtime.GOARCH)
12 }
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/version_darwin.go
+3 -3
@@ -9,10 +9,10 @@ package fuseversion
9 import "C"
10 import "fmt"
11
12 -func getLocalFuseSystems() (*Systems, error) {
13 - sys := Systems{}
12 +func getLocalFuseSystems() (Systems, error) {
13 + sys := make(Systems)
14 sys["OSXFUSE"] = getOSXFUSE()
15 - return &sys, nil
15 + return sys, nil
16 }
17
18 func getOSXFUSE() FuseSystem {
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/version_linux.go
+1 -1
@@ -5,6 +5,6 @@ import (
5 "runtime"
6 )
7
8 -func getLocalFuseSystems() (*Systems, error) {
8 +func getLocalFuseSystems() (Systems, error) {
9 return nil, fmt.Errorf(notImplYet, runtime.GOARCH)
10 }
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/version_windows.go
+1 -1
@@ -5,6 +5,6 @@ import (
5 "runtime"
6 )
7
8 -func getLocalFuseSystems() (*Systems, error) {
8 +func getLocalFuseSystems() (Systems, error) {
9 return nil, fmt.Errorf(notImplYet, runtime.GOARCH)
10 }
core/commands/internal/incfusever/incfusever.go new
+13
@@ -0,0 +1,13 @@
1 +// Package incfusever is only here to prevent go src tools (like godep)
2 +// from thinking fuseversion is not a required package. Though we do not
3 +// actually use github.com/jbenet/go-fuse-version as a library, we
4 +// _may_ need its binary. We avoid it as much as possible as compiling
5 +// it _requires_ fuse headers. Users must be able to install go-ipfs
6 +// without also installing fuse.
7 +package incfusever
8 +
9 +import (
10 + fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
11 +)
12 +
13 +var _ = fuseversion.LocalFuseSystems
core/commands/mount.go deleted
-9
@@ -1,9 +0,0 @@
1 -package commands
2 -
3 -import (
4 - fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
5 -)
6 -
7 -// this file is only here to prevent go src tools (like godep) from
8 -// thinking fuseversion is not a required package by non-darwin archs.
9 -var _ = fuseversion.LocalFuseSystems
core/commands/mount_darwin.go
+186 -23
@@ -1,12 +1,14 @@
1 package commands
2
3 import (
4 + "bytes"
5 "fmt"
6 + "os/exec"
7 "runtime"
8 "strings"
9 "syscall"
10
9 - fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
11 + core "github.com/jbenet/go-ipfs/core"
12 )
13
14 func init() {
@@ -14,19 +16,122 @@ func init() {
16 platformFuseChecks = darwinFuseCheckVersion
17 }
18
17 -func darwinFuseCheckVersion() error {
19 +// dontCheckOSXFUSEConfigKey is a key used to let the user tell us to
20 +// skip fuse checks.
21 +var dontCheckOSXFUSEConfigKey = "DontCheckOSXFUSE"
22 +
23 +// fuseVersionPkg is the go pkg url for fuse-version
24 +var fuseVersionPkg = "github.com/jbenet/go-fuse-version/fuse-version"
25 +
26 +// errStrFuseRequired is returned when we're sure the user does not have fuse.
27 +var errStrFuseRequired = `OSXFUSE not found.
28 +
29 +OSXFUSE is required to mount, please install it.
30 +Note: version 2.7.2 or higher required; prior versions are known to kernel panic!
31 +It is recommended you install it from the OSXFUSE website:
32 +
33 + http://osxfuse.github.io/
34 +
35 +For more help, see:
36 +
37 + https://github.com/jbenet/go-ipfs/issues/177
38 +`
39 +
40 +// errStrNoFuseHeaders is included in the output of `go get <fuseVersionPkg>` if there
41 +// are no fuse headers. this means they dont have OSXFUSE installed.
42 +var errStrNoFuseHeaders = "no such file or directory: '/usr/local/lib/libosxfuse.dylib'"
43 +
44 +var errStrUpgradeFuse = `OSXFUSE version %s not supported.
45 +
46 +OSXFUSE versions <2.7.2 are known to cause kernel panics!
47 +Please upgrade to the latest OSXFUSE version.
48 +It is recommended you install it from the OSXFUSE website:
49 +
50 + http://osxfuse.github.io/
51 +
52 +For more help, see:
53 +
54 + https://github.com/jbenet/go-ipfs/issues/177
55 +`
56 +
57 +var errStrNeedFuseVersion = `unable to check fuse version.
58 +
59 +Dear User,
60 +
61 +Before mounting, we must check your version of OSXFUSE. We are protecting
62 +you from a nasty kernel panic we found in OSXFUSE versions <2.7.2.[1]. To
63 +make matters worse, it's harder than it should be to check whether you have
64 +the right version installed...[2]. We've automated the process with the
65 +help of a little tool. We tried to install it, but something went wrong[3].
66 +Please install it yourself by running:
67 +
68 + go get %s
69 +
70 +You can also stop ipfs from running these checks and use whatever OSXFUSE
71 +version you have by running:
72 +
73 + ipfs config %s true
74 +
75 +[1]: https://github.com/jbenet/go-ipfs/issues/177
76 +[2]: https://github.com/jbenet/go-ipfs/pull/533
77 +[3]: %s
78 +`
79 +
80 +var errStrFailedToRunFuseVersion = `unable to check fuse version.
81 +
82 +Dear User,
83 +
84 +Before mounting, we must check your version of OSXFUSE. We are protecting
85 +you from a nasty kernel panic we found in OSXFUSE versions <2.7.2.[1]. To
86 +make matters worse, it's harder than it should be to check whether you have
87 +the right version installed...[2]. We've automated the process with the
88 +help of a little tool. We tried to run it, but something went wrong[3].
89 +Please, try to run it yourself with:
90 +
91 + go get %s
92 + fuse-version
93 +
94 +You should see something like this:
95 +
96 + > fuse-version
97 + fuse-version -only agent
98 + OSXFUSE.AgentVersion: 2.7.3
99 +
100 +Just make sure the number is 2.7.2 or higher. You can then stop ipfs from
101 +trying to run these checks with:
102 +
103 + ipfs config %s true
104 +
105 +[1]: https://github.com/jbenet/go-ipfs/issues/177
106 +[2]: https://github.com/jbenet/go-ipfs/pull/533
107 +[3]: %s
108 +`
109 +
110 +var errStrFixConfig = `config key invalid: %s %s
111 +You may be able to get this error to go away by setting it again:
112 +
113 + ipfs config %s true
114 +
115 +Either way, please tell us at: http://github.com/jbenet/go-ipfs/issues
116 +`
117 +
118 +func darwinFuseCheckVersion(node *core.IpfsNode) error {
119 // on OSX, check FUSE version.
120 if runtime.GOOS != "darwin" {
121 return nil
122 }
123
23 - ov, err := tryGFV()
24 - if err != nil {
25 - log.Debug(err)
26 - ov, err = trySysctl()
27 - if err != nil {
28 - log.Debug(err)
29 - return fmt.Errorf("cannot determine osxfuse version. is it installed?")
124 + ov, errGFV := tryGFV()
125 + if errGFV != nil {
126 + // if we failed AND the user has told us to ignore the check we
127 + // continue. this is in case fuse-version breaks or the user cannot
128 + // install it, but is sure their fuse version will work.
129 + if skip, err := userAskedToSkipFuseCheck(node); err != nil {
130 + return err
131 + } else if skip {
132 + return nil // user told us not to check version... ok....
133 + } else {
134 + return errGFV
135 }
136 }
137
@@ -35,25 +140,18 @@ func darwinFuseCheckVersion() error {
140 return nil
141 }
142
38 - return fmt.Errorf("osxfuse version %s not supported.\n%s\n%s", ov,
39 - "Older versions of osxfuse have kernel panic bugs; please upgrade!",
40 - "https://github.com/jbenet/go-ipfs/issues/177")
143 + return fmt.Errorf(errStrUpgradeFuse, ov)
144 }
145
146 func tryGFV() (string, error) {
44 - sys, err := fuseversion.LocalFuseSystems()
45 - if err != nil {
46 - log.Debug("mount: fuseversion:", "failed")
47 - return "", err
48 - }
49 -
50 - for _, s := range *sys {
51 - v := s.AgentVersion
52 - log.Debug("mount: fuseversion:", v)
53 - return v, nil
147 + // first try sysctl. it may work!
148 + ov, err := trySysctl()
149 + if err == nil {
150 + return ov, nil
151 }
152 + log.Debug(err)
153
56 - return "", fmt.Errorf("fuseversion: no system found")
154 + return tryGFVFromFuseVersion()
155 }
156
157 func trySysctl() (string, error) {
@@ -65,3 +163,68 @@ func trySysctl() (string, error) {
163 log.Debug("mount: sysctl osxfuse.version.number:", v)
164 return v, nil
165 }
166 +
167 +func tryGFVFromFuseVersion() (string, error) {
168 + if err := ensureFuseVersionIsInstalled(); err != nil {
169 + return "", err
170 + }
171 +
172 + cmd := exec.Command("fuse-version", "-q", "-only", "agent", "-s", "OSXFUSE")
173 + var out bytes.Buffer
174 + cmd.Stdout = &out
175 + if err := cmd.Run(); err != nil {
176 + return "", fmt.Errorf(errStrFailedToRunFuseVersion, fuseVersionPkg, dontCheckOSXFUSEConfigKey, err)
177 + }
178 +
179 + return out.String(), nil
180 +}
181 +
182 +func ensureFuseVersionIsInstalled() error {
183 + // see if fuse-version is there
184 + if _, err := exec.LookPath("fuse-version"); err == nil {
185 + return nil // got it!
186 + }
187 +
188 + // try installing it...
189 + log.Debug("fuse-version: no fuse-version. attempting to install.")
190 + cmd := exec.Command("go", "get", "github.com/jbenet/go-fuse-version/fuse-version")
191 + var cmdout bytes.Buffer
192 + cmd.Stdout = &cmdout
193 + cmd.Stderr = &cmdout
194 + if err := cmd.Run(); err != nil {
195 + // Ok, install fuse-version failed. is it they dont have fuse?
196 + cmdoutstr := cmdout.String()
197 + if strings.Contains(cmdoutstr, errStrNoFuseHeaders) {
198 + // yes! it is! they dont have fuse!
199 + return fmt.Errorf(errStrFuseRequired)
200 + }
201 +
202 + log.Debug("fuse-version: failed to install.")
203 + s := err.Error() + "\n" + cmdoutstr
204 + return fmt.Errorf(errStrNeedFuseVersion, fuseVersionPkg, dontCheckOSXFUSEConfigKey, s)
205 + }
206 +
207 + // ok, try again...
208 + if _, err := exec.LookPath("fuse-version"); err != nil {
209 + log.Debug("fuse-version: failed to install?")
210 + return fmt.Errorf(errStrNeedFuseVersion, fuseVersionPkg, dontCheckOSXFUSEConfigKey, err)
211 + }
212 +
213 + log.Debug("fuse-version: install success")
214 + return nil
215 +}
216 +
217 +func userAskedToSkipFuseCheck(node *core.IpfsNode) (skip bool, err error) {
218 + val, err := node.Repo.GetConfigKey(dontCheckOSXFUSEConfigKey)
219 + if err != nil {
220 + return false, nil // failed to get config value. dont skip check.
221 + }
222 + s, ok := val.(string)
223 + if !ok {
224 + // got config value, but it's invalid... dont skip check, ask the user to fix it...
225 + return false, fmt.Errorf(errStrFixConfig, dontCheckOSXFUSEConfigKey, val,
226 + dontCheckOSXFUSEConfigKey)
227 + }
228 + // only "true" counts as telling us to skip.
229 + return s == "true", nil
230 +}
core/commands/mount_unix.go
+7 -5
@@ -23,6 +23,12 @@ const mountTimeout = time.Second
23 // fuseNoDirectory used to check the returning fuse error
24 const fuseNoDirectory = "fusermount: failed to access mountpoint"
25
26 +// platformFuseChecks can get overridden by arch-specific files
27 +// to run fuse checks (like checking the OSXFUSE version)
28 +var platformFuseChecks = func(*core.IpfsNode) error {
29 + return nil
30 +}
31 +
32 var MountCmd = &cmds.Command{
33 Helptext: cmds.HelpText{
34 Tagline: "Mounts IPFS to the filesystem (read-only)",
@@ -161,7 +167,7 @@ func Mount(node *core.IpfsNode, fsdir, nsdir string) error {
167 node.Mounts.Ipns.Unmount()
168 }
169
164 - if err := platformFuseChecks(); err != nil {
170 + if err := platformFuseChecks(node); err != nil {
171 return err
172 }
173
@@ -173,10 +179,6 @@ func Mount(node *core.IpfsNode, fsdir, nsdir string) error {
179 return nil
180 }
181
176 -var platformFuseChecks = func() error {
177 - return nil
178 -}
179 -
182 func doMount(node *core.IpfsNode, fsdir, nsdir string) error {
183 fmtFuseErr := func(err error) error {
184 s := err.Error()