How to redirect Stdout of child process to a file and exit in Go? - go

Is there a way to start a child process, redirect its Stdout to a file and exit while allowing the child process to still run and use its Stdout to write the file?
I'd like the code to be platform independent. I guess in Unix-like systems Cmd.ExtraFiles can be used for this purpose however it is not supported on Windows.
Demo app below
package main
import (
"fmt"
"os"
"os/exec"
"time"
)
func main() {
if len(os.Args) == 2 {
child()
return
}
executable, err := os.Executable()
if err != nil {
panic(err)
}
tempFile, err := os.CreateTemp("", "demo.*.txt")
if err != nil {
panic(err)
}
fmt.Println("tempFile", tempFile.Name())
_, err = tempFile.WriteString("parent\n")
if err != nil {
panic(err)
}
command := exec.Command(executable, "child")
command.Stdout = tempFile
err = command.Start()
if err != nil {
panic(err)
}
fmt.Println("parent end")
}
func child() {
fmt.Println("child start")
time.Sleep(5 * time.Second)
fmt.Println("child end")
}
writes file with content
parent
child start

Related

Check if the program is running and stop more than one [duplicate]

I need to only allow one instance of my Golang executable at a time. I'm not sure how to use a Global Mutex to make sure no other instances are running.
This would be running on a Windows Machine.
I know this topic is a bit old, but I needed it recently on Windows and I'll post here how I did it in case someone else needs.
Thx to #VonC for pointing me in the right direction.
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procCreateMutex = kernel32.NewProc("CreateMutexW")
)
func CreateMutex(name string) (uintptr, error) {
ret, _, err := procCreateMutex.Call(
0,
0,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(name))),
)
switch int(err.(syscall.Errno)) {
case 0:
return ret, nil
default:
return ret, err
}
}
// mutexName starting with "Global\" will work across all user sessions
_, err := CreateMutex("SomeMutexName")
I created a lib with a more complete example: https://github.com/rodolfoag/gow32
Thx!
There doesn't seem to be a cross-platform solution (beside writing a file, and looking for that file at start time).
On Windows, this thread reports
the recommended approach (and the one that has worked great for me) is to use the CreateSemaphore function.
If the name you specify starts with "Global\", then the semaphore is unique for the entire system and a second attempt to open it will fail.
This is a kernel32 call, which has some wrapper in Go available.
kostix adds in the comments:
look at the Go source code around the pkg\syscall hierarchy -- it contains a good wealth of examples on how to call out to DLLs on Windows using syscalls (and that's how you access anything in Windows API).
That would be syscall/dll_windows.go. (And here is a gist)
The odbc package by brainman is another example of direct API calls on Windows -- possibly easier to digest.
Like api/zapi_windows.go.
You could use sockets, simple to use and will work on everything really.
package main
import (
"fmt"
"net"
"os"
"strings"
)
const (
INSTANCE_PORT = 9292
)
func main() {
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", INSTANCE_PORT))
if err != nil {
if strings.Index(err.Error(), "in use") != -1 {
//optionally send command line arguments to the other instance
fmt.Fprintln(os.Stderr, "Already running.")
return
} else {
panic(err)
}
}
for {
conn, err := listener.Accept()
if err != nil {
println("Error accept:", err.Error())
return
}
go do_something_with(conn)
}
}
You could adapt the code from tendo's python library source
what they do is
for windows :
creating a file made of the executable absolute path (well it's a library, so in your case, you can just define an identifier, to prevent you from "i put the executable in 2 places")
For windows: trying first to remove the file if existing, and if not creating the file with os.O_CREAT | os.O_EXCL | os.O_RDWR
For POSIX compatible systems: trying first to remove the file if existing and if not creating the file and acquiring a lock on it using fcntl.LOCK_EX | fcntl.LOCK_NB
any failure mean the program is already running
and then you can use a defer action to remove the lock (on posix system) and delete the file
Go permit you to create both version wit a build comment to tell which file to compile depending on your OS so you have
for unix system
// +build !windows
package main
import (
"os"
"syscall"
)
func create_lock_file(filename string) (*os.File, error) {
file, err := os.OpenFile(filename, os.O_WRONLY, 0666)
if err != nil {
return nil, err
}
err = syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
if err != nil {
return nil, err
}
return file, nil
}
for windows:
// +build !windows
package main
import (
"os"
)
func create_lock_file(filename string) (*os.File, error) {
if _, err := os.Stat(filename); err == nil {
err = os.Remove(filename)
if err != nil {
return nil, err
}
}
return os.OpenFile(filename, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666)
}
and a test
package main
import (
"fmt"
"time"
)
func main() {
_, err := create_lock_file("plop.lock")
if err != nil {
fmt.Println("error ", err.Error())
}
time.Sleep(10 * time.Second)
fmt.Println("end ")
}
I've started a library out of it that you can find here
Improvements to this answer. (I am unsure if this answer will distort the original meaning, so I have written a new answer.)
Features:
deprecated: StringToUTF16Ptr is deprecated. Use UTF16PtrFromString instead.
Add the CloseHandle so that you can cancel the CreateMutexW.
package _test
import (
"syscall"
"testing"
"unsafe"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procCreateMutexW = kernel32.NewProc("CreateMutexW")
procCloseHandle = kernel32.NewProc("CloseHandle")
)
// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexW#return-value
func CreateMutexW(proc *syscall.LazyProc, name string) (uintptr, error) {
if proc.Name != "CreateMutexW" {
panic("proc.Name != CreateMutexW")
}
lpName, _ := syscall.UTF16PtrFromString(name) // LPCWSTR
if handleID, _, err := proc.Call(
0,
0,
uintptr(unsafe.Pointer(lpName)),
); err.(syscall.Errno) == 0 {
return handleID, nil
} else {
return handleID, err
}
}
// https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle?redirectedfrom=MSDN
func CloseHandle(proc *syscall.LazyProc, handle uintptr) error {
if proc.Name != "CloseHandle" {
panic("proc.Name != CloseHandle")
}
val, _, err := proc.Call(handle)
if val == 0 {
return err
}
return nil
}
func TestCreateMutexW(t *testing.T) {
handle, err := CreateMutexW(procCreateMutexW, "hello world")
if err != nil {
t.Fatalf(err.Error())
}
_, err = CreateMutexW(procCreateMutexW, "hello world")
if err == nil || err != syscall.ERROR_ALREADY_EXISTS {
t.Error("should panic")
}
if err = CloseHandle(procCloseHandle, handle); err != nil {
t.Error(err)
}
// We can create again since we have closed.
handle, _ = CreateMutexW(procCreateMutexW, "hello world")
if err = CloseHandle(procCloseHandle, handle); err != nil {
t.Error(err)
}
}

Sending binaries or strings by a client socket

I'm studying networks, and I'm doing a tcp server with Go. One of the challenges I'm studying is to send binaries or strings by a socket client to a server, save the server response to a txt, and compare it to the original data that was sent.
The problem is that the binaries do not arrive completely on the server.
Server
package main
import (
"fmt"
"io"
"log"
"net"
)
func main() {
l, err := net.Listen("tcp", ":8000")
if nil != err {
log.Println(err)
}
defer l.Close()
for {
conn, err := l.Accept()
if nil != err {
log.Println(err)
continue
}
defer conn.Close()
go ConnHandler(conn)
}
}
func ConnHandler(conn net.Conn) {
recvBuf := make([]byte, 4096)
for {
n, err := conn.Read(recvBuf)
if nil != err {
if io.EOF == err {
log.Println(err)
return
}
log.Println(err)
return
}
if 0 < n {
data := recvBuf[:n]
fmt.Println(string(data))
}
}
}
Client
package main
import (
"fmt"
"log"
"net"
)
func main() {
conn, err := net.Dial("tcp", ":8000")
if nil != err {
log.Println(err)
}
var s string
fmt.Scanln(&s)
conn.Write([]byte(s))
conn.Close()
}
I'm generating the binaries using the command on linux:
head -c100000 /dev/urandom > binary_message.txt
I run the server:
./server > result.txt
And I send this data by the client using:
./client < binary_data.txt
In the end the file binary_data.txt have 98KB but the result .txt only has 0KB.
The problem is with scanning the binary from input. You didn't see it because the errors were ignored and not printed or otherwise handled. fmt.Scanln returns an error (so does the Write function). You should always check for possible errors happening.
I rewrote the client to load the file from disk itself as I don't think using stdin is a good fit for binary data.
package main
import (
"flag"
"io"
"log"
"net"
"os"
)
var fileName = flag.String("file", "", "file to send")
func main() {
flag.Parse()
conn, err := net.Dial("tcp", ":8000")
if nil != err {
log.Println(err)
}
defer conn.Close()
f, err := os.Open(*fileName)
if err != nil {
log.Println(err)
return
}
defer f.Close()
b := make([]byte, 1024)
for {
n, err := f.Read(b)
if err != nil {
if err == io.EOF {
log.Println("Done sending")
return
}
log.Println(err)
return
}
if _, err := conn.Write(b[:n]); err != nil {
log.Println(err)
return
}
}
}
You can use it with:
go run . -file=binary_message.txt
or if you have built the binary:
./client -file=binary_message.txt
I suggest you do the same for the server. Open a file for writing and write the binary data into that file. Use a flag to pass in the filename to write to. That will be cleaner than piping stdout to a file.

golang os.Close() function works, but os.Remove() function does not

I am trying to create a file, open it, do some processing on it & close it. Finally, I want to delete the file.
All these operations are executed successfully, except the deletion.
My code is:
package main
import (
"fmt"
"os"
"log"
)
func main() {
fmt.Println("Hello")
metaFileName := "./metadata.txt"
_, err2 := os.Create(metaFileName)
if err2 != nil {
log.Fatal(err2)
}
openMetaFile, err := os.Open(metaFileName)
if err != nil {
log.Fatal(err)
}
err = openMetaFile.Close()
if err != nil {
log.Fatal(err)
}
err = os.Remove(metaFileName)
if err != nil {
log.Fatal(err)
}
fmt.Println("Success")
}
The output is:
Hello
2020/08/24 00:00:00 remove ./metadata.txt: The process cannot access the file be
cause it is being used by another process.
I am clueless about this
The problem is related to the first opened file.
package main
import (
"fmt"
"log"
"os"
)
const metaFileName = "./metadata.txt"
func main() {
var (
err error
tmpFile, openMetaFile *os.File
)
fmt.Println("Hello")
if tmpFile, err = os.Create(metaFileName); err != nil {
log.Fatal(err)
}
if err = tmpFile.Close(); err != nil {
log.Fatal(err)
}
if openMetaFile, err = os.Open(metaFileName); err != nil {
log.Fatal(err)
}
if err = openMetaFile.Close(); err != nil {
log.Fatal(err)
}
if err = os.Remove(metaFileName); err != nil {
log.Fatal(err)
}
fmt.Println("Success")
}
As you can see, i've used the var a in order to close the first file that you have opened. The result is following one:
Hello
Success

How to call an external program and process its output?

I am trying to call an external command (e.g., seq 10) and take its output, process the output then print out the processed results. But the following code does not work. Could you please let me know how to make it work?
// vim: set noexpandtab tabstop=2:
package main
import (
"bufio"
"io"
"os"
"log"
"os/exec"
)
func main() {
cmd := exec.Command("seq", "10")
stdin := bufio.NewReader(cmd.Stdout)
err := cmd.Run()
if err != nil {
log.Fatalf("cmd.Run() failed with %s\n", err)
}
for {
line, err := stdin.ReadBytes('\n')
if err == io.EOF {
if len(line) == 0 { break }
} else {
if err != nil { log.Fatal(err) }
line = line[:(len(line)-1)]
}
os.Stdout.Write(line)
os.Stdout.Write([]byte{'\n'})
}
}
$ $ go run main.go
# command-line-arguments
./main.go:15:30: cannot use cmd.Stdout (type io.Writer) as type io.Reader in argument to bufio.NewReader:
io.Writer does not implement io.Reader (missing Read method)
EDIT: I also tried this. But it also has error. Could anybody show me a working example.
// vim: set noexpandtab tabstop=2:
package main
import (
"bufio"
"io"
"os"
"log"
"os/exec"
)
func main() {
cmd := exec.Command("seq", "10")
stdout, err := cmd.StdoutPipe()
if err != nil { log.Fatal(err) }
stdin := bufio.NewReader(stdout)
err = cmd.Run()
if err != nil {
log.Fatalf("cmd.Run() failed with %s\n", err)
}
for {
line, err := stdin.ReadBytes('\n')
if err == io.EOF {
if len(line) == 0 { break }
} else {
if err != nil { log.Fatal(err) }
line = line[:(len(line)-1)]
}
os.Stdout.Write(line)
os.Stdout.Write([]byte{'\n'})
}
}
Another method (and a cleaner one) is to use bufio.Scanner which handles \n (or any other delimiter) automatically. Another advantage is that this method doesn't have race issues (been there, done that):
package main
import (
"bufio"
"fmt"
"log"
"os/exec"
)
func main() {
cmd := exec.Command("seq", "10")
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
err = cmd.Start()
if err != nil {
log.Fatalf("cmd.Start() failed with %s\n", err)
}
stdin := bufio.NewScanner(stdout)
for stdin.Scan() {
fmt.Println(stdin.Text())
}
cmd.Wait()
}
stdin.Scan() returns false on EOF, which is given once the process exits. cmd.Wait() will close StdoutPipe, and you can read err.(exec.ExitError).ExitCode() to get the exit code (if exited non-zero).
You need to pipe the standard out to the reader using .StdoutPipe(), you also need to use exec.Command(..).Start() to read incrementally (.Run() waits for the process to exit).
Here is the working code:
// vim: set noexpandtab tabstop=2:
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("seq", "10")
cmdStdOut, err := cmd.StdoutPipe()
cmdStdErr, err := cmd.StderrPipe()
defer cmdStdOut.Close()
if err != nil {
log.Fatalf("command failed with %s\n", err)
}
stdoutReader := bufio.NewReader(cmdStdOut)
stderrReader := bufio.NewReader(cmdStdErr)
err = cmd.Start()
if err != nil {
log.Fatalf("cmd.Run() failed with %s\n", err)
}
// Read stdout
for {
line, err := stdoutReader.ReadBytes('\n')
if err == io.EOF {
if len(line) == 0 {
break
}
} else {
if err != nil {
log.Fatal(err)
}
line = line[:(len(line) - 1)]
}
os.Stdout.Write(line)
os.Stdout.Write([]byte{'\n'})
}
// Read stderr
for {
line, err := stderrReader.ReadBytes('\n')
if err == io.EOF {
if len(line) == 0 {
break
}
} else {
if err != nil {
log.Fatal(err)
}
line = line[:(len(line) - 1)]
}
os.Stderr.Write(line)
os.Stderr.Write([]byte{'\n'})
}
cmd.Wait()
fmt.Println(cmd.ProcessState.ExitCode())
}

Interact with external application from within code

I need to be able to run an external application and interact with it as though I was manually running it from the command-line. All the examples I find only deal with running the program and capturing the output.
Below is a very simple example that I hope illustrates what I am trying to accomplish.
package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
cmd := exec.Command("rm", "-i", "somefile.txt")
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err)
}
if string(out) == "Remove file 'somefile.txt'?" {
// send the response 'y' back to the rm process
}
// program completes normally...
}
I've tried to tweak various examples that I've found to accomplish this with zero success. It seems that even though 'rm' is waiting for a response, Go closes the process.
Any examples, articles, or advice you can provide would be greatly appreciated. Many thanks in advance.
You have two possibilities. First is to use ReadLine() but that works only if application output is full lines, and you can wait for \n. This is not the case with rm, so you have to develop a custom SplitFunction for Scanner. Both versions can be found below.
Please note that you can not use CombinedOutput, as it can not be Scanned. You have to use the pipes.
package main
import (
"bufio"
//"fmt"
"log"
"os/exec"
)
func main() {
cmd := exec.Command("rm", "-i", "somefile.txt")
// Stdout + stderr
out, err := cmd.StderrPipe() // rm writes the prompt to err
if err != nil {
log.Fatal(err)
}
r := bufio.NewReader(out)
// Stdin
in, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
defer in.Close()
// Start the command!
err = cmd.Start()
if err != nil {
log.Fatal(err)
}
line, _, err := r.ReadLine()
for err != nil {
if string(line) == "Remove file 'somefile.txt'?" {
in.Write([]byte("y\n"))
}
line, _, err = r.ReadLine()
}
// program completes normally...s
}
This is a second version with the scanner, and it uses both \n and ? as line delimiters:
package main
import (
"bufio"
"bytes"
"fmt"
"log"
"os/exec"
)
// Ugly hack, this is bufio.ScanLines with ? added as an other delimiter :D
func new_scanner(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, '\n'); i >= 0 {
// We have a full newline-terminated line.
fmt.Printf("nn\n")
return i + 1, data[0:i], nil
}
if i := bytes.IndexByte(data, '?'); i >= 0 {
// We have a full ?-terminated line.
return i + 1, data[0:i], nil
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
return len(data), data, nil
}
// Request more data.
return 0, nil, nil
}
func main() {
cmd := exec.Command("rm", "-i", "somefile.txt")
// Stdout + stderr
out, err := cmd.StderrPipe() // Again, rm writes prompts to stderr
if err != nil {
log.Fatal(err)
}
scanner := bufio.NewScanner(out)
scanner.Split(new_scanner)
// Stdin
in, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
defer in.Close()
// Start the command!
err = cmd.Start()
if err != nil {
log.Fatal(err)
}
// Start scanning
for scanner.Scan() {
line := scanner.Text()
if line == "rm: remove regular empty file ‘somefile.txt’" {
in.Write([]byte("y\n"))
}
}
// Report scanner's errors
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
// program completes normally...s
}

Resources