diff --git a/internal/firewalls/ddos_protection.go b/internal/firewalls/ddos_protection.go index d9932bd..9a2afb6 100644 --- a/internal/firewalls/ddos_protection.go +++ b/internal/firewalls/ddos_protection.go @@ -14,6 +14,7 @@ import ( "github.com/TeaOSLab/EdgeNode/internal/firewalls/nftables" "github.com/TeaOSLab/EdgeNode/internal/remotelogs" "github.com/TeaOSLab/EdgeNode/internal/utils" + executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" "github.com/TeaOSLab/EdgeNode/internal/zero" "github.com/iwind/TeaGo/lists" "github.com/iwind/TeaGo/types" @@ -21,6 +22,7 @@ import ( "net" "os/exec" "strings" + "time" ) var SharedDDoSProtectionManager = NewDDoSProtectionManager() @@ -263,23 +265,21 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig) // 添加新规则 for _, port := range ports { if maxConnections > 0 { - var cmd = exec.Command(this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "count", "over", types.String(maxConnections), "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnections", types.String(maxConnections)})) - var stderr = &bytes.Buffer{} - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(10*time.Second, this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "count", "over", types.String(maxConnections), "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnections", types.String(maxConnections)})) + cmd.WithStderr() err := cmd.Run() if err != nil { - return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + stderr.String() + ")") + return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")") } } // TODO 让用户选择是drop还是reject if maxConnectionsPerIP > 0 { - var cmd = exec.Command(this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "meter", "meter-"+protocol+"-"+types.String(port)+"-max-connections", "{ "+protocol+" saddr ct count over "+types.String(maxConnectionsPerIP)+" }", "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnectionsPerIP", types.String(maxConnectionsPerIP)})) - var stderr = &bytes.Buffer{} - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(10*time.Second, this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "meter", "meter-"+protocol+"-"+types.String(port)+"-max-connections", "{ "+protocol+" saddr ct count over "+types.String(maxConnectionsPerIP)+" }", "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnectionsPerIP", types.String(maxConnectionsPerIP)})) + cmd.WithStderr() err := cmd.Run() if err != nil { - return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + stderr.String() + ")") + return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")") } } @@ -287,20 +287,18 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig) // TODO 让用户选择是drop还是reject if newConnectionsMinutelyRate > 0 { if newConnectionsMinutelyRateBlockTimeout > 0 { - var cmd = exec.Command(this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsMinutelyRate)+"/minute burst "+types.String(newConnectionsMinutelyRate+3)+" packets }", "add", "@deny_set", "{"+protocol+" saddr timeout "+types.String(newConnectionsMinutelyRateBlockTimeout)+"s}", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsRate", types.String(newConnectionsMinutelyRate), types.String(newConnectionsMinutelyRateBlockTimeout)})) - var stderr = &bytes.Buffer{} - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(10*time.Second, this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsMinutelyRate)+"/minute burst "+types.String(newConnectionsMinutelyRate+3)+" packets }", "add", "@deny_set", "{"+protocol+" saddr timeout "+types.String(newConnectionsMinutelyRateBlockTimeout)+"s}", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsRate", types.String(newConnectionsMinutelyRate), types.String(newConnectionsMinutelyRateBlockTimeout)})) + cmd.WithStderr() err := cmd.Run() if err != nil { - return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + stderr.String() + ")") + return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")") } } else { - var cmd = exec.Command(this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsMinutelyRate)+"/minute burst "+types.String(newConnectionsMinutelyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsRate", "0"})) - var stderr = &bytes.Buffer{} - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(10*time.Second, this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsMinutelyRate)+"/minute burst "+types.String(newConnectionsMinutelyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsRate", "0"})) + cmd.WithStderr() err := cmd.Run() if err != nil { - return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + stderr.String() + ")") + return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")") } } } @@ -309,20 +307,18 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig) // TODO 让用户选择是drop还是reject if newConnectionsSecondlyRate > 0 { if newConnectionsSecondlyRateBlockTimeout > 0 { - var cmd = exec.Command(this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-secondly-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsSecondlyRate)+"/second burst "+types.String(newConnectionsSecondlyRate+3)+" packets }", "add", "@deny_set", "{"+protocol+" saddr timeout "+types.String(newConnectionsSecondlyRateBlockTimeout)+"s}", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsSecondlyRate", types.String(newConnectionsSecondlyRate), types.String(newConnectionsSecondlyRateBlockTimeout)})) - var stderr = &bytes.Buffer{} - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(10*time.Second, this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-secondly-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsSecondlyRate)+"/second burst "+types.String(newConnectionsSecondlyRate+3)+" packets }", "add", "@deny_set", "{"+protocol+" saddr timeout "+types.String(newConnectionsSecondlyRateBlockTimeout)+"s}", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsSecondlyRate", types.String(newConnectionsSecondlyRate), types.String(newConnectionsSecondlyRateBlockTimeout)})) + cmd.WithStderr() err := cmd.Run() if err != nil { - return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + stderr.String() + ")") + return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")") } } else { - var cmd = exec.Command(this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-secondly-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsSecondlyRate)+"/second burst "+types.String(newConnectionsSecondlyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsSecondlyRate", "0"})) - var stderr = &bytes.Buffer{} - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(10*time.Second, this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-secondly-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsSecondlyRate)+"/second burst "+types.String(newConnectionsSecondlyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsSecondlyRate", "0"})) + cmd.WithStderr() err := cmd.Run() if err != nil { - return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + stderr.String() + ")") + return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")") } } } diff --git a/internal/firewalls/firewall_firewalld.go b/internal/firewalls/firewall_firewalld.go index 81146bc..6fbca5b 100644 --- a/internal/firewalls/firewall_firewalld.go +++ b/internal/firewalls/firewall_firewalld.go @@ -7,13 +7,15 @@ import ( "github.com/TeaOSLab/EdgeNode/internal/conns" "github.com/TeaOSLab/EdgeNode/internal/goman" "github.com/TeaOSLab/EdgeNode/internal/remotelogs" + executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" "github.com/iwind/TeaGo/types" "os/exec" "strings" + "time" ) type firewalldCmd struct { - cmd *exec.Cmd + cmd *executils.Cmd denyIP string } @@ -32,7 +34,7 @@ func NewFirewalld() *Firewalld { path, err := exec.LookPath("firewall-cmd") if err == nil && len(path) > 0 { - var cmd = exec.Command(path, "--state") + var cmd = executils.NewTimeoutCmd(3*time.Second, path, "--state") err := cmd.Run() if err == nil { firewalld.exe = path @@ -85,7 +87,7 @@ func (this *Firewalld) AllowPort(port int, protocol string) error { if !this.isReady { return nil } - var cmd = exec.Command(this.exe, "--add-port="+types.String(port)+"/"+protocol) + var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--add-port="+types.String(port)+"/"+protocol) this.pushCmd(cmd, "") return nil } @@ -95,12 +97,12 @@ func (this *Firewalld) AllowPortRangesPermanently(portRanges [][2]int, protocol var port = this.PortRangeString(portRange, protocol) { - var cmd = exec.Command(this.exe, "--add-port="+port, "--permanent") + var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--add-port="+port, "--permanent") this.pushCmd(cmd, "") } { - var cmd = exec.Command(this.exe, "--add-port="+port) + var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--add-port="+port) this.pushCmd(cmd, "") } } @@ -112,7 +114,7 @@ func (this *Firewalld) RemovePort(port int, protocol string) error { if !this.isReady { return nil } - var cmd = exec.Command(this.exe, "--remove-port="+types.String(port)+"/"+protocol) + var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--remove-port="+types.String(port)+"/"+protocol) this.pushCmd(cmd, "") return nil } @@ -121,12 +123,12 @@ func (this *Firewalld) RemovePortRangePermanently(portRange [2]int, protocol str var port = this.PortRangeString(portRange, protocol) { - var cmd = exec.Command(this.exe, "--remove-port="+port, "--permanent") + var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--remove-port="+port, "--permanent") this.pushCmd(cmd, "") } { - var cmd = exec.Command(this.exe, "--remove-port="+port) + var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--remove-port="+port) this.pushCmd(cmd, "") } @@ -159,7 +161,7 @@ func (this *Firewalld) RejectSourceIP(ip string, timeoutSeconds int) error { if timeoutSeconds > 0 { args = append(args, "--timeout="+types.String(timeoutSeconds)+"s") } - var cmd = exec.Command(this.exe, args...) + var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, args...) this.pushCmd(cmd, ip) return nil } @@ -182,7 +184,7 @@ func (this *Firewalld) DropSourceIP(ip string, timeoutSeconds int, async bool) e if timeoutSeconds > 0 { args = append(args, "--timeout="+types.String(timeoutSeconds)+"s") } - var cmd = exec.Command(this.exe, args...) + var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, args...) if async { this.pushCmd(cmd, ip) return nil @@ -209,13 +211,13 @@ func (this *Firewalld) RemoveSourceIP(ip string) error { } for _, action := range []string{"reject", "drop"} { var args = []string{"--remove-rich-rule=rule family='" + family + "' source address='" + ip + "' " + action} - var cmd = exec.Command(this.exe, args...) + var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, args...) this.pushCmd(cmd, "") } return nil } -func (this *Firewalld) pushCmd(cmd *exec.Cmd, denyIP string) { +func (this *Firewalld) pushCmd(cmd *executils.Cmd, denyIP string) { select { case this.cmdQueue <- &firewalldCmd{cmd: cmd, denyIP: denyIP}: default: diff --git a/internal/firewalls/firewall_nftables.go b/internal/firewalls/firewall_nftables.go index 3632b39..ff536bc 100644 --- a/internal/firewalls/firewall_nftables.go +++ b/internal/firewalls/firewall_nftables.go @@ -5,7 +5,6 @@ package firewalls import ( - "bytes" "errors" "github.com/TeaOSLab/EdgeNode/internal/conns" teaconst "github.com/TeaOSLab/EdgeNode/internal/const" @@ -13,6 +12,7 @@ import ( "github.com/TeaOSLab/EdgeNode/internal/firewalls/nftables" "github.com/TeaOSLab/EdgeNode/internal/goman" "github.com/TeaOSLab/EdgeNode/internal/remotelogs" + executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" "github.com/iwind/TeaGo/types" "net" "os/exec" @@ -432,15 +432,14 @@ func (this *NFTablesFirewall) RemoveSourceIP(ip string) error { // 读取版本号 func (this *NFTablesFirewall) readVersion(nftPath string) string { - var cmd = exec.Command(nftPath, "--version") - var output = &bytes.Buffer{} - cmd.Stdout = output + var cmd = executils.NewTimeoutCmd(10*time.Second, nftPath, "--version") + cmd.WithStdout() err := cmd.Run() if err != nil { return "" } - var outputString = output.String() + var outputString = cmd.Stdout() var versionMatches = regexp.MustCompile(`nftables v([\d.]+)`).FindStringSubmatch(outputString) if len(versionMatches) <= 1 { return "" diff --git a/internal/iplibrary/action_firewalld.go b/internal/iplibrary/action_firewalld.go index 4c97b71..2ad63a1 100644 --- a/internal/iplibrary/action_firewalld.go +++ b/internal/iplibrary/action_firewalld.go @@ -1,11 +1,11 @@ package iplibrary import ( - "bytes" "errors" "fmt" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs" + executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" "os/exec" "runtime" "time" @@ -13,9 +13,9 @@ import ( // FirewalldAction Firewalld动作管理 // 常用命令: -// - 查询列表: firewall-cmd --list-all -// - 添加IP:firewall-cmd --add-rich-rule="rule family='ipv4' source address='192.168.2.32' reject" --timeout=30s -// - 删除IP:firewall-cmd --remove-rich-rule="rule family='ipv4' source address='192.168.2.32' reject" --timeout=30s +// - 查询列表: firewall-cmd --list-all +// - 添加IP:firewall-cmd --add-rich-rule="rule family='ipv4' source address='192.168.2.32' reject" --timeout=30s +// - 删除IP:firewall-cmd --remove-rich-rule="rule family='ipv4' source address='192.168.2.32' reject" --timeout=30s type FirewalldAction struct { BaseAction @@ -144,12 +144,11 @@ func (this *FirewalldAction) runActionSingleIP(action string, listType IPListTyp // MAC OS直接返回 return nil } - cmd := exec.Command(path, args...) - stderr := bytes.NewBuffer([]byte{}) - cmd.Stderr = stderr + cmd := executils.NewTimeoutCmd(30*time.Second, path, args...) + cmd.WithStderr() err = cmd.Run() if err != nil { - return errors.New(err.Error() + ", output: " + string(stderr.Bytes())) + return errors.New(err.Error() + ", output: " + cmd.Stderr()) } return nil } diff --git a/internal/iplibrary/action_ipset.go b/internal/iplibrary/action_ipset.go index d6eef88..6afd46a 100644 --- a/internal/iplibrary/action_ipset.go +++ b/internal/iplibrary/action_ipset.go @@ -1,10 +1,10 @@ package iplibrary import ( - "bytes" "errors" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs" + executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" "github.com/iwind/TeaGo/types" "os/exec" "runtime" @@ -16,12 +16,12 @@ import ( // IPSetAction IPSet动作 // 相关命令: // - 利用Firewalld管理set: -// - 添加:firewall-cmd --permanent --new-ipset=edge_ip_list --type=hash:ip --option="timeout=0" -// - 删除:firewall-cmd --permanent --delete-ipset=edge_ip_list -// - 重载:firewall-cmd --reload -// - firewalld+ipset: firewall-cmd --permanent --add-rich-rule="rule source ipset='edge_ip_list' reject" +// - 添加:firewall-cmd --permanent --new-ipset=edge_ip_list --type=hash:ip --option="timeout=0" +// - 删除:firewall-cmd --permanent --delete-ipset=edge_ip_list +// - 重载:firewall-cmd --reload +// - firewalld+ipset: firewall-cmd --permanent --add-rich-rule="rule source ipset='edge_ip_list' reject" // - 利用IPTables管理set: -// - 添加:iptables -A INPUT -m set --match-set edge_ip_list src -j REJECT +// - 添加:iptables -A INPUT -m set --match-set edge_ip_list src -j REJECT // - 添加Item:ipset add edge_ip_list 192.168.2.32 timeout 30 // - 删除Item: ipset del edge_ip_list 192.168.2.32 // - 创建set:ipset create edge_ip_list hash:ip timeout 0 @@ -30,16 +30,13 @@ import ( type IPSetAction struct { BaseAction - config *firewallconfigs.FirewallActionIPSetConfig - errorBuf *bytes.Buffer + config *firewallconfigs.FirewallActionIPSetConfig ipsetNotfound bool } func NewIPSetAction() *IPSetAction { - return &IPSetAction{ - errorBuf: &bytes.Buffer{}, - } + return &IPSetAction{} } func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) error { @@ -68,14 +65,13 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro if len(listName) == 0 { continue } - var cmd = exec.Command(path, "create", listName, "hash:ip", "timeout", "0", "maxelem", "1000000") - var stderr = bytes.NewBuffer([]byte{}) - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(30*time.Second, path, "create", listName, "hash:ip", "timeout", "0", "maxelem", "1000000") + cmd.WithStderr() err := cmd.Run() if err != nil { - var output = stderr.Bytes() - if !bytes.Contains(output, []byte("already exists")) { - return errors.New("create ipset '" + listName + "': " + err.Error() + ", output: " + string(output)) + var output = cmd.Stderr() + if !strings.Contains(output, "already exists") { + return errors.New("create ipset '" + listName + "': " + err.Error() + ", output: " + output) } else { err = nil } @@ -87,14 +83,13 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro if len(listName) == 0 { continue } - var cmd = exec.Command(path, "create", listName, "hash:ip", "family", "inet6", "timeout", "0", "maxelem", "1000000") - var stderr = bytes.NewBuffer([]byte{}) - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(30*time.Second, path, "create", listName, "hash:ip", "family", "inet6", "timeout", "0", "maxelem", "1000000") + cmd.WithStderr() err := cmd.Run() if err != nil { - var output = stderr.Bytes() - if !bytes.Contains(output, []byte("already exists")) { - return errors.New("create ipset '" + listName + "': " + err.Error() + ", output: " + string(output)) + var output = cmd.Stderr() + if !strings.Contains(output, "already exists") { + return errors.New("create ipset '" + listName + "': " + err.Error() + ", output: " + output) } else { err = nil } @@ -114,16 +109,15 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro if len(listName) == 0 { continue } - cmd := exec.Command(path, "--permanent", "--new-ipset="+listName, "--type=hash:ip", "--option=timeout=0", "--option=maxelem=1000000") - stderr := bytes.NewBuffer([]byte{}) - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(30*time.Second, path, "--permanent", "--new-ipset="+listName, "--type=hash:ip", "--option=timeout=0", "--option=maxelem=1000000") + cmd.WithStderr() err := cmd.Run() if err != nil { - output := stderr.Bytes() - if bytes.Contains(output, []byte("NAME_CONFLICT")) { + var output = cmd.Stderr() + if strings.Contains(output, "NAME_CONFLICT") { err = nil } else { - return errors.New("firewall-cmd add ipset '" + listName + "': " + err.Error() + ", output: " + string(output)) + return errors.New("firewall-cmd add ipset '" + listName + "': " + err.Error() + ", output: " + output) } } } @@ -133,16 +127,15 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro if len(listName) == 0 { continue } - cmd := exec.Command(path, "--permanent", "--new-ipset="+listName, "--type=hash:ip", "--option=family=inet6", "--option=timeout=0", "--option=maxelem=1000000") - stderr := bytes.NewBuffer([]byte{}) - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(30*time.Second, path, "--permanent", "--new-ipset="+listName, "--type=hash:ip", "--option=family=inet6", "--option=timeout=0", "--option=maxelem=1000000") + cmd.WithStderr() err := cmd.Run() if err != nil { - var output = stderr.Bytes() - if bytes.Contains(output, []byte("NAME_CONFLICT")) { + var output = cmd.Stderr() + if strings.Contains(output, "NAME_CONFLICT") { err = nil } else { - return errors.New("firewall-cmd add ipset '" + listName + "': " + err.Error() + ", output: " + string(output)) + return errors.New("firewall-cmd add ipset '" + listName + "': " + err.Error() + ", output: " + output) } } } @@ -152,13 +145,11 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro if len(listName) == 0 { continue } - var cmd = exec.Command(path, "--permanent", "--add-rich-rule=rule source ipset='"+listName+"' accept") - var stderr = bytes.NewBuffer([]byte{}) - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(30*time.Second, path, "--permanent", "--add-rich-rule=rule source ipset='"+listName+"' accept") + cmd.WithStderr() err := cmd.Run() if err != nil { - var output = stderr.Bytes() - return errors.New("firewall-cmd add rich rule '" + listName + "': " + err.Error() + ", output: " + string(output)) + return errors.New("firewall-cmd add rich rule '" + listName + "': " + err.Error() + ", output: " + cmd.Stderr()) } } @@ -167,25 +158,21 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro if len(listName) == 0 { continue } - var cmd = exec.Command(path, "--permanent", "--add-rich-rule=rule source ipset='"+listName+"' reject") - var stderr = bytes.NewBuffer([]byte{}) - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(30*time.Second, path, "--permanent", "--add-rich-rule=rule source ipset='"+listName+"' reject") + cmd.WithStderr() err := cmd.Run() if err != nil { - var output = stderr.Bytes() - return errors.New("firewall-cmd add rich rule '" + listName + "': " + err.Error() + ", output: " + string(output)) + return errors.New("firewall-cmd add rich rule '" + listName + "': " + err.Error() + ", output: " + cmd.Stderr()) } } // reload { - cmd := exec.Command(path, "--reload") - stderr := bytes.NewBuffer([]byte{}) - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(30*time.Second, path, "--reload") + cmd.WithStderr() err := cmd.Run() if err != nil { - var output = stderr.Bytes() - return errors.New("firewall-cmd reload: " + err.Error() + ", output: " + string(output)) + return errors.New("firewall-cmd reload: " + err.Error() + ", output: " + cmd.Stderr()) } } } @@ -204,19 +191,17 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro } // 检查规则是否存在 - var cmd = exec.Command(path, "-C", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "ACCEPT") + var cmd = executils.NewTimeoutCmd(30*time.Second, path, "-C", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "ACCEPT") err := cmd.Run() var exists = err == nil // 添加规则 if !exists { - var cmd = exec.Command(path, "-A", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "ACCEPT") - var stderr = bytes.NewBuffer([]byte{}) - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(30*time.Second, path, "-A", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "ACCEPT") + cmd.WithStderr() err := cmd.Run() if err != nil { - var output = stderr.Bytes() - return errors.New("iptables add rule: " + err.Error() + ", output: " + string(output)) + return errors.New("iptables add rule: " + err.Error() + ", output: " + cmd.Stderr()) } } } @@ -228,18 +213,16 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro } // 检查规则是否存在 - var cmd = exec.Command(path, "-C", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "REJECT") + var cmd = executils.NewTimeoutCmd(30*time.Second, path, "-C", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "REJECT") err := cmd.Run() var exists = err == nil if !exists { - var cmd = exec.Command(path, "-A", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "REJECT") - var stderr = bytes.NewBuffer([]byte{}) - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(30*time.Second, path, "-A", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "REJECT") + cmd.WithStderr() err := cmd.Run() if err != nil { - var output = stderr.Bytes() - return errors.New("iptables add rule: " + err.Error() + ", output: " + string(output)) + return errors.New("iptables add rule: " + err.Error() + ", output: " + cmd.Stderr()) } } } @@ -361,12 +344,11 @@ func (this *IPSetAction) runActionSingleIP(action string, listType IPListType, i return nil } - this.errorBuf.Reset() - var cmd = exec.Command(path, args...) - cmd.Stderr = this.errorBuf + var cmd = executils.NewTimeoutCmd(30*time.Second, path, args...) + cmd.WithStderr() err = cmd.Run() if err != nil { - var errString = this.errorBuf.String() + var errString = cmd.Stderr() if action == "deleteItem" && strings.Contains(errString, "not added") { return nil } diff --git a/internal/iplibrary/action_iptables.go b/internal/iplibrary/action_iptables.go index 37e0ced..dd23cd1 100644 --- a/internal/iplibrary/action_iptables.go +++ b/internal/iplibrary/action_iptables.go @@ -1,20 +1,23 @@ package iplibrary import ( - "bytes" "errors" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs" + executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" "os/exec" "runtime" + "strings" + "time" ) // IPTablesAction IPTables动作 // 相关命令: -// iptables -A INPUT -s "192.168.2.32" -j ACCEPT -// iptables -A INPUT -s "192.168.2.32" -j REJECT -// iptables -D INPUT ... -// iptables -F INPUT +// +// iptables -A INPUT -s "192.168.2.32" -j ACCEPT +// iptables -A INPUT -s "192.168.2.32" -j REJECT +// iptables -D INPUT ... +// iptables -F INPUT type IPTablesAction struct { BaseAction @@ -110,16 +113,15 @@ func (this *IPTablesAction) runActionSingleIP(action string, listType IPListType return nil } - cmd := exec.Command(path, args...) - stderr := bytes.NewBuffer([]byte{}) - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(30*time.Second, path, args...) + cmd.WithStderr() err = cmd.Run() if err != nil { - output := stderr.Bytes() - if bytes.Contains(output, []byte("No chain/target/match")) { + var output = cmd.Stderr() + if strings.Contains(output, "No chain/target/match") { err = nil } else { - return errors.New(err.Error() + ", output: " + string(output)) + return errors.New(err.Error() + ", output: " + output) } } return nil diff --git a/internal/iplibrary/action_script.go b/internal/iplibrary/action_script.go index 83cb786..731adb3 100644 --- a/internal/iplibrary/action_script.go +++ b/internal/iplibrary/action_script.go @@ -1,13 +1,13 @@ package iplibrary import ( - "bytes" "errors" "fmt" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs" - "os/exec" + executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" "path/filepath" + "time" ) // ScriptAction 脚本命令动作 @@ -45,25 +45,24 @@ func (this *ScriptAction) DeleteItem(listType IPListType, item *pb.IPItem) error func (this *ScriptAction) runAction(action string, listType IPListType, item *pb.IPItem) error { // TODO 智能支持 .sh 脚本文件 - cmd := exec.Command(this.config.Path) - cmd.Env = []string{ + var cmd = executils.NewTimeoutCmd(30*time.Second, this.config.Path) + cmd.WithEnv([]string{ "ACTION=" + action, "TYPE=" + item.Type, "IP_FROM=" + item.IpFrom, "IP_TO=" + item.IpTo, "EXPIRED_AT=" + fmt.Sprintf("%d", item.ExpiredAt), "LIST_TYPE=" + listType, - } + }) if len(this.config.Cwd) > 0 { - cmd.Dir = this.config.Cwd + cmd.WithDir(this.config.Cwd) } else { - cmd.Dir = filepath.Dir(this.config.Path) + cmd.WithDir(filepath.Dir(this.config.Path)) } - stderr := bytes.NewBuffer([]byte{}) - cmd.Stderr = stderr + cmd.WithStderr() err := cmd.Run() if err != nil { - return errors.New(err.Error() + ", output: " + string(stderr.Bytes())) + return errors.New(err.Error() + ", output: " + cmd.Stderr()) } return nil } diff --git a/internal/nodes/api_stream.go b/internal/nodes/api_stream.go index ac64c9d..57db159 100644 --- a/internal/nodes/api_stream.go +++ b/internal/nodes/api_stream.go @@ -1,7 +1,6 @@ package nodes import ( - "bytes" "context" "encoding/json" "fmt" @@ -17,7 +16,7 @@ import ( "github.com/TeaOSLab/EdgeNode/internal/goman" "github.com/TeaOSLab/EdgeNode/internal/remotelogs" "github.com/TeaOSLab/EdgeNode/internal/rpc" - "github.com/TeaOSLab/EdgeNode/internal/utils" + executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" "github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/maps" "net/url" @@ -347,15 +346,15 @@ func (this *APIStream) handleCheckSystemdService(message *pb.NodeStreamMessage) return nil } - var cmd = utils.NewCommandExecutor() - shortName := teaconst.SystemdServiceName - cmd.Add(systemctl, "is-enabled", shortName) - output, err := cmd.Run() + var shortName = teaconst.SystemdServiceName + var cmd = executils.NewTimeoutCmd(10*time.Second, systemctl, "is-enabled", shortName) + cmd.WithStdout() + err = cmd.Run() if err != nil { this.replyFail(message.RequestId, "'systemctl' command error: "+err.Error()) return nil } - if output == "enabled" { + if cmd.Stdout() == "enabled" { this.replyOk(message.RequestId, "ok") } else { this.replyFail(message.RequestId, "not installed") @@ -385,16 +384,15 @@ func (this *APIStream) handleCheckLocalFirewall(message *pb.NodeStreamMessage) e return nil } - var cmd = exec.Command(nft, "--version") - var output = &bytes.Buffer{} - cmd.Stdout = output + var cmd = executils.NewTimeoutCmd(10*time.Second, nft, "--version") + cmd.WithStdout() err = cmd.Run() if err != nil { this.replyFail(message.RequestId, "get version failed: "+err.Error()) return nil } - var outputString = output.String() + var outputString = cmd.Stdout() var versionMatches = regexp.MustCompile(`nftables v([\d.]+)`).FindStringSubmatch(outputString) if len(versionMatches) <= 1 { this.replyFail(message.RequestId, "can not get nft version") diff --git a/internal/nodes/listener_manager.go b/internal/nodes/listener_manager.go index c76d216..0cbe9de 100644 --- a/internal/nodes/listener_manager.go +++ b/internal/nodes/listener_manager.go @@ -1,7 +1,6 @@ package nodes import ( - "bytes" "errors" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" @@ -9,6 +8,7 @@ import ( "github.com/TeaOSLab/EdgeNode/internal/goman" "github.com/TeaOSLab/EdgeNode/internal/remotelogs" "github.com/TeaOSLab/EdgeNode/internal/utils" + executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" "github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/lists" "github.com/iwind/TeaGo/maps" @@ -213,15 +213,14 @@ func (this *ListenerManager) findProcessNameWithPort(isUdp bool, port string) st option = "u" } - var cmd = exec.Command(path, "-"+option+"lpn", "sport = :"+port) - var output = &bytes.Buffer{} - cmd.Stdout = output + var cmd = executils.NewTimeoutCmd(10*time.Second, path, "-"+option+"lpn", "sport = :"+port) + cmd.WithStdout() err = cmd.Run() if err != nil { return "" } - var matches = regexp.MustCompile(`(?U)\(\("(.+)",pid=\d+,fd=\d+\)\)`).FindStringSubmatch(output.String()) + var matches = regexp.MustCompile(`(?U)\(\("(.+)",pid=\d+,fd=\d+\)\)`).FindStringSubmatch(cmd.Stdout()) if len(matches) > 1 { return matches[1] } diff --git a/internal/nodes/system_services.go b/internal/nodes/system_services.go index 2294da6..ce8e457 100644 --- a/internal/nodes/system_services.go +++ b/internal/nodes/system_services.go @@ -10,6 +10,7 @@ import ( "github.com/TeaOSLab/EdgeNode/internal/goman" "github.com/TeaOSLab/EdgeNode/internal/remotelogs" "github.com/TeaOSLab/EdgeNode/internal/utils" + executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" "github.com/iwind/TeaGo/maps" "os" "os/exec" @@ -84,10 +85,10 @@ func (this *SystemServiceManager) setupSystemd(params maps.Map) error { if len(systemctl) == 0 { return errors.New("can not find 'systemctl' on the system") } - cmd := utils.NewCommandExecutor() shortName := teaconst.SystemdServiceName - cmd.Add(systemctl, "is-enabled", shortName) - output, err := cmd.Run() + var cmd = executils.NewTimeoutCmd(10*time.Second, systemctl, "is-enabled", shortName) + cmd.WithStdout() + err = cmd.Run() if err != nil { return err } @@ -100,10 +101,10 @@ func (this *SystemServiceManager) setupSystemd(params maps.Map) error { // 启动Service goman.New(func() { time.Sleep(5 * time.Second) - _ = exec.Command(systemctl, "start", teaconst.SystemdServiceName).Start() + _ = executils.NewTimeoutCmd(30*time.Second, systemctl, "start", teaconst.SystemdServiceName).Start() }) - if output == "enabled" { + if cmd.Stdout() == "enabled" { // 检查文件路径是否变化 data, err := os.ReadFile("/etc/systemd/system/" + teaconst.SystemdServiceName + ".service") if err == nil && bytes.Index(data, []byte(exe)) > 0 { diff --git a/internal/nodes/toa_manager.go b/internal/nodes/toa_manager.go index 5f64a08..73662b7 100644 --- a/internal/nodes/toa_manager.go +++ b/internal/nodes/toa_manager.go @@ -1,14 +1,15 @@ package nodes import ( + "errors" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeNode/internal/events" "github.com/TeaOSLab/EdgeNode/internal/goman" "github.com/TeaOSLab/EdgeNode/internal/remotelogs" + executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" "github.com/iwind/TeaGo/Tea" "net" "os" - "os/exec" "strings" "time" ) @@ -61,12 +62,16 @@ func (this *TOAManager) Run(config *nodeconfigs.TOAConfig) error { } remotelogs.Println("TOA", "starting ...") remotelogs.Println("TOA", "args: "+strings.Join(config.AsArgs(), " ")) - cmd := exec.Command(binPath, config.AsArgs()...) + cmd := executils.NewCmd(binPath, config.AsArgs()...) err = cmd.Start() if err != nil { return err } - this.pid = cmd.Process.Pid + var process = cmd.Process() + if process == nil { + return errors.New("start failed") + } + this.pid = process.Pid goman.New(func() { _ = cmd.Wait() diff --git a/internal/nodes/upgrade_manager.go b/internal/nodes/upgrade_manager.go index 64dd2d8..3d55b51 100644 --- a/internal/nodes/upgrade_manager.go +++ b/internal/nodes/upgrade_manager.go @@ -12,11 +12,11 @@ import ( "github.com/TeaOSLab/EdgeNode/internal/remotelogs" "github.com/TeaOSLab/EdgeNode/internal/rpc" "github.com/TeaOSLab/EdgeNode/internal/utils" + executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" "github.com/iwind/TeaGo/Tea" stringutil "github.com/iwind/TeaGo/utils/string" "github.com/iwind/gosock/pkg/gosock" "os" - "os/exec" "path/filepath" "runtime" "time" @@ -252,7 +252,7 @@ func (this *UpgradeManager) restart() error { // 启动 exe = filepath.Dir(exe) + "/" + teaconst.ProcessName - var cmd = exec.Command(exe, "start") + var cmd = executils.NewCmd(exe, "start") err = cmd.Start() if err != nil { return err diff --git a/internal/utils/clock/utils.go b/internal/utils/clock/utils.go index 9651855..ac40b99 100644 --- a/internal/utils/clock/utils.go +++ b/internal/utils/clock/utils.go @@ -3,9 +3,9 @@ package clock import ( - "bytes" "errors" "github.com/TeaOSLab/EdgeNode/internal/remotelogs" + executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" "os/exec" "runtime" "time" @@ -46,12 +46,11 @@ func Sync() error { } func syncNtpdate(ntpdate string) error { - var cmd = exec.Command(ntpdate, "pool.ntp.org") - var stderr = &bytes.Buffer{} - cmd.Stderr = stderr + var cmd = executils.NewTimeoutCmd(30*time.Second, ntpdate, "pool.ntp.org") + cmd.WithStderr() err := cmd.Run() if err != nil { - return errors.New(err.Error() + ": " + stderr.String()) + return errors.New(err.Error() + ": " + cmd.Stderr()) } return nil diff --git a/internal/utils/command.go b/internal/utils/command.go deleted file mode 100644 index 5ab60e8..0000000 --- a/internal/utils/command.go +++ /dev/null @@ -1,7 +0,0 @@ -package utils - -// 命令定义 -type Command struct { - Name string - Args []string -} diff --git a/internal/utils/command_executor.go b/internal/utils/command_executor.go deleted file mode 100644 index 8d7acc3..0000000 --- a/internal/utils/command_executor.go +++ /dev/null @@ -1,61 +0,0 @@ -package utils - -import ( - "bytes" - "errors" - "os/exec" -) - -// 命令执行器 -type CommandExecutor struct { - commands []*Command -} - -// 获取新对象 -func NewCommandExecutor() *CommandExecutor { - return &CommandExecutor{} -} - -// 添加命令 -func (this *CommandExecutor) Add(command string, arg ...string) { - this.commands = append(this.commands, &Command{ - Name: command, - Args: arg, - }) -} - -// 执行命令 -func (this *CommandExecutor) Run() (output string, err error) { - if len(this.commands) == 0 { - return "", errors.New("no commands no run") - } - var lastCmd *exec.Cmd = nil - var lastData []byte = nil - for _, command := range this.commands { - cmd := exec.Command(command.Name, command.Args...) - stdout := bytes.NewBuffer([]byte{}) - cmd.Stdout = stdout - if lastCmd != nil { - cmd.Stdin = bytes.NewBuffer(lastData) - } - err = cmd.Start() - if err != nil { - return "", err - } - - err = cmd.Wait() - if err != nil { - _, ok := err.(*exec.ExitError) - if ok { - return "", nil - } - - return "", err - } - lastData = stdout.Bytes() - - lastCmd = cmd - } - - return string(bytes.TrimSpace(lastData)), nil -} diff --git a/internal/utils/exec/cmd.go b/internal/utils/exec/cmd.go new file mode 100644 index 0000000..299b0a2 --- /dev/null +++ b/internal/utils/exec/cmd.go @@ -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 +} diff --git a/internal/utils/exec/cmd_test.go b/internal/utils/exec/cmd_test.go new file mode 100644 index 0000000..79698f6 --- /dev/null +++ b/internal/utils/exec/cmd_test.go @@ -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/EdgeNode/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()) +}