System programming in Go – 1

System Programming Part 1

System programming is one of the most fun aspects of programming, and many developers like Terry Davis (God bless his soul) gave their lives to tweaking compilers, debuggers, and basic operating systems by themselves. Yes, he made a complete functioning Operating System on his own, and he is also known for creating specialized file formats (such as a text document that could dynamically display small animations). And therefore in the same vein, we shall start our journey into System Programming with the programming language of the next decade, Go. So let’s get started with Part 1 of system programming in Golang.

System Programming – a definition

So what exactly IS system programming? It may be defined as the programming of the computer system software, i.e, instead of providing services to the end-user (like you and me), it provides services to “other” software, called Application Software.

Two defining characteristics that differentiate system software from an application software(like MS word or a PC game) is that:

  • It does not interact with the user directly.
  • It is much closer to the hardware(this is called hardware aware). This means that it knows how the hardware works and is built to improve performance rather than comfort.
  • It has full control of memory allocation.

Important Packages for Go System Programming

The single most important package that allows you to manipulate files and directories as entities is the os package, which we will use extensively.

If you consider files as boxes with contents, the os package allows you to move them, put them into the wastebasket, change their names, visit them, and decide which ones you want to use, whereas the io package allows you to manipulate the contents of a box without worrying too much about the box itself.

The flag package, lets you define and process your own flags and manipulate the command-line arguments of a Go program.
The filepath package is extremely handy as it includes the filepath.Walk() function that allows you to traverse entire directory structures in an easy way.

Code a simple function

So as a way to start off with coding some basic filesystem operations that interact with the Linux OS API, we shall make a simple pwd.go file. If you’re familiar with terminal commands, then you already know that pwd command fetches the full path of the directory where the executing script resides.

First, we import the necessary packages:

package main

import (
	"fmt"
	"os"
	"path/filepath"
)

Our main function will be something like this, which is explained below:

func main() {
	arguments := os.Args
	pwd, err := os.Getwd()
	if err != nil {
		fmt.Println("Error:", err)
	}
	if len(arguments) == 1 {
		return
	}
	if arguments[1] != "-P" {
		return
	}
	fileinfo, err := os.Lstat(pwd)
	if fileinfo.Mode()&os.ModeSymlink != 0 {
		realpath, err := filepath.EvalSymlinks(pwd)
		if err != nil {
			log.Println(err)
		}
		fmt.Println(realpath)
	}
}
  • The os.Args by default takes the immediate executing-script directory. However, it is possible to specify a file when we go run.
  • Getwd returns a rooted path name corresponding to the current directory.
  • The os.Lstat function just gives the file path info.
  • A FileMode represents a file’s mode and permission bits. ModeSymlink defines a symbolic link.

If we run pwd.go, we get:

Pwd Go Filepath
Pwd Go Filepath

Next, we’ll look into permissions. Let’s make a file permissions.go, and what we want to do is print the permissions of a file or directory using Go.

func main() {
	arguments := os.Args
	if len(arguments) == 1 {
		fmt.Println("Please provide an argument!")
		os.Exit(1)
	}
	file := arguments[1]
	info, err := os.Stat(file)
	if err != nil {
		fmt.Println("Error:", err)
		os.Exit(1)
	}
	mode := info.Mode()
	fmt.Print(file, ": ", mode, "\n")
}
  • Most of the code handles the command line argument (don’t forget to give an argument).
  • The os.Stat function is used to get the fileInfo for the file
  • The permissions are extracted using Mode() method.

Here is the output of the two scenarios – without and with arguments

Permissions Go File
Permissions Go File

You must be getting the pattern of this method. Try to look through the os.Stat package for more interesting things to fetch.

We’ll be going over some i/o operations in the next part, so get on to that. Check out System Programming in Go Part – 2 for i/o operations.

Extra Discussions

It is important to understand and clarify a few concepts, mainly for beginners.

Like, is Go a high level or low-level language like C?

First – Go is more a server language, not a system language. Second – C is not a low level language, it is a high level language with some degree of access to low-level programming functions.

References

  1. Hands-On System Programming with Go: Build modern and concurrent applications for Unix and Linux systems using Golang, by Alex Guerrieri
  2. Go Systems Programming: Master Linux and Unix system level programming with Go,by Mihalis Tsoukalos
  3. https://pkg.go.dev/os