I have a problem which seems rather simple to me, but I cannot make it work properly on Windows:
I'm running a console program in Go, and some commands will need to run as an elevated process
That's how I restart my console program as an elevated user, and it works fine:
func RunElevated() error {
verb := "runas"
exe, _ := os.Executable()
cwd, _ := os.Getwd()
args := "--as-child " + strings.Join(os.Args[1:], " ")
verbPtr, _ := syscall.UTF16PtrFromString(verb)
exePtr, _ := syscall.UTF16PtrFromString(exe)
cwdPtr, _ := syscall.UTF16PtrFromString(cwd)
argPtr, _ := syscall.UTF16PtrFromString(args)
var showCmd int32 = 1 //SW_NORMAL
// also tried with zero as first parameter; I think it's about GUI window, not console?
err := windows.ShellExecute(GetConsoleWindow(), verbPtr, exePtr, argPtr, cwdPtr, showCmd)
if err != nil {
return err
}
return nil
}
// GetConsoleWindow from windows kernel32 API
func GetConsoleWindow() windows.Handle {
getConsoleWindow := kernel32.NewProc("GetConsoleWindow")
ret, _, _ := getConsoleWindow.Call()
return windows.Handle(ret)
}
... but the problem is that it's starting in a new console window.
I'd like to close it and reattach to the parent one. So here's the code:
const (
ATTACH_PARENT_PROCESS windows.Handle = 0x0ffffffff
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
)
// AttachParentConsole detach from the current console and attach to the parent process console
func AttachParentConsole() error {
err := FreeConsole()
if err != nil {
return err
}
err = AttachConsole(ATTACH_PARENT_PROCESS)
if err != nil {
return err
}
return nil
}
// FreeConsole from windows kernel32 API
func FreeConsole() error {
freeConsole := kernel32.NewProc("FreeConsole")
ret, _, _ := freeConsole.Call()
if ret == 0 {
return syscall.GetLastError()
}
return nil
}
// AttachConsole from windows kernel32 API
func AttachConsole(consoleOwner windows.Handle) error {
attachConsole := kernel32.NewProc("AttachConsole")
ret, _, _ := attachConsole.Call(uintptr(consoleOwner))
if ret == 0 {
return syscall.GetLastError()
}
return nil
}
At that point, Windows seems happy enough: it reports no error, and a flashing console appear and disappear as fast as you can refresh the screen.
I also believe the child command runs fine as it waits from an input from the user - I can see it in still running in the task manager; but nothing is displayed on the parent console.
So I was thinking the go runtime might keep the original handle somewhere? I had a look at the initialisation, and since these files don't exist on Windows I'm thinking they should be magic values for the go runtime:
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)
So I tried re-assigning os.Stdout and its friends using the same code in case the "/dev/stdout" would pick up the new console, but no luck here. Still nothing displayed.
Am I missing something or doing something stupid here?
Some help would be much appreciated :)
Related
I'm trying to get the PID of active foreground window on windows 10 using Go.
first I call this function and get handle:
func GetForegroundWindow() uintptr {
us32 := syscall.MustLoadDLL("user32.dll")
prc := us32.MustFindProc("GetForegroundWindow")
ret, _, _ := prc.Call()
return ret
}
later using the handle returned from the function above, I'm calling this function:
func GetWindowThreadProcessId(hwnd uintptr) uintptr {
us32 := syscall.MustLoadDLL("user32.dll")
prc := us32.MustFindProc("GetWindowThreadProcessId")
ret, _, err := prc.Call(hwnd, uintptr(unsafe.Pointer(&hwnd)))
fmt.Println("ProcessId: ", ret, " Error: ", err)
return ret
}
When I cross check the returned PID by running tasklist /fi "pid eq returnedPid" it doesn't match any process.
And the call to OpenProcess is always returning 0, with an error saying "The parameter is incorrect."
func GetOpenProcess(pid uintptr) {
kernel32 := syscall.MustLoadDLL("kernel32.dll")
proc := kernel32.MustFindProc("OpenProcess")
res, _, err := proc.Call(ptr(PROCESS_ALL_ACCESS|PROCESS_QUERY_INFO), ptr(true), ptr(pid))
fmt.Println("OpenProcess: result:", res, " Error:", err)
}
what am I doing wrong?
I was able to fix the issue based on the comment from https://stackoverflow.com/users/1889329/iinspectable
What I was doing wrong was, trying to use the value returned from syscall and I was passing a pointer to the window handle as a second parameter to GetWindowThreadProcessId() function, which was also wrong. Instead, to get Active Window process Id I had to pass a new pointer(second argument) and use the value from that pointer after the function returns instead of the returned value. Because, the GetWindowThreadProcessId function writes the PID to that location which the second parameter points to.
Please find the modified Code below:
func GetWindowThreadProcessId(hwnd uintptr) uintptr {
var prcsId uintptr = 0
us32 := syscall.MustLoadDLL("user32.dll")
prc := us32.MustFindProc("GetWindowThreadProcessId")
ret, _, err := prc.Call(hwnd, uintptr(unsafe.Pointer(&prcsId)))
log.Println("ProcessId: ", prcsId, "ret", ret, " Message: ", err)
return prcsId
}
log of the function:
2020/04/18 14:31:21 ProcessId: 13652 ret 13644 Message: The operation completed successfully.
I am trying to send a window to the background, but SetKeepBelow has no effect on windows. Is there any way to achieve this? I am using golang and gotk3, but I can add additional bindings if needed.
Another option would probably be using this: https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setwindowpos
But I am not sure how exactly, as I can't retrieve the window handle from within gtk.
So, I have found a solution and like mentioned in the comments of the questions, it is hacky! However, I didn't want to leave this unsolved. So what I am doing is using the SetWindowPos function of windows in order to move it on the z axis. However, in order to be able to do so, you first need the pointer to the hWnd, I am retrieving that with FindWindowA which takes a classname and a windowtitle, sicne I don't know the class, I am passing nil. Passing nil causes the function to only search by windowtitle. Ofc this solution only works if your window has a unique title!
package main
import (
"unsafe"
"github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/gtk"
"golang.org/x/sys/windows"
)
var (
mod = windows.NewLazyDLL("user32.dll")
setWindowPosFunction = mod.NewProc("SetWindowPos")
findWindowFunction = mod.NewProc("FindWindowA")
)
//ToBackground moves the window to the background, preserving its size and position
func ToBackground(hwnd uintptr, gtkWindow *gtk.Window) error {
x, y := gtkWindow.GetPosition()
width, height := gtkWindow.GetSize()
_, _, err := setWindowPosFunction.Call(hwnd, uintptr(1), uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0)
if err != nil && err.Error() == "The operation completed successfully." {
return nil
}
return err
}
func main() {
gtk.Init(nil)
window, _ := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
window.SetTypeHint(gdk.WINDOW_TYPE_HINT_UTILITY)
title := "unique-identifier"
window.SetTitle(title)
window.SetPosition(gtk.WIN_POS_CENTER)
window.ShowAll()
titleAsByteArray := []byte(title)
hwnd, _, windowRetrieveError := findWindowFunction.Call(0, uintptr(unsafe.Pointer(&titleAsByteArray[0])))
if windowRetrieveError != nil && windowRetrieveError.Error() != "The operation completed successfully." {
panic(windowRetrieveError)
}
toBackgroundError := ToBackground(hwnd, window)
if toBackgroundError != nil {
panic(toBackgroundError)
}
gtk.Main()
}
What would be an effective way to kill a process with Go code if you only know the process name? I see some functions provided by the os package like:
func FindProcess(pid int) (*Process, error)
func (p *Process) Kill() error
func (p *Process) Signal(sig Signal) error
Is there a good/common practice to get the pid without having to execute commands and then parse the output?
I have found a way to get back the pid using a command like the following:
echo $(ps cax | grep myapp | grep -o '^[ ]*[0-9]*')
and I have used it with exec.Command() but I would like to avoid it if there is a better approach.
Running external commands is probably the best way to do this. However, the following code runs on Ubuntu at least as long as you are the owner of the process to kill.
// killprocess project main.go
package main
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
"strings"
)
// args holds the commandline args
var args []string
// findAndKillProcess walks iterative through the /process directory tree
// looking up the process name found in each /proc/<pid>/status file. If
// the name matches the name in the argument the process with the corresponding
// <pid> will be killed.
func findAndKillProcess(path string, info os.FileInfo, err error) error {
// We just return in case of errors, as they are likely due to insufficient
// privileges. We shouldn't get any errors for accessing the information we
// are interested in. Run as root (sudo) and log the error, in case you want
// this information.
if err != nil {
// log.Println(err)
return nil
}
// We are only interested in files with a path looking like /proc/<pid>/status.
if strings.Count(path, "/") == 3 {
if strings.Contains(path, "/status") {
// Let's extract the middle part of the path with the <pid> and
// convert the <pid> into an integer. Log an error if it fails.
pid, err := strconv.Atoi(path[6:strings.LastIndex(path, "/")])
if err != nil {
log.Println(err)
return nil
}
// The status file contains the name of the process in its first line.
// The line looks like "Name: theProcess".
// Log an error in case we cant read the file.
f, err := ioutil.ReadFile(path)
if err != nil {
log.Println(err)
return nil
}
// Extract the process name from within the first line in the buffer
name := string(f[6:bytes.IndexByte(f, '\n')])
if name == args[1] {
fmt.Printf("PID: %d, Name: %s will be killed.\n", pid, name)
proc, err := os.FindProcess(pid)
if err != nil {
log.Println(err)
}
// Kill the process
proc.Kill()
// Let's return a fake error to abort the walk through the
// rest of the /proc directory tree
return io.EOF
}
}
}
return nil
}
// main is the entry point of any go application
func main() {
args = os.Args
if len(args) != 2 {
log.Fatalln("Usage: killprocess <processname>")
}
fmt.Printf("trying to kill process \"%s\"\n", args[1])
err := filepath.Walk("/proc", findAndKillProcess)
if err != nil {
if err == io.EOF {
// Not an error, just a signal when we are done
err = nil
} else {
log.Fatal(err)
}
}
}
It's just an example that certainly can be improved. I wrote this for Linux and tested the code on Ubuntu 15.10. It will not run on Windows.
I finally used something like the following:
// `echo "sudo_password" | sudo -S [command]`
// is used in order to run the command with `sudo`
_, err := exec.Command("sh", "-c", "echo '"+ sudopassword +"' | sudo -S pkill -SIGINT my_app_name").Output()
if err != nil {
// ...
} else {
// ...
}
I used the SIGINT signal to gracefully stop the app.
From wikipedia:
SIGINT
The SIGINT signal is sent to a process by its controlling terminal when a user wishes to interrupt the process. This is typically initiated by pressing Ctrl+C, but on some systems, the "delete" character or "break" key can be used.
SIGKILL
The SIGKILL signal is sent to a process to cause it to terminate immediately (kill). In contrast to SIGTERM and SIGINT, this signal cannot be caught or ignored, and the receiving process cannot perform any clean-up upon receiving this signal. The following exceptions apply:
Cross-Platform (3rd party) Solution
I've implemented various solutions to do this for months now, and for some reason it took me that long to find gopsutil. It is a 3rd party library and that may or may not be a deal breaker for you, but it has worked flawlessly for our cross-platform projects. The following example will kill the first process with the matching name, but it can easily be adapted to kill all processes with the name.
import "github.com/shirou/gopsutil/v3/process"
func KillProcess(name string) error {
processes, err := process.Processes()
if err != nil {
return err
}
for _, p := range processes {
n, err := p.Name()
if err != nil {
return err
}
if n == name {
return p.Kill()
}
}
return fmt.Errorf("process not found")
}
With Context Support
As an added bonus, the library also supports context cancellation on all process related operations including process queries, and killing the process.
func KillAllProcessesCtx(ctx context.Context, name string) error {
processes, err := process.ProcessesWithContext(ctx)
if err != nil {
return err
}
for _, p := range processes {
n, err := p.NameWithContext(ctx)
if err != nil {
return err
}
if n == name {
err = p.KillWithContext(ctx)
if err != nil {
return err
}
}
}
return nil
}
Graceful Termination
The library also supports graceful termination by sending your own signal to the process.
// Do this
err = p.SendSignal(syscall.SIGINT)
// Instead of this
err = p.Kill()
You can can already kill a process by process ID with Go, so the real question
here is getting the process ID from the process name. Here is example for
Windows:
package main
import (
"fmt"
"golang.org/x/sys/windows"
)
// unsafe.Sizeof(windows.ProcessEntry32{})
const processEntrySize = 568
func processID(name string) (uint32, error) {
h, e := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
if e != nil { return 0, e }
p := windows.ProcessEntry32{Size: processEntrySize}
for {
e := windows.Process32Next(h, &p)
if e != nil { return 0, e }
if windows.UTF16ToString(p.ExeFile[:]) == name {
return p.ProcessID, nil
}
}
return 0, fmt.Errorf("%q not found", name)
}
func main() {
n, e := processID("WindowsTerminal.exe")
if e != nil {
panic(e)
}
println(n)
}
https://pkg.go.dev/golang.org/x/sys/windows#CreateToolhelp32Snapshot
For Windows:
You can use below method. Pass process name which you want to terminate.
func killProcessByName(procname string) int {
kill := exec.Command("taskkill", "/im", procname, "/T", "/F")
err := kill.Run()
if err != nil {
return -1
}
return 0
}
Ref: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/taskkill
I am making a program that automates some tedious tasks for me, after the program is done I want to shutdown windows. I know this can be done in for example C#
How can I shutdown window using golang?
There is no "Shutdown OS" function in the syscall package, because there is no common interface provided for that by all operating systems.
Note: there is a syscall.Shutdown() function, but that is to shutdown a socket, and not to shutdown the operating system.
The easiest would be to just execute the shutdown command using the os/exec package, e.g.
if err := exec.Command("cmd", "/C", "shutdown", "/s").Run(); err != nil {
fmt.Println("Failed to initiate shutdown:", err)
}
The above command initiates a shutdown sequence which usually takes 1 minute to really shut down the system (and there's room to abort it with shutdown /a). You may provide different arguments to the shutdown command to not wait 1 minute but proceed immediately: shutdown /t 0 /s (execute shutdown /? to get a list of all options).
There is also a Windows API call to shutdown the system: ExitWindowsEx(). It has 2 parameters, first is the flags to define the shutdown type (0x08 means Shuts down the system and turns off the power.), second is to provide a reason for the shutdown. To call this from Go, you can do that like this:
user32 := syscall.MustLoadDLL("user32")
defer user32.Release()
exitwin := user32.MustFindProc("ExitWindowsEx")
r1, _, err := exitwin.Call(0x08, 0)
if r1 != 1 {
fmt.Println("Failed to initiate shutdown:", err)
}
But know that you need SE_SHUTDOWN_NAME privilege to call ExitWindowsEx(), else you get an error message like:
Failed to initiate shutdown: A required privilege is not held by the client.
See this example how to acquire the required privilege.
Thanks for the post, very helpful.
here is a complete function that performs a reboot. It follows exactly the Microsoft example as noted before. should help saving time in figuring out the structs:
import (
"fmt"
"syscall"
"unsafe"
)
// error is nil on success
func reboot() error {
user32 := syscall.MustLoadDLL("user32")
defer user32.Release()
kernel32 := syscall.MustLoadDLL("kernel32")
defer user32.Release()
advapi32 := syscall.MustLoadDLL("advapi32")
defer advapi32.Release()
ExitWindowsEx := user32.MustFindProc("ExitWindowsEx")
GetCurrentProcess := kernel32.MustFindProc("GetCurrentProcess")
GetLastError := kernel32.MustFindProc("GetLastError")
OpenProdcessToken := advapi32.MustFindProc("OpenProcessToken")
LookupPrivilegeValue := advapi32.MustFindProc("LookupPrivilegeValueW")
AdjustTokenPrivileges := advapi32.MustFindProc("AdjustTokenPrivileges")
currentProcess, _, _ := GetCurrentProcess.Call()
const tokenAdjustPrivileges = 0x0020
const tokenQuery = 0x0008
var hToken uintptr
result, _, err := OpenProdcessToken.Call(currentProcess, tokenAdjustPrivileges|tokenQuery, uintptr(unsafe.Pointer(&hToken)))
if result != 1 {
fmt.Println("OpenProcessToken(): ", result, " err: ", err)
return err
}
//fmt.Println("hToken: ", hToken)
const SeShutdownName = "SeShutdownPrivilege"
type Luid struct {
lowPart uint32 // DWORD
highPart int32 // long
}
type LuidAndAttributes struct {
luid Luid // LUID
attributes uint32 // DWORD
}
type TokenPrivileges struct {
privilegeCount uint32 // DWORD
privileges [1]LuidAndAttributes
}
var tkp TokenPrivileges
result, _, err = LookupPrivilegeValue.Call(uintptr(0), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(SeShutdownName))), uintptr(unsafe.Pointer(&(tkp.privileges[0].luid))))
if result != 1 {
fmt.Println("LookupPrivilegeValue(): ", result, " err: ", err)
return err
}
//fmt.Println("LookupPrivilegeValue luid: ", tkp.privileges[0].luid)
const SePrivilegeEnabled uint32 = 0x00000002
tkp.privilegeCount = 1
tkp.privileges[0].attributes = SePrivilegeEnabled
result, _, err = AdjustTokenPrivileges.Call(hToken, 0, uintptr(unsafe.Pointer(&tkp)), 0, uintptr(0), 0)
if result != 1 {
fmt.Println("AdjustTokenPrivileges() ", result, " err: ", err)
return err
}
result, _, _ = GetLastError.Call()
if result != 0 {
fmt.Println("GetLastError() ", result)
return err
}
const ewxForceIfHung = 0x00000010
const ewxReboot = 0x00000002
const shutdownReasonMajorSoftware = 0x00030000
result, _, err = ExitWindowsEx.Call(ewxReboot|ewxForceIfHung, shutdownReasonMajorSoftware)
if result != 1 {
fmt.Println("Failed to initiate reboot:", err)
return err
}
return nil
}
Last days i am kinda struggling with forking a process and kill the parent from the forked one(child)
I don't know why but it seems not to kill the parent at all. first i tough it where the open connections that keeps the process running because of the graceful shutdown process, But its not that issue.
if i send SIGTERM to the parent from the terminal it worked perfect, but when the child send SIGTERM it does not stop, force quitting is no option cause of graceful shutdown.
edit * the processes are still in the process list. Maybe that is for parent to track its children?
Some code that does the fork, maybe i do something wrong here
func (s *Server) Upgrade() error {
tl := s.listener.(*listener)
addr := s.listener.Addr()
name := fmt.Sprintf("%s:%s->", addr.Network(), addr.String())
os.Setenv("PROX_NAME", name)
fl, err := tl.File()
if err != nil {
return fmt.Errorf("Failed to extract file desciptor, %v", err)
}
fd := fl.Fd()
argv0, err := exec.LookPath(os.Args[0])
if err != nil {
return fmt.Errorf("Failed to execute lookpath, %v", err)
}
noCloseOnExec(fd)
files := make([]*os.File, fd+1)
files[syscall.Stdin] = os.Stdin
files[syscall.Stdout] = os.Stdout
files[syscall.Stderr] = os.Stderr
files[fd] = os.NewFile(fd, name)
wd, err := os.Getwd()
if err != nil {
return err
}
os.Setenv("GPROXY_FD", fmt.Sprintf("%d", fd))
os.Setenv("GPROXY_PID", fmt.Sprintf("%d", syscall.Getpid()))
args := []string{"gproxy", "-debug", "start"}
_, err = os.StartProcess(argv0, args, &os.ProcAttr{
Dir: wd,
Env: os.Environ(),
Files: files,
})
return err
}
the termination of the parent
func termParentProcess() error {
pid := syscall.Getppid()
return syscall.Kill(pid, syscall.SIGTERM)
}
Long story short, you can't fork, there's a very old and ongoing issue for it here.
You could use something like https://github.com/icattlecoder/godaemon but the parent will have to terminate itself not the child.
var isChild = flag.Bool("child", false, "parent pid")
func main() {
flag.Parse()
var s string
if !*isChild { //parent
fmt.Println("forking")
files := make([]*os.File, 3)
files[syscall.Stdin] = os.Stdin
files[syscall.Stdout] = os.Stdout
files[syscall.Stderr] = os.Stderr
fmt.Println(os.StartProcess(os.Args[0], append(os.Args, "-child"), &os.ProcAttr{
Dir: "/tmp",
Env: os.Environ(),
Files: files,
}))
fmt.Scan(&s) // block
} else {
ppid := os.Getppid()
fmt.Println("ppid", ppid, "kill:", syscall.Kill(ppid, syscall.SIGTERM))
time.Sleep(5 * time.Second)
fmt.Println("child dying")
}
}
The problem my parent process wont terminate on a TERM signal is because of a inner for loop that wont break. I fixed the for loop and let the main function return. If in Go the main returns the program exits. sorry for bothering you guys with a big mistake of myself