|
@@ -0,0 +1,301 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "git.swzry.com/zry/GoHiedaLogger/hieda_ginutil"
|
|
|
+ "git.swzry.com/zry/GoHiedaLogger/hieda_yamlutil"
|
|
|
+ "git.swzry.com/zry/GoHiedaLogger/hiedalog"
|
|
|
+ "git.swzry.com/zry/pathutils"
|
|
|
+ "github.com/gin-gonic/gin"
|
|
|
+ "github.com/pascaldekloe/jwt"
|
|
|
+ "gopkg.in/yaml.v2"
|
|
|
+ "io"
|
|
|
+ "io/ioutil"
|
|
|
+ "os"
|
|
|
+ "os/exec"
|
|
|
+ "path"
|
|
|
+ "path/filepath"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+ "text/template"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+const TimeFormatLayout = "2006-01-02 15:04:05"
|
|
|
+
|
|
|
+type Program struct {
|
|
|
+ localLogWriter io.Writer
|
|
|
+ hyu *hieda_yamlutil.HiedaLogYamlUtil
|
|
|
+ ge *gin.Engine
|
|
|
+ config YamlDef_PoweroffConfig
|
|
|
+ executableFileName string
|
|
|
+ programPath string
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) SetLocalLogWriter(writer io.Writer) {
|
|
|
+ p.localLogWriter = writer
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) Start() error {
|
|
|
+ var err error
|
|
|
+ p.executableFileName, err = filepath.Abs(os.Args[0])
|
|
|
+ if err != nil {
|
|
|
+ _, _ = fmt.Fprintln(p.localLogWriter, "Failed get executable path: ", err.Error())
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ p.programPath = filepath.Dir(p.executableFileName)
|
|
|
+ ok := p.initLogger()
|
|
|
+ if !ok {
|
|
|
+ return fmt.Errorf("faild init logger")
|
|
|
+ }
|
|
|
+ ok = p.readConfig()
|
|
|
+ if !ok {
|
|
|
+ return fmt.Errorf("faild load poweroffd config")
|
|
|
+ }
|
|
|
+ p.ge = gin.Default()
|
|
|
+ if p.hyu != nil {
|
|
|
+ glucfg := hieda_ginutil.GinLoggerConfig{
|
|
|
+ Logger: p.hyu.Logger,
|
|
|
+ ModuleName: "webserver",
|
|
|
+ LevelMapFunc: hieda_ginutil.GetDefaultLevelMapFunc(),
|
|
|
+ }
|
|
|
+ gl := hieda_ginutil.GinLoggerWithComplexLogger(glucfg)
|
|
|
+ p.ge.Use(gl)
|
|
|
+ }
|
|
|
+ p.ge.GET("/", p.homepage)
|
|
|
+ p.ge.GET("/api/poweroff.satori", p.poweroff)
|
|
|
+ p.ge.GET("/jwt-tool/", p.jwtGenTool)
|
|
|
+ p.ge.POST("/api/jwt-tool.satori", p.jwtGenToolBackend)
|
|
|
+ if p.config.Listen.TCP.Enable {
|
|
|
+ p.printLog("webtcp", hiedalog.DLN_INFO, "Loading...")
|
|
|
+ go func() {
|
|
|
+ err := p.ge.Run(p.config.Listen.TCP.Listen)
|
|
|
+ if err != nil {
|
|
|
+ p.printLog("webtcp", hiedalog.DLN_ERROR, "Faild run TCP listener: %s", err.Error())
|
|
|
+ }
|
|
|
+ p.printLog("webtcp", hiedalog.DLN_INFO, "Listener end.")
|
|
|
+ }()
|
|
|
+ }
|
|
|
+ if p.config.Listen.SockFile.Enable {
|
|
|
+ p.printLog("webunix", hiedalog.DLN_INFO, "Loading...")
|
|
|
+ if e, _ := pathutils.PathExists(p.config.Listen.SockFile.Filepath); e {
|
|
|
+ err := os.Remove(p.config.Listen.SockFile.Filepath)
|
|
|
+ if err != nil {
|
|
|
+ p.printLog("webunix", hiedalog.DLN_ERROR, "Faild remove old unix sock file: %s", err.Error())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ go func() {
|
|
|
+ err := p.ge.RunUnix(p.config.Listen.SockFile.Filepath)
|
|
|
+ if err != nil {
|
|
|
+ p.printLog("webunix", hiedalog.DLN_ERROR, "Faild run Unix sockfile listener: %s", err.Error())
|
|
|
+ }
|
|
|
+ p.printLog("webunix", hiedalog.DLN_INFO, "Listener end.")
|
|
|
+ }()
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) Stop() error {
|
|
|
+ if p.hyu != nil {
|
|
|
+ p.hyu.SafeShutdown()
|
|
|
+ }
|
|
|
+ os.Exit(0)
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) OtherOperation(op string, args []string) string {
|
|
|
+ return fmt.Sprintf("Invalid command '%s'.", op)
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) GetStatus() (status string, hasExtra bool, extra map[string]string) {
|
|
|
+ return "running", false, map[string]string{}
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) initLogger() bool {
|
|
|
+ ycfd, err := ioutil.ReadFile(path.Join(p.programPath, "log.config.yaml"))
|
|
|
+ if err != nil {
|
|
|
+ _, _ = fmt.Fprintln(p.localLogWriter, "Failed read file 'log.config.yaml':", err)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ var ycd hieda_yamlutil.CommonLogConfigYAML
|
|
|
+ err = yaml.Unmarshal(ycfd, &ycd)
|
|
|
+ if err != nil {
|
|
|
+ _, _ = fmt.Fprintln(p.localLogWriter, "Failed parse file 'log.config.yaml':", err)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ p.hyu, err = hieda_yamlutil.CreateHiedaLoggerFromYAMLData(ycd, true)
|
|
|
+ if err != nil {
|
|
|
+ _, _ = fmt.Fprintln(p.localLogWriter, "Failed Init Logger:", err)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ p.hyu.Logger.LogString("logger", hiedalog.DLN_INFO, "Logger initialized.")
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) readConfig() bool {
|
|
|
+ ycfd, err := ioutil.ReadFile(path.Join(p.programPath, "poweroff.config.yaml"))
|
|
|
+ if err != nil {
|
|
|
+ p.printLog("config", hiedalog.DLN_FATAL, "Failed read config file 'poweroff.config.yaml':%s", err)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ err = yaml.Unmarshal(ycfd, &(p.config))
|
|
|
+ if err != nil {
|
|
|
+ p.printLog("config", hiedalog.DLN_FATAL, "Failed parse config file 'poweroff.config.yaml':%s", err)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) homepage(ctx *gin.Context) {
|
|
|
+ ctx.Header("content-type", "text/html")
|
|
|
+ ctx.String(200, Html_Home)
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) jwtGenTool(ctx *gin.Context) {
|
|
|
+ ctx.Header("content-type", "text/html")
|
|
|
+ ctx.String(200, Html_jwtTool)
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) jwtGenToolBackend(ctx *gin.Context) {
|
|
|
+ jwtkey := ctx.Request.PostFormValue("jwtkey")
|
|
|
+ caller := ctx.Request.PostFormValue("caller")
|
|
|
+ msg := ctx.Request.PostFormValue("msg")
|
|
|
+ clm := jwt.Claims{}
|
|
|
+ clm.Issuer = caller
|
|
|
+ ntm := jwt.NewNumericTime(time.Now())
|
|
|
+ clm.Issued = ntm
|
|
|
+ clm.NotBefore = ntm
|
|
|
+ exptime := time.Now().Add(5 * time.Minute)
|
|
|
+ clm.Expires = jwt.NewNumericTime(exptime)
|
|
|
+ clm.Set = map[string]interface{}{
|
|
|
+ "msg": msg,
|
|
|
+ }
|
|
|
+ tok, err := clm.HMACSign(jwt.HS256, []byte(jwtkey))
|
|
|
+ if err != nil {
|
|
|
+ ctx.Header("content-type", "text/html")
|
|
|
+ ctx.String(200, Html_jwtToolFail, err.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+ otpl := template.New("result")
|
|
|
+ tpl, err := otpl.Parse(Html_jwtToolResult)
|
|
|
+ if err != nil {
|
|
|
+ ctx.Header("content-type", "text/html")
|
|
|
+ ctx.String(200, Html_jwtToolFail, err.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+ sb := strings.Builder{}
|
|
|
+ err = tpl.Execute(&sb, map[string]string{
|
|
|
+ "token": string(tok),
|
|
|
+ "expire": exptime.Format(TimeFormatLayout),
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ ctx.Header("content-type", "text/html")
|
|
|
+ ctx.String(200, Html_jwtToolFail, err.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ctx.Header("content-type", "text/html")
|
|
|
+ ctx.String(200, sb.String())
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) poweroff(ctx *gin.Context) {
|
|
|
+ jwtstr := ctx.Request.FormValue("token")
|
|
|
+ vclm, err := jwt.HMACCheck([]byte(jwtstr), []byte(p.config.Token.JwtKey))
|
|
|
+ if err != nil {
|
|
|
+ if p.hyu != nil {
|
|
|
+ ld := map[string]string{
|
|
|
+ "type": "auth_failed",
|
|
|
+ "client_ip": ctx.ClientIP(),
|
|
|
+ }
|
|
|
+ p.hyu.Logger.LogComplex("auth", hiedalog.DLN_INFO, ld)
|
|
|
+ }
|
|
|
+ ctx.JSON(200, gin.H{
|
|
|
+ "suc": false,
|
|
|
+ "err": "auth failed",
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ isr := vclm.Issuer
|
|
|
+ var msg string
|
|
|
+ imsg, ok := vclm.Set["msg"]
|
|
|
+ if !ok {
|
|
|
+ msg = "<No Message>"
|
|
|
+ }
|
|
|
+ msg, ok = imsg.(string)
|
|
|
+ if !ok {
|
|
|
+ msg = "<No Message>"
|
|
|
+ }
|
|
|
+ if p.hyu != nil {
|
|
|
+ ld := map[string]string{
|
|
|
+ "caller": isr,
|
|
|
+ "msg": msg,
|
|
|
+ "client_ip": ctx.ClientIP(),
|
|
|
+ "time": time.Now().Format(TimeFormatLayout),
|
|
|
+ "timestamp": strconv.FormatInt(time.Now().Unix(), 10),
|
|
|
+ }
|
|
|
+ p.hyu.Logger.LogComplex("record", hiedalog.DLN_INFO, ld)
|
|
|
+ }
|
|
|
+ if p.config.PoweroffLock {
|
|
|
+ p.printLog("shutdown", hiedalog.DLN_WARN, "WARNING: 'poweroff_lock' option is enable. it will not shutdown.")
|
|
|
+ ctx.JSON(200, gin.H{
|
|
|
+ "suc": true,
|
|
|
+ "caller": isr,
|
|
|
+ "msg": msg,
|
|
|
+ "client_ip": ctx.ClientIP(),
|
|
|
+ "time": time.Now().Format(TimeFormatLayout),
|
|
|
+ "timestamp": strconv.FormatInt(time.Now().Unix(), 10),
|
|
|
+ "poweroff_lock": p.config.PoweroffLock,
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ ok := p.doShutdown()
|
|
|
+ if !ok {
|
|
|
+ ctx.JSON(200, gin.H{
|
|
|
+ "suc": false,
|
|
|
+ "err": "exec shutdown commands failed",
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ ctx.JSON(200, gin.H{
|
|
|
+ "suc": true,
|
|
|
+ "caller": isr,
|
|
|
+ "msg": msg,
|
|
|
+ "client_ip": ctx.ClientIP(),
|
|
|
+ "time": time.Now().Format(TimeFormatLayout),
|
|
|
+ "timestamp": strconv.FormatInt(time.Now().Unix(), 10),
|
|
|
+ "poweroff_lock": p.config.PoweroffLock,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) printLog(m string, lv string, f string, a ...interface{}) {
|
|
|
+ fs := fmt.Sprintf(f, a...)
|
|
|
+ if p.hyu != nil {
|
|
|
+ p.hyu.Logger.LogString(m, lv, fs)
|
|
|
+ } else {
|
|
|
+ _, _ = fmt.Fprintln(p.localLogWriter, fs)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) execCmd(cmd string) error {
|
|
|
+ c := exec.Command(cmd, "")
|
|
|
+ return c.Run()
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Program) doShutdown() bool {
|
|
|
+ for i := 0; i < 3; i++ {
|
|
|
+ err := p.execCmd("/bin/sync")
|
|
|
+ if err != nil {
|
|
|
+ p.printLog("shutdown", hiedalog.DLN_WARN, "exec sync error: %s", err.Error())
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ err := p.execCmd("/sbin/poweroff")
|
|
|
+ if err != nil {
|
|
|
+ p.printLog("shutdown", hiedalog.DLN_WARN, "exec poweroff error: %s", err.Error())
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if p.hyu != nil {
|
|
|
+ p.hyu.SafeShutdown()
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|