CLI client for Syncthing in Go

main.go 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "path"
  6. "git.dtluna.net/dtluna/syncthing-cli/commands"
  7. "git.dtluna.net/dtluna/syncthing-cli/config"
  8. "git.dtluna.net/dtluna/syncthing-cli/constants"
  9. "github.com/OpenPeeDeeP/xdg"
  10. "github.com/Unknwon/com"
  11. "gopkg.in/alecthomas/kingpin.v2"
  12. )
  13. var (
  14. commit = ""
  15. version = ""
  16. date = ""
  17. )
  18. func printToStderr(v interface{}) {
  19. fmt.Fprintf(
  20. os.Stderr,
  21. "%v: error: creating default config file: %v\n",
  22. constants.AppName,
  23. v,
  24. )
  25. }
  26. func fatalIfError(err error) {
  27. if err != nil {
  28. printToStderr(err)
  29. os.Exit(1)
  30. }
  31. }
  32. func main() {
  33. x := xdg.New("", constants.AppName)
  34. configDir := x.ConfigHome()
  35. defaultConfigPath := path.Join(configDir, "config.ini")
  36. if !com.IsExist(defaultConfigPath) {
  37. if !com.IsExist(configDir) {
  38. err := os.MkdirAll(configDir, os.ModePerm)
  39. fatalIfError(err)
  40. }
  41. err := config.CreateBlankConfigFile(defaultConfigPath)
  42. fatalIfError(err)
  43. }
  44. app := kingpin.New(constants.AppName, "CLI client for Syncthing")
  45. app.Version(fmt.Sprintf("%s, commit %s, built at %s", version, commit, date))
  46. configPath := app.Flag("config", "Location of the config file.").
  47. Short('c').
  48. Default(defaultConfigPath).
  49. ExistingFile()
  50. address := app.Flag("address", "Address of the Syncthing daemon.").
  51. Short('a').
  52. TCP()
  53. apiKey := app.Flag("api-key", "API key to access the REST API of the Syncthing daemon.").
  54. Short('k').
  55. String()
  56. version := app.Command("version", "Show the current Syncthing version information.").Alias("v")
  57. configCmd := app.Command("config", "Show the current configuration.").Alias("c").Alias("conf")
  58. device := app.Command("device", "Work with devices.").Alias("d").Alias("dev")
  59. deviceList := device.Command("list", "List devices.").Alias("l").Alias("ls")
  60. deviceStats := device.Command("stats", "Show device stats.").Alias("s").Alias("st")
  61. deviceAdd := device.Command("add", "Add a new device.").Alias("a")
  62. deviceAddID := deviceAdd.Arg("ID", "ID of the new device.").Required().String()
  63. deviceAddName := deviceAdd.Arg("name", "Name of the new device.").String()
  64. deviceAddAddresses := deviceAdd.Flag("addresses", "Addresses of the new device.").
  65. Default(constants.DynamicAddress).
  66. Strings()
  67. deviceAddIntroducer := deviceAdd.Flag("introducer", "Mark device as an introducer.").
  68. Short('i').
  69. Bool()
  70. deviceAddCompression := deviceAdd.Flag("compression", "Specify the compression to use.").
  71. Default(constants.CompressionMetadataOnly).
  72. Enum(
  73. constants.CompressionMetadataOnly,
  74. constants.CompressionAllData,
  75. constants.CompressionOff,
  76. )
  77. deviceAddCertName := deviceAdd.Flag("cert-name", "Specify the certificate name.").String()
  78. deviceRemove := device.Command("remove", "Remove a device.").Alias("r").Alias("rm")
  79. deviceRemoveID := deviceRemove.Arg("ID", "ID of the device to remove.").Required().String()
  80. folder := app.Command("folder", "Work with folders.").Alias("f").Alias("fl").Alias("fold")
  81. folderList := folder.Command("list", "List folders.").Alias("l").Alias("ls")
  82. folderStats := folder.Command("stats", "Show folder stats.").Alias("s").Alias("st")
  83. folderAdd := folder.Command("add", "Add a new folder.").Alias("a")
  84. folderAddLabel := folderAdd.Arg("label", "Label of the new folder.").Required().String()
  85. folderAddPath := folderAdd.Arg("path", "Path to the new folder.").Required().ExistingDir()
  86. folderAddID := folderAdd.Flag("id", "ID of the new folder.").Short('i').String()
  87. folderAddType := folderAdd.Flag("type", "Type of the new folder.").
  88. Default(constants.FolderTypeSendReceive).
  89. Enum(
  90. constants.FolderTypeSendReceive,
  91. constants.FolderTypeSendOnly,
  92. constants.FolderTypeReceiveOnly,
  93. )
  94. folderAddShareWith := folderAdd.Flag("share", "IDs of devices to share this folder with.").
  95. Short('s').
  96. Strings()
  97. folderAddOrder := folderAdd.Flag("order", "File pull order.").
  98. Short('o').
  99. Default(constants.FilePullOrderRandom).
  100. Enum(
  101. constants.FilePullOrderRandom,
  102. constants.FilePullOrderAlphabetic,
  103. constants.FilePullOrderSmallestFirst,
  104. constants.FilePullOrderLargestFirst,
  105. constants.FilePullOrderOldestFirst,
  106. constants.FilePullOrderNewestFirst,
  107. )
  108. folderAddMinDiskFreePct := folderAdd.Flag("min-free-space", "Minimum free disk space in percents.").
  109. Short('m').
  110. Int()
  111. folderAddFSWatcherEnabled := folderAdd.Flag(
  112. "fs-watcher-enabled",
  113. "Watch for changes. Watching for changes discovers most changes without periodic scanning.",
  114. ).
  115. Short('w').
  116. Bool()
  117. folderAddIgnorePerms := folderAdd.Flag(
  118. "ignore-perms",
  119. "File permission bits are ignored when looking for changes. Use on FAT file systems.",
  120. ).
  121. Bool()
  122. folderAddIgnoreDelete := folderAdd.Flag(
  123. "ignore-delete",
  124. "When set to true, this device will pretend not to see instructions to delete files from other devices.",
  125. ).
  126. Bool()
  127. folderAddRescanInterval := folderAdd.Flag(
  128. "rescan-interval",
  129. "Full folder rescan interval in seconds.",
  130. ).
  131. Short('r').
  132. Default("3600").
  133. Int()
  134. folderRemove := folder.Command("remove", "Remove a folder.").Alias("r").Alias("rm")
  135. folderRemoveID := folderRemove.Arg("ID", "ID of the folder to remove.").Required().String()
  136. restart := app.Command("restart", "Restart the Syncthing daemon.")
  137. shutdown := app.Command("shutdown", "Shutdown the Syncthing daemon.")
  138. pause := app.Command("pause", "Pause the given devices or all devices.").Alias("p")
  139. pauseDevices := pause.Arg("devices", "Devices to pause. If non specified all devices are paused.").
  140. Strings()
  141. resume := app.Command("resume", "Resume the given devices or all devices.").Alias("r")
  142. resumeDevices := resume.Arg("devices", "Devices to resume. If non specified all devices are resumed.").
  143. Strings()
  144. commandName := kingpin.MustParse(app.Parse(os.Args[1:]))
  145. cfg, err := config.Parse(*configPath)
  146. app.FatalIfError(err, "parsing config")
  147. cfg, err = config.Merge(*cfg, *address, *apiKey)
  148. app.FatalIfError(err, "merging config with arguments")
  149. err = func() error {
  150. switch commandName {
  151. case configCmd.FullCommand():
  152. return commands.Config(cfg)
  153. case version.FullCommand():
  154. return commands.Version(cfg)
  155. case deviceList.FullCommand():
  156. return commands.DeviceList(cfg)
  157. case deviceStats.FullCommand():
  158. return commands.DeviceStats(cfg)
  159. case deviceAdd.FullCommand():
  160. return commands.DeviceAdd(
  161. cfg,
  162. *deviceAddID,
  163. *deviceAddName,
  164. *deviceAddCompression,
  165. *deviceAddCertName,
  166. *deviceAddAddresses,
  167. *deviceAddIntroducer,
  168. )
  169. case deviceRemove.FullCommand():
  170. return commands.DeviceRemove(cfg, *deviceRemoveID)
  171. case folderList.FullCommand():
  172. return commands.FolderList(cfg)
  173. case folderStats.FullCommand():
  174. return commands.FolderStats(cfg)
  175. case folderAdd.FullCommand():
  176. return commands.FolderAdd(
  177. cfg,
  178. *folderAddLabel,
  179. *folderAddID,
  180. *folderAddPath,
  181. *folderAddType,
  182. *folderAddOrder,
  183. *folderAddShareWith,
  184. *folderAddMinDiskFreePct,
  185. *folderAddRescanInterval,
  186. *folderAddFSWatcherEnabled,
  187. *folderAddIgnorePerms,
  188. *folderAddIgnoreDelete,
  189. )
  190. case folderRemove.FullCommand():
  191. return commands.FolderRemove(cfg, *folderRemoveID)
  192. case restart.FullCommand():
  193. return commands.Restart(cfg)
  194. case shutdown.FullCommand():
  195. return commands.Shutdown(cfg)
  196. case pause.FullCommand():
  197. return commands.Pause(cfg, *pauseDevices)
  198. case resume.FullCommand():
  199. return commands.Resume(cfg, *resumeDevices)
  200. }
  201. return nil
  202. }()
  203. app.FatalIfError(err, "")
  204. }