github.com/cs3org/reva/v2@v2.27.7/cmd/revad/main.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package main 20 21 import ( 22 "flag" 23 "fmt" 24 "os" 25 "path" 26 "regexp" 27 "sync" 28 "syscall" 29 30 "github.com/cs3org/reva/v2/cmd/revad/internal/config" 31 "github.com/cs3org/reva/v2/cmd/revad/internal/grace" 32 "github.com/cs3org/reva/v2/cmd/revad/runtime" 33 "github.com/cs3org/reva/v2/pkg/sysinfo" 34 35 "github.com/google/uuid" 36 ) 37 38 var ( 39 versionFlag = flag.Bool("version", false, "show version and exit") 40 testFlag = flag.Bool("t", false, "test configuration and exit") 41 signalFlag = flag.String("s", "", "send signal to a master process: stop, quit, reload") 42 configFlag = flag.String("c", "/etc/revad/revad.toml", "set configuration file") 43 pidFlag = flag.String("p", "", "pid file. If empty defaults to a random file in the OS temporary directory") 44 logFlag = flag.String("log", "", "log messages with the given severity or above. One of: [trace, debug, info, warn, error, fatal, panic]") 45 dirFlag = flag.String("dev-dir", "", "runs any toml file in the specified directory. Intended for development use only") 46 47 // Compile time variables initialized with gcc flags. 48 gitCommit, buildDate, version, goVersion string 49 ) 50 51 func main() { 52 flag.Parse() 53 54 // initialize the global system information 55 if err := sysinfo.InitSystemInfo(&sysinfo.RevaVersion{Version: version, BuildDate: buildDate, GitCommit: gitCommit, GoVersion: goVersion}); err != nil { 56 fmt.Fprintf(os.Stderr, "error initializing system info: %s\n", err.Error()) 57 // This is not really a fatal error, so don't panic 58 } 59 60 handleVersionFlag() 61 handleSignalFlag() 62 63 confs, err := getConfigs() 64 if err != nil { 65 fmt.Fprintf(os.Stderr, "error reading the configuration file(s): %s\n", err.Error()) 66 os.Exit(1) 67 } 68 69 // if there is more than one configuration available and 70 // the pid flag has been set we abort as the pid flag 71 // is meant to work only with one main configuration. 72 if len(confs) != 1 && *pidFlag != "" { 73 fmt.Fprintf(os.Stderr, "cannot run with with multiple configurations and one pid file, remote the -p flag\n") 74 os.Exit(1) 75 } 76 77 // if test flag is true we exit as this flag only tests for valid configurations. 78 if *testFlag { 79 os.Exit(0) 80 } 81 82 runConfigs(confs) 83 } 84 85 func handleVersionFlag() { 86 if *versionFlag { 87 fmt.Fprintf(os.Stderr, "%s\n", getVersionString()) 88 os.Exit(0) 89 } 90 } 91 92 func getVersionString() string { 93 msg := "version=%s " 94 msg += "commit=%s " 95 msg += "go_version=%s " 96 msg += "build_date=%s" 97 98 return fmt.Sprintf(msg, version, gitCommit, goVersion, buildDate) 99 } 100 101 func handleSignalFlag() { 102 if *signalFlag != "" { 103 var signal syscall.Signal 104 switch *signalFlag { 105 case "reload": 106 signal = syscall.SIGHUP 107 case "quit": 108 signal = syscall.SIGQUIT 109 case "stop": 110 signal = syscall.SIGTERM 111 default: 112 fmt.Fprintf(os.Stderr, "unknown signal %q\n", *signalFlag) 113 os.Exit(1) 114 } 115 116 // check that we have a valid pidfile 117 if *pidFlag == "" { 118 fmt.Fprintf(os.Stderr, "-s flag not set, no clue where the pidfile is stored. Check the logs for its location.\n") 119 os.Exit(1) 120 } 121 process, err := grace.GetProcessFromFile(*pidFlag) 122 if err != nil { 123 fmt.Fprintf(os.Stderr, "error getting process from pidfile: %v\n", err) 124 os.Exit(1) 125 } 126 127 // kill process with signal 128 if err := process.Signal(signal); err != nil { 129 fmt.Fprintf(os.Stderr, "error signaling process %d with signal %s\n", process.Pid, signal) 130 os.Exit(1) 131 } 132 133 os.Exit(0) 134 } 135 } 136 137 func getConfigs() ([]map[string]interface{}, error) { 138 var confs []string 139 // give priority to read from dev-dir 140 if *dirFlag != "" { 141 cfgs, err := getConfigsFromDir(*dirFlag) 142 if err != nil { 143 return nil, err 144 } 145 confs = append(confs, cfgs...) 146 } else { 147 confs = append(confs, *configFlag) 148 } 149 150 // if we don't have a config file we abort 151 if len(confs) == 0 { 152 fmt.Fprintf(os.Stderr, "no configuration found\n") 153 os.Exit(1) 154 } 155 156 configs, err := readConfigs(confs) 157 if err != nil { 158 return nil, err 159 } 160 161 return configs, nil 162 } 163 164 func getConfigsFromDir(dir string) (confs []string, err error) { 165 files, err := os.ReadDir(*dirFlag) 166 if err != nil { 167 return nil, err 168 } 169 170 for _, value := range files { 171 if !value.IsDir() { 172 expr := regexp.MustCompile(`[\w].toml`) 173 if expr.Match([]byte(value.Name())) { 174 confs = append(confs, path.Join(dir, value.Name())) 175 } 176 } 177 } 178 return 179 } 180 181 func readConfigs(files []string) ([]map[string]interface{}, error) { 182 confs := make([]map[string]interface{}, 0, len(files)) 183 for _, conf := range files { 184 fd, err := os.Open(conf) 185 if err != nil { 186 return nil, err 187 } 188 defer fd.Close() 189 190 v, err := config.Read(fd) 191 if err != nil { 192 return nil, err 193 } 194 confs = append(confs, v) 195 } 196 return confs, nil 197 } 198 199 func runConfigs(confs []map[string]interface{}) { 200 if len(confs) == 1 { 201 runSingle(confs[0]) 202 return 203 } 204 205 runMultiple(confs) 206 } 207 208 func runSingle(conf map[string]interface{}) { 209 if *pidFlag == "" { 210 *pidFlag = getPidfile() 211 } 212 213 runtime.Run(conf, *pidFlag, *logFlag) 214 } 215 216 func getPidfile() string { 217 uuid := uuid.New().String() 218 name := fmt.Sprintf("revad-%s.pid", uuid) 219 220 return path.Join(os.TempDir(), name) 221 } 222 223 func runMultiple(confs []map[string]interface{}) { 224 var wg sync.WaitGroup 225 for _, conf := range confs { 226 wg.Add(1) 227 pidfile := getPidfile() 228 go func(wg *sync.WaitGroup, conf map[string]interface{}) { 229 defer wg.Done() 230 runtime.Run(conf, pidfile, *logFlag) 231 }(&wg, conf) 232 } 233 wg.Wait() 234 os.Exit(0) 235 }