system API

system

package

API reference for the system package.

F
function

GetProcessList

GetProcessList returns a list of all processes running on the system.

Returns

error
pkg/v1/system/proc.go:35-57
func GetProcessList() ([]types.Process, error)

{
	var processes []types.Process

	// Read the list of files in the /proc directory
	files, err := os.ReadDir("/proc")
	if err != nil {
		return nil, fmt.Errorf("error reading /proc directory: %v", err)
	}

	// Iterate over each file in /proc and check if it's a process
	for _, file := range files {
		pid, err := strconv.Atoi(file.Name())
		if err == nil {
			// If the file name is a number, it's a process
			process, err := GetProcessInfo(pid)
			if err == nil {
				processes = append(processes, *process)
			}
		}
	}

	return processes, nil
}

Example

processes, err := system.GetProcessList()
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}

for _, process := range processes {
	fmt.Printf("PID: %d\n", process.PID)
	fmt.Printf("Name: %s\n", process.Name)
	fmt.Printf("State: %s\n", process.State)
}
F
function

GetProcessInfo

GetProcessInfo returns information about a specific process.

Parameters

pid
int

Returns

error
pkg/v1/system/proc.go:72-117
func GetProcessInfo(pid int) (*types.Process, error)

{
	process := &types.Process{PID: pid}

	// Read information from the /proc/{pid}/stat file
	statPath := fmt.Sprintf("/proc/%d/stat", pid)
	statContent, err := os.ReadFile(statPath)
	if err != nil {
		return nil, fmt.Errorf("error reading %s: %v", statPath, err)
	}

	// Extract information from /proc/{pid}/stat content
	fields := strings.Fields(string(statContent))
	if len(fields) < 24 {
		return nil, fmt.Errorf("insufficient fields in %s", statPath)
	}

	process.Name = fields[1][1 : len(fields[1])-1]
	process.State = fields[2]
	process.PPID, _ = strconv.Atoi(fields[3])
	process.Priority, _ = strconv.Atoi(fields[17])
	process.Nice, _ = strconv.Atoi(fields[18])
	process.Threads, _ = strconv.Atoi(fields[19])

	// Read information from the /proc/{pid}/status file
	statusPath := fmt.Sprintf("/proc/%d/status", pid)
	statusContent, err := os.ReadFile(statusPath)
	if err != nil {
		return nil, fmt.Errorf("error reading %s: %v", statusPath, err)
	}

	// Extract information from /proc/{pid}/status content
	lines := strings.Split(string(statusContent), "\n")
	for _, line := range lines {
		fields := strings.Fields(line)
		if len(fields) >= 2 {
			switch fields[0] {
			case "Uid:":
				process.UID, _ = strconv.Atoi(fields[1])
			case "Gid:":
				process.GID, _ = strconv.Atoi(fields[1])
			}
		}
	}

	return process, nil
}

Example

process, err := system.GetProcessInfo(1)
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}

fmt.Printf("PID: %d\n", process.PID)
fmt.Printf("Name: %s\n", process.Name)
fmt.Printf("State: %s\n", process.State)
F
function

KillProcess

KillProcess terminates a process given its PID.

Parameters

pid
int

Returns

error
pkg/v1/system/proc.go:128-140
func KillProcess(pid int) error

{
	process, err := os.FindProcess(pid)
	if err != nil {
		return fmt.Errorf("unable to find process with PID %d: %v", pid, err)
	}

	err = process.Kill()
	if err != nil {
		return fmt.Errorf("unable to kill process with PID %d: %v", pid, err)
	}

	return nil
}

Example

err := system.KillProcess(2024)
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
F
function

GetSystemInfo

GetSystemInfo returns information about the system, such as OS, version,
codename, architecture and machine type. If the machine type cannot be
determined, it will be set to BareMetal. If any error occurs, it will
be returned.

Returns

pkg/v1/system/system.go:34-58
func GetSystemInfo() (*types.SystemInfo, error)

{
	hostInfo, err := host.Info()
	if err != nil {
		return nil, err
	}

	osReleaseInfo, err := readOSRelease()
	if err != nil {
		return nil, err
	}

	machineType, err := getMachineType()
	if err != nil {
		return nil, err
	}

	info := &types.SystemInfo{
		OS:          osReleaseInfo.Name,
		Version:     osReleaseInfo.Version,
		Codename:    osReleaseInfo.Codename,
		Arch:        hostInfo.KernelArch,
		MachineType: machineType,
	}
	return info, nil
}

Example

info, err := system.GetSystemInfo()
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("OS: %s\n", info.OS)
F
function

readOSRelease

readOSRelease reads the /etc/os-release file and returns the OS name,
version and codename. In the future releases on Vanilla OS, we may
consider storing this information in a different file, perhaps using
a better format. If any error occurs, it will be returned.

Returns

pkg/v1/system/system.go:64-101
func readOSRelease() (*types.OSReleaseInfo, error)

