github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/cmd/cammount/cammount.go (about)

     1  // +build linux darwin
     2  
     3  /*
     4  Copyright 2011 Google Inc.
     5  
     6  Licensed under the Apache License, Version 2.0 (the "License");
     7  you may not use this file except in compliance with the License.
     8  You may obtain a copy of the License at
     9  
    10       http://www.apache.org/licenses/LICENSE-2.0
    11  
    12  Unless required by applicable law or agreed to in writing, software
    13  distributed under the License is distributed on an "AS IS" BASIS,
    14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  See the License for the specific language governing permissions and
    16  limitations under the License.
    17  */
    18  
    19  package main
    20  
    21  import (
    22  	"flag"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"log"
    26  	"net/http"
    27  	"os"
    28  	"os/exec"
    29  	"os/signal"
    30  	"runtime"
    31  	"strings"
    32  	"syscall"
    33  	"time"
    34  
    35  	"camlistore.org/pkg/blob"
    36  	"camlistore.org/pkg/cacher"
    37  	"camlistore.org/pkg/client"
    38  	"camlistore.org/pkg/fs"
    39  	"camlistore.org/pkg/osutil"
    40  	"camlistore.org/pkg/search"
    41  	"camlistore.org/third_party/bazil.org/fuse"
    42  	fusefs "camlistore.org/third_party/bazil.org/fuse/fs"
    43  )
    44  
    45  var (
    46  	debug = flag.Bool("debug", false, "print debugging messages.")
    47  	xterm = flag.Bool("xterm", false, "Run an xterm in the mounted directory. Shut down when xterm ends.")
    48  	term  = flag.Bool("term", false, "Open a terminal window. Doesn't shut down when exited. Mostly for demos.")
    49  	open  = flag.Bool("open", false, "Open a GUI window")
    50  )
    51  
    52  func usage() {
    53  	fmt.Fprint(os.Stderr, "usage: cammount [opts] [<mountpoint> [<root-blobref>|<share URL>|<root-name>]]\n")
    54  	flag.PrintDefaults()
    55  	os.Exit(2)
    56  }
    57  
    58  func main() {
    59  	var conn *fuse.Conn
    60  
    61  	// Scans the arg list and sets up flags
    62  	client.AddFlags()
    63  	flag.Usage = usage
    64  	flag.Parse()
    65  
    66  	narg := flag.NArg()
    67  	if narg > 2 {
    68  		usage()
    69  	}
    70  
    71  	var mountPoint string
    72  	var err error
    73  	if narg > 0 {
    74  		mountPoint = flag.Arg(0)
    75  	} else {
    76  		mountPoint, err = ioutil.TempDir("", "cammount")
    77  		if err != nil {
    78  			log.Fatal(err)
    79  		}
    80  		log.Printf("No mount point given. Using: %s", mountPoint)
    81  		defer os.Remove(mountPoint)
    82  	}
    83  
    84  	errorf := func(msg string, args ...interface{}) {
    85  		fmt.Fprintf(os.Stderr, msg, args...)
    86  		fmt.Fprint(os.Stderr, "\n")
    87  		usage()
    88  	}
    89  
    90  	var (
    91  		cl    *client.Client
    92  		root  blob.Ref // nil if only one arg
    93  		camfs *fs.CamliFileSystem
    94  	)
    95  	if narg == 2 {
    96  		rootArg := flag.Arg(1)
    97  		// not trying very hard since NewFromShareRoot will do it better with a regex
    98  		if strings.HasPrefix(rootArg, "http://") ||
    99  			strings.HasPrefix(rootArg, "https://") {
   100  			if client.ExplicitServer() != "" {
   101  				errorf("Can't use an explicit blobserver with a share URL; the blobserver is implicit from the share URL.")
   102  			}
   103  			var err error
   104  			cl, root, err = client.NewFromShareRoot(rootArg)
   105  			if err != nil {
   106  				log.Fatal(err)
   107  			}
   108  		} else {
   109  			cl = client.NewOrFail() // automatic from flags
   110  			cl.SetHTTPClient(&http.Client{Transport: cl.TransportForConfig(nil)})
   111  
   112  			var ok bool
   113  			root, ok = blob.Parse(rootArg)
   114  
   115  			if !ok {
   116  				// not a blobref, check for root name instead
   117  				req := &search.WithAttrRequest{N: 1, Attr: "camliRoot", Value: rootArg}
   118  				wres, err := cl.GetPermanodesWithAttr(req)
   119  
   120  				if err != nil {
   121  					log.Fatal("could not query search")
   122  				}
   123  
   124  				if wres.WithAttr != nil {
   125  					root = wres.WithAttr[0].Permanode
   126  				} else {
   127  					log.Fatalf("root specified is not a blobref or name of a root: %q\n", rootArg)
   128  				}
   129  			}
   130  		}
   131  	} else {
   132  		cl = client.NewOrFail() // automatic from flags
   133  		cl.SetHTTPClient(&http.Client{Transport: cl.TransportForConfig(nil)})
   134  	}
   135  
   136  	diskCacheFetcher, err := cacher.NewDiskCache(cl)
   137  	if err != nil {
   138  		log.Fatalf("Error setting up local disk cache: %v", err)
   139  	}
   140  	defer diskCacheFetcher.Clean()
   141  	if root.Valid() {
   142  		var err error
   143  		camfs, err = fs.NewRootedCamliFileSystem(cl, diskCacheFetcher, root)
   144  		if err != nil {
   145  			log.Fatalf("Error creating root with %v: %v", root, err)
   146  		}
   147  	} else {
   148  		camfs = fs.NewDefaultCamliFileSystem(cl, diskCacheFetcher)
   149  	}
   150  
   151  	if *debug {
   152  		fuse.Debug = func(msg interface{}) { log.Print(msg) }
   153  		// TODO: set fs's logger
   154  	}
   155  
   156  	// This doesn't appear to work on OS X:
   157  	sigc := make(chan os.Signal, 1)
   158  
   159  	conn, err = fuse.Mount(mountPoint)
   160  	if err != nil {
   161  		if err.Error() == "cannot find load_fusefs" && runtime.GOOS == "darwin" {
   162  			log.Fatal("FUSE not available; install from http://osxfuse.github.io/")
   163  		}
   164  		log.Fatalf("Mount: %v", err)
   165  	}
   166  
   167  	xtermDone := make(chan bool, 1)
   168  	if *xterm {
   169  		cmd := exec.Command("xterm")
   170  		cmd.Dir = mountPoint
   171  		if err := cmd.Start(); err != nil {
   172  			log.Printf("Error starting xterm: %v", err)
   173  		} else {
   174  			go func() {
   175  				cmd.Wait()
   176  				xtermDone <- true
   177  			}()
   178  			defer cmd.Process.Kill()
   179  		}
   180  	}
   181  	if *open {
   182  		if runtime.GOOS == "darwin" {
   183  			go exec.Command("open", mountPoint).Run()
   184  		}
   185  	}
   186  	if *term {
   187  		if runtime.GOOS == "darwin" {
   188  			if osutil.DirExists("/Applications/iTerm.app/") {
   189  				go exec.Command("open", "-a", "iTerm", mountPoint).Run()
   190  			} else {
   191  				log.Printf("TODO: iTerm not installed. Figure out how to open with Terminal.app instead.")
   192  			}
   193  		}
   194  	}
   195  
   196  	signal.Notify(sigc, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
   197  
   198  	doneServe := make(chan error, 1)
   199  	go func() {
   200  		doneServe <- fusefs.Serve(conn, camfs)
   201  	}()
   202  
   203  	quitKey := make(chan bool, 1)
   204  	go awaitQuitKey(quitKey)
   205  
   206  	select {
   207  	case err := <-doneServe:
   208  		log.Printf("conn.Serve returned %v", err)
   209  	case sig := <-sigc:
   210  		log.Printf("Signal %s received, shutting down.", sig)
   211  	case <-quitKey:
   212  		log.Printf("Quit key pressed. Shutting down.")
   213  	case <-xtermDone:
   214  		log.Printf("xterm done")
   215  	}
   216  
   217  	time.AfterFunc(2*time.Second, func() {
   218  		os.Exit(1)
   219  	})
   220  	log.Printf("Unmounting...")
   221  	err = fs.Unmount(mountPoint)
   222  	log.Printf("Unmount = %v", err)
   223  
   224  	log.Printf("cammount FUSE process ending.")
   225  }
   226  
   227  func awaitQuitKey(done chan<- bool) {
   228  	var buf [1]byte
   229  	for {
   230  		_, err := os.Stdin.Read(buf[:])
   231  		if err != nil {
   232  			return
   233  		}
   234  		if buf[0] == 'q' {
   235  			done <- true
   236  			return
   237  		}
   238  	}
   239  }