mirror of
				https://github.com/TeaOSLab/EdgeAPI.git
				synced 2025-11-04 16:00:24 +08:00 
			
		
		
		
	增加service命令
This commit is contained in:
		@@ -22,7 +22,7 @@ func main() {
 | 
				
			|||||||
	app := apps.NewAppCmd()
 | 
						app := apps.NewAppCmd()
 | 
				
			||||||
	app.Version(teaconst.Version)
 | 
						app.Version(teaconst.Version)
 | 
				
			||||||
	app.Product(teaconst.ProductName)
 | 
						app.Product(teaconst.ProductName)
 | 
				
			||||||
	app.Usage(teaconst.ProcessName + " [start|stop|restart|setup|upgrade]")
 | 
						app.Usage(teaconst.ProcessName + " [start|stop|restart|setup|upgrade|service|daemon]")
 | 
				
			||||||
	app.On("setup", func() {
 | 
						app.On("setup", func() {
 | 
				
			||||||
		setupCmd := setup.NewSetupFromCmd()
 | 
							setupCmd := setup.NewSetupFromCmd()
 | 
				
			||||||
		err := setupCmd.Run()
 | 
							err := setupCmd.Run()
 | 
				
			||||||
@@ -56,6 +56,16 @@ func main() {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		fmt.Println("finished!")
 | 
							fmt.Println("finished!")
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
						app.On("daemon", func() {
 | 
				
			||||||
 | 
							nodes.NewAPINode().Daemon()
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						app.On("service", func() {
 | 
				
			||||||
 | 
							err := nodes.NewAPINode().InstallSystemService()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								fmt.Println("[ERROR]install failed: " + err.Error())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							fmt.Println("done")
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
	app.Run(func() {
 | 
						app.Run(func() {
 | 
				
			||||||
		nodes.NewAPINode().Start()
 | 
							nodes.NewAPINode().Start()
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,4 +13,6 @@ const (
 | 
				
			|||||||
	EncryptMethod = "aes-256-cfb"
 | 
						EncryptMethod = "aes-256-cfb"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ErrServer = "服务器出了点小问题,请稍后重试"
 | 
						ErrServer = "服务器出了点小问题,请稍后重试"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SystemdServiceName = "edge-api"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ import (
 | 
				
			|||||||
	"github.com/TeaOSLab/EdgeAPI/internal/configs"
 | 
						"github.com/TeaOSLab/EdgeAPI/internal/configs"
 | 
				
			||||||
	teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
 | 
						teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAPI/internal/db/models"
 | 
						"github.com/TeaOSLab/EdgeAPI/internal/db/models"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAPI/internal/events"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
 | 
						"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
 | 
						"github.com/TeaOSLab/EdgeAPI/internal/rpc/services"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAPI/internal/setup"
 | 
						"github.com/TeaOSLab/EdgeAPI/internal/setup"
 | 
				
			||||||
@@ -14,14 +15,18 @@ import (
 | 
				
			|||||||
	"github.com/go-yaml/yaml"
 | 
						"github.com/go-yaml/yaml"
 | 
				
			||||||
	"github.com/iwind/TeaGo/Tea"
 | 
						"github.com/iwind/TeaGo/Tea"
 | 
				
			||||||
	"github.com/iwind/TeaGo/dbs"
 | 
						"github.com/iwind/TeaGo/dbs"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/lists"
 | 
				
			||||||
	"github.com/iwind/TeaGo/logs"
 | 
						"github.com/iwind/TeaGo/logs"
 | 
				
			||||||
	stringutil "github.com/iwind/TeaGo/utils/string"
 | 
						stringutil "github.com/iwind/TeaGo/utils/string"
 | 
				
			||||||
	"google.golang.org/grpc"
 | 
						"google.golang.org/grpc"
 | 
				
			||||||
	"google.golang.org/grpc/credentials"
 | 
						"google.golang.org/grpc/credentials"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var sharedAPIConfig *configs.APIConfig = nil
 | 
					var sharedAPIConfig *configs.APIConfig = nil
 | 
				
			||||||
@@ -36,8 +41,15 @@ func NewAPINode() *APINode {
 | 
				
			|||||||
func (this *APINode) Start() {
 | 
					func (this *APINode) Start() {
 | 
				
			||||||
	logs.Println("[API_NODE]start api node, pid: " + strconv.Itoa(os.Getpid()))
 | 
						logs.Println("[API_NODE]start api node, pid: " + strconv.Itoa(os.Getpid()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 本地Sock
 | 
				
			||||||
 | 
						err := this.listenSock()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logs.Println("[API_NODE]" + err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 自动升级
 | 
						// 自动升级
 | 
				
			||||||
	err := this.autoUpgrade()
 | 
						err = this.autoUpgrade()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logs.Println("[API_NODE]auto upgrade failed: " + err.Error())
 | 
							logs.Println("[API_NODE]auto upgrade failed: " + err.Error())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -86,6 +98,68 @@ func (this *APINode) Start() {
 | 
				
			|||||||
	select {}
 | 
						select {}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 实现守护进程
 | 
				
			||||||
 | 
					func (this *APINode) Daemon() {
 | 
				
			||||||
 | 
						path := os.TempDir() + "/edge-api.sock"
 | 
				
			||||||
 | 
						isDebug := lists.ContainsString(os.Args, "debug")
 | 
				
			||||||
 | 
						isDebug = true
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							conn, err := net.DialTimeout("unix", path, 1*time.Second)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if isDebug {
 | 
				
			||||||
 | 
									log.Println("[DAEMON]starting ...")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 尝试启动
 | 
				
			||||||
 | 
								err = func() error {
 | 
				
			||||||
 | 
									exe, err := os.Executable()
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									cmd := exec.Command(exe)
 | 
				
			||||||
 | 
									err = cmd.Start()
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									err = cmd.Wait()
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if isDebug {
 | 
				
			||||||
 | 
										log.Println("[DAEMON]", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									time.Sleep(1 * time.Second)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									time.Sleep(5 * time.Second)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								_ = conn.Close()
 | 
				
			||||||
 | 
								time.Sleep(5 * time.Second)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 安装系统服务
 | 
				
			||||||
 | 
					func (this *APINode) InstallSystemService() error {
 | 
				
			||||||
 | 
						shortName := teaconst.SystemdServiceName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exe, err := os.Executable()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						manager := utils.NewServiceManager(shortName, teaconst.ProductName)
 | 
				
			||||||
 | 
						err = manager.Install(exe, []string{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 启动RPC监听
 | 
					// 启动RPC监听
 | 
				
			||||||
func (this *APINode) listenRPC(listener net.Listener, tlsConfig *tls.Config) error {
 | 
					func (this *APINode) listenRPC(listener net.Listener, tlsConfig *tls.Config) error {
 | 
				
			||||||
	var rpcServer *grpc.Server
 | 
						var rpcServer *grpc.Server
 | 
				
			||||||
@@ -338,3 +412,40 @@ func (this *APINode) listenPorts(apiNode *models.APINode) (isListening bool) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 监听本地sock
 | 
				
			||||||
 | 
					func (this *APINode) listenSock() error {
 | 
				
			||||||
 | 
						path := os.TempDir() + "/edge-api.sock"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 检查是否已经存在
 | 
				
			||||||
 | 
						_, err := os.Stat(path)
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							conn, err := net.Dial("unix", path)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								_ = os.Remove(path)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								_ = conn.Close()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 新的监听任务
 | 
				
			||||||
 | 
						listener, err := net.Listen("unix", path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						events.On(events.EventQuit, func() {
 | 
				
			||||||
 | 
							remotelogs.Println("API_NODE", "quit unix sock")
 | 
				
			||||||
 | 
							_ = listener.Close()
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							for {
 | 
				
			||||||
 | 
								_, err := listener.Accept()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										111
									
								
								internal/utils/service.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								internal/utils/service.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
				
			|||||||
 | 
					package utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/Tea"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/files"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/logs"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 服务管理器
 | 
				
			||||||
 | 
					type ServiceManager struct {
 | 
				
			||||||
 | 
						Name        string
 | 
				
			||||||
 | 
						Description string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fp         *os.File
 | 
				
			||||||
 | 
						logger     *log.Logger
 | 
				
			||||||
 | 
						onceLocker sync.Once
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取对象
 | 
				
			||||||
 | 
					func NewServiceManager(name, description string) *ServiceManager {
 | 
				
			||||||
 | 
						manager := &ServiceManager{
 | 
				
			||||||
 | 
							Name:        name,
 | 
				
			||||||
 | 
							Description: description,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// root
 | 
				
			||||||
 | 
						manager.resetRoot()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return manager
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 设置服务
 | 
				
			||||||
 | 
					func (this *ServiceManager) setup() {
 | 
				
			||||||
 | 
						this.onceLocker.Do(func() {
 | 
				
			||||||
 | 
							logFile := files.NewFile(Tea.Root + "/logs/service.log")
 | 
				
			||||||
 | 
							if logFile.Exists() {
 | 
				
			||||||
 | 
								logFile.Delete()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							//logger
 | 
				
			||||||
 | 
							fp, err := os.OpenFile(Tea.Root+"/logs/service.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								logs.Error(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							this.fp = fp
 | 
				
			||||||
 | 
							this.logger = log.New(fp, "", log.LstdFlags)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 记录普通日志
 | 
				
			||||||
 | 
					func (this *ServiceManager) Log(msg string) {
 | 
				
			||||||
 | 
						this.setup()
 | 
				
			||||||
 | 
						if this.logger == nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						this.logger.Println("[info]" + msg)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 记录错误日志
 | 
				
			||||||
 | 
					func (this *ServiceManager) LogError(msg string) {
 | 
				
			||||||
 | 
						this.setup()
 | 
				
			||||||
 | 
						if this.logger == nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						this.logger.Println("[error]" + msg)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 关闭
 | 
				
			||||||
 | 
					func (this *ServiceManager) Close() error {
 | 
				
			||||||
 | 
						if this.fp != nil {
 | 
				
			||||||
 | 
							return this.fp.Close()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 重置Root
 | 
				
			||||||
 | 
					func (this *ServiceManager) resetRoot() {
 | 
				
			||||||
 | 
						if !Tea.IsTesting() {
 | 
				
			||||||
 | 
							exePath, err := os.Executable()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								exePath = os.Args[0]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							link, err := filepath.EvalSymlinks(exePath)
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								exePath = link
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							fullPath, err := filepath.Abs(exePath)
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								Tea.UpdateRoot(filepath.Dir(filepath.Dir(fullPath)))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						Tea.SetPublicDir(Tea.Root + Tea.DS + "web" + Tea.DS + "public")
 | 
				
			||||||
 | 
						Tea.SetViewsDir(Tea.Root + Tea.DS + "web" + Tea.DS + "views")
 | 
				
			||||||
 | 
						Tea.SetTmpDir(Tea.Root + Tea.DS + "web" + Tea.DS + "tmp")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 保持命令行窗口是打开的
 | 
				
			||||||
 | 
					func (this *ServiceManager) PauseWindow() {
 | 
				
			||||||
 | 
						if runtime.GOOS != "windows" {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b := make([]byte, 1)
 | 
				
			||||||
 | 
						_, _ = os.Stdin.Read(b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										154
									
								
								internal/utils/service_linux.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								internal/utils/service_linux.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,154 @@
 | 
				
			|||||||
 | 
					// +build linux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/Tea"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/files"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var systemdServiceFile = "/etc/systemd/system/edge-api.service"
 | 
				
			||||||
 | 
					var initServiceFile = "/etc/init.d/" + teaconst.SystemdServiceName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 安装服务
 | 
				
			||||||
 | 
					func (this *ServiceManager) Install(exePath string, args []string) error {
 | 
				
			||||||
 | 
						if os.Getgid() != 0 {
 | 
				
			||||||
 | 
							return errors.New("only root users can install the service")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						systemd, err := exec.LookPath("systemctl")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return this.installInitService(exePath, args)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return this.installSystemdService(systemd, exePath, args)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 启动服务
 | 
				
			||||||
 | 
					func (this *ServiceManager) Start() error {
 | 
				
			||||||
 | 
						if os.Getgid() != 0 {
 | 
				
			||||||
 | 
							return errors.New("only root users can start the service")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if files.NewFile(systemdServiceFile).Exists() {
 | 
				
			||||||
 | 
							systemd, err := exec.LookPath("systemctl")
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return exec.Command(systemd, "start", teaconst.SystemdServiceName+".service").Start()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return exec.Command("service", teaconst.ProcessName, "start").Start()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 删除服务
 | 
				
			||||||
 | 
					func (this *ServiceManager) Uninstall() error {
 | 
				
			||||||
 | 
						if os.Getgid() != 0 {
 | 
				
			||||||
 | 
							return errors.New("only root users can uninstall the service")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if files.NewFile(systemdServiceFile).Exists() {
 | 
				
			||||||
 | 
							systemd, err := exec.LookPath("systemctl")
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// disable service
 | 
				
			||||||
 | 
							exec.Command(systemd, "disable", teaconst.SystemdServiceName+".service").Start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// reload
 | 
				
			||||||
 | 
							exec.Command(systemd, "daemon-reload")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return files.NewFile(systemdServiceFile).Delete()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f := files.NewFile(initServiceFile)
 | 
				
			||||||
 | 
						if f.Exists() {
 | 
				
			||||||
 | 
							return f.Delete()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// install init service
 | 
				
			||||||
 | 
					func (this *ServiceManager) installInitService(exePath string, args []string) error {
 | 
				
			||||||
 | 
						shortName := teaconst.SystemdServiceName
 | 
				
			||||||
 | 
						scriptFile := Tea.Root + "/scripts/" + shortName
 | 
				
			||||||
 | 
						if !files.NewFile(scriptFile).Exists() {
 | 
				
			||||||
 | 
							return errors.New("'scripts/" + shortName + "' file not exists")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile(scriptFile)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data = regexp.MustCompile("INSTALL_DIR=.+").ReplaceAll(data, []byte("INSTALL_DIR="+Tea.Root))
 | 
				
			||||||
 | 
						err = ioutil.WriteFile(initServiceFile, data, 0777)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						chkCmd, err := exec.LookPath("chkconfig")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = exec.Command(chkCmd, "--add", teaconst.ProcessName).Start()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// install systemd service
 | 
				
			||||||
 | 
					func (this *ServiceManager) installSystemdService(systemd, exePath string, args []string) error {
 | 
				
			||||||
 | 
						shortName := teaconst.SystemdServiceName
 | 
				
			||||||
 | 
						longName := "GoEdge API" // TODO 将来可以修改
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						desc := `# Provides:          ` + shortName + `
 | 
				
			||||||
 | 
					# Required-Start:    $all
 | 
				
			||||||
 | 
					# Required-Stop:
 | 
				
			||||||
 | 
					# Default-Start:     2 3 4 5
 | 
				
			||||||
 | 
					# Default-Stop:
 | 
				
			||||||
 | 
					# Short-Description: ` + longName + ` Service
 | 
				
			||||||
 | 
					### END INIT INFO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Unit]
 | 
				
			||||||
 | 
					Description=` + longName + ` Service
 | 
				
			||||||
 | 
					Before=shutdown.target
 | 
				
			||||||
 | 
					After=network-online.target
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Service]
 | 
				
			||||||
 | 
					Type=simple
 | 
				
			||||||
 | 
					Restart=always
 | 
				
			||||||
 | 
					RestartSec=1s
 | 
				
			||||||
 | 
					ExecStart=` + exePath + ` daemon
 | 
				
			||||||
 | 
					ExecStop=` + exePath + ` stop
 | 
				
			||||||
 | 
					ExecReload=` + exePath + ` reload
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Install]
 | 
				
			||||||
 | 
					WantedBy=multi-user.target`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// write file
 | 
				
			||||||
 | 
						err := ioutil.WriteFile(systemdServiceFile, []byte(desc), 0777)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// stop current systemd service if running
 | 
				
			||||||
 | 
						exec.Command(systemd, "stop", shortName+".service")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// reload
 | 
				
			||||||
 | 
						exec.Command(systemd, "daemon-reload")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// enable
 | 
				
			||||||
 | 
						cmd := exec.Command(systemd, "enable", shortName+".service")
 | 
				
			||||||
 | 
						return cmd.Run()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								internal/utils/service_others.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								internal/utils/service_others.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					// +build !linux,!windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 安装服务
 | 
				
			||||||
 | 
					func (this *ServiceManager) Install(exePath string, args []string) error {
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 启动服务
 | 
				
			||||||
 | 
					func (this *ServiceManager) Start() error {
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 删除服务
 | 
				
			||||||
 | 
					func (this *ServiceManager) Uninstall() error {
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										12
									
								
								internal/utils/service_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								internal/utils/service_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					package utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestServiceManager_Log(t *testing.T) {
 | 
				
			||||||
 | 
						manager := NewServiceManager(teaconst.ProductName, teaconst.ProductName+" Server")
 | 
				
			||||||
 | 
						manager.Log("Hello, World")
 | 
				
			||||||
 | 
						manager.LogError("Hello, World")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										173
									
								
								internal/utils/service_windows.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								internal/utils/service_windows.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,173 @@
 | 
				
			|||||||
 | 
					// +build windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/Tea"
 | 
				
			||||||
 | 
						"golang.org/x/sys/windows"
 | 
				
			||||||
 | 
						"golang.org/x/sys/windows/svc"
 | 
				
			||||||
 | 
						"golang.org/x/sys/windows/svc/mgr"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 安装服务
 | 
				
			||||||
 | 
					func (this *ServiceManager) Install(exePath string, args []string) error {
 | 
				
			||||||
 | 
						m, err := mgr.Connect()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("connecting: %s please 'Run as administrator' again", err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer m.Disconnect()
 | 
				
			||||||
 | 
						s, err := m.OpenService(this.Name)
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							s.Close()
 | 
				
			||||||
 | 
							return fmt.Errorf("service %s already exists", this.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s, err = m.CreateService(this.Name, exePath, mgr.Config{
 | 
				
			||||||
 | 
							DisplayName: this.Name,
 | 
				
			||||||
 | 
							Description: this.Description,
 | 
				
			||||||
 | 
							StartType:   windows.SERVICE_AUTO_START,
 | 
				
			||||||
 | 
						}, args...)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("creating: %s", err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer s.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 启动服务
 | 
				
			||||||
 | 
					func (this *ServiceManager) Start() error {
 | 
				
			||||||
 | 
						m, err := mgr.Connect()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer m.Disconnect()
 | 
				
			||||||
 | 
						s, err := m.OpenService(this.Name)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("could not access service: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer s.Close()
 | 
				
			||||||
 | 
						err = s.Start("service")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("could not start service: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 删除服务
 | 
				
			||||||
 | 
					func (this *ServiceManager) Uninstall() error {
 | 
				
			||||||
 | 
						m, err := mgr.Connect()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("connecting: %s please 'Run as administrator' again", err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer m.Disconnect()
 | 
				
			||||||
 | 
						s, err := m.OpenService(this.Name)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("open service: %s", err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// shutdown service
 | 
				
			||||||
 | 
						_, err = s.Control(svc.Stop)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Printf("shutdown service: %s\n", err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer s.Close()
 | 
				
			||||||
 | 
						err = s.Delete()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("deleting: %s", err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 运行
 | 
				
			||||||
 | 
					func (this *ServiceManager) Run() {
 | 
				
			||||||
 | 
						err := svc.Run(this.Name, this)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							this.LogError(err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 同服务管理器的交互
 | 
				
			||||||
 | 
					func (this *ServiceManager) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
 | 
				
			||||||
 | 
						const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						changes <- svc.Status{
 | 
				
			||||||
 | 
							State: svc.StartPending,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						changes <- svc.Status{
 | 
				
			||||||
 | 
							State:   svc.Running,
 | 
				
			||||||
 | 
							Accepts: cmdsAccepted,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// start service
 | 
				
			||||||
 | 
						this.Log("start")
 | 
				
			||||||
 | 
						this.cmdStart()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					loop:
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case c := <-r:
 | 
				
			||||||
 | 
								switch c.Cmd {
 | 
				
			||||||
 | 
								case svc.Interrogate:
 | 
				
			||||||
 | 
									this.Log("cmd: Interrogate")
 | 
				
			||||||
 | 
									changes <- c.CurrentStatus
 | 
				
			||||||
 | 
								case svc.Stop, svc.Shutdown:
 | 
				
			||||||
 | 
									this.Log("cmd: Stop|Shutdown")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// stop service
 | 
				
			||||||
 | 
									this.cmdStop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									break loop
 | 
				
			||||||
 | 
								case svc.Pause:
 | 
				
			||||||
 | 
									this.Log("cmd: Pause")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// stop service
 | 
				
			||||||
 | 
									this.cmdStop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									changes <- svc.Status{
 | 
				
			||||||
 | 
										State:   svc.Paused,
 | 
				
			||||||
 | 
										Accepts: cmdsAccepted,
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								case svc.Continue:
 | 
				
			||||||
 | 
									this.Log("cmd: Continue")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// start service
 | 
				
			||||||
 | 
									this.cmdStart()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									changes <- svc.Status{
 | 
				
			||||||
 | 
										State:   svc.Running,
 | 
				
			||||||
 | 
										Accepts: cmdsAccepted,
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									this.LogError(fmt.Sprintf("unexpected control request #%d\r\n", c))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						changes <- svc.Status{
 | 
				
			||||||
 | 
							State: svc.StopPending,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 启动Web服务
 | 
				
			||||||
 | 
					func (this *ServiceManager) cmdStart() {
 | 
				
			||||||
 | 
						cmd := exec.Command(Tea.Root+Tea.DS+"bin"+Tea.DS+teaconst.SystemdServiceName+".exe", "start")
 | 
				
			||||||
 | 
						err := cmd.Start()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							this.LogError(err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 停止Web服务
 | 
				
			||||||
 | 
					func (this *ServiceManager) cmdStop() {
 | 
				
			||||||
 | 
						cmd := exec.Command(Tea.Root+Tea.DS+"bin"+Tea.DS+teaconst.SystemdServiceName+".exe", "stop")
 | 
				
			||||||
 | 
						err := cmd.Start()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							this.LogError(err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user