GetProcessList returns a list of all processes running on the system.
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)
}
{
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
}
GetProcessInfo returns information about a specific process.
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)
{
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
}
KillProcess terminates a process given its PID.
err := system.KillProcess(2024)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
{
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
}
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.
info, err := system.GetSystemInfo()
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("OS: %s\n", info.OS)
{
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
}
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.
{
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
}
getMachineType returns the machine type, which can be BareMetal, VM or
Container. If any error occurs, it will be returned.
{
// 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
}
RunningInVM returns true if the system is running in a virtual machine,
otherwise it returns false.
if system.RunningInVM() {
fmt.Println("Running in a virtual machine")
} else {
fmt.Println("Not running in a virtual machine")
}
{
info, err := getMachineType()
if err != nil {
return false
}
return info == types.VM
}
RunningInContainer returns true if the system is running in a container,
otherwise it returns false.
if system.RunningInContainer() {
fmt.Println("Running in a container")
} else {
fmt.Println("Not running in a container")
}
{
info, err := getMachineType()
if err != nil {
return false
}
return info == types.Container
}
RunningInBareMetal returns true if the system is running on bare metal,
otherwise it returns false.
if system.RunningInBareMetal() {
fmt.Println("Running on bare metal")
} else {
fmt.Println("Not running on bare metal")
}
{
info, err := getMachineType()
if err != nil {
return false
}
return info == types.BareMetal
}
RunningInVMOrContainer returns true if the system is running in a virtual
machine or a container, otherwise it returns false.
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")
}
{
info, err := getMachineType()
if err != nil {
return false
}
return info == types.VM || info == types.Container
}
GetSupportedTimezones returns a list of supported timezones.
The list is generated by reading the contents of /usr/share/zoneinfo.
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)
}
{
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
}
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.
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)
}
{
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
}
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.
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)
}
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.
{
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
}
GetAllGroups retrieves information about all groups on the system by
parsing /etc/group.
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)
}
{
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
}
import "fmt"
import "os"
import "strconv"
import "strings"
import "github.com/vanilla-os/sdk/pkg/v1/system/types"
import "bufio"
import "fmt"
import "os"
import "strings"
import "github.com/shirou/gopsutil/host"
import "github.com/vanilla-os/sdk/pkg/v1/system/types"
import "os"
import "path/filepath"
import "strings"
import "github.com/vanilla-os/sdk/pkg/v1/system/types"
import "fmt"
import "os"
import "os/user"
import "strings"
import "github.com/vanilla-os/sdk/pkg/v1/system/types"