Add SysInfo Functions
Add sysinfo functions, template updates, stuff like that
This commit is contained in:
parent
2157e8a343
commit
c901ee769b
@ -3,6 +3,7 @@ package paste
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/rocky-linux/rpaste/modules/utility"
|
"github.com/rocky-linux/rpaste/modules/utility"
|
||||||
@ -21,47 +22,36 @@ var (
|
|||||||
|
|
||||||
func runPaste(ctx *cli.Context) error {
|
func runPaste(ctx *cli.Context) error {
|
||||||
// check
|
// check
|
||||||
stdResults := utility.StdInChecker()
|
stdMethod := utility.StdInChecker()
|
||||||
|
sysInfoMethod := ctx.Bool("sysinfo")
|
||||||
|
|
||||||
// If there's no more than 1 argument (the executable itself is arg 0), we
|
// Check sysinfo is enabled and run through all the required stuff
|
||||||
// will check for stdin, and if not, print out the usage information.
|
if sysInfoMethod {
|
||||||
//if stdResults {
|
PasteData = SysInfoGather()
|
||||||
// fio, err := os.Stdin.Stat()
|
}
|
||||||
// if (fio.Mode() & os.ModeCharDevice) == 0 {
|
|
||||||
// bytes, _ := ioutil.ReadAll(os.Stdin)
|
|
||||||
// PasteData = string(bytes)
|
|
||||||
// } else if err != nil {
|
|
||||||
// fmt.Printf("Could not read from stdin: (%s)\n", err)
|
|
||||||
// os.Exit(1)
|
|
||||||
// }
|
|
||||||
//} else {
|
|
||||||
// cli.ShowAppHelp(ctx)
|
|
||||||
// os.Exit(0)
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Check args for a file or sysinfo
|
if stdMethod && PasteData == "" {
|
||||||
//if len(PasteData) == 0 {
|
fio, err := os.Stdin.Stat()
|
||||||
// if ctx.Bool("sysinfo") {
|
if (fio.Mode() & os.ModeCharDevice) == 0 {
|
||||||
// //PasteData = GatherSysInfo()
|
bytes, _ := ioutil.ReadAll(os.Stdin)
|
||||||
// PasteData = "Sys Info"
|
PasteData = string(bytes)
|
||||||
// } else {
|
} else if err != nil {
|
||||||
// // Path is expected at the end of the command (matches current rpaste)
|
fmt.Printf("Could not read from stdin: (%s)\n", err)
|
||||||
// argList := os.Args
|
os.Exit(1)
|
||||||
// lastArg := argList[len(argList)-1]
|
}
|
||||||
// fileBytes, err := ioutil.ReadFile(lastArg)
|
} else if !stdMethod && PasteData == "" {
|
||||||
// if err != nil {
|
argList := os.Args
|
||||||
// panic(err)
|
lastArg := argList[len(argList)-1]
|
||||||
// }
|
fileBytes, err := ioutil.ReadFile(lastArg)
|
||||||
// PasteData = string(fileBytes)
|
if err != nil {
|
||||||
// }
|
panic(err)
|
||||||
//}
|
}
|
||||||
|
PasteData = string(fileBytes)
|
||||||
|
}
|
||||||
|
|
||||||
// Check that PasteData is text, and not binary
|
// Check that PasteData is text, and not binary
|
||||||
|
|
||||||
fmt.Println(len(PasteData))
|
fmt.Println(PasteData)
|
||||||
for i, arg := range os.Args {
|
|
||||||
fmt.Println("item", i, "is", arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,296 @@
|
|||||||
// sysinfo
|
// sysinfo
|
||||||
package paste
|
package paste
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/rocky-linux/rpaste/modules/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
buf bytes.Buffer
|
||||||
|
)
|
||||||
|
|
||||||
|
type SystemInfo struct {
|
||||||
|
OsRelease string
|
||||||
|
DesktopEnvs string
|
||||||
|
DesktopInstalled string
|
||||||
|
SELinuxStatus string
|
||||||
|
SELinuxErrors string
|
||||||
|
CPUInfo string
|
||||||
|
Support64bit string
|
||||||
|
VirtSupport string
|
||||||
|
LoadAverage string
|
||||||
|
MemUsage string
|
||||||
|
TopCPUHogs string
|
||||||
|
TopMemHogs string
|
||||||
|
DiskUsage string
|
||||||
|
BlockDevs string
|
||||||
|
PciDevs string
|
||||||
|
USBDevs string
|
||||||
|
DRMInfo string
|
||||||
|
XorgModules string
|
||||||
|
GLSupport string
|
||||||
|
XorgErrors string
|
||||||
|
DmesgTail string
|
||||||
|
LastTenReboots string
|
||||||
|
DnfRepoList string
|
||||||
|
DnfRepoFiles string
|
||||||
|
YumConf string
|
||||||
|
LastTwentyPackages string
|
||||||
|
EFISupport string
|
||||||
|
}
|
||||||
|
|
||||||
|
func osRelease() string {
|
||||||
|
app := "uniq"
|
||||||
|
args := []string{"/etc/os-release"}
|
||||||
|
cmd, _ := exec.Command(app, args...).CombinedOutput()
|
||||||
|
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func desktopEnvs() string {
|
||||||
|
// The below just returns whatever the match is, rather than each match. The
|
||||||
|
// original command (and by extension, the original fpaste python script and
|
||||||
|
// rpaste bash script) would spit out more than just the expected result
|
||||||
|
app := "ps"
|
||||||
|
args := []string{
|
||||||
|
"-eo",
|
||||||
|
"comm=",
|
||||||
|
}
|
||||||
|
cmd, _ := exec.Command(app, args...).CombinedOutput()
|
||||||
|
re, _ := regexp.Compile(`(gnome-session|startkde|startactive|xfce.?-session|fluxbox|blackbox|hackedbox|ratpoison|enlightenment|icewm-session|od-session|wmaker|wmx|openbox-lxde|openbox-gnome-session|openbox-kde-session|mwm|e16|fvwm|xmonad|sugar-session|mate-session|lxqt-session|cinnamon)`)
|
||||||
|
values := re.FindString(string(cmd))
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
func desktopInstalled() string {
|
||||||
|
app := "ls"
|
||||||
|
args := []string{
|
||||||
|
"-m",
|
||||||
|
"/usr/share/xsessions/",
|
||||||
|
}
|
||||||
|
cmd, _ := exec.Command(app, args...).CombinedOutput()
|
||||||
|
re, _ := regexp.Compile(`\.desktop`)
|
||||||
|
values := re.ReplaceAllString(string(cmd), "")
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
func selinuxStatus() string {
|
||||||
|
app1 := "getenforce"
|
||||||
|
app2 := "grep -v '^#' /etc/sysconfig/selinux"
|
||||||
|
cmd1, _ := exec.Command(app1).CombinedOutput()
|
||||||
|
cmd2, _ := exec.Command("/bin/sh", "-c", app2).CombinedOutput()
|
||||||
|
combined := string(cmd1) + string(cmd2)
|
||||||
|
return combined
|
||||||
|
}
|
||||||
|
|
||||||
|
func selinuxErrors() string {
|
||||||
|
// I'm not proud of this
|
||||||
|
app := "journalctl --since yesterday |grep avc: | grep -Eo comm='[^ ]+' | sort |uniq -c |sort -rn"
|
||||||
|
cmd, _ := exec.Command("/bin/sh", "-c", app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cpuInfo() string {
|
||||||
|
// I'm not proud of this
|
||||||
|
app := "grep 'model name' /proc/cpuinfo | awk -F: '{print $2}' | uniq -c | sed -re 's/^ +//'"
|
||||||
|
cmd, _ := exec.Command("/bin/sh", "-c", app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func support64Bit() string {
|
||||||
|
text := " lm "
|
||||||
|
file, _ := ioutil.ReadFile("/proc/cpuinfo")
|
||||||
|
string := string(file)
|
||||||
|
return strconv.FormatBool(strings.Contains(string, text))
|
||||||
|
}
|
||||||
|
|
||||||
|
func virtSupport() string {
|
||||||
|
re, _ := regexp.Compile(`(vmx|svm)`)
|
||||||
|
file, _ := ioutil.ReadFile("/proc/cpuinfo")
|
||||||
|
exists := re.Match(file)
|
||||||
|
return strconv.FormatBool(exists)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadAverage() string {
|
||||||
|
app := "uptime"
|
||||||
|
cmd, _ := exec.Command(app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func memUsage() string {
|
||||||
|
app := "free"
|
||||||
|
args := []string{"-m"}
|
||||||
|
cmd, _ := exec.Command(app, args...).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func topCPUHogs() string {
|
||||||
|
// I'm not proud of this
|
||||||
|
app := "ps axuScnh | sort -rnk3 | head -5"
|
||||||
|
cmd, _ := exec.Command("/bin/sh", "-c", app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func topMemHogs() string {
|
||||||
|
// I'm not proud of this
|
||||||
|
app := "ps axuScnh | sort -rnk4 | head -5"
|
||||||
|
cmd, _ := exec.Command("/bin/sh", "-c", app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func diskUsage() string {
|
||||||
|
app := "df"
|
||||||
|
args := []string{"-hT"}
|
||||||
|
cmd, _ := exec.Command(app, args...).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func blockDevs() string {
|
||||||
|
app := "/sbin/blkid"
|
||||||
|
cmd, _ := exec.Command(app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pciDevs() string {
|
||||||
|
app := "/sbin/lspci"
|
||||||
|
args := []string{"-nn"}
|
||||||
|
cmd, _ := exec.Command(app, args...).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func usbDevs() string {
|
||||||
|
app := "/bin/lsusb"
|
||||||
|
cmd, _ := exec.Command(app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func drmInfo() string {
|
||||||
|
// I'm not proud of this
|
||||||
|
app := "journalctl -k -b | grep -o 'kernel:.*drm.*$' | cut -d ' ' -f 2-"
|
||||||
|
cmd, _ := exec.Command("/bin/sh", "-c", app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func xorgModules() string {
|
||||||
|
// I'm not proud of this
|
||||||
|
app := "grep LoadModule /var/log/Xorg.0.log ~/.local/share/xorg/Xorg.0.log | cut -d '\"' -f 2 | xargs"
|
||||||
|
cmd, _ := exec.Command("/bin/sh", "-c", app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func glSupport() string {
|
||||||
|
// I'm not proud of this
|
||||||
|
// turn to golang regexp? we would rather get the error output regardless
|
||||||
|
app := "glxinfo | grep -E 'OpenGL version|OpenGL renderer'"
|
||||||
|
cmd, _ := exec.Command("/bin/sh", "-c", app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func xorgErrors() string {
|
||||||
|
// I'm not proud of this
|
||||||
|
app := "grep '^\\[.*(EE)' /var/log/Xorg.0.log ~/.local/share/xorg/Xorg.0.log | cut -d ':' -f 2-"
|
||||||
|
cmd, _ := exec.Command("/bin/sh", "-c", app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmesgTail() string {
|
||||||
|
// I'm not proud of this
|
||||||
|
// Maybe I can implement a tail-like function
|
||||||
|
app := "dmesg | tail"
|
||||||
|
cmd, _ := exec.Command("/bin/sh", "-c", app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lastTenReboots() string {
|
||||||
|
app := "last"
|
||||||
|
args := []string{
|
||||||
|
"-x",
|
||||||
|
"-n10",
|
||||||
|
"reboot",
|
||||||
|
"runlevel",
|
||||||
|
}
|
||||||
|
cmd, _ := exec.Command(app, args...).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dnfRepoList() string {
|
||||||
|
app := "dnf"
|
||||||
|
args := []string{"repolist"}
|
||||||
|
cmd, _ := exec.Command(app, args...).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dnfRepoFiles() string {
|
||||||
|
app := "ls"
|
||||||
|
args := []string{
|
||||||
|
"-l",
|
||||||
|
"/etc/yum.repos.d/",
|
||||||
|
}
|
||||||
|
cmd, _ := exec.Command(app, args...).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func yumConf() string {
|
||||||
|
app := "grep -v '^#' /etc/yum.conf"
|
||||||
|
cmd, _ := exec.Command("/bin/sh", "-c", app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lastTwentyPkgs() string {
|
||||||
|
app := "rpm -qa --nodigest --nosignature --last | head -20"
|
||||||
|
cmd, _ := exec.Command("/bin/sh", "-c", app).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func efiSupport() string {
|
||||||
|
app := "efibootmgr"
|
||||||
|
args := []string{"-v"}
|
||||||
|
cmd, _ := exec.Command(app, args...).CombinedOutput()
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SysInfoGather() string {
|
||||||
|
sysInfoData := SystemInfo{
|
||||||
|
OsRelease: osRelease(),
|
||||||
|
DesktopEnvs: desktopEnvs(),
|
||||||
|
DesktopInstalled: desktopInstalled(),
|
||||||
|
SELinuxStatus: selinuxStatus(),
|
||||||
|
SELinuxErrors: selinuxErrors(),
|
||||||
|
CPUInfo: cpuInfo(),
|
||||||
|
Support64bit: support64Bit(),
|
||||||
|
VirtSupport: virtSupport(),
|
||||||
|
LoadAverage: loadAverage(),
|
||||||
|
MemUsage: memUsage(),
|
||||||
|
TopCPUHogs: topCPUHogs(),
|
||||||
|
TopMemHogs: topMemHogs(),
|
||||||
|
DiskUsage: diskUsage(),
|
||||||
|
BlockDevs: blockDevs(),
|
||||||
|
PciDevs: pciDevs(),
|
||||||
|
USBDevs: usbDevs(),
|
||||||
|
DRMInfo: drmInfo(),
|
||||||
|
XorgModules: xorgModules(),
|
||||||
|
GLSupport: glSupport(),
|
||||||
|
XorgErrors: xorgErrors(),
|
||||||
|
DmesgTail: dmesgTail(),
|
||||||
|
LastTenReboots: lastTenReboots(),
|
||||||
|
DnfRepoList: dnfRepoList(),
|
||||||
|
DnfRepoFiles: dnfRepoFiles(),
|
||||||
|
YumConf: yumConf(),
|
||||||
|
LastTwentyPackages: lastTwentyPkgs(),
|
||||||
|
EFISupport: efiSupport(),
|
||||||
|
}
|
||||||
|
|
||||||
|
t := template.New("sysinfo")
|
||||||
|
t, _ = t.Parse(setting.SysInfoTemplate)
|
||||||
|
t.Execute(&buf, sysInfoData)
|
||||||
|
sysInfoResults := buf.String()
|
||||||
|
return sysInfoResults
|
||||||
|
}
|
||||||
|
129
modules/setting/template.go
Normal file
129
modules/setting/template.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package setting
|
||||||
|
|
||||||
|
var SysInfoTemplate = `################################################################################
|
||||||
|
# OS Release
|
||||||
|
#
|
||||||
|
{{.OsRelease}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Desktop Environments
|
||||||
|
#
|
||||||
|
{{.DesktopEnvs}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Desktop Installed
|
||||||
|
#
|
||||||
|
{{.DesktopInstalled}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# SELinux Status
|
||||||
|
#
|
||||||
|
{{.SELinuxStatus}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# SELinux Errors
|
||||||
|
#
|
||||||
|
{{.SELinuxErrors}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# CPU Info
|
||||||
|
#
|
||||||
|
{{.CPUInfo}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# 64-bit Support
|
||||||
|
#
|
||||||
|
{{.Support64bit}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Virtualization Support
|
||||||
|
#
|
||||||
|
{{.VirtSupport}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Load Average
|
||||||
|
#
|
||||||
|
{{.LoadAverage}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Memory usage
|
||||||
|
#
|
||||||
|
{{.MemUsage}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Top 5 CPU hogs
|
||||||
|
#
|
||||||
|
{{.TopCPUHogs}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Top 5 memory hogs
|
||||||
|
#
|
||||||
|
{{.TopMemHogs}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Disk space usage
|
||||||
|
#
|
||||||
|
{{.DiskUsage}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Block Devices
|
||||||
|
#
|
||||||
|
{{.BlockDevs}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# PCI devices
|
||||||
|
#
|
||||||
|
{{.PciDevs}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# USB devices
|
||||||
|
#
|
||||||
|
{{.USBDevs}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# DRM Information
|
||||||
|
#
|
||||||
|
{{.DRMInfo}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Xorg modules
|
||||||
|
#
|
||||||
|
{{.XorgModules}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# GL Support
|
||||||
|
#
|
||||||
|
{{.GLSupport}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Xorg errors
|
||||||
|
#
|
||||||
|
{{.XorgErrors}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Kernel Buffer Tail
|
||||||
|
#
|
||||||
|
{{.DmesgTail}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Last few reboots
|
||||||
|
#
|
||||||
|
{{.LastTenReboots}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# DNF Repos
|
||||||
|
#
|
||||||
|
{{.DnfRepoList}}
|
||||||
|
{{.DnfRepoFiles}}
|
||||||
|
{{.YumConf}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Last 20 packages
|
||||||
|
#
|
||||||
|
{{.LastTwentyPackages}}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# EFI Support
|
||||||
|
#
|
||||||
|
{{.EFISupport}}
|
||||||
|
`
|
53
modules/utility/pipes.go
Normal file
53
modules/utility/pipes.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package utility
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExecutePipe(outbuffer *bytes.Buffer, stack ...*exec.Cmd) (err error) {
|
||||||
|
var errbuffer bytes.Buffer
|
||||||
|
pipestack := make([]*io.PipeWriter, len(stack)-1)
|
||||||
|
i := 0
|
||||||
|
|
||||||
|
for ; i < len(stack)-1; i++ {
|
||||||
|
stdin_pipe, stdoutpipe := io.Pipe()
|
||||||
|
stack[i].Stdout = stdoutpipe
|
||||||
|
stack[i].Stderr = &errbuffer
|
||||||
|
stack[i+1].Stdin = stdin_pipe
|
||||||
|
pipestack[i] = stdoutpipe
|
||||||
|
}
|
||||||
|
|
||||||
|
stack[i].Stdout = outbuffer
|
||||||
|
stack[i].Stderr = &errbuffer
|
||||||
|
|
||||||
|
if err := callpipe(stack, pipestack); err != nil {
|
||||||
|
log.Fatalln(string(errbuffer.Bytes()), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func callpipe(stack []*exec.Cmd, pipes []*io.PipeWriter) (err error) {
|
||||||
|
if stack[0].Process == nil {
|
||||||
|
if err = stack[0].Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(stack) > 1 {
|
||||||
|
if err = stack[1].Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err == nil {
|
||||||
|
pipes[0].Close()
|
||||||
|
err = callpipe(stack[1:], pipes[1:])
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
return stack[0].Wait()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user