Browse Source

Version 1.0

zry 3 years ago
parent
commit
9120ac61b8
20 changed files with 1018 additions and 0 deletions
  1. 2 0
      .idea/.gitignore
  2. 5 0
      .idea/inspectionProfiles/profiles_settings.xml
  3. 6 0
      .idea/misc.xml
  4. 8 0
      .idea/modules.xml
  5. 6 0
      .idea/vcs.xml
  6. 13 0
      .idea/zdaemon.iml
  7. 67 0
      check_stat.go
  8. 14 0
      config.go
  9. 56 0
      help.go
  10. 83 0
      initd_openwrt.go
  11. 86 0
      initd_sysv.go
  12. 11 0
      install.go
  13. 166 0
      ipc_client.go
  14. 25 0
      ipc_jsondef.go
  15. 11 0
      program_interface.go
  16. 60 0
      run_unix.go
  17. 45 0
      run_windows.go
  18. 76 0
      systemd.go
  19. 11 0
      uninstall.go
  20. 267 0
      zdaemon.go

+ 2 - 0
.idea/.gitignore

@@ -0,0 +1,2 @@
+# Default ignored files
+/workspace.xml

+ 5 - 0
.idea/inspectionProfiles/profiles_settings.xml

@@ -0,0 +1,5 @@
+<component name="InspectionProjectProfileManager">
+  <settings>
+    <option name="PROJECT_PROFILE" />
+  </settings>
+</component>

+ 6 - 0
.idea/misc.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JavaScriptSettings">
+    <option name="languageLevel" value="ES6" />
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/zdaemon.iml" filepath="$PROJECT_DIR$/.idea/zdaemon.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 13 - 0
.idea/zdaemon.iml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="Go">
+    <buildTags>
+      <option name="os" value="linux" />
+    </buildTags>
+  </component>
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 67 - 0
check_stat.go

@@ -0,0 +1,67 @@
+package zdaemon
+
+import (
+	"fmt"
+	"git.swzry.com/zry/pathutils"
+	"gopkg.in/yaml.v2"
+	"io/ioutil"
+	"os"
+	"path"
+	"runtime"
+)
+
+type yamldef_DaemonConfig struct {
+	OS          string `yaml:"os"`
+	InstallMode string `yaml:"install_mode"`
+	IsRunning   bool   `yaml:"is_running"`
+	PID         int    `yaml:"pid"`
+	IpcAddr     string `yaml:"ipc_addr"`
+}
+
+func (zd *ZDaemon) getStatus() {
+	zd.resetStatus()
+	cpath := path.Join(zd.zdaemonPath, "zdaemon.yaml")
+	fe, _ := pathutils.PathExists(cpath)
+	if fe {
+		ydata, err := ioutil.ReadFile(cpath)
+		if err != nil {
+			zd.resetStatus()
+			zd.writeStatus()
+			return
+		}
+		err = yaml.Unmarshal(ydata, zd.daemonConfig)
+		if err != nil {
+			zd.resetStatus()
+			zd.writeStatus()
+			return
+		}
+	} else {
+		zd.resetStatus()
+		zd.writeStatus()
+		return
+	}
+}
+
+func (zd *ZDaemon) resetStatus() {
+	zd.daemonConfig = &yamldef_DaemonConfig{
+		OS:          runtime.GOOS,
+		InstallMode: "free",
+		IsRunning:   false,
+		PID:         -1,
+		IpcAddr:     "",
+	}
+}
+
+func (zd *ZDaemon) writeStatus() {
+	cpath := path.Join(zd.zdaemonPath, "zdaemon.yaml")
+	ydata, err := yaml.Marshal(zd.daemonConfig)
+	if err != nil {
+		fmt.Println("Failed marshal daemon.yaml: ", err.Error())
+		os.Exit(-1)
+	}
+	err = ioutil.WriteFile(cpath, ydata, 0666)
+	if err != nil {
+		fmt.Println("Failed write daemon.yaml: ", err.Error())
+		os.Exit(-1)
+	}
+}

+ 14 - 0
config.go

