You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

246 lines
6.7 KiB

  1. package command
  2. import (
  3. "fmt"
  4. "strings"
  5. "strconv"
  6. "time"
  7. "os"
  8. )
  9. func init() {
  10. cmdFuse.Run = runFuse // break init cycle
  11. }
  12. type parameter struct {
  13. name string
  14. value string
  15. }
  16. func runFuse(cmd *Command, args []string) bool {
  17. rawArgs := strings.Join(args, " ")
  18. rawArgsLen := len(rawArgs)
  19. option := strings.Builder{}
  20. options := []parameter{}
  21. masterProcess := true
  22. // first parameter
  23. i := 0
  24. for i = 0; i < rawArgsLen && rawArgs[i] != ' '; i++ {
  25. option.WriteByte(rawArgs[i])
  26. }
  27. options = append(options, parameter{"arg0", option.String()})
  28. option.Reset()
  29. for i++; i < rawArgsLen; i++ {
  30. // space separator check for filled option
  31. if rawArgs[i] == ' ' {
  32. if option.Len() > 0 {
  33. options = append(options, parameter{option.String(), "true"})
  34. option.Reset()
  35. }
  36. // dash separator read option until next space
  37. } else if rawArgs[i] == '-' {
  38. for i++; i < rawArgsLen && rawArgs[i] != ' '; i++ {
  39. option.WriteByte(rawArgs[i])
  40. }
  41. options = append(options, parameter{option.String(), "true"})
  42. option.Reset()
  43. // equal separator start option with pending value
  44. } else if rawArgs[i] == '=' {
  45. name := option.String()
  46. option.Reset()
  47. for i++; i < rawArgsLen && rawArgs[i] != ','; i++ {
  48. // double quote separator read option until next double quote
  49. if rawArgs[i] == '"' {
  50. for i++; i < rawArgsLen && rawArgs[i] != '"'; i++ {
  51. option.WriteByte(rawArgs[i])
  52. }
  53. // single quote separator read option until next single quote
  54. } else if rawArgs[i] == '\'' {
  55. for i++; i < rawArgsLen && rawArgs[i] != '\''; i++ {
  56. option.WriteByte(rawArgs[i])
  57. }
  58. // add chars before comma
  59. } else if rawArgs[i] != ' ' {
  60. option.WriteByte(rawArgs[i])
  61. }
  62. }
  63. options = append(options, parameter{name, option.String()})
  64. option.Reset()
  65. // comma separator just read current option
  66. } else if rawArgs[i] == ',' {
  67. options = append(options, parameter{option.String(), "true"})
  68. option.Reset()
  69. // what is not a separator fill option buffer
  70. } else {
  71. option.WriteByte(rawArgs[i])
  72. }
  73. }
  74. // get residual option data
  75. if option.Len() > 0 {
  76. // add value to pending option
  77. options = append(options, parameter{option.String(), "true"})
  78. option.Reset()
  79. }
  80. // scan each parameter
  81. for i := 0; i < len(options); i++ {
  82. parameter := options[i]
  83. switch parameter.name {
  84. case "child":
  85. masterProcess = false
  86. case "arg0":
  87. mountOptions.dir = &parameter.value
  88. case "filer":
  89. mountOptions.filer = &parameter.value
  90. case "filer.path":
  91. mountOptions.filerMountRootPath = &parameter.value
  92. case "dirAutoCreate":
  93. if parsed, err := strconv.ParseBool(parameter.value); err != nil {
  94. mountOptions.dirAutoCreate = &parsed
  95. } else {
  96. panic(fmt.Errorf("dirAutoCreate: %s", err))
  97. }
  98. case "collection":
  99. mountOptions.collection = &parameter.value
  100. case "replication":
  101. mountOptions.replication = &parameter.value
  102. case "disk":
  103. mountOptions.diskType = &parameter.value
  104. case "ttl":
  105. if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err != nil {
  106. intValue := int(parsed)
  107. mountOptions.ttlSec = &intValue
  108. } else {
  109. panic(fmt.Errorf("ttl: %s", err))
  110. }
  111. case "chunkSizeLimitMB":
  112. if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err != nil {
  113. intValue := int(parsed)
  114. mountOptions.chunkSizeLimitMB = &intValue
  115. } else {
  116. panic(fmt.Errorf("chunkSizeLimitMB: %s", err))
  117. }
  118. case "concurrentWriters":
  119. i++
  120. if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err != nil {
  121. intValue := int(parsed)
  122. mountOptions.concurrentWriters = &intValue
  123. } else {
  124. panic(fmt.Errorf("concurrentWriters: %s", err))
  125. }
  126. case "cacheDir":
  127. mountOptions.cacheDir = &parameter.value
  128. case "cacheCapacityMB":
  129. if parsed, err := strconv.ParseInt(parameter.value, 0, 64); err != nil {
  130. mountOptions.cacheSizeMB = &parsed
  131. } else {
  132. panic(fmt.Errorf("cacheCapacityMB: %s", err))
  133. }
  134. case "dataCenter":
  135. mountOptions.dataCenter = &parameter.value
  136. case "allowOthers":
  137. if parsed, err := strconv.ParseBool(parameter.value); err != nil {
  138. mountOptions.allowOthers = &parsed
  139. } else {
  140. panic(fmt.Errorf("allowOthers: %s", err))
  141. }
  142. case "umask":
  143. mountOptions.umaskString = &parameter.value
  144. case "nonempty":
  145. if parsed, err := strconv.ParseBool(parameter.value); err != nil {
  146. mountOptions.nonempty = &parsed
  147. } else {
  148. panic(fmt.Errorf("nonempty: %s", err))
  149. }
  150. case "volumeServerAccess":
  151. mountOptions.volumeServerAccess = &parameter.value
  152. case "map.uid":
  153. mountOptions.uidMap = &parameter.value
  154. case "map.gid":
  155. mountOptions.gidMap = &parameter.value
  156. case "readOnly":
  157. if parsed, err := strconv.ParseBool(parameter.value); err != nil {
  158. mountOptions.readOnly = &parsed
  159. } else {
  160. panic(fmt.Errorf("readOnly: %s", err))
  161. }
  162. case "cpuprofile":
  163. mountCpuProfile = &parameter.value
  164. case "memprofile":
  165. mountMemProfile = &parameter.value
  166. case "readRetryTime":
  167. if parsed, err := time.ParseDuration(parameter.value); err != nil {
  168. mountReadRetryTime = &parsed
  169. } else {
  170. panic(fmt.Errorf("readRetryTime: %s", err))
  171. }
  172. }
  173. }
  174. // the master start the child, release it then finish himself
  175. if masterProcess {
  176. arg0 := os.Args[0]
  177. argv := append(os.Args, "-o", "child")
  178. attr := os.ProcAttr{}
  179. child, err := os.StartProcess(arg0, argv, &attr)
  180. if err != nil {
  181. panic(fmt.Errorf("master process can not start child process: %s", err))
  182. }
  183. err = child.Release()
  184. if err != nil {
  185. panic(fmt.Errorf("master process can not release child process: %s", err))
  186. }
  187. return true
  188. }
  189. // I don't know why PATH environment variable is lost
  190. if err := os.Setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"); err != nil {
  191. panic(fmt.Errorf("setenv: %s", err))
  192. }
  193. // just call "weed mount" command
  194. return runMount(cmdMount, []string{})
  195. }
  196. var cmdFuse = &Command{
  197. UsageLine: "fuse /mnt/mount/point -o \"filer=localhost:8888,filer.path=/\"",
  198. Short: "Allow use weed with linux's mount command",
  199. Long: `Allow use weed with linux's mount command
  200. You can use -t weed on mount command:
  201. mv weed /sbin/mount.weed
  202. mount -t weed fuse /mnt -o "filer=localhost:8888,filer.path=/"
  203. Or you can use -t fuse on mount command:
  204. mv weed /sbin/weed
  205. mount -t fuse.weed fuse /mnt -o "filer=localhost:8888,filer.path=/"
  206. mount -t fuse "weed#fuse" /mnt -o "filer=localhost:8888,filer.path=/"
  207. To use without mess with your /sbin:
  208. mount -t fuse./home/user/bin/weed fuse /mnt -o "filer=localhost:8888,filer.path=/"
  209. mount -t fuse "/home/user/bin/weed#fuse" /mnt -o "filer=localhost:8888,filer.path=/"
  210. To pass more than one parameter use quotes, example:
  211. mount -t weed fuse /mnt -o "filer='192.168.0.1:8888,192.168.0.2:8888',filer.path=/"
  212. To check valid options look "weed mount --help"
  213. `,
  214. }