package cmpSnipper import ( "io" "sync" "strconv" "os/exec" "context" "time" "strings" "syscall" ) type Ping2Struct struct { Addr string Win string Pid int } func (app *App) PingSnip(parm any) (any, error) { // log.Println("PingSnip") path, err := exec.LookPath("ping") app.Log("Prog:", path) if err != nil { return map[string]interface{}{ "retval" : 127, "error" : "No prog", }, nil } ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second) defer cancel() cmd := exec.CommandContext(ctx, "ping", "-c", "4", "8.8.8.8") cmd.Stdin = strings.NewReader("") var stdout strings.Builder cmd.Stdout = &stdout var stderr strings.Builder cmd.Stderr = &stderr var exitCode int; err = cmd.Run() if err != nil { // try to get the exit code if exitError, ok := err.(*exec.ExitError); ok { ws := exitError.Sys().(syscall.WaitStatus) exitCode = ws.ExitStatus() } else { // This will happen (in OSX) if `name` is not available in $PATH, // in this situation, exit code could not be get, and stderr will be // empty string very likely, so we use the default fail code, and format err // to string and set to stderr app.Log("Could not get exit code for failed program") exitCode = 255 } } else { // success, exitCode should be 0 if go is ok ws := cmd.ProcessState.Sys().(syscall.WaitStatus) exitCode = ws.ExitStatus() } return map[string]interface{}{ "retval" : exitCode, "error" : err, "stdout" : stdout.String(), "stderr" : stderr.String(), }, nil } func (app *App) copyAndCapture(r io.Reader, strWin string) ([]byte, error) { var out []byte buf := make([]byte, 1024, 1024) for { n, err := r.Read(buf[:]) if n > 0 { d := buf[:n] out = append(out, d...) str := strings.Trim(string(d), "\n\r") app.Log("Std: %s", str) app.Ping2SendStr(str) } if err != nil { // Read returns io.EOF at the end of file, which is not an error for us if err == io.EOF { err = nil } return out, err } } } func (app *App) Ping2SendObj(obj map[string]interface{}) (error) { if app.CliCur == nil { app.Log("No cli") return nil } app.HttpWebSockSendTo(*app.CliCur, "ping2Data", obj) return nil } func (app *App) Ping2SendStr(str string) (error) { return app.Ping2SendObj(map[string]interface{}{ "data" : "stdout", "string": str, }) } func (app *App) ping2Capture(r io.Reader, strWin string) ([]byte, error) { var out []byte buf := make([]byte, 1024, 1024) for { n, err := r.Read(buf[:]) if n > 0 { d := buf[:n] out = append(out, d...) str := strings.Trim(string(d), "\n\r") app.Log("Std: %s", str) app.Ping2SendStr(str) } if err != nil { // Read returns io.EOF at the end of file, which is not an error for us if err == io.EOF { err = nil } return out, err } } } func (app *App) Ping2ExecStart(strAddr string, strWin string) (any, error) { var Ping2Free *Ping2Struct = nil for key, val := range app.Ping2List { if val.Pid == 0 { Ping2Free = &val break } if val.Addr == strAddr { app.Ping2List[key].Win = strWin return map[string]interface{}{ "stutus" : "success", }, nil } } if Ping2Free == nil { var NewPing2Struct Ping2Struct Ping2Free = &NewPing2Struct app.Ping2List = append(app.Ping2List, NewPing2Struct) } var err error ctx, cancel := context.WithTimeout(context.Background(), 100 * time.Second) defer cancel() cmd := exec.CommandContext(ctx, "ping", "-c", "40", strAddr) var stdout, stderr []byte stdoutIn, _ := cmd.StdoutPipe() stderrIn, _ := cmd.StderrPipe() var exitCode int; err = cmd.Start() if err != nil { app.Log(err) return nil, err } Ping2Free.Addr = strAddr Ping2Free.Win = strWin Ping2Free.Pid = cmd.Process.Pid go app.Ping2SendObj(map[string]interface{}{ "data" : "procid", "procid": Ping2Free.Pid, }) var wg sync.WaitGroup wg.Add(1) go func() { stdout, err = app.ping2Capture(stdoutIn, strWin) wg.Done() }() stderr, err = app.ping2Capture(stderrIn, strWin) wg.Wait() err = cmd.Wait() if err != nil { // try to get the exit code if exitError, ok := err.(*exec.ExitError); ok { ws := exitError.Sys().(syscall.WaitStatus) exitCode = ws.ExitStatus() } else { // This will happen (in OSX) if `name` is not available in $PATH, // in this situation, exit code could not be get, and stderr will be // empty string very likely, so we use the default fail code, and format err // to string and set to stderr app.Log("Could not get exit code for failed program") exitCode = 255 } } else { // success, exitCode should be 0 if go is ok ws := cmd.ProcessState.Sys().(syscall.WaitStatus) exitCode = ws.ExitStatus() } app.Log("Exit code %s", exitCode) if false { /* ??? */ outStr, errStr := string(stdout), string(stderr) app.Log("\nout:\n%s\nerr:\n%s\n", outStr, errStr) } Ping2Free.Pid = 0 return map[string]interface{}{ "stutus" : "error", "error" : "Invalid IPv4 address", }, nil } func (app *App) Ping2ExecSign(strAddr string, strWin string, strSign string) (any, error) { for _, val := range app.Ping2List { if val.Pid == 0 { continue } if val.Addr != strAddr { continue } var err error ctx, cancel := context.WithTimeout(context.Background(), 100 * time.Second) defer cancel() pidStr := strconv.Itoa(val.Pid) app.Log("kill %s", pidStr) cmd := exec.CommandContext(ctx, "kill", strSign, pidStr) var exitCode int; err = cmd.Run() if err != nil { // try to get the exit code if exitError, ok := err.(*exec.ExitError); ok { ws := exitError.Sys().(syscall.WaitStatus) exitCode = ws.ExitStatus() } else { // This will happen (in OSX) if `name` is not available in $PATH, // in this situation, exit code could not be get, and stderr will be // empty string very likely, so we use the default fail code, and format err // to string and set to stderr app.Log("Could not get exit code for failed program") exitCode = 255 } } else { // success, exitCode should be 0 if go is ok ws := cmd.ProcessState.Sys().(syscall.WaitStatus) exitCode = ws.ExitStatus() } app.Log("Exit code %s", exitCode) break } return map[string]interface{}{ "stutus" : "success", }, nil } func (app *App) Ping2ExecStop(strAddr string, strWin string) (any, error) { return app.Ping2ExecSign(strAddr, strWin, "-SIGINT") } func (app *App) Ping2ExecStat(strAddr string, strWin string) (any, error) { return app.Ping2ExecSign(strAddr, strWin, "-SIGQUIT") } func (app *App) Ping2ExecSnip(parm any) (any, error) { var err error var ok bool var ifcAddr interface{} var ifcCmd interface{} var ifcWin interface{} ifcAddr, err = app.IfcGet(parm, "InputArgument", "addr") if err != nil { return map[string]interface{}{ "stutus" : "error", "error" : "Unset IPv4 address", }, nil } ifcCmd, err = app.IfcGet(parm, "InputArgument", "cmd") if err != nil { return map[string]interface{}{ "stutus" : "error", "error" : "Unset command", }, nil } ifcWin, err = app.IfcGet(parm, "InputArgument", "win") if err != nil { return map[string]interface{}{ "stutus" : "error", "error" : "Unset window", }, nil } var strAddr string var strCmd string var strWin string strAddr, ok = ifcAddr.(string) if !ok { return map[string]interface{}{ "stutus" : "error", "error" : "Invalid IPv4 address type", }, nil } strCmd, ok = ifcCmd.(string) if !ok { return map[string]interface{}{ "stutus" : "error", "error" : "Invalid command type", }, nil } strWin, ok = ifcWin.(string) if !ok { intWin, ok := ifcWin.(float64) if !ok { return map[string]interface{}{ "stutus" : "error", "error" : "Invalid window type", }, nil } strWin = strconv.FormatFloat(intWin, 'f', -1, 32) } switch(strAddr) { case "1.1.1.1": break; case "8.8.8.8": break; case "8.8.4.4": break; case "9.9.9.9": break; default: return map[string]interface{}{ "stutus" : "error", "error" : "Invalid IPv4 address", }, nil } switch(strCmd) { case "start": go app.Ping2ExecStart(strAddr, strWin) return map[string]interface{}{ "status" : "success", }, nil case "stop": return app.Ping2ExecStop(strAddr, strWin) case "stat": return app.Ping2ExecStat(strAddr, strWin) default: return map[string]interface{}{ "stutus" : "error", "error" : "Invalid command", }, nil } return nil, nil }