@@ -0,0 +1,14 @@
+package zdaemon
+
+import "os"
+
+type ZDaemonConfig struct {
+	DaemonName         string
+	Description        string
+	InitRunnerFunction func() ProgramInterface
+	StartOrder         int
+	StopOrder          int
+	ProgramStdin       *os.File
+	ProgramStdout      *os.File
+	ProgramStderr      *os.File
+}

+ 56 - 0
help.go

@@ -0,0 +1,56 @@
+package zdaemon
+
+import (
+	"fmt"
+	"os"
+)
+
+func (zd *ZDaemon) help_install() {
+	fmt.Println("Usage: ", zd.daemonName, "install <install_type>")
+	fmt.Println("install_type:")
+	fmt.Println("\twinsvc\t\tWindows service")
+	fmt.Println("\twintray\t\tWindows program with tray icon")
+	fmt.Println("\tsysv\t\tLinux sysv")
+	fmt.Println("\tsystemd\t\tLinux systemd")
+	fmt.Println("\topenwrt\t\tOpenwrt service")
+}
+
+func (zd *ZDaemon) help() {
+	if len(os.Args) == 3 {
+		cmd := os.Args[2]
+		switch cmd {
+		case "install":
+			zd.help_install()
+			break
+		case "uninstall":
+			fmt.Println("This command will uninstall what installed.")
+			break
+		case "start":
+			fmt.Println("This command will start daemon.")
+			break
+		case "stop":
+			fmt.Println("This command will stop daemon.")
+			break
+		case "restart":
+			fmt.Println("This command will restart daemon.")
+			break
+		case "status":
+			fmt.Println("This command will print daemon status.")
+			break
+		case "help":
+			fmt.Println("...? What's your problem? Do you like recurse?")
+			break
+		case "enable":
+			fmt.Println("This command will enable auto startup")
+			break
+		case "disable":
+			fmt.Println("This command will disable auto startup")
+			break
+		default:
+			fmt.Println("Usage: ", zd.daemonName, "help {start|stop|restart|install|uninstall|enable|disable}")
+			break
+		}
+	} else {
+		fmt.Println("Usage: ", zd.daemonName, "help {start|stop|restart|install|uninstall|enable|disable}")
+	}
+}

+ 83 - 0
initd_openwrt.go

@@ -0,0 +1,83 @@
+package zdaemon
+
+import (
+	"fmt"
+	"git.swzry.com/zry/pathutils"
+	"html/template"
+	"os"
+	"path"
+	"strconv"
+)
+
+const tpl_initd_openwrt = `#!/bin/sh /etc/rc.common
+
+START={{ .startnum }}
+STOP={{ .stopnum }}
+
+cmd={{ .executable }}
+
+start() {
+	$cmd start
+}                 
+
+stop() {
+	$cmd stop
+}
+`
+
+func (zd *ZDaemon) install_openwrt() {
+	if zd.daemonConfig.InstallMode != "free" {
+		fmt.Println("Failed install: already installed.")
+		return
+	}
+	initsp := path.Join("/etc/init.d/", zd.daemonName)
+	pe, _ := pathutils.PathExists(initsp)
+	if pe {
+		err := os.Remove(initsp)
+		if err != nil {
+			fmt.Println("Failed install, old file exist and error in deleting old file: ", err.Error())
+			return
+		}
+	}
+	otpl := template.New(zd.daemonName)
+	tpl, err := otpl.Parse(tpl_initd_openwrt)
+	if err != nil {
+		fmt.Println("Failed install: ", err.Error())
+		return
+	}
+	fids, err := os.OpenFile(initsp, os.O_WRONLY|os.O_CREATE, 0777)
+	if err != nil {
+		fmt.Println("Failed install: ", err.Error())
+		return
+	}
+	tdat := map[string]string{
+		"startnum":   strconv.Itoa(zd.startOrder),
+		"stopnum":    strconv.Itoa(zd.stopOrder),
+		"executable": zd.executableFileName,
+	}
+	err = tpl.Execute(fids, tdat)
+	if err != nil {
+		fmt.Println("Failed install: ", err.Error())
+		_ = fids.Close()
+		return
+	}
+	_ = fids.Close()
+	zd.daemonConfig.InstallMode = "openwrt"
+	zd.writeStatus()
+	fmt.Println("Installed.")
+}
+
+func (zd *ZDaemon) uninstall_openwrt() {
+	initsp := path.Join("/etc/init.d/", zd.daemonName)
+	pe, _ := pathutils.PathExists(initsp)
+	if pe {
+		err := os.Remove(initsp)
+		if err != nil {
+			fmt.Println("Failed uninstall: ", err.Error())
+			return
+		}
+	}
+	zd.daemonConfig.InstallMode = "free"
+	zd.writeStatus()
+	fmt.Println("Uninstalled.")
+}

