提交 7ae3706b 作者: Juan Batiz-Benet

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.
上级 56b14d8e
......@@ -147,7 +147,7 @@
},
{
"ImportPath": "github.com/jbenet/go-fuse-version",
"Rev": "c723f93ceeb1d1e21eb7fe6fd39aa21a9fe7db99"
"Rev": "b733dfc0597e1f6780510ee7afad8b6e3c7af3eb"
},
{
"ImportPath": "github.com/jbenet/go-is-domain",
......
......@@ -32,14 +32,14 @@ func main() {
}
```
## fuseprint
## fuse-print
If you dont use Go, you can also install the example as the silly util fuseprint:
If you dont use Go, you can also install the example as the silly util `fuse-version`:
```
> go get github.com/jbenet/go-fuse-version/fuseprint
> go install github.com/jbenet/go-fuse-version/fuseprint
> fuseprint
> go get github.com/jbenet/go-fuse-version/fuse-version
> go install github.com/jbenet/go-fuse-version/fuse-version
> fuse-version
FuseVersion, AgentVersion, Agent
27, 2.7.2, OSXFUSE
```
package main
import (
"bytes"
"flag"
"fmt"
"os"
fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
)
// flags
var (
flagSystem string
flagOnly string
flagQuiet bool
)
var usage = `usage: %s [flags]
print fuse and fuse agent versions
`
func init() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, usage, os.Args[0])
flag.PrintDefaults()
}
flag.StringVar(&flagSystem, "s", "", "show only one system (e.g. OSXFUSE)")
flag.StringVar(&flagOnly, "only", "", "show one of {fuse, agent, agent-name}")
flag.BoolVar(&flagQuiet, "q", false, "quiet output, no newline (use with --only)")
}
func main() {
flag.Parse()
sys, err := fuseversion.LocalFuseSystems()
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
all := flagSystem == ""
// if user specified a system and we dont have it, error out.
if !all {
checkExists(sys, flagSystem)
}
var buf bytes.Buffer
for name, s := range sys {
if !all && flagSystem != name {
continue
}
switch flagOnly {
case "fuse":
fmt.Fprintf(&buf, PartString(name, "FuseVersion", s.FuseVersion))
case "agent":
fmt.Fprintf(&buf, PartString(name, "AgentVersion", s.AgentVersion))
case "agent-name":
fmt.Fprintf(&buf, PartString(name, "AgentName", s.AgentName))
default:
fmt.Fprintf(&buf, SystemString(name, s))
}
if all && flagQuiet { // if all & quiet, need to break between systems
fmt.Fprintf(&buf, "\n")
}
}
out := buf.Bytes()
if flagQuiet {
out = bytes.TrimSpace(out)
}
os.Stdout.Write(out)
}
func checkExists(all fuseversion.Systems, name string) {
if _, found := all[name]; found {
return
}
if !flagQuiet {
fmt.Fprintf(os.Stderr, "error: %s system not found.\nHave: ")
for name := range all {
fmt.Fprintf(os.Stderr, "%s ", name)
}
fmt.Fprintf(os.Stderr, "\n")
}
os.Exit(1)
}
func SystemString(name string, sys fuseversion.FuseSystem) (s string) {
s += PartString(name, "FuseVersion", sys.FuseVersion)
s += PartString(name, "AgentVersion", sys.AgentVersion)
s += PartString(name, "AgentName", sys.AgentName)
return s
}
func PartString(sysname, partname, part string) string {
if !flagQuiet {
return fmt.Sprintf("%s.%s: %s\n", sysname, partname, part)
}
return part + "\t"
}
package main
import (
"fmt"
"os"
fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
)
func main() {
sys, err := fuseversion.LocalFuseSystems()
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
fmt.Printf("FuseVersion, AgentVersion, Agent\n")
for _, s := range *sys {
fmt.Printf("%s, %s, %s\n", s.FuseVersion, s.AgentVersion, s.AgentName)
}
}
......@@ -29,7 +29,7 @@ type FuseSystem struct {
// // Outputs:
// // OSXFUSE, , 2.7.2
//
func LocalFuseSystems() (*Systems, error) {
func LocalFuseSystems() (Systems, error) {
return getLocalFuseSystems() // implemented by each platform
}
......
......@@ -7,6 +7,6 @@ import (
"runtime"
)
func getLocalFuseSystems() (*Systems, error) {
func getLocalFuseSystems() (Systems, error) {
return nil, fmt.Errorf(notImplYet, runtime.GOARCH)
}
......@@ -9,10 +9,10 @@ package fuseversion
import "C"
import "fmt"
func getLocalFuseSystems() (*Systems, error) {
sys := Systems{}
func getLocalFuseSystems() (Systems, error) {
sys := make(Systems)
sys["OSXFUSE"] = getOSXFUSE()
return &sys, nil
return sys, nil
}
func getOSXFUSE() FuseSystem {
......
......@@ -5,6 +5,6 @@ import (
"runtime"
)
func getLocalFuseSystems() (*Systems, error) {
func getLocalFuseSystems() (Systems, error) {
return nil, fmt.Errorf(notImplYet, runtime.GOARCH)
}
......@@ -5,6 +5,6 @@ import (
"runtime"
)
func getLocalFuseSystems() (*Systems, error) {
func getLocalFuseSystems() (Systems, error) {
return nil, fmt.Errorf(notImplYet, runtime.GOARCH)
}
package commands
// Package incfusever is only here to prevent go src tools (like godep)
// from thinking fuseversion is not a required package. Though we do not
// actually use github.com/jbenet/go-fuse-version as a library, we
// _may_ need its binary. We avoid it as much as possible as compiling
// it _requires_ fuse headers. Users must be able to install go-ipfs
// without also installing fuse.
package incfusever
import (
fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
)
// this file is only here to prevent go src tools (like godep) from
// thinking fuseversion is not a required package by non-darwin archs.
var _ = fuseversion.LocalFuseSystems
package commands
import (
"bytes"
"fmt"
"os/exec"
"runtime"
"strings"
"syscall"
fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
core "github.com/jbenet/go-ipfs/core"
)
func init() {
......@@ -14,19 +16,122 @@ func init() {
platformFuseChecks = darwinFuseCheckVersion
}
func darwinFuseCheckVersion() error {
// dontCheckOSXFUSEConfigKey is a key used to let the user tell us to
// skip fuse checks.
var dontCheckOSXFUSEConfigKey = "DontCheckOSXFUSE"
// fuseVersionPkg is the go pkg url for fuse-version
var fuseVersionPkg = "github.com/jbenet/go-fuse-version/fuse-version"
// errStrFuseRequired is returned when we're sure the user does not have fuse.
var errStrFuseRequired = `OSXFUSE not found.
OSXFUSE is required to mount, please install it.
Note: version 2.7.2 or higher required; prior versions are known to kernel panic!
It is recommended you install it from the OSXFUSE website:
http://osxfuse.github.io/
For more help, see:
https://github.com/jbenet/go-ipfs/issues/177
`
// errStrNoFuseHeaders is included in the output of `go get <fuseVersionPkg>` if there
// are no fuse headers. this means they dont have OSXFUSE installed.
var errStrNoFuseHeaders = "no such file or directory: '/usr/local/lib/libosxfuse.dylib'"
var errStrUpgradeFuse = `OSXFUSE version %s not supported.
OSXFUSE versions <2.7.2 are known to cause kernel panics!
Please upgrade to the latest OSXFUSE version.
It is recommended you install it from the OSXFUSE website:
http://osxfuse.github.io/
For more help, see:
https://github.com/jbenet/go-ipfs/issues/177
`
var errStrNeedFuseVersion = `unable to check fuse version.
Dear User,
Before mounting, we must check your version of OSXFUSE. We are protecting
you from a nasty kernel panic we found in OSXFUSE versions <2.7.2.[1]. To
make matters worse, it's harder than it should be to check whether you have
the right version installed...[2]. We've automated the process with the
help of a little tool. We tried to install it, but something went wrong[3].
Please install it yourself by running:
go get %s
You can also stop ipfs from running these checks and use whatever OSXFUSE
version you have by running:
ipfs config %s true
[1]: https://github.com/jbenet/go-ipfs/issues/177
[2]: https://github.com/jbenet/go-ipfs/pull/533
[3]: %s
`
var errStrFailedToRunFuseVersion = `unable to check fuse version.
Dear User,
Before mounting, we must check your version of OSXFUSE. We are protecting
you from a nasty kernel panic we found in OSXFUSE versions <2.7.2.[1]. To
make matters worse, it's harder than it should be to check whether you have
the right version installed...[2]. We've automated the process with the
help of a little tool. We tried to run it, but something went wrong[3].
Please, try to run it yourself with:
go get %s
fuse-version
You should see something like this:
> fuse-version
fuse-version -only agent
OSXFUSE.AgentVersion: 2.7.3
Just make sure the number is 2.7.2 or higher. You can then stop ipfs from
trying to run these checks with:
ipfs config %s true
[1]: https://github.com/jbenet/go-ipfs/issues/177
[2]: https://github.com/jbenet/go-ipfs/pull/533
[3]: %s
`
var errStrFixConfig = `config key invalid: %s %s
You may be able to get this error to go away by setting it again:
ipfs config %s true
Either way, please tell us at: http://github.com/jbenet/go-ipfs/issues
`
func darwinFuseCheckVersion(node *core.IpfsNode) error {
// on OSX, check FUSE version.
if runtime.GOOS != "darwin" {
return nil
}
ov, err := tryGFV()
if err != nil {
log.Debug(err)
ov, err = trySysctl()
if err != nil {
log.Debug(err)
return fmt.Errorf("cannot determine osxfuse version. is it installed?")
ov, errGFV := tryGFV()
if errGFV != nil {
// if we failed AND the user has told us to ignore the check we
// continue. this is in case fuse-version breaks or the user cannot
// install it, but is sure their fuse version will work.
if skip, err := userAskedToSkipFuseCheck(node); err != nil {
return err
} else if skip {
return nil // user told us not to check version... ok....
} else {
return errGFV
}
}
......@@ -35,25 +140,18 @@ func darwinFuseCheckVersion() error {
return nil
}
return fmt.Errorf("osxfuse version %s not supported.\n%s\n%s", ov,
"Older versions of osxfuse have kernel panic bugs; please upgrade!",
"https://github.com/jbenet/go-ipfs/issues/177")
return fmt.Errorf(errStrUpgradeFuse, ov)
}
func tryGFV() (string, error) {
sys, err := fuseversion.LocalFuseSystems()
if err != nil {
log.Debug("mount: fuseversion:", "failed")
return "", err
}
for _, s := range *sys {
v := s.AgentVersion
log.Debug("mount: fuseversion:", v)
return v, nil
// first try sysctl. it may work!
ov, err := trySysctl()
if err == nil {
return ov, nil
}
log.Debug(err)
return "", fmt.Errorf("fuseversion: no system found")
return tryGFVFromFuseVersion()
}
func trySysctl() (string, error) {
......@@ -65,3 +163,68 @@ func trySysctl() (string, error) {
log.Debug("mount: sysctl osxfuse.version.number:", v)
return v, nil
}
func tryGFVFromFuseVersion() (string, error) {
if err := ensureFuseVersionIsInstalled(); err != nil {
return "", err
}
cmd := exec.Command("fuse-version", "-q", "-only", "agent", "-s", "OSXFUSE")
var out bytes.Buffer
cmd.Stdout = &out
if err := cmd.Run(); err != nil {
return "", fmt.Errorf(errStrFailedToRunFuseVersion, fuseVersionPkg, dontCheckOSXFUSEConfigKey, err)
}
return out.String(), nil
}
func ensureFuseVersionIsInstalled() error {
// see if fuse-version is there
if _, err := exec.LookPath("fuse-version"); err == nil {
return nil // got it!
}
// try installing it...
log.Debug("fuse-version: no fuse-version. attempting to install.")
cmd := exec.Command("go", "get", "github.com/jbenet/go-fuse-version/fuse-version")
var cmdout bytes.Buffer
cmd.Stdout = &cmdout
cmd.Stderr = &cmdout
if err := cmd.Run(); err != nil {
// Ok, install fuse-version failed. is it they dont have fuse?
cmdoutstr := cmdout.String()
if strings.Contains(cmdoutstr, errStrNoFuseHeaders) {
// yes! it is! they dont have fuse!
return fmt.Errorf(errStrFuseRequired)
}
log.Debug("fuse-version: failed to install.")
s := err.Error() + "\n" + cmdoutstr
return fmt.Errorf(errStrNeedFuseVersion, fuseVersionPkg, dontCheckOSXFUSEConfigKey, s)
}
// ok, try again...
if _, err := exec.LookPath("fuse-version"); err != nil {
log.Debug("fuse-version: failed to install?")
return fmt.Errorf(errStrNeedFuseVersion, fuseVersionPkg, dontCheckOSXFUSEConfigKey, err)
}
log.Debug("fuse-version: install success")
return nil
}
func userAskedToSkipFuseCheck(node *core.IpfsNode) (skip bool, err error) {
val, err := node.Repo.GetConfigKey(dontCheckOSXFUSEConfigKey)
if err != nil {
return false, nil // failed to get config value. dont skip check.
}
s, ok := val.(string)
if !ok {
// got config value, but it's invalid... dont skip check, ask the user to fix it...
return false, fmt.Errorf(errStrFixConfig, dontCheckOSXFUSEConfigKey, val,
dontCheckOSXFUSEConfigKey)
}
// only "true" counts as telling us to skip.
return s == "true", nil
}
......@@ -23,6 +23,12 @@ const mountTimeout = time.Second
// fuseNoDirectory used to check the returning fuse error
const fuseNoDirectory = "fusermount: failed to access mountpoint"
// platformFuseChecks can get overridden by arch-specific files
// to run fuse checks (like checking the OSXFUSE version)
var platformFuseChecks = func(*core.IpfsNode) error {
return nil
}
var MountCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Mounts IPFS to the filesystem (read-only)",
......@@ -161,7 +167,7 @@ func Mount(node *core.IpfsNode, fsdir, nsdir string) error {
node.Mounts.Ipns.Unmount()
}
if err := platformFuseChecks(); err != nil {
if err := platformFuseChecks(node); err != nil {
return err
}
......@@ -173,10 +179,6 @@ func Mount(node *core.IpfsNode, fsdir, nsdir string) error {
return nil
}
var platformFuseChecks = func() error {
return nil
}
func doMount(node *core.IpfsNode, fsdir, nsdir string) error {
fmtFuseErr := func(err error) error {
s := err.Error()
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论