{
	osReleaseInfo := &types.OSReleaseInfo{}

	file, err := os.Open("/etc/os-release")
	if err != nil {
		return nil, fmt.Errorf("failed to open /etc/os-release: %v", err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.SplitN(line, "=", 2)
		if len(parts) == 2 {
			key := strings.TrimSpace(parts[0])
			value := strings.Trim(strings.TrimSpace(parts[1]), "\"")

			switch key {
			case "NAME":
				osReleaseInfo.Name = value
			case "VERSION_ID":
				osReleaseInfo.Version = value
			case "VERSION_CODENAME":
				osReleaseInfo.Codename = value
			}
		}
	}

	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("error reading /etc/os-release: %v", err)
	}

	if osReleaseInfo.Name == "" || osReleaseInfo.Version == "" {
		return nil, fmt.Errorf("missing or invalid information in /etc/os-release")
	}

	return osReleaseInfo, nil
}
F
function

getMachineType

getMachineType returns the machine type, which can be BareMetal, VM or
Container. If any error occurs, it will be returned.

Returns

pkg/v1/system/system.go:105-127
func getMachineType() (types.MachineType, error)

{
	// There are many ways to check if the system is running in a container,
	// we have to check multiple methods to be sure.

	// Check if /run/.containerenv file exists
	if _, err := os.Stat("/run/.containerenv"); err == nil {
		return types.Container, nil
	}

	// Check if /.dockerenv file exists
	if _, err := os.Stat("/.dockerenv"); err == nil {
		return types.Container, nil
	}

	// Check if hypervisor information is present in /proc/cpuinfo
	cpuInfo, err := os.ReadFile("/proc/cpuinfo")
	if err == nil && strings.Contains(string(cpuInfo), "hypervisor") {
		return types.VM, nil
	}

	// No clear indication of VM or Container, assuming BareMetal
	return types.BareMetal, nil
}
F
function

RunningInVM

RunningInVM returns true if the system is running in a virtual machine,
otherwise it returns false.

Returns

bool
pkg/v1/system/system.go:139-145
func RunningInVM() bool

{
	info, err := getMachineType()
	if err != nil {
		return false
	}
	return info == types.VM
}

Example

if system.RunningInVM() {
	fmt.Println("Running in a virtual machine")
} else {
	fmt.Println("Not running in a virtual machine")
}
F
function

RunningInContainer

RunningInContainer returns true if the system is running in a container,
otherwise it returns false.

Returns

bool
pkg/v1/system/system.go:157-163
func RunningInContainer() bool

{
	info, err := getMachineType()
	if err != nil {
		return false
	}
	return info == types.Container
}

Example

if system.RunningInContainer() {
	fmt.Println("Running in a container")
} else {
	fmt.Println("Not running in a container")
}
F
function

RunningInBareMetal

RunningInBareMetal returns true if the system is running on bare metal,
otherwise it returns false.

Returns

bool
pkg/v1/system/system.go:175-181
func RunningInBareMetal() bool

{
	info, err := getMachineType()
	if err != nil {
		return false
	}
	return info == types.BareMetal
}

Example

if system.RunningInBareMetal() {
	fmt.Println("Running on bare metal")
} else {
	fmt.Println("Not running on bare metal")
}
F
function

RunningInVMOrContainer

RunningInVMOrContainer returns true if the system is running in a virtual
machine or a container, otherwise it returns false.

Returns

bool
pkg/v1/system/system.go:193-199
func RunningInVMOrContainer() bool

{
	info, err := getMachineType()
	if err != nil {
		return false
	}
	return info == types.VM || info == types.Container
}

Example

if system.RunningInVMOrContainer() {
	fmt.Println("Running in a virtual machine or a container")
} else {
	fmt.Println("Not running in a virtual machine or a container")
}
F
function

GetSupportedTimezones

GetSupportedTimezones returns a list of supported timezones.
The list is generated by reading the contents of /usr/share/zoneinfo.

Returns

error
pkg/v1/system/timezone.go:33-66
func GetSupportedTimezones() ([]types.Timezone, error)

{
	const zoneinfoPath = "/usr/share/zoneinfo"

	files, err := os.ReadDir(zoneinfoPath)
	if err != nil {
		return nil, err
	}

	var supportedTimezones []types.Timezone

	for _, file := range files {
		timezonePath := filepath.Join(zoneinfoPath, file.Name())

		if file.IsDir() {
			subregionFiles, err := os.ReadDir(timezonePath)
			if err != nil {
				return nil, err
			}

			for _, subregionFile := range subregionFiles {
				subregion := strings.Join([]string{file.Name(), subregionFile.Name()}, "/")

				timezone := types.Timezone{
					Name:     subregion,
					Location: filepath.Join("/usr/share/zoneinfo", subregion),
				}

				supportedTimezones = append(supportedTimezones, timezone)
			}
		}
	}

	return supportedTimezones, nil
}

Example