+ 86 - 0
initd_sysv.go

@@ -0,0 +1,86 @@
+package zdaemon
+
+import (
+	"fmt"
+	"git.swzry.com/zry/pathutils"
+	"os"
+	"path"
+	"strconv"
+	"text/template"
+)
+
+const tpl_initd_sysv = `#!/bin/sh
+# chkconfig: - {{.StartOrder}} {{.StopOrder}}
+# description: {{.Description}}
+# processname: {{.ExecPath}}
+# Provides:          {{.ExecPath}}
+# Required-Start:
+# Required-Stop:
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: {{.Name}}
+# Description:       {{.Description}}
+
+cmd={{ .ExecPath }}
+$cmd $1
+exit 0
+`
+
+func (zd *ZDaemon) install_sysv() {
+	if zd.daemonConfig.InstallMode != "free" {
+		fmt.Println("Failed install: already installed.")
+		return
+	}
+	initsp := path.Join("/etc/init.d/", zd.daemonName)
+	pe, _ := pathutils.PathExists(initsp)
+	if pe {
+		err := os.Remove(initsp)
+		if err != nil {
+			fmt.Println("Failed install, old file exist and error in deleting old file: ", err.Error())
+			return
+		}
+	}
+	otpl := template.New(zd.daemonName)
+	tpl, err := otpl.Parse(tpl_initd_sysv)
+	if err != nil {
+		fmt.Println("Failed install: ", err.Error())
+		return
+	}
+	fids, err := os.OpenFile(initsp, os.O_WRONLY|os.O_CREATE, 0777)
+	if err != nil {
+		fmt.Println("Failed install: ", err.Error())
+		return
+	}
+	tdat := map[string]string{
+		"StartOrder":  strconv.Itoa(zd.startOrder),
+		"StopOrder":   strconv.Itoa(zd.stopOrder),
+		"Description": zd.description,
+		"ExecPath":    zd.executableFileName,
+		"Name":        zd.daemonName,
+	}
+	err = tpl.Execute(fids, tdat)
+	if err != nil {
+		fmt.Println("Failed install: ", err.Error())
+		_ = fids.Close()
+		return
+	}
+	_ = fids.Close()
+	zd.daemonConfig.InstallMode = "sysv"
+	zd.writeStatus()
+	fmt.Println("Installed.")
+}
+
+func (zd *ZDaemon) uninstall_sysv() {
+	initsp := path.Join("/etc/init.d/", zd.daemonName)
+	pe, _ := pathutils.PathExists(initsp)
+	if pe {
+		err := os.Remove(initsp)
+		if err != nil {
+			fmt.Println("Failed uninstall: ", err.Error())
+			return
+		}
+	}
+	zd.daemonConfig.InstallMode = "free"
+	zd.writeStatus()
+	fmt.Println("Uninstalled.")
+}

+ 11 - 0
install.go

@@ -0,0 +1,11 @@
+package zdaemon
+
+import "fmt"
+
+func (zd *ZDaemon) install_winsvc() {
+	fmt.Println("Not supported yet.")
+}
+
+func (zd *ZDaemon) install_wintray() {
+	fmt.Println("Not supported yet.")
+}

+ 166 - 0
ipc_client.go

