From 4f9a5d238c23de77b0f1854ce3b709299f9870af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E7=A5=A5=E8=B6=85?= Date: Wed, 5 Jul 2023 11:14:51 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9C=AC=E5=9C=B0mysql?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E8=87=AA=E5=8A=A8=E5=90=AF=E5=8A=A8=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/db/utils/utils.go | 84 ++++++++++++++++++++++++++++++ internal/db/utils/utils_test.go | 8 +++ internal/nodes/api_node.go | 13 ++--- internal/utils/exec/look_linux.go | 59 +++++++++++++++++++++ internal/utils/exec/look_others.go | 10 ++++ 5 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 internal/utils/exec/look_linux.go create mode 100644 internal/utils/exec/look_others.go diff --git a/internal/db/utils/utils.go b/internal/db/utils/utils.go index aee0dc31..9c8ee824 100644 --- a/internal/db/utils/utils.go +++ b/internal/db/utils/utils.go @@ -1,10 +1,18 @@ package dbutils import ( + executils "github.com/TeaOSLab/EdgeAPI/internal/utils/exec" + "github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/dbs" + "github.com/iwind/TeaGo/lists" + "github.com/iwind/TeaGo/logs" "github.com/iwind/TeaGo/types" "net" + "os" + "os/exec" + "regexp" "strings" + "time" ) // NewQuery 构造Query @@ -127,3 +135,79 @@ func MySQLVersionFrom8() (bool, error) { } return false, nil } + +// FindMySQLPath find out mysqld_safe path from system processes +func FindMySQLPath() string { + psExe, err := executils.LookPath("ps") + if err != nil { + return "" + } + var cmd = executils.NewTimeoutCmd(3*time.Second, psExe, "-ef"). + WithStdout(). + WithStderr() + err = cmd.Run() + if err != nil { + return "" + } + var reg = regexp.MustCompile(`\s(/\S+/mysqld_safe)\s`) + var matches = reg.FindStringSubmatch(cmd.Stdout()) + if len(matches) > 1 { + var path = matches[1] + _, err = os.Stat(path) + if err != nil { + return "" + } + return path + } + return "" +} + +// FindMySQLPathAndRemember find out mysqld_safe path then remember it for future usage +func FindMySQLPathAndRemember() { + var path = FindMySQLPath() + if len(path) == 0 { + return + } + + var cacheFile = Tea.Root + "/data/mysql-path.cache" + _ = os.WriteFile(cacheFile, []byte(path), 0666) // ignore error +} + +// StartLocalMySQL try to start local mysql server +func StartLocalMySQL() { + // possible installed paths + var mysqldSafeFiles = []string{} + + // read last path from cache file + var cacheFile = Tea.Root + "/data/mysql-path.cache" + cacheData, err := os.ReadFile(cacheFile) + if err == nil && len(cacheData) > 0 { + mysqldSafeFiles = append(mysqldSafeFiles, string(cacheData)) + } + + // from $PATH variable + exePath, lookErr := executils.LookPath("mysqld_safe") + if lookErr == nil && len(exePath) > 0 && !lists.ContainsString(mysqldSafeFiles, exePath) { + mysqldSafeFiles = append(mysqldSafeFiles, exePath) + } + + // these installed by edge-boot or foolish-mysql + for _, path := range []string{ + "/usr/local/mysql/bin/mysqld_safe", + "/usr/local/mysql8/bin/mysqld_safe", + } { + if !lists.ContainsString(mysqldSafeFiles, path) { + mysqldSafeFiles = append(mysqldSafeFiles, path) + } + } + + for _, mysqldSafeFile := range mysqldSafeFiles { + _, err := os.Stat(mysqldSafeFile) + if err == nil { + logs.Println("[API_NODE]try to start local mysql server from '" + mysqldSafeFile + "' ...") + var mysqlCmd = exec.Command(mysqldSafeFile) + _ = mysqlCmd.Start() + break + } + } +} diff --git a/internal/db/utils/utils_test.go b/internal/db/utils/utils_test.go index 35d62083..32f93a27 100644 --- a/internal/db/utils/utils_test.go +++ b/internal/db/utils/utils_test.go @@ -35,3 +35,11 @@ func TestMySQLVersion(t *testing.T) { func TestMySQLVersionFrom8(t *testing.T) { t.Log(dbutils.MySQLVersionFrom8()) } + +func TestFindMySQLPath(t *testing.T) { + t.Log(dbutils.FindMySQLPath()) +} + +func TestStartLocalMySQL(t *testing.T) { + dbutils.StartLocalMySQL() +} \ No newline at end of file diff --git a/internal/nodes/api_node.go b/internal/nodes/api_node.go index 2af31fa2..5b8d9bc0 100644 --- a/internal/nodes/api_node.go +++ b/internal/nodes/api_node.go @@ -295,6 +295,9 @@ func (this *APINode) listenRPC(listener net.Listener, tlsConfig *tls.Config) err func (this *APINode) checkDB() error { logs.Println("[API_NODE]checking database connection ...") + // lookup mysqld_safe process + go dbutils.FindMySQLPathAndRemember() + db, err := dbs.Default() if err != nil { return err @@ -311,13 +314,7 @@ func (this *APINode) checkDB() error { if strings.Contains(err.Error(), "connection refused") { config, _ := db.Config() if config != nil && (strings.Contains(config.Dsn, "tcp(127.0.0.1:") || strings.Contains(config.Dsn, "tcp(localhost:")) && os.Getgid() == 0 /** ROOT 用户 **/ { - var mysqldSafeFile = "/usr/local/mysql/bin/mysqld_safe" - _, err = os.Stat(mysqldSafeFile) - if err == nil { - logs.Println("[API_NODE]try to start local mysql server from '" + mysqldSafeFile + "' ...") - var mysqlCmd = exec.Command(mysqldSafeFile) - _ = mysqlCmd.Start() - } + dbutils.StartLocalMySQL() } } @@ -835,7 +832,7 @@ func (this *APINode) unaryInterceptor(ctx context.Context, req any, info *grpc.U if err != nil { statusErr, ok := status.FromError(err) if ok { - err = status.Error(statusErr.Code(), "'" + info.FullMethod + "()' says: " + err.Error()) + err = status.Error(statusErr.Code(), "'"+info.FullMethod+"()' says: "+err.Error()) } else { err = errors.New("'" + info.FullMethod + "()' says: " + err.Error()) } diff --git a/internal/utils/exec/look_linux.go b/internal/utils/exec/look_linux.go new file mode 100644 index 00000000..9b158350 --- /dev/null +++ b/internal/utils/exec/look_linux.go @@ -0,0 +1,59 @@ +// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn . +//go:build linux + +package executils + +import ( + "golang.org/x/sys/unix" + "io/fs" + "os" + "os/exec" + "syscall" +) + +// LookPath customize our LookPath() function, to work in broken $PATH environment variable +func LookPath(file string) (string, error) { + result, err := exec.LookPath(file) + if err == nil && len(result) > 0 { + return result, nil + } + + // add common dirs contains executable files these may be excluded in $PATH environment variable + var binPaths = []string{ + "/usr/sbin", + "/usr/bin", + "/usr/local/sbin", + "/usr/local/bin", + } + + for _, binPath := range binPaths { + var fullPath = binPath + string(os.PathSeparator) + file + + stat, err := os.Stat(fullPath) + if err != nil { + continue + } + if stat.IsDir() { + return "", syscall.EISDIR + } + + var mode = stat.Mode() + if mode.IsDir() { + return "", syscall.EISDIR + } + err = syscall.Faccessat(unix.AT_FDCWD, fullPath, unix.X_OK, unix.AT_EACCESS) + if err == nil || (err != syscall.ENOSYS && err != syscall.EPERM) { + return fullPath, err + } + if mode&0111 != 0 { + return fullPath, nil + } + return "", fs.ErrPermission + } + + return "", &exec.Error{ + Name: file, + Err: exec.ErrNotFound, + } +} + diff --git a/internal/utils/exec/look_others.go b/internal/utils/exec/look_others.go new file mode 100644 index 00000000..a759c8ef --- /dev/null +++ b/internal/utils/exec/look_others.go @@ -0,0 +1,10 @@ +// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn . +//go:build !linux + +package executils + +import "os/exec" + +func LookPath(file string) (string, error) { + return exec.LookPath(file) +}