github.com/t2y/goofys@v0.19.1-0.20190123053037-27053313e616/main.go (about) 1 // Copyright 2015 - 2017 Ka-Hing Cheung 2 // Copyright 2015 - 2017 Google Inc. All Rights Reserved. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package main 17 18 import ( 19 goofys "github.com/kahing/goofys/api" 20 . "github.com/kahing/goofys/internal" 21 22 "fmt" 23 "os" 24 "os/signal" 25 "strings" 26 "sync" 27 "syscall" 28 "time" 29 30 "golang.org/x/net/context" 31 32 "github.com/jacobsa/fuse" 33 "github.com/jinzhu/copier" 34 "github.com/kardianos/osext" 35 "github.com/urfave/cli" 36 37 daemon "github.com/sevlyar/go-daemon" 38 ) 39 40 var log = GetLogger("main") 41 42 func registerSIGINTHandler(fs *Goofys, flags *FlagStorage) { 43 // Register for SIGINT. 44 signalChan := make(chan os.Signal, 1) 45 signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM, syscall.SIGUSR1) 46 47 // Start a goroutine that will unmount when the signal is received. 48 go func() { 49 for { 50 s := <-signalChan 51 if s == syscall.SIGUSR1 { 52 log.Infof("Received %v", s) 53 fs.SigUsr1() 54 continue 55 } 56 57 if len(flags.Cache) == 0 { 58 log.Infof("Received %v, attempting to unmount...", s) 59 60 err := TryUnmount(flags.MountPoint) 61 if err != nil { 62 log.Errorf("Failed to unmount in response to %v: %v", s, err) 63 } else { 64 log.Printf("Successfully unmounted %v in response to %v", 65 flags.MountPoint, s) 66 return 67 } 68 } else { 69 log.Infof("Received %v", s) 70 // wait for catfs to die and cleanup 71 } 72 } 73 }() 74 } 75 76 var waitedForSignal os.Signal 77 78 func waitForSignal(wg *sync.WaitGroup) { 79 signalChan := make(chan os.Signal, 1) 80 signal.Notify(signalChan, syscall.SIGUSR1, syscall.SIGUSR2) 81 82 wg.Add(1) 83 go func() { 84 waitedForSignal = <-signalChan 85 wg.Done() 86 }() 87 } 88 89 func kill(pid int, s os.Signal) (err error) { 90 p, err := os.FindProcess(pid) 91 if err != nil { 92 return err 93 } 94 95 defer p.Release() 96 97 err = p.Signal(s) 98 if err != nil { 99 return err 100 } 101 return 102 } 103 104 // Mount the file system based on the supplied arguments, returning a 105 // fuse.MountedFileSystem that can be joined to wait for unmounting. 106 func mount( 107 ctx context.Context, 108 bucketName string, 109 flags *FlagStorage) (fs *Goofys, mfs *fuse.MountedFileSystem, err error) { 110 111 // XXX really silly copy here! in goofys.Mount we will copy it 112 // back to FlagStorage. But I don't see a easier way to expose 113 // Config in the api package 114 var config goofys.Config 115 copier.Copy(&config, *flags) 116 117 return goofys.Mount(ctx, bucketName, &config) 118 } 119 120 func massagePath() { 121 for _, e := range os.Environ() { 122 if strings.HasPrefix(e, "PATH=") { 123 return 124 } 125 } 126 127 // mount -a seems to run goofys without PATH 128 // usually fusermount is in /bin 129 os.Setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") 130 } 131 132 func massageArg0() { 133 var err error 134 os.Args[0], err = osext.Executable() 135 if err != nil { 136 panic(fmt.Sprintf("Unable to discover current executable: %v", err)) 137 } 138 } 139 140 var Version = "use `make build' to fill version hash correctly" 141 142 func main() { 143 VersionHash = Version 144 145 massagePath() 146 147 app := NewApp() 148 149 var flags *FlagStorage 150 var child *os.Process 151 152 app.Action = func(c *cli.Context) (err error) { 153 // We should get two arguments exactly. Otherwise error out. 154 if len(c.Args()) != 2 { 155 fmt.Fprintf( 156 os.Stderr, 157 "Error: %s takes exactly two arguments.\n\n", 158 app.Name) 159 cli.ShowAppHelp(c) 160 os.Exit(1) 161 } 162 163 // Populate and parse flags. 164 bucketName := c.Args()[0] 165 flags = PopulateFlags(c) 166 if flags == nil { 167 cli.ShowAppHelp(c) 168 err = fmt.Errorf("invalid arguments") 169 return 170 } 171 defer func() { 172 time.Sleep(time.Second) 173 flags.Cleanup() 174 }() 175 176 if !flags.Foreground { 177 var wg sync.WaitGroup 178 waitForSignal(&wg) 179 180 massageArg0() 181 182 ctx := new(daemon.Context) 183 child, err = ctx.Reborn() 184 185 if err != nil { 186 panic(fmt.Sprintf("unable to daemonize: %v", err)) 187 } 188 189 InitLoggers(!flags.Foreground && child == nil) 190 191 if child != nil { 192 // attempt to wait for child to notify parent 193 wg.Wait() 194 if waitedForSignal == syscall.SIGUSR1 { 195 return 196 } else { 197 return fuse.EINVAL 198 } 199 } else { 200 // kill our own waiting goroutine 201 kill(os.Getpid(), syscall.SIGUSR1) 202 wg.Wait() 203 defer ctx.Release() 204 } 205 206 } else { 207 InitLoggers(!flags.Foreground) 208 } 209 210 // Mount the file system. 211 var mfs *fuse.MountedFileSystem 212 var fs *Goofys 213 fs, mfs, err = mount( 214 context.Background(), 215 bucketName, 216 flags) 217 218 if err != nil { 219 if !flags.Foreground { 220 kill(os.Getppid(), syscall.SIGUSR2) 221 } 222 log.Fatalf("Mounting file system: %v", err) 223 // fatal also terminates itself 224 } else { 225 if !flags.Foreground { 226 kill(os.Getppid(), syscall.SIGUSR1) 227 } 228 log.Println("File system has been successfully mounted.") 229 // Let the user unmount with Ctrl-C 230 // (SIGINT). But if cache is on, catfs will 231 // receive the signal and we would detect that exiting 232 registerSIGINTHandler(fs, flags) 233 234 // Wait for the file system to be unmounted. 235 err = mfs.Join(context.Background()) 236 if err != nil { 237 err = fmt.Errorf("MountedFileSystem.Join: %v", err) 238 return 239 } 240 241 log.Println("Successfully exiting.") 242 } 243 return 244 } 245 246 err := app.Run(MassageMountFlags(os.Args)) 247 if err != nil { 248 if flags != nil && !flags.Foreground && child != nil { 249 log.Fatalln("Unable to mount file system, see syslog for details") 250 } 251 os.Exit(1) 252 } 253 }