@@ -0,0 +1,166 @@
+package zdaemon
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"fmt"
+	"git.swzry.com/zry/SatorIPC/golang/sipc_conn"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"strings"
+	"time"
+)
+
+func (zd *ZDaemon) ipcDial(ipcaddr string) *http.Client {
+	tr := &http.Transport{
+		DialContext: func(ctx context.Context, network, addr string) (conn net.Conn, err error) {
+			return sipc_conn.Dial(ipcaddr, 5*time.Second)
+		},
+	}
+	c := &http.Client{
+		Transport: tr,
+	}
+	return c
+}
+
+func (zd *ZDaemon) ipcAppCheckAlive() bool {
+	ipcaddr := zd.daemonConfig.IpcAddr
+	c := zd.ipcDial(ipcaddr)
+	uri := fmt.Sprintf("http://%s/check-alive.satori", zd.ipcurlhost)
+	res, err := c.Get(uri)
+	if err != nil {
+		return false
+	}
+	if res.StatusCode == 200 {
+		jr := res.Body
+		jd, err := ioutil.ReadAll(jr)
+		_ = jr.Close()
+		if err != nil {
+			return false
+		}
+		var jv IPCJsonDef_CheckAlive
+		err = json.Unmarshal(jd, &jv)
+		if err != nil {
+			return false
+		}
+		return jv.Alive
+	} else {
+		return false
+	}
+}
+
+func (zd *ZDaemon) ipcAppStop() bool {
+	ipcaddr := zd.daemonConfig.IpcAddr
+	c := zd.ipcDial(ipcaddr)
+	uri := fmt.Sprintf("http://%s/stop.satori", zd.ipcurlhost)
+	res, err := c.Get(uri)
+	if err != nil {
+		if strings.Contains(err.Error(), "EOF") {
+			fmt.Println("Success stop daemon.")
+			return true
+		}
+		fmt.Println("Error in stopping daemon: ", err.Error())
+		return false
+	}
+	if res.StatusCode == 200 {
+		jr := res.Body
+		jd, err := ioutil.ReadAll(jr)
+		_ = jr.Close()
+		if err != nil {
+			fmt.Println("Error in stopping daemon: ", err.Error())
+			return false
+		}
+		var jv IPCJsonDef_Stop
+		err = json.Unmarshal(jd, &jv)
+		if err != nil {
+			fmt.Println("Error in stopping daemon: ", err.Error())
+			return false
+		}
+		if jv.Success {
+			fmt.Println("Success stop daemon. Extra message: ", jv.Msg)
+		} else {
+			fmt.Println("Failed stop daemon. Extra message: ", jv.Msg)
+		}
+		return jv.Success
+	} else {
+		return false
+	}
+}
+
+func (zd *ZDaemon) ipcAppGetStatus() {
+	ipcaddr := zd.daemonConfig.IpcAddr
+	c := zd.ipcDial(ipcaddr)
+	uri := fmt.Sprintf("http://%s/status.satori", zd.ipcurlhost)
+	res, err := c.Get(uri)
+	if err != nil {
+		fmt.Println("Error in getting daemon status: ", err.Error())
+		return
+	}
+	if res.StatusCode == 200 {
+		jr := res.Body
+		jd, err := ioutil.ReadAll(jr)
+		_ = jr.Close()
+		if err != nil {
+			fmt.Println("Error in getting daemon status: ", err.Error())
+			return
+		}
+		var jv IPCJsonDef_GetStatus
+		err = json.Unmarshal(jd, &jv)
+		if err != nil {
+			fmt.Println("Error in getting daemon status: ", err.Error())
+			return
+		}
+		fmt.Println("Status: ", jv.Status)
+		if jv.HasExtra {
+			fmt.Println("Extra Info:")
+			for k, v := range jv.ExtraInfo {
+				fmt.Println("\t", k, "=", v)
+			}
+		}
+	} else {
+		fmt.Println("Error in getting daemon status: HTTP", res.StatusCode, res.Status)
+		return
+	}
+}
+
+func (zd *ZDaemon) ipcAppOtherOperation(op string, args []string) {
+	ipcaddr := zd.daemonConfig.IpcAddr
+	c := zd.ipcDial(ipcaddr)
+	uri := fmt.Sprintf("http://%s/other-operation.satori", zd.ipcurlhost)
+	jvreq := &IPCJsonDef_OtherOperationRequest{
+		Operation: op,
+		Args:      args,
+	}
+	jdat, err := json.Marshal(jvreq)
+	if err != nil {
+		fmt.Println("Error in operation", op, ": ", err.Error())
+		return
+	}
+	jbuf := bytes.NewReader(jdat)
+	res, err := c.Post(uri, "application/json", jbuf)
+	if err != nil {
+		fmt.Println("Error in operation", op, ": ", err.Error())
+		return
+	}
+	if res.StatusCode == 200 {
+		jr := res.Body
+		jd, err := ioutil.ReadAll(jr)
+		_ = jr.Close()
+		if err != nil {
+			fmt.Println("Error in operation", op, ": ", err.Error())
+			return
+		}
+		var jv IPCJsonDef_OtherOperationResponse
+		err = json.Unmarshal(jd, &jv)
+		if err != nil {
+			fmt.Println("Error in operation", op, ": ", err.Error())
+			return
+		}
+		fmt.Println(jv.Result)
+	} else {
+		fmt.Println("Error in operation", op, ": HTTP", res.StatusCode, res.Status)
+		return
+	}
+}

