core.go 3.9 KB

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