Fix syncthing binary path detection.
[src/xds/xds-server.git] / lib / xdsconfig / fileconfig.go
1 package xdsconfig
2
3 import (
4         "encoding/json"
5         "os"
6         "os/user"
7         "path"
8         "path/filepath"
9         "strings"
10
11         common "github.com/iotbzh/xds-common/golib"
12 )
13
14 const (
15         // ConfigDir Directory in user HOME directory where xds config will be saved
16         ConfigDir = ".xds-server"
17         // GlobalConfigFilename Global config filename
18         GlobalConfigFilename = "config.json"
19         // FoldersConfigFilename Folders config filename
20         FoldersConfigFilename = "server-config_folders.xml"
21 )
22
23 // SyncThingConf definition
24 type SyncThingConf struct {
25         BinDir          string `json:"binDir"`
26         Home            string `json:"home"`
27         GuiAddress      string `json:"gui-address"`
28         GuiAPIKey       string `json:"gui-apikey"`
29         RescanIntervalS int    `json:"rescanIntervalS"`
30 }
31
32 // FileConfig is the JSON structure of xds-server config file (config.json)
33 type FileConfig struct {
34         WebAppDir    string         `json:"webAppDir"`
35         ShareRootDir string         `json:"shareRootDir"`
36         SdkRootDir   string         `json:"sdkRootDir"`
37         HTTPPort     string         `json:"httpPort"`
38         SThgConf     *SyncThingConf `json:"syncthing"`
39         LogsDir      string         `json:"logsDir"`
40 }
41
42 // readGlobalConfig reads configuration from a config file.
43 // Order to determine which config file is used:
44 //  1/ from command line option: "--config myConfig.json"
45 //  2/ $HOME/.xds-server/config.json file
46 //  3/ /etc/xds-server/config.json file
47 //  4/ <xds-server executable dir>/config.json file
48 func readGlobalConfig(c *Config, confFile string) error {
49
50         searchIn := make([]string, 0, 3)
51         if confFile != "" {
52                 searchIn = append(searchIn, confFile)
53         }
54         if usr, err := user.Current(); err == nil {
55                 searchIn = append(searchIn, path.Join(usr.HomeDir, ConfigDir,
56                         GlobalConfigFilename))
57         }
58
59         searchIn = append(searchIn, "/etc/xds-server/config.json")
60
61         exePath := os.Args[0]
62         ee, _ := os.Executable()
63         exeAbsPath, err := filepath.Abs(ee)
64         if err == nil {
65                 exePath, err = filepath.EvalSymlinks(exeAbsPath)
66                 if err == nil {
67                         exePath = filepath.Dir(ee)
68                 } else {
69                         exePath = filepath.Dir(exeAbsPath)
70                 }
71         }
72         searchIn = append(searchIn, path.Join(exePath, "config.json"))
73
74         var cFile *string
75         for _, p := range searchIn {
76                 if _, err := os.Stat(p); err == nil {
77                         cFile = &p
78                         break
79                 }
80         }
81         if cFile == nil {
82                 // No config file found
83                 return nil
84         }
85         c.Log.Infof("Use config file: %s", *cFile)
86
87         // TODO move on viper package to support comments in JSON and also
88         // bind with flags (command line options)
89         // see https://github.com/spf13/viper#working-with-flags
90         fd, _ := os.Open(*cFile)
91         defer fd.Close()
92         fCfg := FileConfig{}
93         if err := json.NewDecoder(fd).Decode(&fCfg); err != nil {
94                 return err
95         }
96
97         // Support environment variables (IOW ${MY_ENV_VAR} syntax) in config.json
98         vars := []*string{
99                 &fCfg.WebAppDir,
100                 &fCfg.ShareRootDir,
101                 &fCfg.SdkRootDir,
102                 &fCfg.LogsDir}
103         if fCfg.SThgConf != nil {
104                 vars = append(vars, &fCfg.SThgConf.Home, &fCfg.SThgConf.BinDir)
105         }
106         for _, field := range vars {
107                 var err error
108                 if *field, err = common.ResolveEnvVar(*field); err != nil {
109                         return err
110                 }
111         }
112
113         // Use config file settings else use default config
114         if fCfg.WebAppDir == "" {
115                 fCfg.WebAppDir = c.FileConf.WebAppDir
116         }
117         if fCfg.ShareRootDir == "" {
118                 fCfg.ShareRootDir = c.FileConf.ShareRootDir
119         }
120         if fCfg.SdkRootDir == "" {
121                 fCfg.SdkRootDir = c.FileConf.SdkRootDir
122         }
123         if fCfg.HTTPPort == "" {
124                 fCfg.HTTPPort = c.FileConf.HTTPPort
125         }
126         if fCfg.LogsDir == "" {
127                 fCfg.LogsDir = c.FileConf.LogsDir
128         }
129
130         // Resolve webapp dir (support relative or full path)
131         fCfg.WebAppDir = strings.Trim(fCfg.WebAppDir, " ")
132         if !strings.HasPrefix(fCfg.WebAppDir, "/") && exePath != "" {
133                 cwd, _ := os.Getwd()
134
135                 // Check first from current directory
136                 for _, rootD := range []string{exePath, cwd} {
137                         ff := path.Join(rootD, fCfg.WebAppDir, "index.html")
138                         if common.Exists(ff) {
139                                 fCfg.WebAppDir = path.Join(rootD, fCfg.WebAppDir)
140                                 break
141                         }
142                 }
143         }
144
145         c.FileConf = fCfg
146         return nil
147 }
148
149 // FoldersConfigFilenameGet
150 func FoldersConfigFilenameGet() (string, error) {
151         usr, err := user.Current()
152         if err != nil {
153                 return "", err
154         }
155         return path.Join(usr.HomeDir, ConfigDir, FoldersConfigFilename), nil
156 }