+ 25 - 0
ipc_jsondef.go

@@ -0,0 +1,25 @@
+package zdaemon
+
+type IPCJsonDef_CheckAlive struct {
+	Alive bool `json:"alive"`
+}
+
+type IPCJsonDef_Stop struct {
+	Success bool   `json:"suc"`
+	Msg     string `json:"msg"`
+}
+
+type IPCJsonDef_GetStatus struct {
+	Status    string            `json:"suc"`
+	HasExtra  bool              `json:"has_extra"`
+	ExtraInfo map[string]string `json:"extra"`
+}
+
+type IPCJsonDef_OtherOperationRequest struct {
+	Operation string   `json:"opcmd"`
+	Args      []string `json:"args"`
+}
+
+type IPCJsonDef_OtherOperationResponse struct {
+	Result string `json:"result"`
+}

+ 11 - 0
program_interface.go

@@ -0,0 +1,11 @@
+package zdaemon
+
+import "io"
+
+type ProgramInterface interface {
+	SetLocalLogWriter(writer io.Writer)
+	Start() error
+	Stop() error
+	OtherOperation(op string, args []string) string
+	GetStatus() (status string, hasExtra bool, extra map[string]string)
+}

+ 60 - 0
run_unix.go

@@ -0,0 +1,60 @@
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package zdaemon
+
+import (
+	"fmt"
+	"git.swzry.com/zry/pathutils"
+	"os"
+	"path"
+	"syscall"
+)
+
+func (zd *ZDaemon) makeIPCDir() {
+	_ = pathutils.MkDirIfNotExist(path.Join(zd.zdaemonPath, "ipc"), 0777)
+}
+
+func (zd *ZDaemon) cleanIPCUnixSock() {
+	_ = os.RemoveAll(path.Join(zd.zdaemonPath, "ipc"))
+}
+
+func (zd *ZDaemon) start() {
+	if zd.checkRunning() {
+		fmt.Println("Already running. If you think it is dead, using 'restart' instead.")
+		return
+	}
+	sa := &syscall.SysProcAttr{
+		Chroot:                     "",
+		Credential:                 nil,
+		Ptrace:                     false,
+		Setsid:                     false,
+		Setpgid:                    false,
+		Setctty:                    false,
+		Noctty:                     false,
+		Ctty:                       0,
+		Foreground:                 false,
+		Pgid:                       0,
+		Pdeathsig:                  0,
+		Cloneflags:                 0,
+		Unshareflags:               0,
+		UidMappings:                nil,
+		GidMappings:                nil,
+		GidMappingsEnableSetgroups: false,
+		AmbientCaps:                nil,
+	}
+	p, err := os.StartProcess(os.Args[0], []string{os.Args[0], "__run__"}, &os.ProcAttr{
+		Dir: zd.programPath,
+		Env: os.Environ(),
+		Files: []*os.File{
+			zd.programStdin,
+			zd.programStdout,
+			zd.programStderr,
+		},
+		Sys: sa,
+	})
+	if err != nil {
+		fmt.Println("Failed start: ", err.Error())
+	} else {
+		fmt.Println("Daemon", zd.daemonName, "started. PID=", p.Pid)
+	}
+}

