Golang retrieve application uptime - go

I'm trying to retrieve the current uptime of my Go application.
I've seen there's a package syscall which provides a type Sysinfo_t and a method Sysinfo(*Sysinfo_t) which apparently allows you to retrieve the Uptime (since it's a field of the Sysinfo_t struct)
What I've done so far is:
sysi := &syscall.Sysinfo_t{}
if err := syscall.Sysinfo(sysi); err != nil {
return http.StatusInternalServerError, nil
}
The problem is that at compile time I get this:
/path/to/file/res_system.go:43: undefined: syscall.Sysinfo_t
/path/to/file/res_system.go:45: undefined: syscall.Sysinfo
I've searched a bit and apparently that method and type are available only on Linux and I need the application to run both on Linux and OsX (which I'm currently using).
Is there a cross-compatible way to retrieve the application uptime?
NOTE: I'd rather not use any third party libraries (unless they're absolutely necessary)

Simple way to get uptime is to store service start time:
https://play.golang.org/p/by_nkvhzqD
package main
import (
"fmt"
"time"
)
var startTime time.Time
func uptime() time.Duration {
return time.Since(startTime)
}
func init() {
startTime = time.Now()
}
func main() {
fmt.Println("started")
time.Sleep(time.Second * 1)
fmt.Printf("uptime %s\n", uptime())
time.Sleep(time.Second * 5)
fmt.Printf("uptime %s\n", uptime())
}

You should use Since function from time package.
create time value when application start:
startTime := time.Now()
then ask whenever you want:
uptime := time.Since(startTime)

Package syscall was frozen on Go 1.4.
NOTE: This package is locked down. Code outside the standard Go repository should be migrated to use the corresponding package in the golang.org/x/sys repository. That is also where updates required by new systems or versions should be applied. See https://golang.org/s/go1.4-syscall for more information.
Use Sysinfo from golang.org/x/sys it should support this in a cross-platform way, at least on Unix.

The unix package in Go Standard Library go1.19.4 on macOS 13.1 Darwin xnu can now determine process start time using unix.SysctlKinfoProc
I have an open source Go library doing this here: https://github.com/haraldrudell/parl/blob/main/mains/process-start.go
ie.
import "github.com/haraldrudell/parl/mains"
println(mains.ProcessStartTime())
unix.SysctlKinfoProc uses macOS libSystem ie. it is supported by Apple, Inc. and uses direct kernel calls and no dumbities
Code is basically:
if unixKinfoProc, err = unix.SysctlKinfoProc(kernProcPid, os.Getpid()); perrors.Is(&err, "unix.SysctlKinfoProc: %T %+[1]v", err) {
panic(err)
}
var unixTimeval unix.Timeval = unixKinfoProc.Proc.P_starttime
sec, nsec := unixTimeval.Unix()
createTime = time.Unix(sec, nsec)

Difficulties
import "syscall" has been starved on most of its functionality which has been extracted to platform specific code in import "golang.org/x/sys/unix" and import "golang.org/x/sys/windows".
macOS GOOS==Darwin sorts under unix. The code in unix and windows is platform-specific, ie. if windows is imported on unix, the result is
error while importing golang.org/x/sys/windows: build constraints exclude all Go files in …
This means the program has to have a portable layer defining a portable function name, and that function is implemented for each supported platform like _darwin.go _linux.go and _windows.go which has to be tested on the real operating system.
The alternative is to use a third-party package where portability is already implemented. What you do then is to browse to Go Package search and pick a well-written candidate.
Solution
I browsed to Go Package search for Sysinfo: https://pkg.go.dev/search?q=sysinfo
Top result is gosysinfo "github.com/elastic/go-sysinfo". This package is awkwardly written as can be seen by a hyphen in its name and a peculiar package structure. It works, and the code goes like:
import (
gosysinfo "github.com/elastic/go-sysinfo"
"github.com/elastic/go-sysinfo/types"
"github.com/haraldrudell/parl"
)
func goSysinfo() {
var process types.Process
var err error
if process, err = gosysinfo.Self(); err != nil {
panic(parl.Errorf("go-sysinfo.Self: %w", err))
}
var processInfo types.ProcessInfo
if processInfo, err = process.Info(); err != nil {
panic(parl.Errorf("go-sysinfo.Info: %w", err))
}
startTime := processInfo.StartTime
fmt.Printf("Process start time: %s\n", startTime.Format(parl.Rfc3339s))
}
→
Process start time: 2022-03-22 10:15:05-07:00

