About
This post takes a look at the unix package and how it can be used to make system calls on FreeBSD in Go.
Background
The FreeBSD kernel implements hundreds of system calls and they allow userspace programs to ask the kernel to execute an operation on their behalf. For example, the write(2) system call allows a userspace program to write data to a file descriptor, and the read(2) system call allows a userspace program to read data from a file descriptor, and so on.
Most of the time we're not going to be interested in binding to system calls like read(2) or write(2), but instead we might be interested in system calls more specific to FreeBSD that may not have extensive support in the Go standard library or beyond. For example, I recently worked on a project where I used the jail_get(2) and jail_set(2) system calls to build a high level abstraction around FreeBSD jails. And I wanted to document what I learned in the process when it comes to the unix package and system calls in Go in general.
Experiment
Overview
For this example we will use getpid(2). It does not take any arguments. When we call unix.Syscall, the first value is the trap number (the syscall number), which is 20 for getpid(2) on FreeBSD. The next three values are syscall arguments, and since getpid(2) takes none, we pass 0, 0, and 0. unix.Syscall returns three values: the syscall return value, a second return value that is usually ignored, and errno in case the syscall fails:
package main
import (
"fmt"
"log"
"golang.org/x/sys/unix"
)
var (
sysGetPid = uintptr(20)
)
func main() {
pid, _, errno := unix.Syscall(sysGetPid, 0, 0, 0)
if errno != 0 {
log.Fatalf("%v", errno)
}
fmt.Printf("The process ID is %d\n", pid)
}
Explanation
-
sysGetPid = uintptr(20)
This line defines the system call number for getpid(2) -
pid, _, errno := unix.Syscall(sysGetPid, 0, 0, 0)
This line executes the system call. A breakdown:-
pid
This variable holds the primary return value from the system call. -
_
This variable holds a secondary return value, and it is usually not used. -
errno
This variable holds the syscall error code returned by unix.Syscall.
-
-
if errno != 0 { log.Fatalf("%v", errno) }
This line confirms whether the system call was successful or not. -
fmt.Printf("The process ID is %d\n", pid)
Prints the process ID whenerrno == 0.
Related
-
unix.Syscall6
This function is used when the system call takes 6 arguments instead of 3.