cubefs
307 строк · 7.8 Кб
1// Copyright 2022 The CubeFS Authors.
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
12// implied. See the License for the specific language governing
13// permissions and limitations under the License.
14
15package main
16
17import (
18"flag"
19"fmt"
20syslog "log"
21"net/http"
22"net/http/pprof"
23"os"
24"os/signal"
25"path"
26"path/filepath"
27"runtime"
28"strings"
29"syscall"
30
31"github.com/cubefs/cubefs/blockcache/bcache"
32"github.com/cubefs/cubefs/cmd/common"
33"github.com/cubefs/cubefs/proto"
34"github.com/cubefs/cubefs/util/config"
35"github.com/cubefs/cubefs/util/errors"
36"github.com/cubefs/cubefs/util/log"
37"github.com/cubefs/cubefs/util/stat"
38sysutil "github.com/cubefs/cubefs/util/sys"
39"github.com/cubefs/cubefs/util/ump"
40"github.com/jacobsa/daemonize"
41)
42
43//TODO: remove this later.
44//go:generate golangci-lint run --issues-exit-code=1 -D errcheck -E bodyclose ./...
45
46const (
47ConfigKeyLogDir = "logDir"
48ConfigKeyLogLevel = "logLevel"
49ConfigKeyProfPort = "prof"
50ConfigKeyWarnLogDir = "warnLogDir"
51
52RoleBcache = "blockcache"
53
54LoggerOutput = "output.log"
55)
56
57var (
58configFile = flag.String("c", "", "config file path")
59configVersion = flag.Bool("v", false, "show version")
60configForeground = flag.Bool("f", false, "run foreground")
61)
62
63func interceptSignal(s common.Server) {
64sigC := make(chan os.Signal, 1)
65signal.Notify(sigC, syscall.SIGINT, syscall.SIGTERM)
66syslog.Println("action[interceptSignal] register system signal.")
67go func() {
68for {
69sig := <-sigC
70syslog.Printf("action[interceptSignal] received signal: %s. pid %d", sig.String(), os.Getpid())
71s.Shutdown()
72}
73}()
74}
75
76func modifyOpenFiles() (err error) {
77var rLimit syscall.Rlimit
78err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
79if err != nil {
80return fmt.Errorf("Error Getting Rlimit %v", err.Error())
81}
82syslog.Println(rLimit)
83rLimit.Max = 1024000
84rLimit.Cur = 1024000
85err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
86if err != nil {
87return fmt.Errorf("Error Setting Rlimit %v", err.Error())
88}
89err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
90if err != nil {
91return fmt.Errorf("Error Getting Rlimit %v", err.Error())
92}
93syslog.Println("Rlimit Final", rLimit)
94return
95}
96
97func main() {
98if os.Args[1] == "stop" {
99os.Exit(0)
100}
101
102flag.Parse()
103
104Version := proto.DumpVersion("Server")
105if *configVersion {
106fmt.Printf("%v", Version)
107os.Exit(0)
108}
109
110/*
111* LoadConfigFile should be checked before start daemon, since it will
112* call os.Exit() w/o notifying the parent process.
113*/
114cfg, err := config.LoadConfigFile(*configFile)
115if err != nil {
116daemonize.SignalOutcome(err)
117os.Exit(1)
118}
119
120if !*configForeground {
121if err := startDaemon(); err != nil {
122fmt.Printf("Server start failed: %v\n", err)
123os.Exit(1)
124}
125os.Exit(0)
126}
127
128/*
129* We are in daemon from here.
130* Must notify the parent process through SignalOutcome anyway.
131*/
132
133role := RoleBcache
134logDir := cfg.GetString(ConfigKeyLogDir)
135logLevel := cfg.GetString(ConfigKeyLogLevel)
136profPort := cfg.GetString(ConfigKeyProfPort)
137umpDatadir := cfg.GetString(ConfigKeyWarnLogDir)
138
139// Init server instance with specified role configuration.
140var (
141server common.Server
142module string
143)
144switch role {
145case RoleBcache:
146server = bcache.NewServer()
147module = RoleBcache
148default:
149err = errors.NewErrorf("Fatal: role mismatch: %s", role)
150fmt.Println(err)
151daemonize.SignalOutcome(err)
152os.Exit(1)
153}
154
155// Init logging
156var (
157level log.Level
158)
159switch strings.ToLower(logLevel) {
160case "debug":
161level = log.DebugLevel
162case "info":
163level = log.InfoLevel
164case "warn":
165level = log.WarnLevel
166case "error":
167level = log.ErrorLevel
168default:
169level = log.ErrorLevel
170}
171
172_, err = log.InitLog(logDir, module, level, nil, log.DefaultLogLeftSpaceLimit)
173if err != nil {
174err = errors.NewErrorf("Fatal: failed to init log - %v", err)
175fmt.Println(err)
176daemonize.SignalOutcome(err)
177os.Exit(1)
178}
179defer log.LogFlush()
180
181// Init output file
182outputFilePath := path.Join(logDir, module, LoggerOutput)
183outputFile, err := os.OpenFile(outputFilePath, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0o666)
184if err != nil {
185err = errors.NewErrorf("Fatal: failed to open output path - %v", err)
186fmt.Println(err)
187daemonize.SignalOutcome(err)
188os.Exit(1)
189}
190// stat log
191_, err = stat.NewStatistic(logDir, "blockcache", int64(stat.DefaultStatLogSize),
192stat.DefaultTimeOutUs, true)
193if err != nil {
194err = errors.NewErrorf("Init stat log fail: %v\n", err)
195fmt.Println(err)
196daemonize.SignalOutcome(err)
197os.Exit(1)
198}
199stat.ClearStat()
200
201defer func() {
202outputFile.Sync()
203outputFile.Close()
204}()
205syslog.SetOutput(outputFile)
206
207if err = sysutil.RedirectFD(int(outputFile.Fd()), int(os.Stderr.Fd())); err != nil {
208err = errors.NewErrorf("Fatal: failed to redirect fd - %v", err)
209syslog.Println(err)
210daemonize.SignalOutcome(err)
211os.Exit(1)
212}
213
214syslog.Printf("Hello, CubeFS Storage\n%s\n", Version)
215
216err = modifyOpenFiles()
217if err != nil {
218err = errors.NewErrorf("Fatal: failed to modify open files - %v", err)
219syslog.Println(err)
220daemonize.SignalOutcome(err)
221os.Exit(1)
222}
223
224// for multi-cpu scheduling
225runtime.GOMAXPROCS(runtime.NumCPU())
226if err = ump.InitUmp(role, umpDatadir); err != nil {
227log.LogFlush()
228err = errors.NewErrorf("Fatal: failed to init ump warnLogDir - %v", err)
229syslog.Println(err)
230daemonize.SignalOutcome(err)
231os.Exit(1)
232}
233
234if profPort != "" {
235go func() {
236mainMux := http.NewServeMux()
237mux := http.NewServeMux()
238http.HandleFunc(log.SetLogLevelPath, log.SetLogLevel)
239mux.Handle("/debug/pprof", http.HandlerFunc(pprof.Index))
240mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
241mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
242mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
243mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
244mux.Handle("/debug/", http.HandlerFunc(pprof.Index))
245mainHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
246if strings.HasPrefix(req.URL.Path, "/debug/") {
247mux.ServeHTTP(w, req)
248} else {
249http.DefaultServeMux.ServeHTTP(w, req)
250}
251})
252mainMux.Handle("/", mainHandler)
253e := http.ListenAndServe(fmt.Sprintf(":%v", profPort), mainMux)
254if e != nil {
255log.LogFlush()
256err = errors.NewErrorf("cannot listen pprof %v err %v", profPort, err)
257syslog.Println(err)
258daemonize.SignalOutcome(err)
259os.Exit(1)
260}
261}()
262}
263
264interceptSignal(server)
265err = server.Start(cfg)
266if err != nil {
267log.LogFlush()
268err = errors.NewErrorf("Fatal: failed to start the CubeFS %s daemon err %v - ", role, err)
269syslog.Println(err)
270daemonize.SignalOutcome(err)
271os.Exit(1)
272}
273
274daemonize.SignalOutcome(nil)
275
276// Block main goroutine until server shutdown.
277server.Sync()
278log.LogFlush()
279os.Exit(0)
280}
281
282func startDaemon() error {
283cmdPath, err := os.Executable()
284if err != nil {
285return fmt.Errorf("startDaemon failed: cannot get absolute command path, err(%v)", err)
286}
287
288configPath, err := filepath.Abs(*configFile)
289if err != nil {
290return fmt.Errorf("startDaemon failed: cannot get absolute command path of config file(%v) , err(%v)", *configFile, err)
291}
292
293args := []string{"-f"}
294args = append(args, "-c")
295args = append(args, configPath)
296
297env := []string{
298fmt.Sprintf("PATH=%s", os.Getenv("PATH")),
299}
300
301err = daemonize.Run(cmdPath, args, env, os.Stdout)
302if err != nil {
303return fmt.Errorf("startDaemon failed: daemon start failed, cmd(%v) args(%v) env(%v) err(%v)\n", cmdPath, args, env, err)
304}
305
306return nil
307}
308