package main import ( "database/sql" "encoding/json" "flag" "fmt" "html/template" "log" "net/http" "os" "os/signal" "path/filepath" "strconv" "strings" "syscall" "time" "redisdog/model" _ "github.com/mattn/go-sqlite3" ) type Config struct { Host string Port string Database string RootPwd string LogDir string TmplDir string ResourcesDir string SessionTTL int64 } var ( Cfg *Config Db *sql.DB SessPoll *SessionPoll TmplFuncMap template.FuncMap SysCfg *SysCfgCache Srv *http.Server Sender *MailSender Monitor *RedisMonitor SysLogger *model.SysLog ) func changeWordDir() { if binDir, err := filepath.Abs(filepath.Dir(os.Args[0])); err != nil { log.Fatalf("%s\n", err.Error()) } else { rootDir := filepath.Dir(binDir) os.Chdir(rootDir) } } func loadConfig() *Config { var ( cfgfile string cfg *Config = &Config{} ) flag.StringVar(&cfgfile, "c", "", "config file") flag.Parse() if cfgfile == "" { log.Fatalf("Usage: %s -c \n", os.Args[0]) } wd, err := os.Getwd() if err != nil { log.Fatalf("Get work directory failed: %s\n", err.Error()) } file, err := os.OpenFile(cfgfile, os.O_RDONLY, 0644) if err != nil { log.Fatal(err) } decoder := json.NewDecoder(file) if err = decoder.Decode(cfg); err != nil { log.Fatal(err) } if cfg.Port == "" { cfg.Port = "8080" } if cfg.Database == "" { cfg.Database = filepath.Join(wd, "db", "db.sqlite") } if cfg.RootPwd == "" { cfg.RootPwd = "88888888" } if cfg.LogDir == "" { cfg.LogDir = filepath.Join(wd, "log") } if cfg.TmplDir == "" { cfg.TmplDir = filepath.Join(wd, "tmpl") } if cfg.ResourcesDir == "" { cfg.ResourcesDir = filepath.Join(wd, "resources") } if cfg.SessionTTL == 0 { cfg.SessionTTL = 900 } return cfg } func waitSignal() { ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGUSR1) signal.Notify(ch, syscall.SIGTERM) for { sig := <-ch switch sig { //重启 case syscall.SIGUSR1: close(ch) if Srv != nil { Srv.Close() } SYSLOG("WARN", "服务因重启而停止") syscall.Exec(os.Args[0], os.Args[1:], nil) //退出 case syscall.SIGTERM: close(ch) if Srv != nil { Srv.Close() } SYSLOG("WARN", "服务停止") os.Exit(0) } } } func openDatabase(dbfile string) *sql.DB { db, err := sql.Open("sqlite3", dbfile) if err != nil { log.Fatalf("sql.Open() Error: %s\n", err.Error()) } _, err = db.Exec(Sql_Table_Syscfg) if err != nil { log.Fatalf("sql.Exec() Error: %s\n", err.Error()) } _, err = db.Exec(Sql_Table_Accounts) if err != nil { log.Fatalf("sql.Exec() Error: %s\n", err.Error()) } _, err = db.Exec(Sql_Table_Rediscfg) if err != nil { log.Fatalf("sql.Exec() Error: %s\n", err.Error()) } _, err = db.Exec(Sql_Table_Syslog) if err != nil { log.Fatalf("sql.Exec() Error: %s\n", err.Error()) } _, err = db.Exec(Sql_Index_Syslog) if err != nil { log.Fatalf("sql.Exec() Error: %s\n", err.Error()) } _, err = db.Exec(Sql_Table_Monitorlog) if err != nil { log.Fatalf("sql.Exec() Error: %s\n", err.Error()) } _, err = db.Exec(Sql_Table_Warnlog) if err != nil { log.Fatalf("sql.Exec() Error: %s\n", err.Error()) } _, err = db.Exec(Sql_Index_Warnlog1) if err != nil { log.Fatalf("sql.Exec() Error: %s\n", err.Error()) } _, err = db.Exec(Sql_Index_Warnlog2) if err != nil { log.Fatalf("sql.Exec() Error: %s\n", err.Error()) } _, err = db.Exec(Sql_Table_Processlog) if err != nil { log.Fatalf("sql.Exec() Error: %s\n", err.Error()) } _, err = db.Exec("INSERT OR IGNORE INTO accounts(account,name,password,is_super) VALUES(?,?,?,?)", "root", "超管", model.PasswordConvert(Cfg.RootPwd), 1) if err != nil { log.Fatalf("sql.Exec() Error: %s\n", err.Error()) } _, err = db.Exec(Sql_Values_Syscfg) if err != nil { log.Fatalf("sql.Exec() Error: %s\n", err.Error()) } return db } func startHttpServer() *http.Server { handle := http.NewServeMux() //登录 handle.HandleFunc("/login", login_index) //首页 handle.HandleFunc("/", index_default) handle.HandleFunc("/index/forbidden", index_forbidden) handle.HandleFunc("/index/stats", index_stats) handle.HandleFunc("/index/info", index_info) //用户操作 handle.HandleFunc("/profile/logout", profile_logout) handle.HandleFunc("/profile/passwd", profile_passwd) //配置-redis handle.HandleFunc("/syscfg/redis", syscfg_redis) handle.HandleFunc("/syscfg/redis_list", syscfg_redis_list) handle.HandleFunc("/syscfg/redis_get", syscfg_redis_get) handle.HandleFunc("/syscfg/redis_set", syscfg_redis_set) handle.HandleFunc("/syscfg/redis_del", syscfg_redis_del) //配置-报警 handle.HandleFunc("/syscfg/warn", syscfg_warn) handle.HandleFunc("/syscfg/warn_get", syscfg_warn_get) handle.HandleFunc("/syscfg/warn_set", syscfg_warn_set) //配置-账号 handle.HandleFunc("/syscfg/account", syscfg_account) handle.HandleFunc("/syscfg/account_list", syscfg_account_list) handle.HandleFunc("/syscfg/account_get", syscfg_account_get) handle.HandleFunc("/syscfg/account_set", syscfg_account_set) handle.HandleFunc("/syscfg/account_del", syscfg_account_del) handle.HandleFunc("/syscfg/account_reset_pwd", syscfg_account_reset_pwd) //配置-杂项 handle.HandleFunc("/syscfg/misc", syscfg_misc) handle.HandleFunc("/syscfg/misc_get", syscfg_misc_get) handle.HandleFunc("/syscfg/misc_set", syscfg_misc_set) //日志-Redis监控 handle.HandleFunc("/log/monitor", log_monitor) handle.HandleFunc("/log/monitor_list", log_monitor_list) //日志-Redis报警 handle.HandleFunc("/log/warn", log_warn) handle.HandleFunc("/log/warn_list", log_warn_list) //日志-Redis扩容 handle.HandleFunc("/log/autoprocess", log_autoprocess) handle.HandleFunc("/log/autoprocess_list", log_autoprocess_list) //日志-系统错误 handle.HandleFunc("/log/syslog", log_syslog) handle.HandleFunc("/log/syslog_list", log_syslog_list) //资源文件 handle.Handle("/resources/", http.StripPrefix("/resources/", http.FileServer(http.Dir(Cfg.ResourcesDir)))) //ico文件 handle.HandleFunc("/favicon.ico", func(resp http.ResponseWriter, req *http.Request) { http.ServeFile(resp, req, filepath.Join(Cfg.ResourcesDir, "favicon.ico")) }) //测试 handle.HandleFunc("/debug/sendmail", debug_sendmail) //启动HTTP服务 srv := &http.Server{ Addr: fmt.Sprintf("%s:%s", Cfg.Host, Cfg.Port), Handler: handle, } go func(s *http.Server) { if err := s.ListenAndServe(); err != nil { log.Fatal(err) } }(srv) return srv } func main() { //加载基础配置 changeWordDir() Cfg = loadConfig() //连接DB Db = openDatabase(Cfg.Database) defer Db.Close() //系统日志表对象 SysLogger = model.NewSysLog(Db) //创建HTTP会话池 SessPoll = NewSessionPoll(Cfg.SessionTTL) //添加HTML模板函数 TmplFuncMap = make(template.FuncMap) TmplFuncMap["has_prefix"] = strings.HasPrefix //从DB加载业务配置 SysCfg = NewSysCfgCache() SysCfg.Load() //开始HTTP服务 Srv = startHttpServer() //开始监控Redis状态 Monitor := NewRedisMonitor() go Monitor.Loop() //开始处理邮件发送队列 Sender = NewMailSender() go Sender.Loop() //开始定时清理日志表记录 go func() { //取配置 days := int(30) for { if str, ok := SysCfg.Get("log_kept_days"); ok { if num, err := strconv.Atoi(str); err == nil { days = num } else { SYSLOG("DEBUG", fmt.Sprintf("表syscfg记录cfg_key=log_kept_days的值不是数字:%s", err.Error())) } } ts := time.Now().Unix() - 86400*int64(days) SysLogger.CleanUntil(ts) mdlMonitorLog := model.NewMonitorLog(Db) mdlMonitorLog.CleanUntil(ts) mdlWarnLog := model.NewWarnLog(Db) mdlWarnLog.CleanUntil(ts) mdlProcessLog := model.NewProcessLog(Db) mdlProcessLog.CleanUntil(ts) time.Sleep(time.Hour * 1) } }() //记录启动日志 SYSLOG("DEBUG", "服务启动") //监听信号 waitSignal() }