Compare commits

...

3 Commits

Author SHA1 Message Date
74868ab2d3 Implement 'ectl sv list' 2025-03-12 15:39:06 +02:00
4b37214113 Modify 'ectl sv status' 2025-03-12 15:38:16 +02:00
ce34b7b21a Improve filesystem mounting 2025-03-12 12:15:23 +02:00
7 changed files with 241 additions and 51 deletions

View File

@ -2,7 +2,4 @@ module ectl
go 1.23.4 go 1.23.4
require ( require gopkg.in/yaml.v3 v3.0.1
gopkg.in/yaml.v3 v3.0.1 // indirect
rsc.io/getopt v0.0.0-20170811000552-20be20937449 // indirect
)

View File

@ -1,5 +1,4 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/getopt v0.0.0-20170811000552-20be20937449 h1:UukjJOsjQH0DIuyyrcod6CXHS6cdaMMuJmrt+SN1j4A=
rsc.io/getopt v0.0.0-20170811000552-20be20937449/go.mod h1:dhCdeqAxkyt5u3/sKRkUXuHaMXUu1Pt13GTQAM2xnig=

View File

@ -59,7 +59,28 @@ func main() {
fmt.Println("Usage: ectl service <start/stop/enable/disable/status/list> [service]") fmt.Println("Usage: ectl service <start/stop/enable/disable/status/list> [service]")
return return
} else if flag.Args()[1] == "list" { } else if flag.Args()[1] == "list" {
fmt.Println("list") if _, err := os.Stat(path.Join(runstatedir, "esvm")); err != nil {
log.Fatalf("Could not list services! Error: %s\n", err)
}
entries, err := os.ReadDir(path.Join(runstatedir, "esvm"))
if err != nil {
log.Fatalf("Could not list services! Error: %s\n", err)
}
for _, entry := range entries {
if !entry.IsDir() {
continue
}
state := getServiceState(entry.Name())
enabled := strconv.FormatBool(isServiceEnabled(entry.Name()))
enabled = strings.ToUpper(enabled[:1]) + strings.ToLower(enabled[1:])
fmt.Println("Service name: " + entry.Name())
fmt.Printf(" State: %s\n", state)
fmt.Printf(" Enabled: %s\n", enabled)
}
return return
} else if len(flag.Args()) <= 2 { } else if len(flag.Args()) <= 2 {
fmt.Printf("Usage: ectl service %s <service>\n", flag.Args()[1]) fmt.Printf("Usage: ectl service %s <service>\n", flag.Args()[1])
@ -226,26 +247,13 @@ func main() {
log.Fatalf("Could not get service status! Error: %s\n", err) log.Fatalf("Could not get service status! Error: %s\n", err)
} }
var state uint64 state := getServiceState(flag.Args()[2])
bytes, err := os.ReadFile(path.Join(runstatedir, "esvm", flag.Args()[2], "state")) enabled := strconv.FormatBool(isServiceEnabled(flag.Args()[2]))
if err != nil { enabled = strings.ToUpper(enabled[:1]) + strings.ToLower(enabled[1:])
state = 0
}
state, err = strconv.ParseUint(string(bytes), 10, 8)
fmt.Println("Service name: " + flag.Args()[2]) fmt.Println("Service name: " + flag.Args()[2])
switch state { fmt.Printf(" State: %s\n", state)
case 0: fmt.Printf(" Enabled: %s\n", enabled)
fmt.Println("Service state: Unknown")
case 1:
fmt.Println("Service state: Unloaded")
case 2:
fmt.Println("Service state: Running")
case 3:
fmt.Println("Service state: Stopped")
case 4:
fmt.Println("Service state: Crashed")
}
return return
} }
} }
@ -254,6 +262,53 @@ func main() {
os.Exit(1) os.Exit(1)
} }
func getServiceState(serviceName string) string {
if _, err := os.Stat(path.Join(runstatedir, "esvm", serviceName)); err != nil {
return ""
}
var state uint64
bytes, err := os.ReadFile(path.Join(runstatedir, "esvm", serviceName, "state"))
if err != nil {
state = 0
}
state, err = strconv.ParseUint(string(bytes), 10, 8)
switch state {
case 1:
return "Unloaded"
case 2:
return "Running"
case 3:
return "Stopped"
case 4:
return "Crashed"
case 5:
return "Completed"
default:
return "Unknown"
}
}
func isServiceEnabled(serviceName string) bool {
if _, err := os.Stat(path.Join(sysconfdir, "esvm/enabled_services")); err != nil {
return false
}
file, err := os.ReadFile(path.Join(sysconfdir, "esvm/enabled_services"))
if err != nil {
return false
}
for _, line := range strings.Split(string(file), "\n") {
if strings.TrimSpace(line) == serviceName {
return true
}
}
return false
}
func printUsage() { func printUsage() {
fmt.Println("Available sucommands:") fmt.Println("Available sucommands:")
fmt.Println("ectl version | Show enit version") fmt.Println("ectl version | Show enit version")

View File

@ -1,3 +1,5 @@
module enit module enit
go 1.23.4 go 1.23.4
require golang.org/x/sys v0.31.0

View File

@ -1,4 +1,2 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"errors"
"flag" "flag"
"fmt" "fmt"
"log" "log"
@ -75,43 +74,45 @@ func setProcessName() error {
func mountVirtualFilesystems() { func mountVirtualFilesystems() {
fmt.Print("Mounting virtual filesystems... ") fmt.Print("Mounting virtual filesystems... ")
commonFlags := uintptr(0 | syscall.MS_NOSUID | syscall.MS_RELATIME) commonOptions := "rw,nosuid,relatime"
// Mount /proc // Mount /proc
if err := syscall.Mount("proc", "/proc", "proc", commonFlags|syscall.MS_NODEV|syscall.MS_NOEXEC|syscall.MS_REMOUNT, ""); err != nil { if err := mount("proc", "/proc", "proc", commonOptions+",nodev,noexec", false); err != nil {
panic(err) panic(err)
} }
// Mount /sys // Mount /sys
if err := syscall.Mount("sys", "/sys", "sysfs", commonFlags|syscall.MS_NODEV|syscall.MS_NOEXEC|syscall.MS_REMOUNT, ""); err != nil { if err := mount("sys", "/sys", "sysfs", commonOptions+",nodev,noexec", false); err != nil {
panic(err) panic(err)
} }
// Mount /dev // Mount /dev
if err := syscall.Mount("dev", "/dev", "devtmpfs", commonFlags|syscall.MS_REMOUNT, "mode=755,inode64"); err != nil { if err := mount("dev", "/dev", "devtmpfs", commonOptions+",mode=755,inode64", false); err != nil {
panic(err) panic(err)
} }
// Mount /run // Mount /run
if err := syscall.Mount("run", "/run", "tmpfs", commonFlags|syscall.MS_NODEV|syscall.MS_REMOUNT, "mode=755,inode64"); err != nil { if err := mount("run", "/run", "tmpfs", commonOptions+",nodev,mode=755,inode64", false); err != nil {
panic(err) panic(err)
} }
// Mount /dev/pts // Mount /dev/pts
if err := os.Mkdir("/dev/pts", 0755); err != nil && !errors.Is(err, os.ErrExist) { if err := mount("devpts", "/dev/pts", "devpts", commonOptions+",gid=5,mode=620,ptmxmode=000", true); err != nil {
panic(err)
}
if err := syscall.Mount("devpts", "/dev/pts", "devpts", commonFlags, "gid=5,mode=620,ptmxmode=000"); err != nil {
panic(err) panic(err)
} }
// Mount /dev/shm // Mount /dev/shm
if err := os.Mkdir("/dev/shm", 0755); err != nil && !errors.Is(err, os.ErrExist) { if err := mount("shm", "/dev/shm", "tmpfs", commonOptions+",nodev,inode64", true); err != nil {
panic(err)
}
if err := syscall.Mount("shm", "/dev/shm", "tmpfs", commonFlags|syscall.MS_NODEV, "inode64"); err != nil {
panic(err) panic(err)
} }
// Mount securityfs // Mount securityfs
if err := syscall.Mount("securityfs", "/sys/kernel/security", "securityfs", commonFlags, ""); err != nil { if err := mount("securityfs", "/sys/kernel/security", "securityfs", commonOptions, false); err != nil {
panic(err) panic(err)
} }
// Mount cgroups v2 // Mount cgroups v2
if err := syscall.Mount("cgroup2", "/sys/fs/cgroup", "cgroup2", commonFlags|syscall.MS_NOEXEC, ""); err != nil { if err := mount("cgroup2", "/sys/fs/cgroup", "cgroup2", commonOptions+",noexec,nsdelegate,memory_recursiveprot", false); err != nil {
panic(err) panic(err)
} }
@ -119,12 +120,9 @@ func mountVirtualFilesystems() {
} }
func mountFilesystems() { func mountFilesystems() {
fmt.Print("Mounting filesystems... ") fmt.Print("Mounting fstab entries... ")
cmd := exec.Command("/bin/mount", "-a") if err := mountFstabEntries(); err != nil {
err := cmd.Run()
if err != nil {
log.Println("Could not mount fstab entries!") log.Println("Could not mount fstab entries!")
panic(err) panic(err)
} }

141
cmd/enit/mount.go Normal file
View File

@ -0,0 +1,141 @@
package main
import (
"golang.org/x/sys/unix"
"os"
"slices"
"strings"
)
var flagsEquivalence = map[string]uintptr{
"dirsync": unix.MS_DIRSYNC,
"lazytime": unix.MS_LAZYTIME,
"noatime": unix.MS_NOATIME,
"nodev": unix.MS_NODEV,
"nodiratime": unix.MS_NODIRATIME,
"noexec": unix.MS_NOEXEC,
"nosuid": unix.MS_NOSUID,
"ro": unix.MS_RDONLY,
"rw": 0,
"relatime": unix.MS_RELATIME,
"silent": unix.MS_SILENT,
"strictatime": unix.MS_STRICTATIME,
"sync": unix.MS_SYNCHRONOUS,
"defaults": 0,
}
// Split string flags to mount flags and mount data
func convertMountOptions(options string) (flags []uintptr, data string) {
for _, flag := range strings.Split(options, ",") {
if unixFlag, ok := flagsEquivalence[flag]; ok {
flags = append(flags, unixFlag)
} else {
if data == "" {
data = flag
} else {
data += "," + flag
}
}
}
return flags, data
}
// Combine a unix flag slice or array into a single uintptr
func combineUnixFlags(flagsSlice []uintptr) (flags uintptr) {
flags = 0
for _, flag := range flagsSlice {
flags |= flag
}
return flags
}
// Check whether a certain path is a mountpoint
func isMountpoint(mountpoint string) bool {
if mountpoint != "/" {
mountpoint = strings.TrimRight(mountpoint, "/")
}
if _, err := os.Stat("/proc/mounts"); err != nil {
return false
}
bytes, err := os.ReadFile("/proc/mounts")
if err != nil {
return false
}
for _, line := range strings.Split(string(bytes), "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
if strings.Split(line, " ")[1] == mountpoint {
return true
}
}
return false
}
func mount(source, target, fstype string, options string, mkdir bool) error {
flags, data := convertMountOptions(options)
if isMountpoint(target) && !slices.Contains(flags, unix.MS_REMOUNT) {
flags = append(flags, unix.MS_REMOUNT)
}
if mkdir {
err := os.MkdirAll(target, 0755)
if err != nil {
return err
}
}
if err := unix.Mount(source, target, fstype, combineUnixFlags(flags), data); err != nil {
return err
}
return nil
}
func mountFstabEntries() error {
if _, err := os.Stat("/etc/fstab"); err != nil {
return err
}
bytes, err := os.ReadFile("/etc/fstab")
if err != nil {
return err
}
for _, line := range strings.Split(string(bytes), "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "#") || line == "" {
continue
}
source := strings.Split(line, " ")[0]
target := strings.Split(line, " ")[1]
fstype := strings.Split(line, " ")[2]
options := strings.Split(line, " ")[3]
flags, data := convertMountOptions(options)
if slices.Contains(strings.Split(data, ","), "noauto") {
continue
}
if isMountpoint(target) && !slices.Contains(flags, unix.MS_REMOUNT) {
flags = append(flags, unix.MS_REMOUNT)
}
if err := unix.Mount(source, target, fstype, combineUnixFlags(flags), data); err != nil {
return err
}
}
return nil
}