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

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.

Related

How to check if a public go function/struct is not used outside of the package?

Is there a way to check if a public function/struct is used outside of the package in which it's declared? I'm not writing a public go module that's consumed anywhere else, and simply want to scan whether func Foo() it's used anywhere in my codebase outside of the package in which it's declared.
I'm using GoLand but any programmatic solution would do.
Simplest solution: manually rename Foo() to Foo2(). Build/compile your project: if there are no compilation errors, it's not referenced in your code. Same check also works with any identifiers and with any IDEs (this doesn't use any of the IDE's features).
Obviously if you already have a Foo2 identifier, this will fail. But the idea is to rename it to a non-existing identifier...
You can scan a particular package to see all the available function in it.
In this main.go, app the root package name and there is another package in database directory under the package name database.
By running the code you will found all the function name available inside database package
package main
import (
"fmt"
"app/database"
"go/ast"
"go/parser"
"go/token"
"os"
)
// Name of the package you want to scan
const subPackage = "database"
func main() {
set := token.NewFileSet()
packs, err := parser.ParseDir(set, subPackage, nil, 0)
if err != nil {
fmt.Println("Failed to parse package:", err)
os.Exit(1)
}
funcs := []*ast.FuncDecl{}
for _, pack := range packs {
for _, f := range pack.Files {
for _, d := range f.Decls {
if fn, isFn := d.(*ast.FuncDecl); isFn {
funcs = append(funcs, fn)
}
}
}
}
fmt.Println("All the functions in the package:",subPackage)
for _, fn := range funcs {
fmt.Println(fn.Name.Name)
}
// database Package is called/used
database.Connection()
}
This will get all function declarations in the stated subpackage as an ast.FuncDecl. This isn't an invokable function; it's just a representation of its source code of it.
If you wanted to do anything like call these functions, you'd have to do something more sophisticated. After gathering these functions, you could gather them and output a separate file that calls each of them, then run the resulting file.

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.

Does go provide variable sanitization?

I am a beginner in Golang.
I have a problem with variable type assigning from user input.
When the user enters data like "2012BV352" I need to be able to ignore the BV and pass 2012352 to my next function.
There has a package name gopkg.in/validator.v2 in doc
But what it returns is whether or not the variable is safe or not.
I need to cut off the unusual things.
Any idea on how to achieve this?
You could write your own sanitizing methods and if it becomes something you'll be using more often, I'd package it out and add other methods to cover more use cases.
I provide two different ways to achieve the same result. One is commented out.
I haven't run any benchmarks so i couldn't tell you for certain which is more performant, but you could write your own tests if you wanted to figure it out. It would also expose another important aspect of Go and in my opinion one of it's more powerful tools... testing.
package main
import (
"fmt"
"log"
"regexp"
"strconv"
"strings"
)
// using a regex here which simply targets all digits and ignores everything else. I make it a global var and use MustCompile because the
// regex doesn't need to be created every time.
var extractInts = regexp.MustCompile(`\d+`)
func SanitizeStringToInt(input string) (int, error) {
m := extractInts.FindAllString(input, -1)
s := strings.Join(m, "")
return strconv.Atoi(s)
}
/*
// if you didn't want to use regex you could use a for loop
func SanitizeStringToInt(input string) (int, error) {
var s string
for _, r := range input {
if !unicode.IsLetter(r) {
s += string(r)
}
}
return strconv.Atoi(s)
}
*/
func main() {
a := "2012BV352"
n, err := SanitizeStringToInt(a)
if err != nil {
log.Fatal(err)
}
fmt.Println(n)
}

parse the fragment and find all top level definitions

The service I'm writing receive code snippets and process them, the snippets could either be complement program or a fragment, if it is a fragment, I need to add the enclosing main function. For example, the snippet:
var v int
v = 3
fmt.Println(v)
should be classified as a fragment, and add main to it:
func main() {
var v int
v = 3
fmt.Println(v)
}
If the snippet is:
package main
import "fmt"
func main() {
fmt.Println("hello")
}
then no modification should be done.
The way I'm doing now is run the go parser against the snippet:
var fset *token.FileSet
file, err := parser.ParseFile(fset, "stdin", code, 0)
if err != nil {
// add function
code = fmt.Sprintf("func main() {\n%s\n}", code)
// ...
}
This works for the 1st snippet above, however it fails if the fragment has a main function after some other declarations, e.g.
type S struct {
a int
}
func main() {
fmt.Println("foo")
}
I also try to look into the file returned by ParseFile, check the Decls, but it looks it will stop parsing after the 1st error, so Decls is nil in this case. So my question is is there a robust way to handle this?
PS. The inclusion of package clause and the required imports are not relevant because I'm feeding the processed code into golang.org/x/tools/imports anyway.
The dumbest thing that might work is after reading in the file (to a buffer most likely), is to do a string search for func main(){.
If it isn't formatted already, you might need to change that to a regex with whitespaces, but it should be pretty straight-forward.

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())
})
}
}

Resources