timezones, err := system.GetSupportedTimezones()
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
for _, timezone := range timezones {
	fmt.Printf("%s\n", timezone.Name)
	fmt.Printf("%s\n", timezone.Location)
}
F
function

GetAllUsers

GetAllUsers retrieves information about all users on the system by
parsing /etc/passwd. If includeNoLogin is true, users with /usr/sbin/nologin
as their shell will be included.

Parameters

includeNoLogin
bool

Returns

error
pkg/v1/system/users.go:36-82
func GetAllUsers(includeNoLogin bool) ([]types.UserInfo, error)

{
	etcPasswd, err := os.ReadFile("/etc/passwd")
	if err != nil {
		return nil, fmt.Errorf("error reading /etc/passwd: %v", err)
	}

	var users []types.UserInfo

	for _, line := range strings.Split(string(etcPasswd), "\n") {
		if line == "" {
			continue
		}

		items := strings.Split(line, ":")
		if len(items) < 7 {
			continue
		}

		username := items[0]
		hasLogin := items[6] != "/usr/sbin/nologin"
		shell := items[6]

		if username == "root" || username == "sync" {
			continue
		}

		if !includeNoLogin && !hasLogin {
			continue
		}

		u, err := user.Lookup(username)
		if err != nil {
			return nil, fmt.Errorf("error looking up user %s: %v", u, err)
		}

		users = append(users, types.UserInfo{
			UID:      u.Uid,
			GID:      u.Gid,
			Username: u.Username,
			Name:     u.Name,
			HomeDir:  u.HomeDir,
			Shell:    shell,
		})
	}

	return users, nil
}

Example

users, err := system.GetAllUsers(false)
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}

for _, user := range users {
	fmt.Printf("UID: %s\n", user.UID)
	fmt.Printf("Username: %s\n", user.Username)
}
F
function

GetUsers

GetUsers retrieves users with the given usernames and UIDs. If useUID is
true, the returned map will use the UID as the key, otherwise it will use
the username as the key.

Parameters

usernames
[]string
uids
[]string
useUID
bool

Returns

map[string]types.UserInfo
pkg/v1/system/users.go:106-172
func GetUsers(usernames []string, uids []string, useUID bool) map[string]types.UserInfo

{
	usersMap := make(map[string]types.UserInfo)

	for _, username := range usernames {
		u, err := user.Lookup(username)
		if err != nil {
			// If the user doesn't exist, continue
			continue
		}

		key := u.Username
		if useUID {
			key = u.Uid
		}

		usersMap[key] = types.UserInfo{
			UID:      u.Uid,
			GID:      u.Gid,
			Username: u.Username,
			Name:     u.Name,
			HomeDir:  u.HomeDir,
			Shell:    u.HomeDir,
		}
	}

	for _, uid := range uids {
		u, err := user.LookupId(uid)
		if err != nil {
			// If the user doesn't exist, continue
			continue
		}

		// Check if the user already exists from the previous loop, since the
		// developer may have passed in a username and UID that refer to the
		// same user
		exists := false
		for _, user := range usersMap {
			if useUID && user.UID == u.Uid {
				exists = true
				break
			} else if !useUID && user.Username == u.Username {
				exists = true
				break
			}
		}

		if exists {
			continue
		}

		key := uid
		if !useUID {
			key = u.Username
		}

		usersMap[key] = types.UserInfo{
			UID:      u.Uid,
			GID:      u.Gid,
			Username: u.Username,
			Name:     u.Name,
			HomeDir:  u.HomeDir,
			Shell:    u.HomeDir,
		}
	}

	return usersMap
}

Example

users, err := system.GetUsers([]string{"john", "jane"}, []string{"1000"})
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}

for _, user := range users {
	fmt.Printf("UID: %s\n", user.UID)
	fmt.Printf("Username: %s\n", user.Username)
}

Notes

if a username and UID refer to the same user, the user will only be
returned once. If an user does not match any of the given usernames or UIDs,
it will not be available in the returned map.

F
function

GetAllGroups

GetAllGroups retrieves information about all groups on the system by
parsing /etc/group.

Returns

error
pkg/v1/system/users.go:189-221
func GetAllGroups() ([]types.GroupInfo, error)

{
	etcGroup, err := os.ReadFile("/etc/group")
	if err != nil {
		return nil, fmt.Errorf("error reading /etc/group: %v", err)
	}

	var groups []types.GroupInfo

	for _, line := range strings.Split(string(etcGroup), "\n") {
		if line == "" {
			continue
		}

		items := strings.Split(line, ":")
		if len(items) < 3 {
			continue
		}

		gid := items[2]
		name := items[0]

		if name == "root" {
			continue
		}

		groups = append(groups, types.GroupInfo{
			GID:  gid,
			Name: name,
		})
	}

	return groups, nil
}

Example

groups, err := system.GetGroups()
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}

for _, group := range groups {
	fmt.Printf("GID: %s\n", group.GID)
	fmt.Printf("Name: %s\n", group.Name)
}