Files
cmp-go-snippet/cmpSnipper/cmpSnipperSnipPing.go
2024-05-08 09:05:36 +03:00

434 lines
8.9 KiB
Go

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
}