Compare commits

...

13 commits

Author SHA1 Message Date
ptrcnull 941d8a814b
docs: Update URL 2023-01-05 02:30:16 +01:00
ptrcnull 8c6d9e4ab6
feat: Add proper device handling 2023-01-05 02:29:23 +01:00
ptrcnull 543e1cdd14 docs: Remove 'proper shutdown' from TODO list 2021-11-30 01:00:35 +01:00
ptrcnull 83a7661a7d feat: Add wait4 loop in main
hey, i know this is ugly
but it works!
2021-11-30 00:59:53 +01:00
ptrcnull 180881639b docs: Add README.md 2021-11-29 23:59:04 +01:00
ptrcnull b57328e20b feat: Implement signal handling
shutdown, reboot, re-exec and ctrl-alt-del
2021-11-29 23:49:45 +01:00
ptrcnull 2454b772dc feat: Add spawning processes 2021-11-29 23:08:39 +01:00
ptrcnull e8591c5770 fix: Make inittab parse if error was nil 2021-11-29 23:08:10 +01:00
ptrcnull 068cb742f5 chore: Add .gitignore 2021-11-29 23:06:55 +01:00
ptrcnull 6c8e8c3003 feat: Add method for opening devices 2021-11-29 22:13:48 +01:00
ptrcnull 945b295bdf wip: Add opening /etc/inittab 2021-11-29 22:12:52 +01:00
ptrcnull b5808e7773 feat: Add type InitTab 2021-11-29 22:12:08 +01:00
ptrcnull 8650c833bf style: Rename TabEntry#Id to Device 2021-11-29 22:11:48 +01:00
6 changed files with 194 additions and 8 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
init

11
README.md Normal file
View file

@ -0,0 +1,11 @@
# ptrcnull/init
Experimental implementation of PID 1.
Drop-in replacement for busybox init (kind of)
The canonical URL of this repository is https://git.ptrc.gay/ptrcnull/init
### TODO
- askfirst handling

22
device.go Normal file
View file

@ -0,0 +1,22 @@
package main
import (
"fmt"
"os"
)
var devices = map[string]*os.File{}
func GetDevice(name string) (*os.File, error) {
if dev, ok := devices[name]; ok {
return dev, nil
}
dev, err := os.OpenFile("/dev/"+name, os.O_RDWR, 0644)
if err != nil {
return nil, fmt.Errorf("open: %w", err)
}
devices[name] = dev
return dev, nil
}

View file

@ -30,13 +30,27 @@ var ActionMap = map[string]Action{
"ctrlaltdel": CtrlAltDel,
}
type TabEntry struct {
Id string
type InitTabEntry struct {
Device string
Action Action
Process string
}
var DefaultInittab = []TabEntry{
type InitTab []InitTabEntry
func (i InitTab) Entries(action Action) InitTab {
var res InitTab
for _, entry := range i {
if entry.Action == action {
res = append(res, entry)
}
}
return res
}
var DefaultInitTab = InitTab{
{"", SysInit, "/etc/init.d/rcS"},
{"", AskFirst, "/bin/sh"},
{"", CtrlAltDel, "/sbin/reboot"},
@ -48,8 +62,8 @@ var DefaultInittab = []TabEntry{
{"tty4", AskFirst, "/bin/sh"},
}
func ParseInittab(reader io.Reader) []TabEntry {
var res []TabEntry
func ParseInitTab(reader io.Reader) InitTab {
var res InitTab
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
line := scanner.Text()
@ -67,8 +81,8 @@ func ParseInittab(reader io.Reader) []TabEntry {
continue
}
res = append(res, TabEntry{
Id: tokens[0],
res = append(res, InitTabEntry{
Device: tokens[0],
Action: action,
Process: tokens[3],
})

96
main.go
View file

@ -1,5 +1,99 @@
package main
import (
"fmt"
"os"
"os/signal"
"strings"
"syscall"
)
func main() {
inittab := DefaultInitTab
if file, err := os.OpenFile("/etc/inittab", os.O_RDONLY, 0644); err == nil {
inittab = ParseInitTab(file)
err := file.Close()
if err != nil {
fmt.Printf("error closing inittab: %s\n", err)
}
} else {
fmt.Printf("error reading inittab: %s\n", err)
}
inittab.Entries(SysInit).ExecAll()
inittab.Entries(Wait).ExecAll()
inittab.Entries(Once).SpawnAll()
inittab.Entries(Respawn).RespawnAll()
// TODO implement AskFirst handling
sigs := make(chan os.Signal, 1)
go func() {
for {
sig := <-sigs
switch sig {
case syscall.SIGUSR2:
// shutdown
inittab.Entries(Shutdown).ExecAll()
syscall.Reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
case syscall.SIGTERM:
// reboot
inittab.Entries(Shutdown).ExecAll()
syscall.Reboot(syscall.LINUX_REBOOT_CMD_RESTART)
case syscall.SIGQUIT:
inittab.Entries(Shutdown).ExecAll()
restart := inittab.Entries(Restart)[0]
cmdline := strings.Split(restart.Process, " ")
syscall.Exec(cmdline[0], cmdline[1:], []string{})
case syscall.SIGINT:
inittab.Entries(CtrlAltDel).ExecAll()
}
}
}()
signal.Notify(sigs, syscall.SIGUSR2, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT)
go func() {
for {
syscall.Wait4(-1, nil, 0, nil)
}
}()
select {}
}
func (i InitTab) ExecAll() {
for _, entry := range i {
err := Exec(entry)
if err != nil {
fmt.Printf("error executing \"%s\": %s\n", entry.Process, err)
}
}
}
func (i InitTab) SpawnAll() {
for _, entry := range i {
_, err := Spawn(entry)
if err != nil {
fmt.Printf("error spawning \"%s\": %s\n", entry.Process, err)
}
}
}
func (i InitTab) RespawnAll() {
for _, entry := range i {
go func(entry InitTabEntry) {
for {
err := Exec(entry)
if err != nil {
fmt.Printf("error respawning \"%s\": %s\n", entry.Process, err)
break
}
}
}(entry)
}
}

44
process.go Normal file
View file

@ -0,0 +1,44 @@
package main
import (
"fmt"
"os"
"os/exec"
"strings"
)
func Spawn(entry InitTabEntry) (*exec.Cmd, error) {
cmdline := strings.Split(entry.Process, " ")
cmd := exec.Command(cmdline[0], cmdline[1:]...)
stdio := os.Stdout
if entry.Device != "" {
dev, err := GetDevice(entry.Device)
if err != nil {
return nil, fmt.Errorf("open device %s: %w", entry.Device, err)
}
stdio = dev
}
cmd.Stdin = stdio
cmd.Stdout = stdio
cmd.Stderr = stdio
err := cmd.Start()
if err != nil {
return nil, err
}
return cmd, nil
}
func Exec(entry InitTabEntry) error {
cmd, err := Spawn(entry)
if err != nil {
return fmt.Errorf("spawn: %w", err)
}
// skipping error handling due to wait4 in main
cmd.Wait()
return nil
}