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.

280 lines
5.7 KiB

13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
  1. package main
  2. import (
  3. "code.google.com/p/weed-fs/go/glog"
  4. "encoding/json"
  5. "flag"
  6. "fmt"
  7. "io"
  8. "math/rand"
  9. "net"
  10. "net/http"
  11. "os"
  12. "path/filepath"
  13. "strings"
  14. "sync"
  15. "text/template"
  16. "time"
  17. "unicode"
  18. "unicode/utf8"
  19. )
  20. var IsDebug *bool
  21. var server *string
  22. var commands = []*Command{
  23. cmdCompact,
  24. cmdFix,
  25. cmdMaster,
  26. cmdUpload,
  27. cmdDownload,
  28. cmdShell,
  29. cmdVersion,
  30. cmdVolume,
  31. cmdExport,
  32. }
  33. var exitStatus = 0
  34. var exitMu sync.Mutex
  35. func setExitStatus(n int) {
  36. exitMu.Lock()
  37. if exitStatus < n {
  38. exitStatus = n
  39. }
  40. exitMu.Unlock()
  41. }
  42. func main() {
  43. glog.ToStderrAndLog()
  44. glog.MaxSize = 1024 * 1024 * 32
  45. rand.Seed(time.Now().UnixNano())
  46. flag.Usage = usage
  47. flag.Parse()
  48. args := flag.Args()
  49. if len(args) < 1 {
  50. usage()
  51. }
  52. if args[0] == "help" {
  53. help(args[1:])
  54. for _, cmd := range commands {
  55. if len(args) >= 2 && cmd.Name() == args[1] && cmd.Run != nil {
  56. fmt.Fprintf(os.Stderr, "Default Parameters:\n")
  57. cmd.Flag.PrintDefaults()
  58. }
  59. }
  60. return
  61. }
  62. for _, cmd := range commands {
  63. if cmd.Name() == args[0] && cmd.Run != nil {
  64. cmd.Flag.Usage = func() { cmd.Usage() }
  65. cmd.Flag.Parse(args[1:])
  66. args = cmd.Flag.Args()
  67. IsDebug = cmd.IsDebug
  68. if !cmd.Run(cmd, args) {
  69. fmt.Fprintf(os.Stderr, "\n")
  70. cmd.Flag.Usage()
  71. fmt.Fprintf(os.Stderr, "Default Parameters:\n")
  72. cmd.Flag.PrintDefaults()
  73. }
  74. exit()
  75. return
  76. }
  77. }
  78. fmt.Fprintf(os.Stderr, "weed: unknown subcommand %q\nRun 'weed help' for usage.\n", args[0])
  79. setExitStatus(2)
  80. exit()
  81. }
  82. var usageTemplate = `WeedFS is a software to store billions of files and serve them fast!
  83. Usage:
  84. weed command [arguments]
  85. The commands are:
  86. {{range .}}{{if .Runnable}}
  87. {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
  88. Use "weed help [command]" for more information about a command.
  89. `
  90. var helpTemplate = `{{if .Runnable}}Usage: weed {{.UsageLine}}
  91. {{end}}
  92. {{.Long}}
  93. `
  94. // tmpl executes the given template text on data, writing the result to w.
  95. func tmpl(w io.Writer, text string, data interface{}) {
  96. t := template.New("top")
  97. t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize})
  98. template.Must(t.Parse(text))
  99. if err := t.Execute(w, data); err != nil {
  100. panic(err)
  101. }
  102. }
  103. func capitalize(s string) string {
  104. if s == "" {
  105. return s
  106. }
  107. r, n := utf8.DecodeRuneInString(s)
  108. return string(unicode.ToTitle(r)) + s[n:]
  109. }
  110. func printUsage(w io.Writer) {
  111. tmpl(w, usageTemplate, commands)
  112. }
  113. func usage() {
  114. printUsage(os.Stderr)
  115. os.Exit(2)
  116. }
  117. // help implements the 'help' command.
  118. func help(args []string) {
  119. if len(args) == 0 {
  120. printUsage(os.Stdout)
  121. // not exit 2: succeeded at 'weed help'.
  122. return
  123. }
  124. if len(args) != 1 {
  125. fmt.Fprintf(os.Stderr, "usage: weed help command\n\nToo many arguments given.\n")
  126. os.Exit(2) // failed at 'weed help'
  127. }
  128. arg := args[0]
  129. for _, cmd := range commands {
  130. if cmd.Name() == arg {
  131. tmpl(os.Stdout, helpTemplate, cmd)
  132. // not exit 2: succeeded at 'weed help cmd'.
  133. return
  134. }
  135. }
  136. fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'weed help'.\n", arg)
  137. os.Exit(2) // failed at 'weed help cmd'
  138. }
  139. var atexitFuncs []func()
  140. func atexit(f func()) {
  141. atexitFuncs = append(atexitFuncs, f)
  142. }
  143. func exit() {
  144. for _, f := range atexitFuncs {
  145. f()
  146. }
  147. os.Exit(exitStatus)
  148. }
  149. func exitIfErrors() {
  150. if exitStatus != 0 {
  151. exit()
  152. }
  153. }
  154. func writeJson(w http.ResponseWriter, r *http.Request, obj interface{}) (err error) {
  155. w.Header().Set("Content-Type", "application/javascript")
  156. var bytes []byte
  157. if r.FormValue("pretty") != "" {
  158. bytes, err = json.MarshalIndent(obj, "", " ")
  159. } else {
  160. bytes, err = json.Marshal(obj)
  161. }
  162. if err != nil {
  163. return
  164. }
  165. callback := r.FormValue("callback")
  166. if callback == "" {
  167. _, err = w.Write(bytes)
  168. } else {
  169. if _, err = w.Write([]uint8(callback)); err != nil {
  170. return
  171. }
  172. if _, err = w.Write([]uint8("(")); err != nil {
  173. return
  174. }
  175. fmt.Fprint(w, string(bytes))
  176. if _, err = w.Write([]uint8(")")); err != nil {
  177. return
  178. }
  179. }
  180. return
  181. }
  182. // wrapper for writeJson - just logs errors
  183. func writeJsonQuiet(w http.ResponseWriter, r *http.Request, obj interface{}) {
  184. if err := writeJson(w, r, obj); err != nil {
  185. glog.V(0).Infof("error writing JSON %s: %s", obj, err.Error())
  186. }
  187. }
  188. func writeJsonError(w http.ResponseWriter, r *http.Request, err error) {
  189. m := make(map[string]interface{})
  190. m["error"] = err.Error()
  191. writeJsonQuiet(w, r, m)
  192. }
  193. func debug(params ...interface{}) {
  194. if *IsDebug {
  195. glog.V(0).Infoln(params)
  196. }
  197. }
  198. func secure(whiteList []string, f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
  199. return func(w http.ResponseWriter, r *http.Request) {
  200. if len(whiteList) == 0 {
  201. f(w, r)
  202. return
  203. }
  204. host, _, err := net.SplitHostPort(r.RemoteAddr)
  205. if err == nil {
  206. for _, ip := range whiteList {
  207. if ip == host {
  208. f(w, r)
  209. return
  210. }
  211. }
  212. }
  213. writeJsonQuiet(w, r, map[string]interface{}{"error": "No write permisson from " + host})
  214. }
  215. }
  216. func parseURLPath(path string) (vid, fid, filename, ext string, isVolumeIdOnly bool) {
  217. switch strings.Count(path, "/") {
  218. case 3:
  219. parts := strings.Split(path, "/")
  220. vid, fid, filename = parts[1], parts[2], parts[3]
  221. ext = filepath.Ext(filename)
  222. case 2:
  223. parts := strings.Split(path, "/")
  224. vid, fid = parts[1], parts[2]
  225. dotIndex := strings.LastIndex(fid, ".")
  226. if dotIndex > 0 {
  227. ext = fid[dotIndex:]
  228. fid = fid[0:dotIndex]
  229. }
  230. default:
  231. sepIndex := strings.LastIndex(path, "/")
  232. commaIndex := strings.LastIndex(path[sepIndex:], ",")
  233. if commaIndex <= 0 {
  234. vid, isVolumeIdOnly = path[sepIndex+1:], true
  235. return
  236. }
  237. dotIndex := strings.LastIndex(path[sepIndex:], ".")
  238. vid = path[sepIndex+1 : commaIndex]
  239. fid = path[commaIndex+1:]
  240. ext = ""
  241. if dotIndex > 0 {
  242. fid = path[commaIndex+1 : dotIndex]
  243. ext = path[dotIndex:]
  244. }
  245. }
  246. return
  247. }