优化可执行文件程序

This commit is contained in:
刘祥超
2023-12-26 09:09:21 +08:00
parent bb56ec9ec5
commit e97d9fb8a0
8 changed files with 311 additions and 17 deletions

162
internal/utils/exec/cmd.go Normal file
View File

@@ -0,0 +1,162 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package executils
import (
"bytes"
"context"
"os"
"os/exec"
"strings"
"time"
)
type Cmd struct {
name string
args []string
env []string
dir string
ctx context.Context
timeout time.Duration
cancelFunc func()
captureStdout bool
captureStderr bool
stdout *bytes.Buffer
stderr *bytes.Buffer
rawCmd *exec.Cmd
}
func NewCmd(name string, args ...string) *Cmd {
return &Cmd{
name: name,
args: args,
}
}
func NewTimeoutCmd(timeout time.Duration, name string, args ...string) *Cmd {
return (&Cmd{
name: name,
args: args,
}).WithTimeout(timeout)
}
func (this *Cmd) WithTimeout(timeout time.Duration) *Cmd {
this.timeout = timeout
ctx, cancelFunc := context.WithTimeout(context.Background(), timeout)
this.ctx = ctx
this.cancelFunc = cancelFunc
return this
}
func (this *Cmd) WithStdout() *Cmd {
this.captureStdout = true
return this
}
func (this *Cmd) WithStderr() *Cmd {
this.captureStderr = true
return this
}
func (this *Cmd) WithEnv(env []string) *Cmd {
this.env = env
return this
}
func (this *Cmd) WithDir(dir string) *Cmd {
this.dir = dir
return this
}
func (this *Cmd) Start() error {
var cmd = this.compose()
return cmd.Start()
}
func (this *Cmd) Wait() error {
var cmd = this.compose()
return cmd.Wait()
}
func (this *Cmd) Run() error {
if this.cancelFunc != nil {
defer this.cancelFunc()
}
var cmd = this.compose()
return cmd.Run()
}
func (this *Cmd) RawStdout() string {
if this.stdout != nil {
return this.stdout.String()
}
return ""
}
func (this *Cmd) Stdout() string {
return strings.TrimSpace(this.RawStdout())
}
func (this *Cmd) RawStderr() string {
if this.stderr != nil {
return this.stderr.String()
}
return ""
}
func (this *Cmd) Stderr() string {
return strings.TrimSpace(this.RawStderr())
}
func (this *Cmd) String() string {
if this.rawCmd != nil {
return this.rawCmd.String()
}
var newCmd = exec.Command(this.name, this.args...)
return newCmd.String()
}
func (this *Cmd) Process() *os.Process {
if this.rawCmd != nil {
return this.rawCmd.Process
}
return nil
}
func (this *Cmd) compose() *exec.Cmd {
if this.rawCmd != nil {
return this.rawCmd
}
if this.ctx != nil {
this.rawCmd = exec.CommandContext(this.ctx, this.name, this.args...)
} else {
this.rawCmd = exec.Command(this.name, this.args...)
}
if this.env != nil {
this.rawCmd.Env = this.env
}
if len(this.dir) > 0 {
this.rawCmd.Dir = this.dir
}
if this.captureStdout {
this.stdout = &bytes.Buffer{}
this.rawCmd.Stdout = this.stdout
}
if this.captureStderr {
this.stderr = &bytes.Buffer{}
this.rawCmd.Stderr = this.stderr
}
return this.rawCmd
}

View File

@@ -0,0 +1,61 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package executils_test
import (
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
"testing"
"time"
)
func TestNewTimeoutCmd_Sleep(t *testing.T) {
var cmd = executils.NewTimeoutCmd(1*time.Second, "sleep", "3")
cmd.WithStdout()
cmd.WithStderr()
err := cmd.Run()
t.Log("error:", err)
t.Log("stdout:", cmd.Stdout())
t.Log("stderr:", cmd.Stderr())
}
func TestNewTimeoutCmd_Echo(t *testing.T) {
var cmd = executils.NewTimeoutCmd(10*time.Second, "echo", "-n", "hello")
cmd.WithStdout()
cmd.WithStderr()
err := cmd.Run()
t.Log("error:", err)
t.Log("stdout:", cmd.Stdout())
t.Log("stderr:", cmd.Stderr())
}
func TestNewTimeoutCmd_Echo2(t *testing.T) {
var cmd = executils.NewCmd("echo", "hello")
cmd.WithStdout()
cmd.WithStderr()
err := cmd.Run()
t.Log("error:", err)
t.Log("stdout:", cmd.Stdout())
t.Log("raw stdout:", cmd.RawStdout())
t.Log("stderr:", cmd.Stderr())
t.Log("raw stderr:", cmd.RawStderr())
}
func TestNewTimeoutCmd_Echo3(t *testing.T) {
var cmd = executils.NewCmd("echo", "-n", "hello")
err := cmd.Run()
t.Log("error:", err)
t.Log("stdout:", cmd.Stdout())
t.Log("stderr:", cmd.Stderr())
}
func TestCmd_Process(t *testing.T) {
var cmd = executils.NewCmd("echo", "-n", "hello")
err := cmd.Run()
t.Log("error:", err)
t.Log(cmd.Process())
}
func TestNewTimeoutCmd_String(t *testing.T) {
var cmd = executils.NewCmd("echo", "-n", "hello")
t.Log("stdout:", cmd.String())
}

