Go "exec.Command" of tcpdump not doing anything - go

I am attempting to run the following code in Go. I have tried both of the following ways:
out, err := exec.Command("sh", "-c", "tcpdump -i ens0 host 192.168.1.100 -F ./testfile").Output()
fmt.Println(string(out)) // Prints nothing
fmt.Println(err) // exit status 1
I have also tried replacing sh with /bin/bash.
I have also tried the following, with and without sh as the first argument:
out, err := exec.Command("tcpdump", "-i", "ens0", "host", "192.168.1.100", "-F", "./testfile").Output()
fmt.Println(string(out)) // Prints nothing
fmt.Println(err) // exit status 1
None of this is working. Can someone see what I am doing wrong? I have also tried this go package "github.com/kami-zh/go-capturer" to read stderr and again it prints nothing.
Normally I have to use sudo to execute tcpdump from shell, so I build the go binary and execute it as root user.

Something like this should work, i am not sure if there is any specific command like -F available in tcp dump,
If you want to capture plain output of the tcp dump , you can direct the output to file using > file . The -w option is for wireshark/tcpdump format , to read and display
cmd := exec.Command("sh", "-c", "sudo tcpdump -i <eth> host <ip> -w ./testfile")
err := cmd.Run()
if err != nil {
fmt.Println(err)
}
Thanks, #torek, the -c option can be used with tcpdump to exit after capturing n packets
cmd := exec.Command("sh", "-c", "sudo tcpdump -i ens33 -c 100 host localhost -w ./testfile")
err := cmd.Run()
if err != nil {
fmt.Println(err)
}
The other way is to use cmd.Start
cmd := exec.Command("sh", "-c", "sudo tcpdump -i ens33 -c 100 host localhost -w ./testfile")
err := cmd.Start()
if err != nil {
log.Fatal(err)
}
log.Printf("Waiting for command to finish...")
err = cmd.Wait()
log.Printf("Command finished with error: %v", err)
The tcpdump command continue to run infinitely if you use cmd.run without -c option with tcpdump cmd.So you can't see if you put print statement after cmd.Run() call, the reason being the exec.Command failed is, it just work the same way on how it works from the cli, so if you need sodo in front for it , you should put it in command as well or run it from the root user.

Related

golang exec with cmd as argument does not print stdout

cmdstr := "ssh -i ....... blah blah blah" ssh to an ip and run rpm command to install rpm
cmd := exec.Command("/bin/bash", "-c", cmdstr)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
fmt.Println(out.String())
}
out.String() does not print anything
if I have ping command without /bin/bash it prints the out. Anyone knows why ?
cmd.Stdout captures the output of a successful command execution
cmd.Stderr captures the command output if an error occurs during execution
You could try the cmd.Output() variant and capture execution errors from Stderr using
cmdstr := "ssh -i ....... blah blah blah" // ssh to an ip and run rpm command to install rpm
cmd := exec.Command("/bin/bash", "-c", cmdstr)
var errb bytes.Buffer
cmd.Stderr = &errb
output, err := cmd.Output()
if err != nil {
fmt.Printf("error: %s", errb.String()) // capture any error output
}
fmt.Println(string(output)) // when successful

How do I run shell command in golang

I have a shell command
set -a source /etc/environment; set +a
I want to run this command to refresh my env file
the code I tried to do
cmd, err := exec.Command("bash", "set -a source /etc/environment; set +a").Output()
fmt.Println("cmd=================>", cmd)
if err != nil {
fmt.Println(err)
}
it gave me exit status 127
try this
cmd, err := exec.Command("bash","-c", "set -a source /etc/environment; set +a").Output()
fmt.Println("cmd=================>", cmd)
if err != nil {
fmt.Println(err)
}

Unable to run netstat for docker in Go using exec

Trying to run terminal commands in Go using exec to get docker network usage but unable to. The following link shows how to get docker container's network usage using terminal and it works fine in terminal but not using Go. https://docs.docker.com/config/containers/runmetrics/
I get exit code 1, 2, 125, etc. with different combinations.
stdin, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
defer stdin.Close()
io.WriteString(stdin, "CID="+CID) // container ID
io.WriteString(stdin,"TASKS=/sys/fs/cgroup/devices/docker/$CID*/tasks")
io.WriteString(stdin, "PID=$(head -n 1 $TASKS)")
io.WriteString(stdin, "mkdir -p /var/run/netns")
io.WriteString(stdin, "ln -sf /proc/$PID/ns/net /var/run/netns/$CID")
io.WriteString(stdin, "ip netns exec $CID netstat -i")
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", out)
Expected output:
Kernel Interface table
Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0 1450 1228323 0 0 0 1761314 0 0 0 BMRU
If someone looks for the same in the future, here's a working solution
cmd := exec.Command("/bin/sh", "-c", "CID="+CID+" ; TASKS=/sys/fs/cgroup/devices/docker/$CID*/tasks ; PID=$(head -n 1 $TASKS); mkdir -p /var/run/netns ; ln -sf /proc/$PID/ns/net /var/run/netns/$CID ; ip netns exec $CID netstat -i")
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(err)
}
fmt.Printf("%s\n", out)

