core.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package core
  2. import (
  3. "context"
  4. "errors"
  5. "flag"
  6. "fmt"
  7. "log"
  8. "os"
  9. "os/signal"
  10. "os/user"
  11. "path/filepath"
  12. "runtime"
  13. "strconv"
  14. "strings"
  15. "syscall"
  16. "dev.hexasoftware.com/hxs/prettylog"
  17. "github.com/jacobsa/fuse"
  18. "github.com/jacobsa/fuse/fuseutil"
  19. )
  20. // Core struct
  21. type Core struct {
  22. Config Config
  23. Drivers map[string]DriverFactory
  24. CurrentFS Driver
  25. }
  26. // New create a New cloudmount core
  27. func New() *Core {
  28. return &Core{Drivers: map[string]DriverFactory{}}
  29. }
  30. func (c *Core) Init() (err error) {
  31. // TODO: friendly panics
  32. usr, err := user.Current()
  33. if err != nil {
  34. panic(err)
  35. }
  36. uid, err := strconv.Atoi(usr.Uid)
  37. if err != nil {
  38. panic(err)
  39. }
  40. gid, err := strconv.Atoi(usr.Gid)
  41. if err != nil {
  42. panic(gid)
  43. }
  44. // Defaults
  45. c.Config = Config{
  46. HomeDir: filepath.Join(usr.HomeDir, ".cloudmount"),
  47. UID: uint32(uid),
  48. GID: uint32(gid),
  49. VerboseLog: false,
  50. Daemonize: false,
  51. }
  52. err = c.parseFlags()
  53. if err != nil {
  54. return err
  55. }
  56. fsFactory, ok := c.Drivers[c.Config.CloudFSDriver]
  57. if !ok {
  58. log.Fatal("CloudFS not supported")
  59. }
  60. c.CurrentFS = fsFactory(c) // Factory
  61. return
  62. }
  63. func (c *Core) parseFlags() (err error) {
  64. var mountoptsFlag string
  65. flag.StringVar(&c.Config.CloudFSDriver, "t", "gdrive", "which cloud service to use [gdrive]")
  66. flag.BoolVar(&c.Config.Daemonize, "d", false, "Run app in background")
  67. flag.BoolVar(&c.Config.VerboseLog, "v", false, "Verbose log")
  68. flag.StringVar(&c.Config.HomeDir, "w", c.Config.HomeDir, "Work dir, path that holds configurations")
  69. flag.StringVar(&mountoptsFlag, "o", "", "-o [opts] uid,gid")
  70. flag.Usage = func() {
  71. fmt.Fprintf(os.Stderr, "Usage: %s [options] MOUNTPOINT\n\n", os.Args[0])
  72. fmt.Fprintf(os.Stderr, "Options:\n")
  73. flag.PrintDefaults()
  74. fmt.Fprintf(os.Stderr, "\n")
  75. }
  76. flag.Parse()
  77. if len(flag.Args()) < 1 {
  78. flag.Usage()
  79. //fmt.Println("Usage:\n gdrivemount [-d] [-v] MOUNTPOINT")
  80. return errors.New("Missing parameter")
  81. }
  82. /////////////////////////////////////
  83. // Parse mount opts
  84. /////////////////
  85. pmountopts := strings.Split(mountoptsFlag, ",")
  86. mountopts := map[string]string{}
  87. for _, v := range pmountopts {
  88. keypart := strings.Split(v, "=")
  89. if len(keypart) != 2 {
  90. continue
  91. }
  92. mountopts[keypart[0]] = keypart[1]
  93. }
  94. /////////////////////////////////////
  95. // Use mount opts
  96. ///////////////
  97. uidStr, ok := mountopts["uid"]
  98. if ok {
  99. uid, err := strconv.Atoi(uidStr)
  100. if err != nil {
  101. panic(err)
  102. }
  103. c.Config.UID = uint32(uid)
  104. }
  105. gidStr, ok := mountopts["gid"]
  106. if ok {
  107. gid, err := strconv.Atoi(gidStr)
  108. if err != nil {
  109. panic(err)
  110. }
  111. c.Config.GID = uint32(gid)
  112. }
  113. return
  114. }
  115. func (c *Core) Mount() {
  116. // Start Selected driveFS
  117. c.CurrentFS.Start()
  118. //////////////
  119. // Server
  120. /////////
  121. ctx := context.Background()
  122. server := fuseutil.NewFileSystemServer(c.CurrentFS)
  123. mountPath := flag.Arg(0)
  124. var err error
  125. var mfs *fuse.MountedFileSystem
  126. if c.Config.VerboseLog {
  127. mfs, err = fuse.Mount(mountPath, server, &fuse.MountConfig{DebugLogger: prettylog.New("fuse"), ErrorLogger: prettylog.New("fuse-err")})
  128. } else {
  129. mfs, err = fuse.Mount(mountPath, server, &fuse.MountConfig{})
  130. }
  131. if err != nil {
  132. log.Fatal("Failed mounting path", flag.Arg(0), err)
  133. }
  134. // Signal handling to refresh Drives
  135. sigs := make(chan os.Signal, 2)
  136. signal.Notify(sigs, syscall.SIGUSR1, syscall.SIGHUP, syscall.SIGINT, os.Interrupt, syscall.SIGTERM)
  137. go func() {
  138. for sig := range sigs {
  139. log.Println("Signal:", sig)
  140. switch sig {
  141. case syscall.SIGUSR1:
  142. log.Println("Manually Refresh drive")
  143. go c.CurrentFS.Refresh()
  144. case syscall.SIGHUP:
  145. log.Println("GC")
  146. mem := runtime.MemStats{}
  147. runtime.ReadMemStats(&mem)
  148. log.Printf("Mem: %.2fMB", float64(mem.Alloc)/1024/1024)
  149. runtime.GC()
  150. runtime.ReadMemStats(&mem)
  151. log.Printf("After gc: Mem: %.2fMB", float64(mem.Alloc)/1024/1024)
  152. case os.Interrupt:
  153. log.Println("Graceful unmount")
  154. fuse.Unmount(mountPath)
  155. os.Exit(1)
  156. case syscall.SIGTERM:
  157. log.Println("Graceful unmount")
  158. fuse.Unmount(mountPath)
  159. os.Exit(1)
  160. }
  161. }
  162. }()
  163. if err := mfs.Join(ctx); err != nil {
  164. log.Fatalf("Joining: %v", err)
  165. }
  166. }