Skip to main content
โšก Calmops

Operating System Interfaces in Go

Operating System Interfaces in Go

Introduction

Go provides powerful interfaces to interact with the operating system. This guide covers system calls, environment variables, and cross-platform programming.

Understanding OS interfaces enables you to build system utilities, manage processes, and interact with system resources effectively.

Environment Variables

Basic Environment Operations

package main

import (
	"fmt"
	"os"
	"strings"
)

// GetEnv gets an environment variable
func GetEnv(key string) string {
	return os.Getenv(key)
}

// GetEnvOrDefault gets environment variable with default
func GetEnvOrDefault(key, defaultValue string) string {
	if value, exists := os.LookupEnv(key); exists {
		return value
	}
	return defaultValue
}

// SetEnv sets an environment variable
func SetEnv(key, value string) error {
	return os.Setenv(key, value)
}

// UnsetEnv unsets an environment variable
func UnsetEnv(key string) error {
	return os.Unsetenv(key)
}

// GetAllEnv gets all environment variables
func GetAllEnv() map[string]string {
	env := make(map[string]string)

	for _, pair := range os.Environ() {
		parts := strings.SplitN(pair, "=", 2)
		if len(parts) == 2 {
			env[parts[0]] = parts[1]
		}
	}

	return env
}

// Example usage
func EnvironmentExample() {
	// Get environment variable
	home := GetEnv("HOME")
	fmt.Printf("Home: %s\n", home)

	// Get with default
	debug := GetEnvOrDefault("DEBUG", "false")
	fmt.Printf("Debug: %s\n", debug)

	// Set environment variable
	SetEnv("MY_VAR", "my_value")

	// Get all environment variables
	allEnv := GetAllEnv()
	fmt.Printf("Total env vars: %d\n", len(allEnv))
}

Good: Proper OS Interface Implementation

package main

import (
	"fmt"
	"os"
	"os/signal"
	"os/user"
	"path/filepath"
	"runtime"
	"syscall"
	"time"
)

// OSInfo provides OS information
type OSInfo struct {
	OS       string
	Arch     string
	Hostname string
	User     string
	HomeDir  string
	TempDir  string
}

// GetOSInfo gets OS information
func GetOSInfo() (*OSInfo, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, err
	}

	currentUser, err := user.Current()
	if err != nil {
		return nil, err
	}

	return &OSInfo{
		OS:       runtime.GOOS,
		Arch:     runtime.GOARCH,
		Hostname: hostname,
		User:     currentUser.Username,
		HomeDir:  currentUser.HomeDir,
		TempDir:  os.TempDir(),
	}, nil
}

// ProcessInfo provides process information
type ProcessInfo struct {
	PID       int
	PPID      int
	Args      []string
	Env       map[string]string
	WorkDir   string
	StartTime time.Time
}

// GetProcessInfo gets current process information
func GetProcessInfo() *ProcessInfo {
	env := make(map[string]string)
	for _, pair := range os.Environ() {
		parts := strings.SplitN(pair, "=", 2)
		if len(parts) == 2 {
			env[parts[0]] = parts[1]
		}
	}

	wd, _ := os.Getwd()

	return &ProcessInfo{
		PID:     os.Getpid(),
		PPID:    os.Getppid(),
		Args:    os.Args,
		Env:     env,
		WorkDir: wd,
	}
}

// SignalHandler handles OS signals
type SignalHandler struct {
	signals chan os.Signal
	done    chan bool
}

// NewSignalHandler creates a new signal handler
func NewSignalHandler() *SignalHandler {
	return &SignalHandler{
		signals: make(chan os.Signal, 1),
		done:    make(chan bool),
	}
}

// Handle handles signals
func (sh *SignalHandler) Handle(callback func(os.Signal)) {
	signal.Notify(sh.signals, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		for {
			select {
			case sig := <-sh.signals:
				callback(sig)
			case <-sh.done:
				return
			}
		}
	}()
}

// Stop stops signal handling
func (sh *SignalHandler) Stop() {
	signal.Stop(sh.signals)
	close(sh.done)
}

// FilePermissions manages file permissions
type FilePermissions struct {
	path string
}

// NewFilePermissions creates new file permissions manager
func NewFilePermissions(path string) *FilePermissions {
	return &FilePermissions{path: path}
}

// GetPermissions gets file permissions
func (fp *FilePermissions) GetPermissions() (os.FileMode, error) {
	info, err := os.Stat(fp.path)
	if err != nil {
		return 0, err
	}

	return info.Mode().Perm(), nil
}

