github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/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/search"
    40  	"camlistore.org/third_party/code.google.com/p/rsc/fuse"
    41  )
    42  
    43  var (
    44  	debug = flag.Bool("debug", false, "print debugging messages.")
    45  	xterm = flag.Bool("xterm", false, "Run an xterm in the mounted directory. Shut down when xterm ends.")
    46  	open  = flag.Bool("open", false, "Open a GUI window")
    47  )
    48  
    49  func usage() {
    50  	fmt.Fprint(os.Stderr, "usage: cammount [opts] [<mountpoint> [<root-blobref>|<share URL>|<root-name>]]\n")
    51  	flag.PrintDefaults()
    52  	os.Exit(2)
    53  }
    54  
    55  func main() {
    56  	var conn *fuse.Conn
    57  
    58  	// Scans the arg list and sets up flags
    59  	client.AddFlags()
    60  	flag.Usage = usage
    61  	flag.Parse()
    62  
    63  	narg := flag.NArg()
    64  	if narg > 2 {
    65  		usage()
    66  	}
    67  
    68  	var mountPoint string
    69  	var err error
    70  	if narg > 0 {
    71  		mountPoint = flag.Arg(0)
    72  	} else {
    73  		mountPoint, err = ioutil.TempDir("", "cammount")
    74  		if err != nil {
    75  			log.Fatal(err)
    76  		}
    77  		log.Printf("No mount point given. Using: %s", mountPoint)
    78  		defer os.Remove(mountPoint)
    79  	}
    80  
    81  	errorf := func(msg string, args ...interface{}) {
    82  		fmt.Fprintf(os.Stderr, msg, args...)
    83  		fmt.Fprint(os.Stderr, "\n")
    84  		usage()
    85  	}
    86  
    87  	var (
    88  		cl    *client.Client
    89  		root  blob.Ref // nil if only one arg
    90  		camfs *fs.CamliFileSystem
    91  	)
    92  	if narg == 2 {
    93  		rootArg := flag.Arg(1)
    94  		// not trying very hard since NewFromShareRoot will do it better with a regex
    95  		if strings.HasPrefix(rootArg, "http://") ||
    96  			strings.HasPrefix(rootArg, "https://") {
    97  			if client.ExplicitServer() != "" {
    98  				errorf("Can't use an explicit blobserver with a share URL; the blobserver is implicit from the share URL.")
    99  			}
   100  			var err error
   101  			cl, root, err = client.NewFromShareRoot(rootArg)
   102  			if err != nil {
   103  				log.Fatal(err)
   104  			}
   105  		} else {
   106  			cl = client.NewOrFail() // automatic from flags
   107  			cl.SetHTTPClient(&http.Client{Transport: cl.TransportForConfig(nil)})
   108  
   109  			var ok bool
   110  			root, ok = blob.Parse(rootArg)
   111  
   112  			if !ok {
   113  				// not a blobref, check for root name instead
   114  				req := &search.WithAttrRequest{N: 1, Attr: "camliRoot", Value: rootArg}
   115  				wres, err := cl.GetPermanodesWithAttr(req)
   116  
   117  				if err != nil {
   118  					log.Fatal("could not query search")
   119  				}
   120  
   121  				if wres.WithAttr != nil {
   122  					root = wres.WithAttr[0].Permanode
   123  				} else {
   124  					log.Fatalf("root specified is not a blobref or name of a root: %q\n", rootArg)
   125  				}
   126  			}
   127  		}
   128  	} else {
   129  		cl = client.NewOrFail() // automatic from flags
   130  		cl.SetHTTPClient(&http.Client{Transport: cl.TransportForConfig(nil)})
   131  	}
   132  
   133  	diskCacheFetcher, err := cacher.NewDiskCache(cl)
   134  	if err != nil {
   135  		log.Fatalf("Error setting up local disk cache: %v", err)
   136  	}
   137  	defer diskCacheFetcher.Clean()
   138  	if root.Valid() {
   139  		var err error
   140  		camfs, err = fs.NewRootedCamliFileSystem(cl, diskCacheFetcher, root)
   141  		if err != nil {
   142  			log.Fatalf("Error creating root with %v: %v", root, err)
   143  		}
   144  	} else {
   145  		camfs = fs.NewDefaultCamliFileSystem(cl, diskCacheFetcher)
   146  	}
   147  
   148  	if *debug {
   149  		fuse.Debugf = log.Printf
   150  		// TODO: set fs's logger
   151  	}
   152  
   153  	// This doesn't appear to work on OS X:
   154  	sigc := make(chan os.Signal, 1)
   155  
   156  	conn, err = fuse.Mount(mountPoint)
   157  	if err != nil {
   158  		if err.Error() == "cannot find load_fusefs" && runtime.GOOS == "darwin" {
   159  			log.Fatal("FUSE not available; install from http://osxfuse.github.io/")
   160  		}
   161  		log.Fatalf("Mount: %v", err)
   162  	}
   163  
   164  	xtermDone := make(chan bool, 1)
   165  	if *xterm {
   166  		cmd := exec.Command("xterm")
   167  		cmd.Dir = mountPoint
   168  		if err := cmd.Start(); err != nil {
   169  			log.Printf("Error starting xterm: %v", err)
   170  		} else {
   171  			go func() {
   172  				cmd.Wait()
   173  				xtermDone <- true
   174  			}()
   175  			defer cmd.Process.Kill()
   176  		}
   177  	}
   178  	if *open {
   179  		if runtime.GOOS == "darwin" {
   180  			cmd := exec.Command("open", mountPoint)
   181  			go cmd.Run()
   182  		}
   183  	}
   184  
   185  	signal.Notify(sigc, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
   186  
   187  	doneServe := make(chan error, 1)
   188  	go func() {
   189  		doneServe <- conn.Serve(camfs)
   190  	}()
   191  
   192  	quitKey := make(chan bool, 1)
   193  	go awaitQuitKey(quitKey)
   194  
   195  	select {
   196  	case err := <-doneServe:
   197  		log.Printf("conn.Serve returned %v", err)
   198  	case sig := <-sigc:
   199  		log.Printf("Signal %s received, shutting down.", sig)
   200  	case <-quitKey:
   201  		log.Printf("Quit key pressed. Shutting down.")
   202  	case <-xtermDone:
   203  		log.Printf("xterm done")
   204  	}
   205  
   206  	time.AfterFunc(2*time.Second, func() {
   207  		os.Exit(1)
   208  	})
   209  	log.Printf("Unmounting...")
   210  	err = fs.Unmount(mountPoint)
   211  	log.Printf("Unmount = %v", err)
   212  
   213  	log.Printf("cammount FUSE process ending.")
   214  }
   215  
   216  func awaitQuitKey(done chan<- bool) {
   217  	var buf [1]byte
   218  	for {
   219  		_, err := os.Stdin.Read(buf[:])
   220  		if err != nil {
   221  			return
   222  		}
   223  		if buf[0] == 'q' {
   224  			done <- true
   225  			return
   226  		}
   227  	}
   228  }