github.com/StarfishStorage/goofys@v0.23.2-0.20200415030923-535558486b34/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/api/common" 21 . "github.com/kahing/goofys/internal" 22 23 "fmt" 24 "os" 25 "os/signal" 26 "strings" 27 "sync" 28 "syscall" 29 "time" 30 31 "context" 32 33 "github.com/jacobsa/fuse" 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 return goofys.Mount(ctx, bucketName, flags) 112 } 113 114 func massagePath() { 115 for _, e := range os.Environ() { 116 if strings.HasPrefix(e, "PATH=") { 117 return 118 } 119 } 120 121 // mount -a seems to run goofys without PATH 122 // usually fusermount is in /bin 123 os.Setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") 124 } 125 126 func massageArg0() { 127 var err error 128 os.Args[0], err = osext.Executable() 129 if err != nil { 130 panic(fmt.Sprintf("Unable to discover current executable: %v", err)) 131 } 132 } 133 134 var Version = "use `make build' to fill version hash correctly" 135 136 func main() { 137 VersionHash = Version 138 139 massagePath() 140 141 app := NewApp() 142 143 var flags *FlagStorage 144 var child *os.Process 145 146 app.Action = func(c *cli.Context) (err error) { 147 // We should get two arguments exactly. Otherwise error out. 148 if len(c.Args()) != 2 { 149 fmt.Fprintf( 150 os.Stderr, 151 "Error: %s takes exactly two arguments.\n\n", 152 app.Name) 153 cli.ShowAppHelp(c) 154 os.Exit(1) 155 } 156 157 // Populate and parse flags. 158 bucketName := c.Args()[0] 159 flags = PopulateFlags(c) 160 if flags == nil { 161 cli.ShowAppHelp(c) 162 err = fmt.Errorf("invalid arguments") 163 return 164 } 165 defer func() { 166 time.Sleep(time.Second) 167 flags.Cleanup() 168 }() 169 170 if !flags.Foreground { 171 var wg sync.WaitGroup 172 waitForSignal(&wg) 173 174 massageArg0() 175 176 ctx := new(daemon.Context) 177 child, err = ctx.Reborn() 178 179 if err != nil { 180 panic(fmt.Sprintf("unable to daemonize: %v", err)) 181 } 182 183 InitLoggers(!flags.Foreground && child == nil) 184 185 if child != nil { 186 // attempt to wait for child to notify parent 187 wg.Wait() 188 if waitedForSignal == syscall.SIGUSR1 { 189 return 190 } else { 191 return fuse.EINVAL 192 } 193 } else { 194 // kill our own waiting goroutine 195 kill(os.Getpid(), syscall.SIGUSR1) 196 wg.Wait() 197 defer ctx.Release() 198 } 199 200 } else { 201 InitLoggers(!flags.Foreground) 202 } 203 204 // Mount the file system. 205 var mfs *fuse.MountedFileSystem 206 var fs *Goofys 207 fs, mfs, err = mount( 208 context.Background(), 209 bucketName, 210 flags) 211 212 if err != nil { 213 if !flags.Foreground { 214 kill(os.Getppid(), syscall.SIGUSR2) 215 } 216 log.Fatalf("Mounting file system: %v", err) 217 // fatal also terminates itself 218 } else { 219 if !flags.Foreground { 220 kill(os.Getppid(), syscall.SIGUSR1) 221 } 222 log.Println("File system has been successfully mounted.") 223 // Let the user unmount with Ctrl-C 224 // (SIGINT). But if cache is on, catfs will 225 // receive the signal and we would detect that exiting 226 registerSIGINTHandler(fs, flags) 227 228 // Wait for the file system to be unmounted. 229 err = mfs.Join(context.Background()) 230 if err != nil { 231 err = fmt.Errorf("MountedFileSystem.Join: %v", err) 232 return 233 } 234 235 log.Println("Successfully exiting.") 236 } 237 return 238 } 239 240 err := app.Run(MassageMountFlags(os.Args)) 241 if err != nil { 242 if flags != nil && !flags.Foreground && child != nil { 243 log.Fatalln("Unable to mount file system, see syslog for details") 244 } 245 os.Exit(1) 246 } 247 }