// SetPermissions sets file permissions
func (fp *FilePermissions) SetPermissions(mode os.FileMode) error {
	return os.Chmod(fp.path, mode)
}

// IsReadable checks if file is readable
func (fp *FilePermissions) IsReadable() bool {
	_, err := os.Open(fp.path)
	return err == nil
}

// IsWritable checks if file is writable
func (fp *FilePermissions) IsWritable() bool {
	info, err := os.Stat(fp.path)
	if err != nil {
		return false
	}

	return info.Mode().Perm()&0200 != 0
}

// IsExecutable checks if file is executable
func (fp *FilePermissions) IsExecutable() bool {
	info, err := os.Stat(fp.path)
	if err != nil {
		return false
	}

	return info.Mode().Perm()&0111 != 0
}

// Example usage
func OSInterfaceExample() {
	// Get OS info
	osInfo, _ := GetOSInfo()
	fmt.Printf("OS: %s/%s\n", osInfo.OS, osInfo.Arch)
	fmt.Printf("User: %s\n", osInfo.User)

	// Get process info
	procInfo := GetProcessInfo()
	fmt.Printf("PID: %d\n", procInfo.PID)

	// Handle signals
	handler := NewSignalHandler()
	handler.Handle(func(sig os.Signal) {
		fmt.Printf("Received signal: %v\n", sig)
	})

	// Check file permissions
	perms := NewFilePermissions("test.txt")
	readable := perms.IsReadable()
	fmt.Printf("Readable: %v\n", readable)
}

Bad: Improper OS Interface Usage

package main

// BAD: No error handling
func BadGetEnv(key string) string {
	return os.Getenv(key) // Could be empty
}

// BAD: No signal handling
func BadSignalHandling() {
	// No signal handling
	// Process can't be interrupted gracefully
}

// BAD: No permission checking
func BadFileAccess(path string) {
	// No permission checks
	// Could fail at runtime
}

Problems:

  • No error handling
  • No signal handling
  • No permission checking
  • No cross-platform consideration

Cross-Platform Programming

package main

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

// GetConfigDir gets platform-specific config directory
func GetConfigDir() string {
	if runtime.GOOS == "windows" {
		return filepath.Join(os.Getenv("APPDATA"), "MyApp")
	}

	home, _ := os.UserHomeDir()
	return filepath.Join(home, ".config", "myapp")
}

// GetDataDir gets platform-specific data directory
func GetDataDir() string {
	if runtime.GOOS == "windows" {
		return filepath.Join(os.Getenv("APPDATA"), "MyApp", "Data")
	}

	home, _ := os.UserHomeDir()
	return filepath.Join(home, ".local", "share", "myapp")
}

// GetLogDir gets platform-specific log directory
func GetLogDir() string {
	if runtime.GOOS == "windows" {
		return filepath.Join(os.Getenv("APPDATA"), "MyApp", "Logs")
	}

	return "/var/log/myapp"
}

// GetExecutablePath gets executable path
func GetExecutablePath() (string, error) {
	return os.Executable()
}

// GetWorkingDirectory gets working directory
func GetWorkingDirectory() (string, error) {
	return os.Getwd()
}

// ChangeWorkingDirectory changes working directory
func ChangeWorkingDirectory(path string) error {
	return os.Chdir(path)
}

Best Practices

1. Always Check Errors

if err := os.Setenv(key, value); err != nil {
	return err
}

2. Handle Signals Gracefully

handler := NewSignalHandler()
handler.Handle(func(sig os.Signal) {
	// Cleanup
})

3. Use Cross-Platform Paths

path := filepath.Join(homeDir, ".config", "app")

4. Check Permissions

perms := NewFilePermissions(path)
if !perms.IsReadable() {
	return fmt.Errorf("file not readable")
}

Common Pitfalls

1. No Error Handling

Always check errors from OS operations.

2. Platform Assumptions

Don’t assume Unix-like environment.

3. No Signal Handling

Handle signals for graceful shutdown.

4. Hardcoded Paths

Use platform-specific path functions.

Resources

Summary

Proper OS interface usage is essential. Key takeaways:

  • Handle environment variables safely
  • Implement signal handling
  • Check file permissions
  • Use cross-platform paths
  • Handle errors properly
  • Support multiple platforms
  • Test on target platforms

By mastering OS interfaces, you can build robust system utilities.

Comments