14 "github.com/Sirupsen/logrus"
15 "github.com/codegangsta/cli"
16 "github.com/iotbzh/xds-server/lib/crosssdk"
17 "github.com/iotbzh/xds-server/lib/folder"
18 "github.com/iotbzh/xds-server/lib/model"
19 "github.com/iotbzh/xds-server/lib/syncthing"
20 "github.com/iotbzh/xds-server/lib/webserver"
21 "github.com/iotbzh/xds-server/lib/xdsconfig"
25 appName = "xds-server"
26 appDescription = "X(cross) Development System Server is a web server that allows to remotely cross build applications."
27 appCopyright = "Apache-2.0"
28 appUsage = "X(cross) Development System Server"
31 var appAuthors = []cli.Author{
32 cli.Author{Name: "Sebastien Douheret", Email: "sebastien@iot.bzh"},
35 // AppVersion is the version of this application
36 var AppVersion = "?.?.?"
38 // AppSubVersion is the git tag id added to version string
39 // Should be set by compilation -ldflags "-X main.AppSubVersion=xxx"
40 var AppSubVersion = "unknown-dev"
42 // Context holds the XDS server context
46 Config *xdsconfig.Config
52 MFolders *model.Folders
54 WWWServer *webserver.Server
58 // NewContext Create a new instance of XDS server
59 func NewContext(cliCtx *cli.Context) *Context {
62 // Set logger level and formatter
63 log := cliCtx.App.Metadata["logger"].(*logrus.Logger)
65 logLevel := cliCtx.GlobalString("log")
67 logLevel = "error" // FIXME get from Config DefaultLogLevel
69 if log.Level, err = logrus.ParseLevel(logLevel); err != nil {
70 fmt.Printf("Invalid log level : \"%v\"\n", logLevel)
73 log.Formatter = &logrus.TextFormatter{}
75 sillyVal, sillyLog := os.LookupEnv("XDS_LOG_SILLY")
77 // Define default configuration
79 ProgName: cliCtx.App.Name,
82 LogLevelSilly: (sillyLog && sillyVal == "1"),
83 Exit: make(chan os.Signal, 1),
86 // register handler on SIGTERM / exit
87 signal.Notify(ctx.Exit, os.Interrupt, syscall.SIGTERM)
88 go handlerSigTerm(&ctx)
93 // Handle exit and properly stop/close all stuff
94 func handlerSigTerm(ctx *Context) {
97 ctx.Log.Infof("Stoping Syncthing... (PID %d)", ctx.SThgCmd.Process.Pid)
99 ctx.Log.Infof("Stoping Syncthing-inotify... (PID %d)", ctx.SThgInotCmd.Process.Pid)
100 ctx.SThg.StopInotify()
102 if ctx.WWWServer != nil {
103 ctx.Log.Infof("Stoping Web server...")
109 // Helper function to log message on both stdout and logger
110 func logPrint(ctx *Context, format string, args ...interface{}) {
111 fmt.Printf(format, args...)
112 if ctx.Log.Out != os.Stdout {
113 ctx.Log.Infof(format, args...)
117 // XDS Server application main routine
118 func xdsApp(cliCtx *cli.Context) error {
121 // Create XDS server context
122 ctx := NewContext(cliCtx)
125 cfg, err := xdsconfig.Init(ctx.Cli, ctx.Log)
127 return cli.NewExitError(err, -2)
131 // Logs redirected into a file when logfile option or logsDir config is set
132 ctx.Config.LogVerboseOut = os.Stderr
133 if ctx.Config.FileConf.LogsDir != "" {
134 if ctx.Config.Options.LogFile != "stdout" {
135 logFile := ctx.Config.Options.LogFile
137 fdL, err := os.OpenFile(logFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
139 msgErr := fmt.Sprintf("Cannot create log file %s", logFile)
140 return cli.NewExitError(msgErr, int(syscall.EPERM))
144 logPrint(ctx, "Logging file: %s\n", logFile)
147 logFileHTTPReq := filepath.Join(ctx.Config.FileConf.LogsDir, "xds-server-verbose.log")
148 fdLH, err := os.OpenFile(logFileHTTPReq, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
150 msgErr := fmt.Sprintf("Cannot create log file %s", logFileHTTPReq)
151 return cli.NewExitError(msgErr, int(syscall.EPERM))
153 ctx.Config.LogVerboseOut = fdLH
155 logPrint(ctx, "Logging file for HTTP requests: %s\n", logFileHTTPReq)
158 // Create syncthing instance when section "syncthing" is present in config.json
159 if ctx.Config.FileConf.SThgConf != nil {
160 ctx.SThg = st.NewSyncThing(ctx.Config, ctx.Log)
163 // Start local instance of Syncthing and Syncthing-notify
165 ctx.Log.Infof("Starting Syncthing...")
166 ctx.SThgCmd, err = ctx.SThg.Start()
168 return cli.NewExitError(err, -4)
170 logPrint(ctx, "Syncthing started (PID %d)\n", ctx.SThgCmd.Process.Pid)
172 ctx.Log.Infof("Starting Syncthing-inotify...")
173 ctx.SThgInotCmd, err = ctx.SThg.StartInotify()
175 return cli.NewExitError(err, -4)
177 logPrint(ctx, "Syncthing-inotify started (PID %d)\n", ctx.SThgInotCmd.Process.Pid)
179 // Establish connection with local Syncthing (retry if connection fail)
180 logPrint(ctx, "Establishing connection with Syncthing...\n")
181 time.Sleep(2 * time.Second)
186 if err = ctx.SThg.Connect(); err == nil {
189 ctx.Log.Warningf("Establishing connection to Syncthing (retry %d/%d)", retry, maxRetry)
190 time.Sleep(time.Second)
193 if err != nil || retry == 0 {
194 return cli.NewExitError(err, -4)
197 // FIXME: do we still need Builder notion ? if no cleanup
198 if ctx.Config.Builder, err = xdsconfig.NewBuilderConfig(ctx.SThg.MyID); err != nil {
199 return cli.NewExitError(err, -4)
201 ctx.Config.SupportedSharing[folder.TypeCloudSync] = true
205 ctx.MFolders = model.FoldersNew(ctx.Config, ctx.SThg)
207 // Load initial folders config from disk
208 if err := ctx.MFolders.LoadConfig(); err != nil {
209 return cli.NewExitError(err, -5)
213 ctx.SDKs, err = crosssdk.Init(ctx.Config, ctx.Log)
215 return cli.NewExitError(err, -6)
219 ctx.WWWServer = webserver.New(ctx.Config, ctx.MFolders, ctx.SDKs, ctx.Log, ctx.LogLevelSilly)
221 // Run Web Server until exit requested (blocking call)
222 if err = ctx.WWWServer.Serve(); err != nil {
224 return cli.NewExitError(err, -7)
227 return cli.NewExitError("Program exited ", -99)
233 // Create a new instance of the logger
236 // Create a new App instance
239 app.Description = appDescription
241 app.Version = AppVersion + " (" + AppSubVersion + ")"
242 app.Authors = appAuthors
243 app.Copyright = appCopyright
244 app.Metadata = make(map[string]interface{})
245 app.Metadata["version"] = AppVersion
246 app.Metadata["git-tag"] = AppSubVersion
247 app.Metadata["logger"] = log
249 app.Flags = []cli.Flag{
252 Usage: "JSON config file to use\n\t",
253 EnvVar: "APP_CONFIG",
258 Usage: "logging level (supported levels: panic, fatal, error, warn, info, debug)\n\t",
264 Usage: "filename where logs will be redirected (default stdout)\n\t",
265 EnvVar: "LOG_FILENAME",
268 Name: "no-folderconfig, nfc",
269 Usage: fmt.Sprintf("Do not read folder config file (%s)\n\t", xdsconfig.FoldersConfigFilename),
270 EnvVar: "NO_FOLDERCONFIG",
274 // only one action: Web Server