github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/core/commands/mount_darwin.go (about)

     1  // +build !nofuse
     2  
     3  package commands
     4  
     5  import (
     6  	"bytes"
     7  	"fmt"
     8  	"os/exec"
     9  	"runtime"
    10  	"strings"
    11  	"syscall"
    12  
    13  	core "github.com/ipfs/go-ipfs/core"
    14  )
    15  
    16  func init() {
    17  	// this is a hack, but until we need to do it another way, this works.
    18  	platformFuseChecks = darwinFuseCheckVersion
    19  }
    20  
    21  // dontCheckOSXFUSEConfigKey is a key used to let the user tell us to
    22  // skip fuse checks.
    23  var dontCheckOSXFUSEConfigKey = "DontCheckOSXFUSE"
    24  
    25  // fuseVersionPkg is the go pkg url for fuse-version
    26  var fuseVersionPkg = "github.com/jbenet/go-fuse-version/fuse-version"
    27  
    28  // errStrFuseRequired is returned when we're sure the user does not have fuse.
    29  var errStrFuseRequired = `OSXFUSE not found.
    30  
    31  OSXFUSE is required to mount, please install it.
    32  Note: version 2.7.2 or higher required; prior versions are known to kernel panic!
    33  It is recommended you install it from the OSXFUSE website:
    34  
    35  	http://osxfuse.github.io/
    36  
    37  For more help, see:
    38  
    39  	https://github.com/ipfs/go-ipfs/issues/177
    40  `
    41  
    42  // errStrNoFuseHeaders is included in the output of `go get <fuseVersionPkg>` if there
    43  // are no fuse headers. this means they dont have OSXFUSE installed.
    44  var errStrNoFuseHeaders = "no such file or directory: '/usr/local/lib/libosxfuse.dylib'"
    45  
    46  var errStrUpgradeFuse = `OSXFUSE version %s not supported.
    47  
    48  OSXFUSE versions <2.7.2 are known to cause kernel panics!
    49  Please upgrade to the latest OSXFUSE version.
    50  It is recommended you install it from the OSXFUSE website:
    51  
    52  	http://osxfuse.github.io/
    53  
    54  For more help, see:
    55  
    56  	https://github.com/ipfs/go-ipfs/issues/177
    57  `
    58  
    59  var errStrNeedFuseVersion = `unable to check fuse version.
    60  
    61  Dear User,
    62  
    63  Before mounting, we must check your version of OSXFUSE. We are protecting
    64  you from a nasty kernel panic we found in OSXFUSE versions <2.7.2.[1]. To
    65  make matters worse, it's harder than it should be to check whether you have
    66  the right version installed...[2]. We've automated the process with the
    67  help of a little tool. We tried to install it, but something went wrong[3].
    68  Please install it yourself by running:
    69  
    70  	go get %s
    71  
    72  You can also stop ipfs from running these checks and use whatever OSXFUSE
    73  version you have by running:
    74  
    75  	ipfs config %s true
    76  
    77  [1]: https://github.com/ipfs/go-ipfs/issues/177
    78  [2]: https://github.com/ipfs/go-ipfs/pull/533
    79  [3]: %s
    80  `
    81  
    82  var errStrFailedToRunFuseVersion = `unable to check fuse version.
    83  
    84  Dear User,
    85  
    86  Before mounting, we must check your version of OSXFUSE. We are protecting
    87  you from a nasty kernel panic we found in OSXFUSE versions <2.7.2.[1]. To
    88  make matters worse, it's harder than it should be to check whether you have
    89  the right version installed...[2]. We've automated the process with the
    90  help of a little tool. We tried to run it, but something went wrong[3].
    91  Please, try to run it yourself with:
    92  
    93  	go get %s
    94  	fuse-version
    95  
    96  You should see something like this:
    97  
    98  	> fuse-version
    99  	fuse-version -only agent
   100  	OSXFUSE.AgentVersion: 2.7.3
   101  
   102  Just make sure the number is 2.7.2 or higher. You can then stop ipfs from
   103  trying to run these checks with:
   104  
   105  	ipfs config %s true
   106  
   107  [1]: https://github.com/ipfs/go-ipfs/issues/177
   108  [2]: https://github.com/ipfs/go-ipfs/pull/533
   109  [3]: %s
   110  `
   111  
   112  var errStrFixConfig = `config key invalid: %s %s
   113  You may be able to get this error to go away by setting it again:
   114  
   115  	ipfs config %s true
   116  
   117  Either way, please tell us at: http://github.com/ipfs/go-ipfs/issues
   118  `
   119  
   120  func darwinFuseCheckVersion(node *core.IpfsNode) error {
   121  	// on OSX, check FUSE version.
   122  	if runtime.GOOS != "darwin" {
   123  		return nil
   124  	}
   125  
   126  	ov, errGFV := tryGFV()
   127  	if errGFV != nil {
   128  		// if we failed AND the user has told us to ignore the check we
   129  		// continue. this is in case fuse-version breaks or the user cannot
   130  		// install it, but is sure their fuse version will work.
   131  		if skip, err := userAskedToSkipFuseCheck(node); err != nil {
   132  			return err
   133  		} else if skip {
   134  			return nil // user told us not to check version... ok....
   135  		} else {
   136  			return errGFV
   137  		}
   138  	}
   139  
   140  	log.Debug("mount: osxfuse version:", ov)
   141  	if strings.HasPrefix(ov, "2.7.") || strings.HasPrefix(ov, "2.8.") {
   142  		return nil
   143  	}
   144  
   145  	return fmt.Errorf(errStrUpgradeFuse, ov)
   146  }
   147  
   148  func tryGFV() (string, error) {
   149  	// first try sysctl. it may work!
   150  	ov, err := trySysctl()
   151  	if err == nil {
   152  		return ov, nil
   153  	}
   154  	log.Debug(err)
   155  
   156  	return tryGFVFromFuseVersion()
   157  }
   158  
   159  func trySysctl() (string, error) {
   160  	v, err := syscall.Sysctl("osxfuse.version.number")
   161  	if err != nil {
   162  		log.Debug("mount: sysctl osxfuse.version.number:", "failed")
   163  		return "", err
   164  	}
   165  	log.Debug("mount: sysctl osxfuse.version.number:", v)
   166  	return v, nil
   167  }
   168  
   169  func tryGFVFromFuseVersion() (string, error) {
   170  	if err := ensureFuseVersionIsInstalled(); err != nil {
   171  		return "", err
   172  	}
   173  
   174  	cmd := exec.Command("fuse-version", "-q", "-only", "agent", "-s", "OSXFUSE")
   175  	out := new(bytes.Buffer)
   176  	cmd.Stdout = out
   177  	if err := cmd.Run(); err != nil {
   178  		return "", fmt.Errorf(errStrFailedToRunFuseVersion, fuseVersionPkg, dontCheckOSXFUSEConfigKey, err)
   179  	}
   180  
   181  	return out.String(), nil
   182  }
   183  
   184  func ensureFuseVersionIsInstalled() error {
   185  	// see if fuse-version is there
   186  	if _, err := exec.LookPath("fuse-version"); err == nil {
   187  		return nil // got it!
   188  	}
   189  
   190  	// try installing it...
   191  	log.Debug("fuse-version: no fuse-version. attempting to install.")
   192  	cmd := exec.Command("go", "get", "github.com/jbenet/go-fuse-version/fuse-version")
   193  	cmdout := new(bytes.Buffer)
   194  	cmd.Stdout = cmdout
   195  	cmd.Stderr = cmdout
   196  	if err := cmd.Run(); err != nil {
   197  		// Ok, install fuse-version failed. is it they dont have fuse?
   198  		cmdoutstr := cmdout.String()
   199  		if strings.Contains(cmdoutstr, errStrNoFuseHeaders) {
   200  			// yes! it is! they dont have fuse!
   201  			return fmt.Errorf(errStrFuseRequired)
   202  		}
   203  
   204  		log.Debug("fuse-version: failed to install.")
   205  		s := err.Error() + "\n" + cmdoutstr
   206  		return fmt.Errorf(errStrNeedFuseVersion, fuseVersionPkg, dontCheckOSXFUSEConfigKey, s)
   207  	}
   208  
   209  	// ok, try again...
   210  	if _, err := exec.LookPath("fuse-version"); err != nil {
   211  		log.Debug("fuse-version: failed to install?")
   212  		return fmt.Errorf(errStrNeedFuseVersion, fuseVersionPkg, dontCheckOSXFUSEConfigKey, err)
   213  	}
   214  
   215  	log.Debug("fuse-version: install success")
   216  	return nil
   217  }
   218  
   219  func userAskedToSkipFuseCheck(node *core.IpfsNode) (skip bool, err error) {
   220  	val, err := node.Repo.GetConfigKey(dontCheckOSXFUSEConfigKey)
   221  	if err != nil {
   222  		return false, nil // failed to get config value. dont skip check.
   223  	}
   224  	s, ok := val.(string)
   225  	if !ok {
   226  		// got config value, but it's invalid... dont skip check, ask the user to fix it...
   227  		return false, fmt.Errorf(errStrFixConfig, dontCheckOSXFUSEConfigKey, val,
   228  			dontCheckOSXFUSEConfigKey)
   229  	}
   230  	// only "true" counts as telling us to skip.
   231  	return s == "true", nil
   232  }