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

Related