feat: 添加本地命令执行函数,本地文件复制函数

This commit is contained in:
TsMask
2025-05-13 16:55:48 +08:00
parent 1ccbebce22
commit 6a2d5b3445
9 changed files with 265 additions and 109 deletions

17
go.mod
View File

@@ -3,6 +3,7 @@ module be.ems
go 1.24.0
require (
github.com/creack/pty v1.1.24
github.com/dlclark/regexp2 v1.11.5
github.com/gin-gonic/gin v1.10.0
github.com/glebarez/sqlite v1.11.0
@@ -22,9 +23,9 @@ require (
github.com/penglongli/gin-metrics v0.1.13
github.com/pkg/sftp v1.13.9
github.com/prometheus-community/pro-bing v0.7.0
github.com/redis/go-redis/v9 v9.7.3
github.com/redis/go-redis/v9 v9.8.0
github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil/v4 v4.25.3
github.com/shirou/gopsutil/v4 v4.25.4
github.com/slayercat/GoSNMPServer v0.5.2
github.com/spf13/pflag v1.0.6
github.com/spf13/viper v1.20.1
@@ -32,13 +33,13 @@ require (
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.4
github.com/xuri/excelize/v2 v2.9.0
golang.org/x/crypto v0.37.0
golang.org/x/term v0.31.0
golang.org/x/text v0.24.0
golang.org/x/crypto v0.38.0
golang.org/x/term v0.32.0
golang.org/x/text v0.25.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/mysql v1.5.7
gorm.io/gorm v1.25.12
gorm.io/gorm v1.26.1
xorm.io/xorm v1.3.9
)
@@ -127,8 +128,8 @@ require (
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/image v0.26.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/tools v0.32.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect

34
go.sum
View File

@@ -24,6 +24,8 @@ github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCy
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -227,8 +229,8 @@ github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM=
github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
@@ -246,8 +248,8 @@ github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPr
github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
github.com/shirou/gopsutil/v4 v4.25.4 h1:cdtFO363VEOOFrUCjZRh4XVJkb548lyF0q0uTeMqYPw=
github.com/shirou/gopsutil/v4 v4.25.4/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@@ -333,8 +335,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
@@ -378,8 +380,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -410,8 +412,8 @@ golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -421,8 +423,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@@ -432,8 +434,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -479,8 +481,8 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
gorm.io/gorm v1.26.1 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw=
gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic=
modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU=

View File

@@ -0,0 +1,44 @@
package cmd
import (
"os/exec"
"strings"
)
// CheckIllegal 检查传入的字符串参数中是否包含一些特殊字符
func CheckIllegal(args ...string) bool {
if args == nil {
return false
}
illegalChars := []string{"&", "|", ";", "$", "'", "`", "(", ")", "\""}
for _, arg := range args {
for _, char := range illegalChars {
if strings.Contains(arg, char) {
return true
}
}
}
return false
}
// HasNoPasswordSudo 检查当前用户是否拥有sudo权限
func HasNoPasswordSudo() bool {
cmd2 := exec.Command("sudo", "-n", "uname")
err2 := cmd2.Run()
return err2 == nil
}
// SudoHandleCmd 是否拥有sudo权限并拼接
func SudoHandleCmd() string {
cmd := exec.Command("sudo", "-n", "uname")
if err := cmd.Run(); err == nil {
return "sudo "
}
return ""
}
// Which 可执行文件是否在系统的PATH环境变量中
func Which(name string) bool {
_, err := exec.LookPath(name)
return err == nil
}

View File

@@ -5,10 +5,10 @@ import (
"context"
"fmt"
"os/exec"
"strings"
"time"
)
// Exec 本地执行命令,默认超时20s 列如:("ls -ls")
func Exec(cmdStr string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
@@ -37,35 +37,8 @@ func Exec(cmdStr string) (string, error) {
return stdout.String(), nil
}
func ExecCronjobWithTimeOut(cmdStr string, workdir string, timeout time.Duration) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
cmd := exec.Command("bash", "-c", cmdStr)
cmd.Dir = workdir
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if ctx.Err() == context.DeadlineExceeded {
return "", fmt.Errorf("errCmdTimeout %v", err)
}
errMsg := ""
if len(stderr.String()) != 0 {
errMsg = fmt.Sprintf("stderr:\n %s", stderr.String())
}
if len(stdout.String()) != 0 {
if len(errMsg) != 0 {
errMsg = fmt.Sprintf("%s \n\n; stdout:\n %s", errMsg, stdout.String())
} else {
errMsg = fmt.Sprintf("stdout:\n %s", stdout.String())
}
}
return errMsg, err
}
// Execf 本地执行命令 列如:("ssh %s@%s", "user", "localhost")
func Execf(cmdStr string, a ...interface{}) (string, error) {
func Execf(cmdStr string, a ...any) (string, error) {
cmd := exec.Command("bash", "-c", fmt.Sprintf(cmdStr, a...))
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
@@ -117,31 +90,36 @@ func ExecWithTimeOut(cmdStr string, timeout time.Duration) (string, error) {
return stdout.String(), nil
}
// ExecWithCheck 执行命令程序带参数 例如:("ls", "-r", "-l", "-s")
func ExecWithCheck(name string, a ...string) (string, error) {
cmd := exec.Command(name, a...)
// ExecDirWithTimeOut 指定目录本地执行命令超时退出 列如:("ssh user@localhost", 20*time.Second)
func ExecDirWithTimeOut(workdir string, cmdStr string, timeout time.Duration) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
cmd := exec.Command("bash", "-c", cmdStr)
cmd.Dir = workdir
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
errMsg := ""
if len(stderr.String()) != 0 {
errMsg = fmt.Sprintf("stderr: %s", stderr.String())
}
if len(stdout.String()) != 0 {
if len(errMsg) != 0 {
errMsg = fmt.Sprintf("%s; stdout: %s", errMsg, stdout.String())
} else {
errMsg = fmt.Sprintf("stdout: %s", stdout.String())
}
}
return errMsg, err
if ctx.Err() == context.DeadlineExceeded {
return "", fmt.Errorf("errCmdTimeout %v", err)
}
return stdout.String(), nil
errMsg := ""
if len(stderr.String()) != 0 {
errMsg = fmt.Sprintf("stderr:\n %s", stderr.String())
}
if len(stdout.String()) != 0 {
if len(errMsg) != 0 {
errMsg = fmt.Sprintf("%s \n\n; stdout:\n %s", errMsg, stdout.String())
} else {
errMsg = fmt.Sprintf("stdout:\n %s", stdout.String())
}
}
return errMsg, err
}
func ExecScript(scriptPath, workDir string) (string, error) {
// ExecDirScript 指定目录本地执行脚本文件, 默认超时10分钟 列如:("/tmp", "setup.sh")
func ExecDirScript(workDir, scriptPath string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
cmd := exec.Command("bash", scriptPath)
@@ -170,39 +148,26 @@ func ExecScript(scriptPath, workDir string) (string, error) {
return stdout.String(), nil
}
// CheckIllegal 检查传入的字符串参数中是否包含一些特殊字符
func CheckIllegal(args ...string) bool {
if args == nil {
return false
}
for _, arg := range args {
if strings.Contains(arg, "&") || strings.Contains(arg, "|") || strings.Contains(arg, ";") ||
strings.Contains(arg, "$") || strings.Contains(arg, "'") || strings.Contains(arg, "`") ||
strings.Contains(arg, "(") || strings.Contains(arg, ")") || strings.Contains(arg, "\"") {
return true
// ExecCommand 执行命令程序带参数 例如:("ls", "-r", "-l", "-s")
func ExecCommand(name string, a ...string) (string, error) {
cmd := exec.Command(name, a...)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
errMsg := ""
if len(stderr.String()) != 0 {
errMsg = fmt.Sprintf("stderr: %s", stderr.String())
}
if len(stdout.String()) != 0 {
if len(errMsg) != 0 {
errMsg = fmt.Sprintf("%s; stdout: %s", errMsg, stdout.String())
} else {
errMsg = fmt.Sprintf("stdout: %s", stdout.String())
}
}
return errMsg, err
}
return false
}
// HasNoPasswordSudo 检查当前用户是否拥有sudo权限
func HasNoPasswordSudo() bool {
cmd2 := exec.Command("sudo", "-n", "uname")
err2 := cmd2.Run()
return err2 == nil
}
// SudoHandleCmd 是否拥有sudo权限并拼接
func SudoHandleCmd() string {
cmd := exec.Command("sudo", "-n", "uname")
if err := cmd.Run(); err == nil {
return "sudo "
}
return ""
}
// Which 可执行文件是否在系统的PATH环境变量中
func Which(name string) bool {
_, err := exec.LookPath(name)
return err == nil
return stdout.String(), nil
}

View File

@@ -0,0 +1,76 @@
package cmd
import (
"fmt"
"os"
"os/exec"
"github.com/creack/pty"
)
// NewClientSession 创建本地Bash客户端会话对象
func NewClientSession(cols, rows int) (*LocalClientSession, error) {
// Create arbitrary command.
c := exec.Command("bash")
// Start the command with a pty.
ptmx, err := pty.StartWithSize(c, &pty.Winsize{
Rows: uint16(rows), // ws_row: Number of rows (in cells).
Cols: uint16(cols), // ws_col: Number of columns (in cells).
X: 0, // ws_xpixel: Width in pixels.
Y: 0, // ws_ypixel: Height in pixels.
})
if err != nil {
return nil, err
}
return &LocalClientSession{
Ptmx: ptmx,
}, nil
}
// LocalClientSession 本地Bash客户端会话对象
type LocalClientSession struct {
Ptmx *os.File
}
// Close 关闭会话
func (s *LocalClientSession) Close() {
if s.Ptmx != nil {
s.Ptmx.Close()
}
}
// Write 写入命令 回车(\n)才会执行
func (s *LocalClientSession) Write(cmd string) (int, error) {
if s.Ptmx == nil {
return 0, fmt.Errorf("ssh client session is nil to content write failed")
}
return s.Ptmx.Write([]byte(cmd))
}
// Read 读取结果
func (s *LocalClientSession) Read() []byte {
if s.Ptmx == nil {
return []byte{}
}
// 读取并输出伪终端中的数据
buffer := make([]byte, 1024)
n, err := s.Ptmx.Read(buffer)
if n == 0 || err != nil {
return []byte{}
}
return buffer[:n]
}
// Read 读取结果
func (s *LocalClientSession) WindowChange(cols, rows int) {
if s.Ptmx == nil {
return
}
pty.Setsize(s.Ptmx, &pty.Winsize{
Rows: uint16(rows), // ws_row: Number of rows (in cells).
Cols: uint16(cols), // ws_col: Number of columns (in cells).
X: 0, // ws_xpixel: Width in pixels.
Y: 0, // ws_ypixel: Height in pixels.
})
}

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"strings"
"be.ems/src/framework/cmd"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/cmd"
"be.ems/src/framework/utils/parse"
)

View File

@@ -7,8 +7,8 @@ import (
"strings"
"time"
"be.ems/src/framework/cmd"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/cmd"
gosftp "github.com/pkg/sftp"
gossh "golang.org/x/crypto/ssh"
)
@@ -188,7 +188,7 @@ func (c *ConnSSH) CurrentUserRsaKey(publicKey bool) (string, error) {
// 是否存在私钥并创建
keyPath := fmt.Sprintf("%s/.ssh/id_rsa", usr.HomeDir)
if _, err := os.Stat(keyPath); err != nil {
if _, err := cmd.ExecWithCheck("ssh-keygen", "-t", "rsa", "-P", "", "-f", keyPath); err != nil {
if _, err := cmd.ExecCommand("ssh-keygen", "-t", "rsa", "-P", "", "-f", keyPath); err != nil {
logger.Errorf("CurrentUserPrivateKey ssh-keygen [%s] rsa => %s", usr.Username, err.Error())
}
}

View File

@@ -0,0 +1,68 @@
package file
import (
"fmt"
"io"
"os"
"path/filepath"
)
// CopyFile 复制文件从 localPath 到 newPath
func CopyFile(localPath, newPath string) error {
// 打开源文件
srcFile, err := os.Open(localPath)
if err != nil {
return fmt.Errorf("failed to open source file: %v", err)
}
defer srcFile.Close()
// 创建目标文件
dstFile, err := os.Create(newPath)
if err != nil {
return fmt.Errorf("failed to create destination file: %v", err)
}
defer dstFile.Close()
// 复制内容
_, err = io.Copy(dstFile, srcFile)
if err != nil {
return fmt.Errorf("failed to copy file: %v", err)
}
// 返回成功
return nil
}
// CopyDir 复制目录从 localDir 到 newDir
func CopyDir(localDir, newDir string) error {
// 获取源目录中的所有文件和子目录
entries, err := os.ReadDir(localDir)
if err != nil {
return fmt.Errorf("failed to read source directory: %v", err)
}
// 如果目标目录不存在,创建它
if err := os.MkdirAll(newDir, os.ModePerm); err != nil {
return fmt.Errorf("failed to create destination directory: %v", err)
}
// 遍历源目录中的每一个文件或子目录
for _, entry := range entries {
srcPath := filepath.Join(localDir, entry.Name())
dstPath := filepath.Join(newDir, entry.Name())
if entry.IsDir() {
// 如果是目录,递归调用 CopyDir 复制子目录
if err := CopyDir(srcPath, dstPath); err != nil {
return err
}
} else {
// 如果是文件,调用 CopyFile 复制文件
if err := CopyFile(srcPath, dstPath); err != nil {
return err
}
}
}
return nil
}

View File

@@ -8,10 +8,10 @@ import (
"runtime"
"time"
"be.ems/src/framework/cmd"
"be.ems/src/framework/config"
"be.ems/src/framework/constants"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/cmd"
"be.ems/src/framework/utils/crypto"
"be.ems/src/framework/utils/parse"
)
@@ -163,7 +163,7 @@ func Reset() error {
// return fmt.Errorf("not support window")
} else {
// 重置数据库
if _, err := cmd.Execf("/usr/local/etc/omc/script/setup.sh -i"); err != nil {
if _, err := cmd.Exec("/usr/local/etc/omc/script/setup.sh -i"); err != nil {
return err
}
// 重启服务