Using the os/exec package, I want to run an external command on a *nix OS with another user instead of root. (The main process runs under root user).
The external command runs by go app. But my app can not read /proc/pid/smaps file, following error:
panic: open /proc/2962/smaps: permission denied
goroutine 6 [running]:
main.memwatch(0xc000094000, 0xc00007a0c0)
/src/main.go:41 +0x298
created by main.main
/src/main.go:25 +0x18f
exit status 2
Here is my code:
// main.go
package main
import (
"fmt"
"os"
"os/exec"
"syscall"
"time"
)
func main() {
cmd := exec.Command("sleep", "3")
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
Credential: &syscall.Credential{Uid: 65534, Gid: 65534}, // External command expect run with `nobody` instead of `root` for security reason
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Start()
done := make(chan struct{})
go memwatch(cmd, done)
if err != nil {
panic(err)
}
cmd.Wait()
close(done)
}
func memwatch(cmd *exec.Cmd, done <-chan struct{}) {
// Reuse the reader so we don't have to close and reopen it all the time
smaps, err := os.Open(fmt.Sprintf("/proc/%d/smaps", cmd.Process.Pid))
if err != nil {
panic(err)
}
defer smaps.Close()
for {
select {
case <-done:
return
default:
fmt.Println("running")
time.Sleep(10 * time.Millisecond)
}
}
}
I'm tired. Anyone here for help, please.
I test my code inside a docker container that doesn't have SYS_PTRACE capability. That's why the error shows. The error was gone when I added the SYS_PTRACE capability for that container.
Related
so i need to make a program than can run, then restart or shutdown programmatically. I need to do like this from terminal:
project> go run app.go
apps started, waiting for 5 sec...
project>
other scenario:
project> go run app.go runner.go
apps started from app.go
runner detecting app.go from runner.go
app restarted after 10 sec from app.go
The program will be something like below:
package main
import(
"something"
)
func main () {
something.KillPrograms({
doingWhatever() //my program here
}, 5000) //program will running for only 5 sec
}
any library or anything from golang can do like that? thanks.
You can try with time.Sleep
package main
import (
"fmt"
"time"
)
func main() {
//use goroutine to run your function separately
//if you want it's sync, you can remove `go`
go doingWhatever()
//after 5 seconds, the program will exit
//even though your function has not been done yet
time.Sleep(5 * time.Second)
os.Exit(0)
}
Many libraries can handle restart/watch of the program. check this answer or this one.
I just tried this simple code that will restart itself each 2 seconds and during that he will run a random check to shutdown.
package main
import (
"fmt"
"math/rand"
"os"
"os/exec"
"runtime"
"sync"
"syscall"
"time"
)
var wg sync.WaitGroup
func main() {
t1 := time.NewTimer(time.Second * 2)
wg.Add(1)
// you can handle to restart programmatically
go func() {
defer wg.Done()
<-t1.C
fmt.Println("Timer expired")
RestartSelf()
}()
fmt.Println(time.Now().Format("2006-Jan-02 ( 15:04:05)"))
rand.Seed(time.Now().UnixNano())
// you can handle the shut-down programmatically
if rand.Intn(3) == 1 {
fmt.Println("It is time to shut-down")
os.Exit(0)
}
wg.Wait()
}
func RestartSelf() error {
self, err := os.Executable()
if err != nil {
return err
}
args := os.Args
env := os.Environ()
// Windows does not support exec syscall.
if runtime.GOOS == "windows" {
cmd := exec.Command(self, args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
cmd.Env = env
err := cmd.Run()
if err == nil {
os.Exit(0)
}
return err
}
return syscall.Exec(self, args, env)
}
output
2022-Mar-10 ( 17:59:53)
Timer expired
2022-Mar-10 ( 17:59:55)
Timer expired
2022-Mar-10 ( 17:59:57)
It is time to shut-down
Process finished with the exit code 0
I am using the built-in testing module to run some functional tests I have in my GO project. In my project I have external dependencies, which I connect to in my TestMain method. I save these connections to variables which then I use in the tests themselves, and the connections can take a long time to establish properly (Kafka anyone?). I would like to run the tests on-demand, but after these variables have been setup.
So what I want is to listen to stdin in my TestMain function and either run or quit the tests. But I want it to be controlled by the user so I can have my test environment setup, and the tests will run on my command.
But sadly, it seems that when running go test ... that stdin is mapped directly to /dev/null. So when I try to read os.Stdin I get an EOF error. The minimum code for this is:
package tests
import (
"bufio"
"fmt"
"os"
"testing"
)
func TestMain(m *testing.M) {
reader := bufio.NewReader(os.Stdin)
if input, err := reader.ReadString('\n'); err != nil {
fmt.Println(err)
fmt.Println("-----------")
fmt.Println(input)
os.Exit(1)
}
fmt.Println("ESCAPED!")
os.Exit(m.Run())
}
I have read in circles how to mock this for unit tests and the sort, but my case is more of a functional test runner. Is there a way, or even some trickery, that will allow me to open or change the test processes stdin?
You can redirect os.Stdin, it depends on OS though:
package tests
import (
"fmt"
"os"
"testing"
"bufio"
"runtime"
)
func TestMain(m *testing.M) {
var ttyName string
if runtime.GOOS == "windows" {
ttyName = "con"
} else {
ttyName = "/dev/tty"
}
f, err := os.Open(ttyName)
if err != nil {
panic(err)
}
defer f.Close()
oldStdin := os.Stdin
defer func() { os.Stdin = oldStdin }()
os.Stdin = f
reader := bufio.NewReader(os.Stdin)
if input, err := reader.ReadString('\n'); err != nil {
fmt.Println("Error:", err)
fmt.Println("-----------")
fmt.Println(input)
os.Exit(1)
}
fmt.Println("ESCAPED!")
os.Exit(m.Run())
}
func TestHello(t *testing.T){
fmt.Println("Hello")
}
I am trying to execute tskarh from golang script using the example from
https://tutorialedge.net/golang/executing-system-commands-with-golang/
The script works fine, but i don't receive any kind of output
What i want to get is the following:
Continuously run the script,
capture some packets,
extract some fields values,
and assign to variables
Any help please ?
https://pastebin.com/PeAz7vh9
package main
import (
"fmt"
"os/exec"
"runtime"
)
func execute() {
// here we perform the pwd command.
// we can store the output of this in our out variable
// and catch any errors in err
out, err := exec.Command("tshark", "-i", "em1").CombinedOutput()
// if there is an error with our execution
// handle it here
if err != nil {
fmt.Printf("%s", err)
}
fmt.Println("Command Successfully Executed")
// as the out variable defined above is of type []byte we need to convert
// this to a string or else we will see garbage printed out in our console
// this is how we convert it to a string
output := string(out[:])
// once we have converted it to a string we can then output it.
fmt.Println(output)
}
func main() {
fmt.Println("Simple Shell")
fmt.Println("---------------------")
if runtime.GOOS == "windows" {
fmt.Println("Can't Execute this on a windows machine")
} else {
execute()
}
}
I have no idea of tshark, but here is a code that will work continously, you need os.Interrupt, and select.
package main
import (
"os"
"os/exec"
"os/signal"
)
func main() {
out := exec.Command("ping", "8.8.8.8")
f1, _ := os.OpenFile("./outfile.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755)
f2, _ := os.OpenFile("./errfile.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755)
out.Stdout = f1
out.Stderr = f2
defer func() {
f1.Close()
f2.Close()
}()
err := out.Run()
if err != nil {
panic(err)
}
var ctrlcInt chan os.Signal
ctrlcInt = make(chan os.Signal, 1)
signal.Notify(ctrlcInt, os.Interrupt)
for {
select {
case <-ctrlcInt:
break
default:
continue
}
}
return
}
this code pings 8.8.8.8 and writes out put to outfile.txt, it will exit when you press ctrl+c. If there is error it will write to errfile.txt. You can tail the files and see the output. Hope this helps.
When executing a process and sending signals to it using: Process.Signal I notice that after sending the second signal syscall.SIGCONT I got a: os: process already finished but if using syscall.Kill everything works as expected.
For demonstrative purposes I have created this naive example:
package main
import (
"fmt"
"os"
"os/exec"
"syscall"
"time"
)
func main() {
exit := make(chan error, 1)
go run(exit)
for {
select {
case <-exit:
println("fin, restarting")
run(exit)
default:
time.Sleep(time.Second)
println("running...")
}
}
}
func run(ch chan<- error) {
cmd := exec.Command("sleep", "3")
if err := cmd.Start(); err != nil {
print(err.Error())
os.Exit(1)
}
fmt.Printf("Pid: %d\n", cmd.Process.Pid)
go func() {
ch <- cmd.Wait()
}()
time.Sleep(2 * time.Second)
fmt.Printf("%v\n", cmd.Process.Signal(syscall.SIGSTOP))
time.Sleep(2 * time.Second)
// Using this will return an os: process already finished
fmt.Printf("%v\n", cmd.Process.Signal(syscall.SIGCONT))
// This works as expected
//fmt.Printf("%v\n", syscall.Kill(cmd.Process.Pid, syscall.SIGCONT))
}
So basically if using:
cmd.Process.Signal(syscall.SIGCONT)
os: process already finished is returned
But when using:
syscall.Kill(cmd.Process.Pid, syscall.SIGCONT)
It works as expected.
Could this be a bug on os.exec or it the expected behavior?
UPDATE: seems to be happening only on mac os X
This issue seems to be only happening on Mac OS X, tested on "Sierra" and "El Capitan" https://go-review.googlesource.com/#/c/37610/
So, for now, to keep things cross-platform better user:
syscall.Kill(cmd.Process.Pid, syscall.SIGCONT))
Example code to test if your system has this issue:
package main
import (
"fmt"
"log"
"os/exec"
"syscall"
"unsafe"
)
func main() {
cmd := exec.Command("sleep", "10")
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
// signal when wait4 will return immediately
go func() {
var siginfo [128]byte
psig := &siginfo[0]
_, _, e := syscall.Syscall6(syscall.SYS_WAITID, 1, uintptr(cmd.Process.Pid), uintptr(unsafe.Pointer(psig)), syscall.WEXITED|syscall.WNOWAIT, 0, 0)
fmt.Println("WAITID RETURNED -- this shouldn't happen:", e)
}()
err := cmd.Process.Signal(syscall.SIGSTOP)
if err != nil {
log.Fatal(err)
}
cmd.Wait()
}
While doing a go test -race, I found that a call to os.Process.Kill, was made before the command started cmd.Start(), I came with to posible solutions, one to use a channel:
package main
import "os/exec"
func main() {
cmd := exec.Command("sleep", "10")
started := make(chan struct{}, 1)
go func() {
<-started
cmd.Process.Kill()
}()
if err := cmd.Start(); err != nil {
panic(err)
}
started <- struct{}{}
cmd.Wait()
}
or to use a lock:
package main
import (
"os/exec"
"sync"
)
func main() {
var lock sync.Mutex
cmd := exec.Command("sleep", "10")
lock.Lock()
if err := cmd.Start(); err != nil {
panic(err)
}
lock.Unlock()
go func() {
cmd.Process.Kill()
}()
cmd.Wait()
}
Both options work but wondering what could be the most idiomatic or better approach, while the main goal is just to prevent killing a process that hasn't been started.
I would suggest you use a channel, but let me point out something about your code.
I noticed you used a buffered channel, and then sent data on that channel first, before calling the goroutine that consumes the channel. In my opinion, it would be better to:
1) use an unbuffered channel for signalling, especially in this case.
2) Have the goroutine be responsible for starting the process and calling wait, while signaling to the main that it has started.
Like this:
package main
import "os/exec"
func main() {
cmd := exec.Command("sleep", "10")
started := make(chan struct{})
go func(cmd *exec.Cmd, signal chan struct{}) {
if err := cmd.Start(); err != nil {
panic(err)
}
started <- struct{}{}
cmd.Wait()
}(cmd, started)
<-started
cmd.Process.Kill()
}