program.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. package main
  2. import (
  3. "fmt"
  4. "git.swzry.com/zry/GoHiedaLogger/hieda_ginutil"
  5. "git.swzry.com/zry/GoHiedaLogger/hieda_yamlutil"
  6. "git.swzry.com/zry/GoHiedaLogger/hiedalog"
  7. "git.swzry.com/zry/pathutils"
  8. "github.com/gin-gonic/gin"
  9. "github.com/pascaldekloe/jwt"
  10. "gopkg.in/yaml.v2"
  11. "io"
  12. "io/ioutil"
  13. "os"
  14. "os/exec"
  15. "path"
  16. "path/filepath"
  17. "strconv"
  18. "strings"
  19. "text/template"
  20. "time"
  21. )
  22. const TimeFormatLayout = "2006-01-02 15:04:05"
  23. type Program struct {
  24. localLogWriter io.Writer
  25. hyu *hieda_yamlutil.HiedaLogYamlUtil
  26. ge *gin.Engine
  27. config YamlDef_PoweroffConfig
  28. executableFileName string
  29. programPath string
  30. }
  31. func (p *Program) SetLocalLogWriter(writer io.Writer) {
  32. p.localLogWriter = writer
  33. }
  34. func (p *Program) Start() error {
  35. var err error
  36. p.executableFileName, err = filepath.Abs(os.Args[0])
  37. if err != nil {
  38. _, _ = fmt.Fprintln(p.localLogWriter, "Failed get executable path: ", err.Error())
  39. return err
  40. }
  41. p.programPath = filepath.Dir(p.executableFileName)
  42. ok := p.initLogger()
  43. if !ok {
  44. return fmt.Errorf("faild init logger")
  45. }
  46. ok = p.readConfig()
  47. if !ok {
  48. return fmt.Errorf("faild load poweroffd config")
  49. }
  50. p.ge = gin.Default()
  51. if p.hyu != nil {
  52. glucfg := hieda_ginutil.GinLoggerConfig{
  53. Logger: p.hyu.Logger,
  54. ModuleName: "webserver",
  55. LevelMapFunc: hieda_ginutil.GetDefaultLevelMapFunc(),
  56. }
  57. gl := hieda_ginutil.GinLoggerWithComplexLogger(glucfg)
  58. p.ge.Use(gl)
  59. }
  60. p.ge.GET("/", p.homepage)
  61. p.ge.GET("/api/poweroff.satori", p.poweroff)
  62. p.ge.GET("/jwt-tool/", p.jwtGenTool)
  63. p.ge.POST("/api/jwt-tool.satori", p.jwtGenToolBackend)
  64. if p.config.Listen.TCP.Enable {
  65. p.printLog("webtcp", hiedalog.DLN_INFO, "Loading...")
  66. go func() {
  67. err := p.ge.Run(p.config.Listen.TCP.Listen)
  68. if err != nil {
  69. p.printLog("webtcp", hiedalog.DLN_ERROR, "Faild run TCP listener: %s", err.Error())
  70. }
  71. p.printLog("webtcp", hiedalog.DLN_INFO, "Listener end.")
  72. }()
  73. }
  74. if p.config.Listen.SockFile.Enable {
  75. p.printLog("webunix", hiedalog.DLN_INFO, "Loading...")
  76. if e, _ := pathutils.PathExists(p.config.Listen.SockFile.Filepath); e {
  77. err := os.Remove(p.config.Listen.SockFile.Filepath)
  78. if err != nil {
  79. p.printLog("webunix", hiedalog.DLN_ERROR, "Faild remove old unix sock file: %s", err.Error())
  80. }
  81. }
  82. go func() {
  83. err := p.ge.RunUnix(p.config.Listen.SockFile.Filepath)
  84. if err != nil {
  85. p.printLog("webunix", hiedalog.DLN_ERROR, "Faild run Unix sockfile listener: %s", err.Error())
  86. }
  87. p.printLog("webunix", hiedalog.DLN_INFO, "Listener end.")
  88. }()
  89. }
  90. return nil
  91. }
  92. func (p *Program) Stop() error {
  93. if p.hyu != nil {
  94. p.hyu.SafeShutdown()
  95. }
  96. os.Exit(0)
  97. return nil
  98. }
  99. func (p *Program) OtherOperation(op string, args []string) string {
  100. return fmt.Sprintf("Invalid command '%s'.", op)
  101. }
  102. func (p *Program) GetStatus() (status string, hasExtra bool, extra map[string]string) {
  103. return "running", false, map[string]string{}
  104. }
  105. func (p *Program) initLogger() bool {
  106. ycfd, err := ioutil.ReadFile(path.Join(p.programPath, "log.config.yaml"))
  107. if err != nil {
  108. _, _ = fmt.Fprintln(p.localLogWriter, "Failed read file 'log.config.yaml':", err)
  109. return false
  110. }
  111. var ycd hieda_yamlutil.CommonLogConfigYAML
  112. err = yaml.Unmarshal(ycfd, &ycd)
  113. if err != nil {
  114. _, _ = fmt.Fprintln(p.localLogWriter, "Failed parse file 'log.config.yaml':", err)
  115. return false
  116. }
  117. p.hyu, err = hieda_yamlutil.CreateHiedaLoggerFromYAMLData(ycd, true)
  118. if err != nil {
  119. _, _ = fmt.Fprintln(p.localLogWriter, "Failed Init Logger:", err)
  120. return false
  121. }
  122. p.hyu.Logger.LogString("logger", hiedalog.DLN_INFO, "Logger initialized.")
  123. return true
  124. }
  125. func (p *Program) readConfig() bool {
  126. ycfd, err := ioutil.ReadFile(path.Join(p.programPath, "poweroff.config.yaml"))
  127. if err != nil {
  128. p.printLog("config", hiedalog.DLN_FATAL, "Failed read config file 'poweroff.config.yaml':%s", err)
  129. return false
  130. }
  131. err = yaml.Unmarshal(ycfd, &(p.config))
  132. if err != nil {
  133. p.printLog("config", hiedalog.DLN_FATAL, "Failed parse config file 'poweroff.config.yaml':%s", err)
  134. return false
  135. }
  136. return true
  137. }
  138. func (p *Program) homepage(ctx *gin.Context) {
  139. ctx.Header("content-type", "text/html")
  140. ctx.String(200, Html_Home)
  141. }
  142. func (p *Program) jwtGenTool(ctx *gin.Context) {
  143. ctx.Header("content-type", "text/html")
  144. ctx.String(200, Html_jwtTool)
  145. }
  146. func (p *Program) jwtGenToolBackend(ctx *gin.Context) {
  147. jwtkey := ctx.Request.PostFormValue("jwtkey")
  148. caller := ctx.Request.PostFormValue("caller")
  149. msg := ctx.Request.PostFormValue("msg")
  150. clm := jwt.Claims{}
  151. clm.Issuer = caller
  152. ntm := jwt.NewNumericTime(time.Now())
  153. clm.Issued = ntm
  154. clm.NotBefore = ntm
  155. exptime := time.Now().Add(5 * time.Minute)
  156. clm.Expires = jwt.NewNumericTime(exptime)
  157. clm.Set = map[string]interface{}{
  158. "msg": msg,
  159. }
  160. tok, err := clm.HMACSign(jwt.HS256, []byte(jwtkey))
  161. if err != nil {
  162. ctx.Header("content-type", "text/html")
  163. ctx.String(200, Html_jwtToolFail, err.Error())
  164. return
  165. }
  166. otpl := template.New("result")
  167. tpl, err := otpl.Parse(Html_jwtToolResult)
  168. if err != nil {
  169. ctx.Header("content-type", "text/html")
  170. ctx.String(200, Html_jwtToolFail, err.Error())
  171. return
  172. }
  173. sb := strings.Builder{}
  174. err = tpl.Execute(&sb, map[string]string{
  175. "token": string(tok),
  176. "expire": exptime.Format(TimeFormatLayout),
  177. })
  178. if err != nil {
  179. ctx.Header("content-type", "text/html")
  180. ctx.String(200, Html_jwtToolFail, err.Error())
  181. return
  182. }
  183. ctx.Header("content-type", "text/html")
  184. ctx.String(200, sb.String())
  185. return
  186. }
  187. func (p *Program) poweroff(ctx *gin.Context) {
  188. jwtstr := ctx.Request.FormValue("token")
  189. vclm, err := jwt.HMACCheck([]byte(jwtstr), []byte(p.config.Token.JwtKey))
  190. if err != nil {
  191. if p.hyu != nil {
  192. ld := map[string]string{
  193. "type": "auth_failed",
  194. "client_ip": ctx.ClientIP(),
  195. }
  196. p.hyu.Logger.LogComplex("auth", hiedalog.DLN_INFO, ld)
  197. }
  198. ctx.JSON(200, gin.H{
  199. "suc": false,
  200. "err": "auth failed",
  201. })
  202. return
  203. }
  204. isr := vclm.Issuer
  205. var msg string
  206. imsg, ok := vclm.Set["msg"]
  207. if !ok {
  208. msg = "<No Message>"
  209. }
  210. msg, ok = imsg.(string)
  211. if !ok {
  212. msg = "<No Message>"
  213. }
  214. if p.hyu != nil {
  215. ld := map[string]string{
  216. "caller": isr,
  217. "msg": msg,
  218. "client_ip": ctx.ClientIP(),
  219. "time": time.Now().Format(TimeFormatLayout),
  220. "timestamp": strconv.FormatInt(time.Now().Unix(), 10),
  221. }
  222. p.hyu.Logger.LogComplex("record", hiedalog.DLN_INFO, ld)
  223. }
  224. if p.config.PoweroffLock {
  225. p.printLog("shutdown", hiedalog.DLN_WARN, "WARNING: 'poweroff_lock' option is enable. it will not shutdown.")
  226. ctx.JSON(200, gin.H{
  227. "suc": true,
  228. "caller": isr,
  229. "msg": msg,
  230. "client_ip": ctx.ClientIP(),
  231. "time": time.Now().Format(TimeFormatLayout),
  232. "timestamp": strconv.FormatInt(time.Now().Unix(), 10),
  233. "poweroff_lock": p.config.PoweroffLock,
  234. })
  235. } else {
  236. ok := p.doShutdown()
  237. if !ok {
  238. ctx.JSON(200, gin.H{
  239. "suc": false,
  240. "err": "exec shutdown commands failed",
  241. })
  242. } else {
  243. ctx.JSON(200, gin.H{
  244. "suc": true,
  245. "caller": isr,
  246. "msg": msg,
  247. "client_ip": ctx.ClientIP(),
  248. "time": time.Now().Format(TimeFormatLayout),
  249. "timestamp": strconv.FormatInt(time.Now().Unix(), 10),
  250. "poweroff_lock": p.config.PoweroffLock,
  251. })
  252. }
  253. }
  254. }
  255. func (p *Program) printLog(m string, lv string, f string, a ...interface{}) {
  256. fs := fmt.Sprintf(f, a...)
  257. if p.hyu != nil {
  258. p.hyu.Logger.LogString(m, lv, fs)
  259. } else {
  260. _, _ = fmt.Fprintln(p.localLogWriter, fs)
  261. }
  262. }
  263. func (p *Program) execCmd(cmd string) error {
  264. c := exec.Command(cmd, "")
  265. return c.Run()
  266. }
  267. func (p *Program) doShutdown() bool {
  268. for i := 0; i < 3; i++ {
  269. err := p.execCmd("/bin/sync")
  270. if err != nil {
  271. p.printLog("shutdown", hiedalog.DLN_WARN, "exec sync error: %s", err.Error())
  272. return false
  273. }
  274. }
  275. err := p.execCmd("/sbin/poweroff")
  276. if err != nil {
  277. p.printLog("shutdown", hiedalog.DLN_WARN, "exec poweroff error: %s", err.Error())
  278. return false
  279. }
  280. if p.hyu != nil {
  281. p.hyu.SafeShutdown()
  282. }
  283. return true
  284. }