Related

How do I call a function from the main application from a plugin?

I have recently looked into Go plugins instead of manually loading .so files myself.
Basically, I have a game server application, and I want to be able to load plugins (using plugins package) when the server starts. Then, in the plugin itself, I want to be able to call exported functions that are a part of the server.
Say I have this plugin, which is compiled to example_plugin.so using go build -buildmode=plugin:
package main
import "fmt"
func init() {
fmt.Println("Hello from plugin!")
}
Then say I have this server application, which loads the plugin (and ultimately calls the "init" function under the hood):
package main
import (
"fmt"
"plugin"
)
func main() {
fmt.Println("Server started")
if _, err := plugin.Open("example_plugin.so"); err != nil {
panic(err)
}
}
// some API function that loaded plugins can call
func GetPlayers() {}
The output is:
Server started
Hello from plugin!
This works as expected, however I want to be able to call that GetPlayers function (and any other exported functions in the server application, ideally) from the plugin (and any other plugins.) I was thinking about making some sort of library consisting of interfaces containing API functions that the server implements, however I have no idea where to start. I know I will probably need to use a .a file or something similar.
For clarification, I am developing this application for use on Linux, so I am fine with a solution that only works on Linux.
Apologies if this is poorly worded, first time posting on SO.
As mentioned in the comments, there is a Lookup function. In the documentation for the module they have the following example:
// A Symbol is a pointer to a variable or function.
// For example, a plugin defined as
//
// var V int
//
// func F() { fmt.Printf("Hello, number %d\n", V) }
//
// may be loaded with the Open function and then the exported package
// symbols V and F can be accessed
package main
import (
"fmt"
"plugin"
)
func main() {
p, err := plugin.Open("plugin_name.so")
if err != nil {
panic(err)
}
v, err := p.Lookup("V")
if err != nil {
panic(err)
}
f, err := p.Lookup("F")
if err != nil {
panic(err)
}
*v.(*int) = 7
f.(func())() // prints "Hello, number 7"
}
I think the most confusing lines here are
*v.(*int) = 7
f.(func())() // prints "Hello, number 7"
The first one of them performs a type assertion to *int to assert that v is indeed a pointer to int. That is needed since Lookup returns an interface{} and in order to do anything useful with a value, you should clarify its type.
The second line performs another type assertion, this time making sure that f is a function with no arguments and no return values, after which, immediately calls it. Since function F from the original module was referencing V (which we've replaced with 7), this call will display Hello, number 7.

How to use golang's built in Untar (golang.org/x/build/internal/untar)

This seems like a really simple issue but for the life of me I cannot figure it out.
All I want to do is unzip and extract the contents of a tar.gz file. On godoc there seems to be a reference to a function that does exactly this. (https://godoc.org/golang.org/x/build/internal/untar).
There are no examples though and I can't seem to figure it out. Worse yet, though, I can't even figure out how to get access to the function.
Right now I have:
package main
import (
"io"
"os"
//???
)
func main() {
f, err := os.Open("foo.tar.gz")
if err != nil {
panic(err)
}
defer f.Close()
var freader io.ReadCloser = f
err = untar.Untar(freader, ".") // <- Compiler doesn't recognize
if err != nil {
panic(err)
}
freader.Close()
}
You can't. Packages with internal as part of their name can't be used by any package outside of the same repo. In other words, golang.org/x/build/internal/untar is for golang.org/x/build only, and the compiler won't allow you to reference it from anywhere else.
You can, if you really like, copy the source of that package into a new file, and use it from there, but you can't use it directly. This gives the developers of the internal untar package complete freedom to break their interface without having to worry about other users.

Capture the screen in Go?

Is there is a cross-platform way to capture the screen in Google's Go? Or any way for that matter, but cross platform would be preferred.
Now there is:
https://github.com/vova616/screenshot
go get github.com/vova616/screenshot
Example:
package main
import "github.com/vova616/screenshot"
func main() {
img, err := screenshot.CaptureScreen() // *image.RGBA
myImg := image.Image(img) // can cast to image.Image, but not necessary
}
If you need macOS support as well (until the pull request is merged), get:
https://github.com/kesarion/screenshot
Unfortunately, there is no library to do this. There are a couple of bindings for magickwand (C programming language and the ImageMagick image processing libraries), see http://go-lang.cat-v.org/library-bindings but these are incomplete and do not have the screen capture feature.
Meanwhile as GeertJohan suggested, you can use os.exec to run an external program and capture the screen (see sample code below). For example, you can use import command from imagemagick to capture screen (should work on a platform that can run imagemagick)
package main
import (
"bytes"
"fmt"
"log"
"os/exec"
)
func main() {
var buf bytes.Buffer
path, err := exec.LookPath("import")
if err != nil {
log.Fatal("import not installed !")
}
fmt.Printf("import is available at %s\n", path)
cmd := exec.Command("import", "-window", "root", "root.png")
cmd.Stdout = &buf
cmd.Stderr = &buf
err = cmd.Run()
if err != nil {
panic(err)
}
fmt.Println(buf.String())
}
I don't know of any cross-platform library, but you can do this with the xgbutil library when an X server is present. You can see an example of how to capture a screenshot here.
If you wanted to get this working on Mac/Windows systems, I'd probably start by examining the source for go.wde, which includes backends for Windows and Mac. I doubt you'll directly find code to capture a screenshot in there, but it might give you some hints or a path to follow.
There is no cross-platform way to capture the screen in Google's Go, because capturing screen relies on a specific API of underlying operating systems. But there are libraries for Go that do this.
For example https://github.com/vova616/screenshot
This library seems to meet your needs:
https://godoc.org/github.com/kbinani/screenshot
captures screen-shot image as image.RGBA. Mac, Windows, Linux, FreeBSD, OpenBSD, NetBSD, and Solaris are supported.
func Capture(x, y, width, height int) (*image.RGBA, error)
func CaptureDisplay(displayIndex int) (*image.RGBA, error)
func CaptureRect(rect image.Rectangle) (*image.RGBA, error)
func GetDisplayBounds(displayIndex int) image.Rectangle
func NumActiveDisplays() int
I cannot find a library to do this. Stable cross-platform screen-capturing requires a lot of work. Screen capturing requires interfacing with the operating systems' display manager/server or frame-buffer, which is different for a lot of operating systems and Linux distributions. You would have to write interfaces for each OS API (or wrap the libraries that provide the functionality), and then abstract all the different methods in a single package so it works cross-platform.
Another way to do this would be to run a existing screen capture application (command-line) to do the screen-capture work for you, including saving to a file. Then read the file in your go application. To make a Go application run a third-party application, use the os/exec package, it is in the standard library. For Linux you might use fbgrab to save the frame-buffer to a png file.
It is. It's a 2-step process:
Study https://github.com/ShareX/ShareX/tree/master/ShareX.ScreenCaptureLib to see which win32 API calls to make to capture the screen/window
Translate that logic to Go. You can use one of few existing win32 api Go bindings (e.g. https://github.com/AllenDang/w32). If they're missing needed functionality, you can add more wrappers.
For a native Windows solution, there is an example in C from the official Windows docs:
https://learn.microsoft.com/en-gb/windows/win32/gdi/capturing-an-image
Now, from this example, this is the code using Windigo library:
package main
import (
"runtime"
"unsafe"
"github.com/rodrigocfd/windigo/win"
"github.com/rodrigocfd/windigo/win/co"
)
func main() {
runtime.LockOSThread()
cxScreen := win.GetSystemMetrics(co.SM_CXSCREEN)
cyScreen := win.GetSystemMetrics(co.SM_CYSCREEN)
hdcScreen := win.HWND(0).GetDC()
defer win.HWND(0).ReleaseDC(hdcScreen)
hBmp := hdcScreen.CreateCompatibleBitmap(cxScreen, cyScreen)
defer hBmp.DeleteObject()
hdcMem := hdcScreen.CreateCompatibleDC()
defer hdcMem.DeleteDC()
hBmpOld := hdcMem.SelectObjectBitmap(hBmp)
defer hdcMem.SelectObjectBitmap(hBmpOld)
hdcMem.BitBlt(
win.POINT{X: 0, Y: 0},
win.SIZE{Cx: cxScreen, Cy: cyScreen},
hdcScreen,
win.POINT{X: 0, Y: 0},
co.ROP_SRCCOPY,
)
bi := win.BITMAPINFO{
BmiHeader: win.BITMAPINFOHEADER{
BiWidth: cxScreen,
BiHeight: cyScreen,
BiPlanes: 1,
BiBitCount: 32,
BiCompression: co.BI_RGB,
},
}
bi.BmiHeader.SetBiSize()
bmpObj := win.BITMAP{}
hBmp.GetObject(&bmpObj)
bmpSize := bmpObj.CalcBitmapSize(bi.BmiHeader.BiBitCount)
rawMem := win.GlobalAlloc(co.GMEM_FIXED|co.GMEM_ZEROINIT, bmpSize)
defer rawMem.GlobalFree()
bmpSlice := rawMem.GlobalLock(int(bmpSize))
defer rawMem.GlobalUnlock()
hdcScreen.GetDIBits(hBmp, 0, int(cyScreen), bmpSlice, &bi, co.DIB_RGB_COLORS)
bfh := win.BITMAPFILEHEADER{}
bfh.SetBfType()
bfh.SetBfOffBits(uint32(unsafe.Sizeof(bfh) + unsafe.Sizeof(bi.BmiHeader)))
bfh.SetBfSize(bfh.BfOffBits() + uint32(bmpSize))
fo, _ := win.FileOpen("C:\\users\\rodrigo\\desktop\\a.bmp", co.FILE_OPEN_RW_OPEN_OR_CREATE)
defer fo.Close()
fo.Write(bfh.Serialize())
fo.Write(bi.BmiHeader.Serialize())
fo.Write(bmpSlice)
println("Done")
}

List of currently running process in golang, Windows version

How can I get the list of currently running processes in golang under Windows?
I need something like:
List of currently running process in Golang
but usable under Windows too.
I just implemented the function you need (EnumProcess as axw stated above).
Check out https://github.com/AllenDang/w32. You might want to wait until my pull request is through :)
An example on how to use: https://gist.github.com/3083408
You need to use the Windows API function EnumProcesses. The syscall package on Windows enables you load arbitrary DLLs and their functions (i.e. via LoadLibrary/GetProcAddress). So you can get at EnumProcesses in psapi.dll. This gives you a list of PIDs; you can then use OpenProcess and EnumProcessModules to get the process name.
It's possible that someone has already done the work to implement this, but I don't know of anything. If you can't find anything, take a look at the syscall package's source (say, src/pkg/syscall/zsyscall_windows_386.go) and do something similar to what's done for the other Windows API functions.
according to the syscall package docs: This package is locked down. Code outside the standard Go repository should be migrated to use the corresponding package in the golang.org/x/sys repository.
You can use golang.org/x/sys/windows, it has Process32First and Process32Next to let enumerate system processes.
This seems to do it:
package main
import "golang.org/x/sys/windows"
// unsafe.Sizeof(windows.ProcessEntry32{})
const processEntrySize = 568
func main() {
h, e := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
if e != nil {
panic(e)
}
p := windows.ProcessEntry32{Size: processEntrySize}
for {
e := windows.Process32Next(h, &p)
if e != nil { break }
s := windows.UTF16ToString(p.ExeFile[:])
println(s)
}
}
https://pkg.go.dev/golang.org/x/sys/windows#CreateToolhelp32Snapshot
The code is cleaner if you use Windigo (error checking omitted for brevity):
package main
import (
"fmt"
"github.com/rodrigocfd/windigo/win"
"github.com/rodrigocfd/windigo/win/co"
)
func main() {
pids, _ := win.EnumProcesses()
for _, pid := range pids {
hSnap, _ := win.CreateToolhelp32Snapshot(co.TH32CS_SNAPMODULE, pid)
defer hSnap.CloseHandle()
hSnap.EnumModules(func(me32 *win.MODULEENTRY32) {
fmt.Printf("PID: %d, %s # %s\n",
me32.Th32ProcessID, me32.SzModule(), me32.SzExePath())
})
}
}
Or if you just want the processes, without the modules:
package main
import (
"fmt"
"github.com/rodrigocfd/windigo/win"
"github.com/rodrigocfd/windigo/win/co"
)
func main() {
pids, _ := win.EnumProcesses()
for _, pid := range pids {
hSnap, _ := win.CreateToolhelp32Snapshot(co.TH32CS_SNAPPROCESS, pid)
defer hSnap.CloseHandle()
hSnap.EnumProcesses(func(pe32 *win.PROCESSENTRY32) {
fmt.Printf("PID: %d # %s\n",
pe32.Th32ProcessID, pe32.SzExeFile())
})
}
}

List of currently running process in Go

How can I get the list of currently running processes in Go?
The OS package provides some functions: http://golang.org/pkg/os/
but doesn't give anything to see the list of running processes.
There is no such function in the standard library and most likely never will be.
In most cases, the list of processes isn't required by programs. Go programs usually want to wait for a single or a smaller number of processes, not for all processes. PIDs of processes are usually obtained by other means than searching the list of all processes.
If you are on Linux, the list of processes can be obtained by reading contents of /proc directory. See question Linux API to list running processes?
This library:
github.com/mitchellh/go-ps
worked for me.
import (
ps "github.com/mitchellh/go-ps"
... // other imports here...
)
func whatever(){
processList, err := ps.Processes()
if err != nil {
log.Println("ps.Processes() Failed, are you using windows?")
return
}
// map ages
for x := range processList {
var process ps.Process
process = processList[x]
log.Printf("%d\t%s\n",process.Pid(),process.Executable())
// do os.* stuff on the pid
}
}
I suggest to use for this purpose the following library:
https://github.com/shirou/gopsutil/
Here is an example to get total processes and running ones:
package main
import (
"fmt"
"github.com/shirou/gopsutil/host"
"github.com/shirou/gopsutil/load"
)
func main() {
infoStat, _ := host.Info()
fmt.Printf("Total processes: %d\n", infoStat.Procs)
miscStat, _ := load.Misc()
fmt.Printf("Running processes: %d\n", miscStat.ProcsRunning)
}
The library allows to get several other data.
Take a look at the documentation for available informations provided according to the target operative system.
If you only need the process information, can just run "ps" command from your go code, then parse the text output.
A complete solution can refer to Exercise 29 in Book "Learning Go" # http://www.miek.nl/files/go/
you can use this library github.com/shirou/gopsutil
package main
import (
"fmt"
"github.com/shirou/gopsutil/v3/process"
)
func main() {
processes, _ := process.Processes()
for _, process := range processes {
name, _ := process.Name()
fmt.Println(name)
}
}
in this library,you can also get process info other
For linux
I found a fairly simple solution to get the list of running processes without using a large library:
package main
import (
"fmt"
"os"
"path/filepath"
)
func main() {
matches, err := filepath.Glob("/proc/*/exe")
for _, file := range matches {
target, _ := os.Readlink(file)
if len(target) > 0 {
fmt.Printf("%+v\n", target)
}
}
}
It will print the path for each running process. If you need just the process name, then you can get it with filepath.Base(target)
This works by de-referencing the symlink for the /proc/[procID]/exe file, which is a symlink to the executable file. This is much simpler than reading and extracting the process name from the /proc/[procID]/status file (as suggested in other solutions I found).
PS: This might not work on all distribution because it relies on the exe file in the process' folder, which might not present in all flavors of Linux.

Resources