diff --git a/go.mod b/go.mod index 373d24b3..36b10023 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 47e3fb30..67d7925c 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/src/framework/cmd/check.go b/src/framework/cmd/check.go new file mode 100644 index 00000000..b3632529 --- /dev/null +++ b/src/framework/cmd/check.go @@ -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 +} diff --git a/src/framework/utils/cmd/cmd.go b/src/framework/cmd/cmd.go similarity index 75% rename from src/framework/utils/cmd/cmd.go rename to src/framework/cmd/cmd.go index f26919f8..04d3e6e0 100644 --- a/src/framework/utils/cmd/cmd.go +++ b/src/framework/cmd/cmd.go @@ -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 } diff --git a/src/framework/cmd/cmd_session.go b/src/framework/cmd/cmd_session.go new file mode 100644 index 00000000..dfeb146c --- /dev/null +++ b/src/framework/cmd/cmd_session.go @@ -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. + }) +} diff --git a/src/framework/ssh/files.go b/src/framework/ssh/files.go index 8081877a..61874fc7 100644 --- a/src/framework/ssh/files.go +++ b/src/framework/ssh/files.go @@ -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" ) diff --git a/src/framework/ssh/ssh.go b/src/framework/ssh/ssh.go index f412b718..7d762fd2 100644 --- a/src/framework/ssh/ssh.go +++ b/src/framework/ssh/ssh.go @@ -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()) } } diff --git a/src/framework/utils/file/copy.go b/src/framework/utils/file/copy.go new file mode 100644 index 00000000..e02921cc --- /dev/null +++ b/src/framework/utils/file/copy.go @@ -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 +} diff --git a/src/framework/utils/machine/launch.go b/src/framework/utils/machine/launch.go index 388ddd96..f1edaa8c 100644 --- a/src/framework/utils/machine/launch.go +++ b/src/framework/utils/machine/launch.go @@ -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 } // 重启服务