Is there any way we can execute a multiple commands in exec.Command?

I am trying to execute set of commands in Go using exec.Command(). Where I am trying to detach Gluster peer using Docker Exec.
fmt.Println("About to execute gluster peer detach")
SystemdockerCommand := exec.Command("sh", "-c", "docker exec ", "9aa1124", " gluster peer detach ", "192.168.1.1", " force")
var out bytes.Buffer
var stderr bytes.Buffer
SystemdockerCommand.Stdout = &out
SystemdockerCommand.Stderr = &stderr
err := SystemdockerCommand.Run()
if err != nil {
fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
}
fmt.Println("System Docker exec : " + out.String())
I was expecting a result as "no peer to detatch". But got
exit status 1: "docker exec" requires at least 2 arguments.
Since you have used sh -c, the next parameter should be the full command or commands:
SystemdockerCommand := exec.Command("sh", "-c", "docker exec 9aa1124 gluster peer detach 192.168.1.1 force")
More generally, as in here:
cmd := exec.Command("/bin/sh", "-c", "command1 param1; command2 param2; command3; ...")
err := cmd.Run()
See this example:
sh := os.Getenv("SHELL") //fetch default shell
//execute the needed command with `-c` flag
cmd := exec.Command(sh, "-c ", `docker exec 9aa1124 ...`)
Or this one, putting your commands in a string first:
cmd := "cat /proc/cpuinfo | egrep '^model name' | uniq | awk '{print substr($0, index($0,$4))}'"
out, err := exec.Command("bash","-c",cmd).Output()
if err != nil {
return fmt.Sprintf("Failed to execute command: %s", cmd)
}
multipass exec kube-node-one -- bash -c "ls && ls -a"
Is there any way we can execute a multiple commands in exec.Command
No.

exec command with parameters in Go?

In my shell, I can exec command acme.sh --issue --dns -d exmaple.com --yes-I-know-dns-manual-mode-enough-go-ahead-please and get output.
now I want to do that in go, and my code like:
cmd := exec.Command("bash", "-c", "acme.sh --issue --dns -d exmaple.com --yes-I-know-dns-manual-mode-enough-go-ahead-please");
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("issue failed with error: %s\n", err)
}
fmt.Printf("combined out:\n%s\n", string(out))
but I got error exit status 1.
and as the comment said, I separate argument:
exec.Command("bash", "-c", "acme.sh", "--issue", "--dns", "-d exmaple.com", "--yes-I-know-dns-manual-mode-enough-go-ahead-please");
but the result is that it exec acme.sh without parameters.
Using this script as acme.sh
#!/bin/bash
echo "$*"
with the program as given in the same directory the error you report occurs
However, if I add the current directory to the shell PATH
export PATH=.:$PATH
Then the program executes as hoped for, in the case of my version
$ go run c.go
combined out:
--issue --dns -d exmaple.com --yes-I-know-dns-manual-mode-enough-go-ahead-please
Ok so that's the case when the bash -c takes a single string as the command (more about that in a moment)
If the command is issued like this
cmd := exec.Command("acme.sh", "--issue", "--dns", "-d exmaple.com", "--
yes-I-know-dns-manual-mode-enough-go-ahead-please")
Then as the later edits to your question state, the command acme.sh is run without arguments.
The problem is with the way that bash -c behaves.
From the man page
bash interprets the following options when it
is invoked:
-c If the -c option is present, then commands are read from the
first non-option argument command_string. If there are argu‐
ments after the command_string, they are assigned to the
positional parameters, starting with $0.
In your case what this means is that the first arg to bash -c is accepted as a command. The other args are lost as they are positional args to the new bash shell and not to the acme.sh command
See this for more details on bash -c https://unix.stackexchange.com/questions/144514/add-arguments-to-bash-c
Lastly, what I would do in this situation: Skip the "bash" "-c", ensure that the script has a correct bang line and rely on the kernel binfmt handler
exclude exit status 1 from err will get right result.
cmd := exec.Command("bash", "-c", "acme.sh --issue --dns -d exmaple.com --yes-I-know-dns-manual-mode-enough-go-ahead-please");
out, err := cmd.CombinedOutput()
if err != nil && err.Error() != "exit status 1" {
log.Fatalf("issue failed with error: %s\n", err)
}
fmt.Printf("combined out:\n%s\n", string(out))

Resources