+ 45 - 0
run_windows.go

@@ -0,0 +1,45 @@
+// +build windows
+
+package zdaemon
+
+import (
+	"fmt"
+	"os"
+	"syscall"
+)
+
+func (zd *ZDaemon) makeIPCDir() {
+}
+
+func (zd *ZDaemon) cleanIPCUnixSock() {
+}
+
+func (zd *ZDaemon) start() {
+	if zd.checkRunning() {
+		fmt.Println("Already running. If you think it is dead, using 'restart' instead.")
+		return
+	}
+	sa := &syscall.SysProcAttr{
+		HideWindow:        true,
+		CmdLine:           fmt.Sprintf("%s __run__", os.Args[0]),
+		CreationFlags:     0,
+		Token:             0,
+		ProcessAttributes: nil,
+		ThreadAttributes:  nil,
+	}
+	p, err := os.StartProcess(os.Args[0], []string{"__run__"}, &os.ProcAttr{
+		Dir: zd.programPath,
+		Env: os.Environ(),
+		Files: []*os.File{
+			zd.programStdin,
+			zd.programStdout,
+			zd.programStderr,
+		},
+		Sys: sa,
+	})
+	if err != nil {
+		fmt.Println("Failed start: ", err.Error())
+	} else {
+		fmt.Println("Daemon", zd.daemonName, "started. PID=", p.Pid)
+	}
+}

+ 76 - 0
systemd.go

@@ -0,0 +1,76 @@
+package zdaemon
+
+import (
+	"fmt"
+	"git.swzry.com/zry/pathutils"
+	"os"
+	"path"
+	"text/template"
+)
+
+const tpl_systemd = `[Unit]
+Description={{.Description}}
+
+[Service]
+ExecStart={{.ExecPath}} start
+ExecStop={{.ExecPath}} stop
+
+[Install]
+WantedBy=multi-user.target
+`
+
+func (zd *ZDaemon) install_systemd() {
+	if zd.daemonConfig.InstallMode != "free" {
+		fmt.Println("Failed install: already installed.")
+		return
+	}
+	initsp := path.Join("/usr/lib/systemd/system/", fmt.Sprintf("%d.service", zd.daemonName))
+	pe, _ := pathutils.PathExists(initsp)
+	if pe {
+		err := os.Remove(initsp)
+		if err != nil {
+			fmt.Println("Failed install, old file exist and error in deleting old file: ", err.Error())
+			return
+		}
+	}
+	otpl := template.New(zd.daemonName)
+	tpl, err := otpl.Parse(tpl_systemd)
+	if err != nil {
+		fmt.Println("Failed install: ", err.Error())
+		return
+	}
+	fids, err := os.OpenFile(initsp, os.O_WRONLY|os.O_CREATE, 0777)
+	if err != nil {
+		fmt.Println("Failed install: ", err.Error())
+		return
+	}
+	tdat := map[string]string{
+		"Description": zd.description,
+		"ExecPath":    zd.executableFileName,
+	}
+	err = tpl.Execute(fids, tdat)
+	if err != nil {
+		fmt.Println("Failed install: ", err.Error())
+		_ = fids.Close()
+		return
+	}
+	_ = fids.Close()
+	zd.daemonConfig.InstallMode = "systemd"
+	zd.writeStatus()
+	fmt.Println("Installed.")
+}
+
+func (zd *ZDaemon) uninstall_systemd() {
+	initsp := path.Join("/etc/init.d/", zd.daemonName)
+	pe, _ := pathutils.PathExists(initsp)
+	if pe {
+		err := os.Remove(initsp)
+		if err != nil {
+			fmt.Println("Failed uninstall: ", err.Error())
+			return
+		}
+	}
+	zd.daemonConfig.InstallMode = "free"
+	zd.writeStatus()
+	fmt.Println("Uninstalled.")
+}