View File

@@ -0,0 +1,58 @@
// 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,
}
}

View File

@@ -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)
}

View File

@@ -3,6 +3,7 @@
package utils package utils
import ( import (
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
"github.com/iwind/TeaGo/logs" "github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
"os/exec" "os/exec"
@@ -14,7 +15,7 @@ func AddPortsToFirewall(ports []int) {
// Linux // Linux
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
// firewalld // firewalld
firewallCmd, _ := exec.LookPath("firewall-cmd") firewallCmd, _ := executils.LookPath("firewall-cmd")
if len(firewallCmd) > 0 { if len(firewallCmd) > 0 {
err := exec.Command(firewallCmd, "--add-port="+types.String(port)+"/tcp").Run() err := exec.Command(firewallCmd, "--add-port="+types.String(port)+"/tcp").Run()
if err == nil { if err == nil {

View File

@@ -5,6 +5,7 @@ package utils
import ( import (
"errors" "errors"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/files" "github.com/iwind/TeaGo/files"
"os" "os"
@@ -21,7 +22,7 @@ func (this *ServiceManager) Install(exePath string, args []string) error {
return errors.New("only root users can install the service") return errors.New("only root users can install the service")
} }
systemd, err := exec.LookPath("systemctl") systemd, err := executils.LookPath("systemctl")
if err != nil { if err != nil {
return this.installInitService(exePath, args) return this.installInitService(exePath, args)
} }
@@ -36,7 +37,7 @@ func (this *ServiceManager) Start() error {
} }
if files.NewFile(systemdServiceFile).Exists() { if files.NewFile(systemdServiceFile).Exists() {
systemd, err := exec.LookPath("systemctl") systemd, err := executils.LookPath("systemctl")
if err != nil { if err != nil {
return err return err
} }
@@ -53,7 +54,7 @@ func (this *ServiceManager) Uninstall() error {
} }
if files.NewFile(systemdServiceFile).Exists() { if files.NewFile(systemdServiceFile).Exists() {
systemd, err := exec.LookPath("systemctl") systemd, err := executils.LookPath("systemctl")
if err != nil { if err != nil {
return err return err
} }
@@ -93,7 +94,7 @@ func (this *ServiceManager) installInitService(exePath string, args []string) er
return err return err
} }
chkCmd, err := exec.LookPath("chkconfig") chkCmd, err := executils.LookPath("chkconfig")
if err != nil { if err != nil {
return err return err
} }

View File

@@ -9,6 +9,7 @@ import (
"errors" "errors"
"fmt" "fmt"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
@@ -87,10 +88,10 @@ func (this *UpgradeManager) Start() error {
}() }()
// 检查unzip // 检查unzip
unzipExe, _ := exec.LookPath("unzip") unzipExe, _ := executils.LookPath("unzip")
// 检查cp // 检查cp
cpExe, _ := exec.LookPath("cp") cpExe, _ := executils.LookPath("cp")
if len(cpExe) == 0 { if len(cpExe) == 0 {
return errors.New("can not find 'cp' command") return errors.New("can not find 'cp' command")
} }

View File

@@ -7,6 +7,7 @@ import (
"crypto/rand" "crypto/rand"
"errors" "errors"
"fmt" "fmt"
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/setup/mysql/mysqlinstallers/utils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/setup/mysql/mysqlinstallers/utils"
stringutil "github.com/iwind/TeaGo/utils/string" stringutil "github.com/iwind/TeaGo/utils/string"
timeutil "github.com/iwind/TeaGo/utils/time" timeutil "github.com/iwind/TeaGo/utils/time"
@@ -14,7 +15,6 @@ import (
"net" "net"
"net/http" "net/http"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime" "runtime"
@@ -57,7 +57,7 @@ func (this *MySQLInstaller) InstallFromFile(xzFilePath string, targetDir string)
// check 'tar' command // check 'tar' command
this.log("checking 'tar' command ...") this.log("checking 'tar' command ...")
var tarExe, _ = exec.LookPath("tar") var tarExe, _ = executils.LookPath("tar")
if len(tarExe) == 0 { if len(tarExe) == 0 {
this.log("installing 'tar' command ...") this.log("installing 'tar' command ...")
err = this.installTarCommand() err = this.installTarCommand()
@@ -70,7 +70,7 @@ func (this *MySQLInstaller) InstallFromFile(xzFilePath string, targetDir string)
this.log("checking system commands ...") this.log("checking system commands ...")
var cmdList = []string{"tar" /** again **/, "chown", "sh"} var cmdList = []string{"tar" /** again **/, "chown", "sh"}
for _, cmd := range cmdList { for _, cmd := range cmdList {
cmdPath, err := exec.LookPath(cmd) cmdPath, err := executils.LookPath(cmd)
if err != nil || len(cmdPath) == 0 { if err != nil || len(cmdPath) == 0 {
return errors.New("could not find '" + cmd + "' command in this system") return errors.New("could not find '" + cmd + "' command in this system")
} }
@@ -87,7 +87,7 @@ func (this *MySQLInstaller) InstallFromFile(xzFilePath string, targetDir string)
} }
// ubuntu apt // ubuntu apt
aptExe, err := exec.LookPath("apt") aptExe, err := executils.LookPath("apt")
if err == nil && len(aptExe) > 0 { if err == nil && len(aptExe) > 0 {
for _, lib := range []string{"libaio1", "libncurses5"} { for _, lib := range []string{"libaio1", "libncurses5"} {
this.log("checking " + lib + " ...") this.log("checking " + lib + " ...")
@@ -100,7 +100,7 @@ func (this *MySQLInstaller) InstallFromFile(xzFilePath string, targetDir string)
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
} }
} else { // yum } else { // yum
yumExe, err := exec.LookPath("yum") yumExe, err := executils.LookPath("yum")
if err == nil && len(yumExe) > 0 { if err == nil && len(yumExe) > 0 {
for _, lib := range []string{"libaio", "ncurses-libs", "ncurses-compat-libs", "numactl-libs"} { for _, lib := range []string{"libaio", "ncurses-libs", "ncurses-compat-libs", "numactl-libs"} {
var cmd = utils.NewCmd("yum", "-y", "install", lib) var cmd = utils.NewCmd("yum", "-y", "install", lib)
@@ -562,7 +562,7 @@ func (this *MySQLInstaller) log(message string) {
// copy file // copy file
func (this *MySQLInstaller) installService(baseDir string) error { func (this *MySQLInstaller) installService(baseDir string) error {
_, err := exec.LookPath("systemctl") _, err := executils.LookPath("systemctl")
if err != nil { if err != nil {
return err return err
} }
@@ -618,14 +618,14 @@ WantedBy=multi-user.target`
// install 'tar' command automatically // install 'tar' command automatically
func (this *MySQLInstaller) installTarCommand() error { func (this *MySQLInstaller) installTarCommand() error {
// yum // yum
yumExe, err := exec.LookPath("yum") yumExe, err := executils.LookPath("yum")
if err == nil && len(yumExe) > 0 { if err == nil && len(yumExe) > 0 {
var cmd = utils.NewTimeoutCmd(10*time.Second, yumExe, "-y", "install", "tar") var cmd = utils.NewTimeoutCmd(10*time.Second, yumExe, "-y", "install", "tar")
return cmd.Run() return cmd.Run()
} }
// apt // apt
aptExe, err := exec.LookPath("apt") aptExe, err := executils.LookPath("apt")
if err == nil && len(aptExe) > 0 { if err == nil && len(aptExe) > 0 {
var cmd = utils.NewTimeoutCmd(10*time.Second, aptExe, "-y", "install", "tar") var cmd = utils.NewTimeoutCmd(10*time.Second, aptExe, "-y", "install", "tar")
return cmd.Run() return cmd.Run()
@@ -636,7 +636,7 @@ func (this *MySQLInstaller) installTarCommand() error {
func (this *MySQLInstaller) lookupGroupAdd() (string, error) { func (this *MySQLInstaller) lookupGroupAdd() (string, error) {
for _, cmd := range []string{"groupadd", "addgroup"} { for _, cmd := range []string{"groupadd", "addgroup"} {
path, err := exec.LookPath(cmd) path, err := executils.LookPath(cmd)
if err == nil && len(path) > 0 { if err == nil && len(path) > 0 {
return path, nil return path, nil
} }
@@ -646,7 +646,7 @@ func (this *MySQLInstaller) lookupGroupAdd() (string, error) {
func (this *MySQLInstaller) lookupUserAdd() (string, error) { func (this *MySQLInstaller) lookupUserAdd() (string, error) {
for _, cmd := range []string{"useradd", "adduser"} { for _, cmd := range []string{"useradd", "adduser"} {
path, err := exec.LookPath(cmd) path, err := executils.LookPath(cmd)
if err == nil && len(path) > 0 { if err == nil && len(path) > 0 {
return path, nil return path, nil
} }