+ 11 - 0
uninstall.go

@@ -0,0 +1,11 @@
+package zdaemon
+
+import "fmt"
+
+func (zd *ZDaemon) uninstall_winsvc() {
+	fmt.Println("Not supported yet.")
+}
+
+func (zd *ZDaemon) uninstall_wintray() {
+	fmt.Println("Not supported yet.")
+}

+ 267 - 0
zdaemon.go

@@ -0,0 +1,267 @@
+package zdaemon
+
+import (
+	"fmt"
+	"git.swzry.com/zry/SatorIPC/golang/sipc_conn"
+	"git.swzry.com/zry/pathutils"
+	"github.com/gin-gonic/gin"
+	"net"
+	"os"
+	"os/exec"
+	"path"
+	"path/filepath"
+	"runtime"
+	"strconv"
+)
+
+type ZDaemon struct {
+	daemonName         string
+	description        string
+	initFunc           func() ProgramInterface
+	executableFileName string
+	programPath        string
+	zdaemonPath        string
+	daemonConfig       *yamldef_DaemonConfig
+	daemonProgram      ProgramInterface
+	ipcserver          *sipc_conn.Server
+	webserver          *gin.Engine
+	ipclistener        net.Listener
+	ipcurlhost         string
+	startOrder         int
+	stopOrder          int
+	programStdin       *os.File
+	programStdout      *os.File
+	programStderr      *os.File
+}
+
+func NewZDaemon(cfg *ZDaemonConfig) *ZDaemon {
+	zd := &ZDaemon{
+		daemonName:    cfg.DaemonName,
+		initFunc:      cfg.InitRunnerFunction,
+		ipcurlhost:    fmt.Sprintf("zdaemon-satoripc-client-pid-%d", os.Getpid()),
+		startOrder:    cfg.StartOrder,
+		stopOrder:     cfg.StopOrder,
+		description:   cfg.Description,
+		programStdin:  cfg.ProgramStdin,
+		programStdout: cfg.ProgramStdout,
+		programStderr: cfg.ProgramStderr,
+	}
+	if zd.startOrder <= 0 {
+		zd.startOrder = 99
+	}
+	if zd.startOrder <= 0 {
+		zd.stopOrder = 99
+	}
+	return zd
+}
+
+func (zd *ZDaemon) Run() {
+	var err error
+	zd.executableFileName, err = filepath.Abs(os.Args[0])
+	if err != nil {
+		fmt.Println("Failed get executable path: ", err.Error())
+		return
+	}
+	zd.programPath = filepath.Dir(zd.executableFileName)
+	zd.zdaemonPath = path.Join(zd.programPath, "zdaemon")
+	zd.makeDir()
+	zd.getStatus()
+	if len(os.Args) > 1 {
+		cmdop := os.Args[1]
+		switch cmdop {
+		case "install":
+			zd.install()
+			break
+		case "uninstall":
+			zd.uninstall()
+			break
+		case "start":
+			zd.start()
+			break
+		case "stop":
+			zd.stop()
+			break
+		case "status":
+			zd.status()
+			break
+		case "restart":
+			zd.restart()
+			break
+		case "enable":
+			zd.enable()
+			break
+		case "disable":
+			zd.disable()
+			break
+		case "help":
+			zd.help()
+			break
+		case "__run__":
+			zd.directRun()
+			break
+		default:
+			zd.otherOp(cmdop)
+			break
+		}
+	} else {
+		fmt.Println("Usage: ", zd.daemonName, "{help|start|stop|restart|install|uninstall|enable|disable|...}")
+	}
+}
+
+func (zd *ZDaemon) makeDir() {
+	err := pathutils.MkDirIfNotExist(zd.zdaemonPath, 0777)
+	if err != nil {
+		fmt.Println("Failed create 'zdaemon' directory: ", err.Error())
+		os.Exit(-1)
+	}
+}
+
+func (zd *ZDaemon) install() {
+	if len(os.Args) == 3 {
+		mode := os.Args[2]
+		switch mode {
+		case "winsvc":
+			zd.install_winsvc()
+			break
+		case "wintray":
+			zd.install_wintray()
+			break
+		case "sysv":
+			zd.install_sysv()
+			break
+		case "systemd":
+			zd.install_systemd()
+			break
+		case "openwrt":
+			zd.install_openwrt()
+			break
+		default:
+			zd.help_install()
+			break
+		}
+	} else {
+		zd.help_install()
+	}
+}
+
+func (zd *ZDaemon) enable() {
+	fmt.Println("Not supported yet.")
+}
+
+func (zd *ZDaemon) disable() {
+	fmt.Println("Not supported yet.")
+}
+
+func (zd *ZDaemon) uninstall() {
+	switch zd.daemonConfig.InstallMode {
+	case "winsvc":
+		zd.uninstall_winsvc()
+		break
+	case "wintray":
+		zd.uninstall_wintray()
+		break
+	case "sysv":
+		zd.uninstall_sysv()
+		break
+	case "systemd":
+		zd.uninstall_systemd()
+		break
+	case "openwrt":
+		zd.uninstall_openwrt()
+		break
+	default:
+		fmt.Println("No installed yet.")
+		break
+	}
+}
+
+func (zd *ZDaemon) forceQuit() {
+	zd.killProcess(zd.daemonConfig.PID)
+	zd.daemonConfig.IsRunning = false
+	zd.daemonConfig.IpcAddr = ""
+	zd.daemonConfig.PID = -1
+}
+
+func (zd *ZDaemon) checkRunning() bool {
+	if zd.daemonConfig.IsRunning {
+		if zd.daemonConfig.PID > 0 {
+			if zd.daemonConfig.IpcAddr == "" {
+				zd.forceQuit()
+				return false
+			} else {
+				al := zd.ipcAppCheckAlive()
+				if al {
+					return true
+				} else {
+					zd.forceQuit()
+					return false
+				}
+			}
+		} else {
+			zd.daemonConfig.IsRunning = false
+			return false
+		}
+	}
+	return false
+}
+
+func (zd *ZDaemon) stop() {
+	if !zd.checkRunning() {
+		fmt.Println("Daemon", zd.daemonName, "not running.")
+		return
+	}
+	ks := zd.ipcAppStop()
+	if !ks {
+		fmt.Println("Force killing daemon...")
+		zd.killProcess(zd.daemonConfig.PID)
+		fmt.Println("End.")
+	}
+}
+
+func (zd *ZDaemon) restart() {
+	if zd.checkRunning() {
+		ks := zd.ipcAppStop()
+		if !ks {
+			fmt.Println("Force killing daemon...")
+			zd.killProcess(zd.daemonConfig.PID)
+			fmt.Println("End.")
+		}
+	} else {
+		fmt.Println("Daemon", zd.daemonName, "not running.")
+	}
+	zd.start()
+}
+
+func (zd *ZDaemon) status() {
+	if zd.checkRunning() {
+		zd.ipcAppGetStatus()
+	} else {
+		fmt.Println("Status: not running.")
+		return
+	}
+}
+
+func (zd *ZDaemon) otherOp(op string) {
+	uargs := os.Args[2:]
+	if zd.checkRunning() {
+		zd.ipcAppOtherOperation(op, uargs)
+	} else {
+		fmt.Println("Faild do operation", op, ": daemon not running.")
+		return
+	}
+}
+
+func (zd *ZDaemon) killProcess(pid int) {
+	if pid < 1 {
+		return
+	}
+	if runtime.GOOS == "windows" {
+		cmd := exec.Command("taskkill.exe", "/F", "/PID", strconv.Itoa(pid))
+		_ = cmd.Run()
+	} else {
+		cmd := exec.Command("kill", strconv.Itoa(pid))
+		_ = cmd.Run()
+		cmd = exec.Command("kill", "-9", strconv.Itoa(pid))
+		_ = cmd.Run()
+	}
+}