diff --git a/cmds/cpud/init_freebsd.go b/cmds/cpud/init_freebsd.go new file mode 100644 index 00000000..fd868418 --- /dev/null +++ b/cmds/cpud/init_freebsd.go @@ -0,0 +1,81 @@ +// Copyright 2018-2022 the u-root Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This is init code for the case that cpu finds itself as pid 1. +// This is duplicative of the real init, but we're implementing it +// as a duplicate so we can get some idea of: +// what an init package should have +// what an init interface should have +// So we take a bit of duplication now to better understand these +// things. We also assume for now this is a busybox environment. +// It is unusual (I guess?) for cpu to be an init in anything else. +// So far, the case for an init pkg is not as strong as I thought +// it might be. +package main + +import ( + "log" + "runtime" + "syscall" + "time" + + "github.com/u-root/u-root/pkg/libinit" +) + +func cpuSetup() error { + // The process reaper runs from here, and needs to run + // as PID 1. + runtime.LockOSThread() + log.Printf(` + + #### ##### # # ## + # # # # # # ## + # # # # # ## + # ##### # # ## + # # # # # + #### # #### ## +`) + // libinit.SetEnv() + // libinit.CreateRootfs() + libinit.NetInit() + // Wait for orphans, forever. + // Since there is no way of knowning when we are + // done for good, our work here is never done. + // A complication is that for long periods of time, there + // may be no orphans.In that case, sleep for one second, + // and try again. This background load is hardly enough + // to matter. And, in general, it will happen by definition + // when there is nothing to wait for, i.e. there is nothing + // on the node to be upset about. + // Were this ever to be a concern, an option is to kick off + // a process that will never exit, such that wait4 will always + // block and always return when any child process exits. + go func() { + var numReaped int + for { + var ( + s syscall.WaitStatus + r syscall.Rusage + ) + p, err := syscall.Wait4(-1, &s, 0, &r) + // Once per second, Wait 4 returns if there's nothing + // else to do. + if err != nil && err.Error() == "no child processes" { + continue + } + verbose("orphan reaper: returns with %v", p) + if p == -1 { + verbose("Nothing to wait for, %d wait for so far", numReaped) + time.Sleep(time.Second) + } + if err != nil { + log.Printf("CPUD: a process exited with %v, status %v, rusage %v, err %v", p, s, r, err) + } + numReaped++ + } + }() + + runtime.UnlockOSThread() + return nil +} diff --git a/cmds/cpud/main_freebsd.go b/cmds/cpud/main_freebsd.go new file mode 100644 index 00000000..ba404243 --- /dev/null +++ b/cmds/cpud/main_freebsd.go @@ -0,0 +1,131 @@ +// Copyright 2018-2019 the u-root Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + "log" + "os" + "time" + + // We use this ssh because it implements port redirection. + // It can not, however, unpack password-protected keys yet. + + "github.com/u-root/cpu/session" +) + +var ( + // For the ssh server part + hostKeyFile = flag.String("hk", "" /*"/etc/ssh/ssh_host_rsa_key"*/, "file for host key") + pubKeyFile = flag.String("pk", "key.pub", "file for public key") + port = flag.String("sp", "17010", "cpu default port") + + debug = flag.Bool("d", false, "enable debug prints") + runAsInit = flag.Bool("init", false, "run as init (Debug only; normal test is if we are pid 1") + // v allows debug printing. + // Do not call it directly, call verbose instead. + v = func(string, ...interface{}) {} + remote = flag.Bool("remote", false, "indicates we are the remote side of the cpu session") + network = flag.String("net", "tcp", "network to use") + port9p = flag.String("port9p", "", "port9p # on remote machine for 9p mount") + klog = flag.Bool("klog", false, "Log cpud messages in kernel log, not stdout") + + // Some networks are not well behaved, and for them we implement registration. + registerAddr = flag.String("register", "", "address and port to register with after listen on cpu server port") + registerTO = flag.Duration("registerTO", time.Duration(5*time.Second), "time.Duration for Dial address for registering") + + // if we start up too quickly, mDNS won't work correctly. + // This sleep may be useful for other cases, so it is here, + // not specifically for mDNS uses. + sleepBeforeServing = flag.Duration("sleepBeforeServing", 0, "add a sleep before serving -- usually only needed if cpud runs as init with mDNS") + + pid1 bool +) + +func verbose(f string, a ...interface{}) { + if *remote { + v("CPUD(remote):"+f+"\r\n", a...) + } else { + v("CPUD:"+f, a...) + } +} + +// There are three distinct cases to cover. +// 1. running as init (indicated by pid == 1 OR -init=true switch +// 2. running as server. pid != 1 AND -remote=true AND -init=false +// 3. running as 'remote', i.e. the thing that starts a command for +// a client. Indicated by remote=true. +// +// case (3) overrides case 2 and 1. +// This has evolved over the years, and, likely, the init and remote +// switches ought to be renamed to 'role'. But so it goes. +// The rules on arguments are very strict now. In the remote case, +// os.Args[1] MUST be remote; no other invocation is accepted, because +// the args to remote and the args to server are different. +// This invocation requirement is known to the server package. +func main() { + if len(os.Args) > 1 && (os.Args[1] == "-remote" || os.Args[1] == "-remote=true") { + *remote = true + } + + if *remote { + // remote has far fewer args. Since they are specified by the client, + // we want to limit the set of args it can set. + flag.CommandLine = flag.NewFlagSet("cpud-remote", flag.ExitOnError) + debug = flag.Bool("d", false, "enable debug prints") + remote = flag.Bool("remote", false, "indicates we are the remote side of the cpu session") + port9p = flag.String("port9p", "", "port9p # on remote machine for 9p mount") + + flag.Parse() + if *debug { + v = log.Printf + session.SetVerbose(verbose) + } + // If we are here, no matter what they may set, *remote must be true. + // sadly, cpud -d -remote=true -remote=false ... works. + *remote = true + } else { + flag.Parse() + // If we are here, no matter what they may set, *remote must be false. + *remote = false + if err := commonsetup(); err != nil { + log.Fatal(err) + } + } + pid := os.Getpid() + pid1 = pid == 1 + *runAsInit = *runAsInit || pid1 + verbose("Args %v pid %d *runasinit %v *remote %v env %v", os.Args, pid, *runAsInit, *remote, os.Environ()) + args := flag.Args() + if *remote { + verbose("args %q, port9p %v", args, *port9p) + + // This can happen if the user gets clever and + // invokes cpu with, e.g., nothing but switches. + if len(args) == 0 { + shell, ok := os.LookupEnv("SHELL") + if !ok { + log.Fatal("No arguments and $SHELL is not set") + } + args = []string{shell} + } + s := session.New(*port9p, args[0], args[1:]...) + if err := s.Run(); err != nil { + log.Fatalf("CPUD(remote): %v", err) + } + } else { + log.Printf("CPUD:PID(%d):running as a server (a.k.a. starter of cpud's for sessions)", pid) + if *runAsInit { + log.Printf("CPUD:also running as init") + if err := initsetup(); err != nil { + log.Fatal(err) + } + } + time.Sleep(*sleepBeforeServing) + if err := serve(os.Args[0]); err != nil { + log.Fatal(err) + } + } +} diff --git a/cmds/cpud/serve.go b/cmds/cpud/serve.go index d4ac7985..bf99eb86 100644 --- a/cmds/cpud/serve.go +++ b/cmds/cpud/serve.go @@ -22,8 +22,6 @@ import ( "github.com/gliderlabs/ssh" "github.com/mdlayher/vsock" "github.com/u-root/cpu/server" - "github.com/u-root/u-root/pkg/ulog" - "golang.org/x/sys/unix" ) const any = math.MaxUint32 @@ -65,17 +63,21 @@ func commonsetup() error { server.SetVerbose(verbose) v = log.Printf if *klog { - ulog.KernelLog.Reinit() - v = ulog.KernelLog.Printf + //ulog.KernelLog.Reinit() + log.Panicf("klog: not yet") + //v = ulog.KernelLog.Printf } } return nil } func initsetup() error { - if err := unix.Mount("cpu", "/tmp", "tmpfs", 0, ""); err != nil { - log.Printf("CPUD:Warning: tmpfs mount on /tmp (%v) failed. There will be no 9p mount", err) - } + // no tmpfs in freebsd? + /* + if err := unix.Mount("cpu", "/tmp", "tmpfs", 0, ""); err != nil { + log.Printf("CPUD:Warning: tmpfs mount on /tmp (%v) failed. There will be no 9p mount", err) + } + */ if err := cpuSetup(); err != nil { log.Printf("CPUD:CPU setup error with cpu running as init: %v", err) } diff --git a/go.mod b/go.mod index 76ecc962..8fc27590 100644 --- a/go.mod +++ b/go.mod @@ -16,9 +16,6 @@ require ( github.com/creack/pty v1.1.18 github.com/go-git/go-billy/v5 v5.5.1-0.20240514075308-8f1b719cb6a2 github.com/google/uuid v1.6.0 - github.com/hashicorp/go-multierror v1.1.1 - github.com/jacobsa/fuse v0.0.0-20240909130001-a1c7c8268f12 - github.com/jacobsa/syncutil v0.0.0-20180201203307-228ac8e5a6c3 github.com/mdlayher/vsock v1.2.1 github.com/moby/sys/mountinfo v0.7.1 github.com/shirou/gopsutil v3.21.11+incompatible @@ -32,7 +29,6 @@ require ( github.com/cyphar/filepath-securejoin v0.2.5 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/go-ole/go-ole v1.2.6 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/klauspost/compress v1.16.7 // indirect github.com/klauspost/pgzip v1.2.6 // indirect diff --git a/go.sum b/go.sum index ca6c055b..c93c2b59 100644 --- a/go.sum +++ b/go.sum @@ -21,24 +21,13 @@ github.com/google/goexpect v0.0.0-20191001010744-5b6988669ffa h1:PMkmJA8ju9DjqAJ github.com/google/goterm v0.0.0-20200907032337-555d40f16ae2 h1:CVuJwN34x4xM2aT4sIKhmeib40NeBPhRihNjQmpJsA4= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hugelgupf/p9 v0.2.1-0.20230814004337-e6037077d6dc h1:rYjeymvczXx9XLtaZRRSp/DMYKVXV1QZiCbaimPxAmY= github.com/hugelgupf/p9 v0.2.1-0.20230814004337-e6037077d6dc/go.mod h1:QFmcCPNn66imQcu1wUqJ8sHKxYjs00Gq60QLjt9E+VI= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8= -github.com/jacobsa/fuse v0.0.0-20240909130001-a1c7c8268f12 h1:PIkShcSGp+IJB3h3Du/hNtrvug0b0o88p9pW8GF9xcg= -github.com/jacobsa/fuse v0.0.0-20240909130001-a1c7c8268f12/go.mod h1:JYi9iIxdYNgxmMgLwtSHO/hmVnP2kfX1oc+mtx+XWLA= -github.com/jacobsa/oglematchers v0.0.0-20150720000706-141901ea67cd h1:9GCSedGjMcLZCrusBZuo4tyKLpKUPenUUqi34AkuFmA= -github.com/jacobsa/oglemock v0.0.0-20150831005832-e94d794d06ff h1:2xRHTvkpJ5zJmglXLRqHiZQNjUoOkhUyhTAhEQvPAWw= -github.com/jacobsa/ogletest v0.0.0-20170503003838-80d50a735a11 h1:BMb8s3ENQLt5ulwVIHVDWFHp8eIXmbfSExkvdn9qMXI= -github.com/jacobsa/reqtrace v0.0.0-20150505043853-245c9e0234cb h1:uSWBjJdMf47kQlXMwWEfmc864bA1wAC+Kl3ApryuG9Y= -github.com/jacobsa/syncutil v0.0.0-20180201203307-228ac8e5a6c3 h1:+gHfvQxomE6fI4zg7QYyaGDCnuw2wylD4i6yzrQvAmY= -github.com/jacobsa/syncutil v0.0.0-20180201203307-228ac8e5a6c3/go.mod h1:mPvulh9VKXvo+yOlrD4VYOOYuLdZJ36wa/5QIrtXvWs= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -48,7 +37,6 @@ github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= diff --git a/server/server_freebsd.go b/server/server_freebsd.go new file mode 100644 index 00000000..804fc967 --- /dev/null +++ b/server/server_freebsd.go @@ -0,0 +1,28 @@ +// Copyright 2018-2022 the u-root Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package server + +import ( + "os" + "os/exec" +) + +// cpud can run in one of three modes +// o init +// o daemon started by init +// o manager of one cpu session. +func init() { + // placeholder. It's not clear we ever want to do this. We used to create + // a root file system here, but that should be up to the server. The files + // might magically exist, b/c of initrd; or be automagically mounted via + // some other mechanism. + if os.Getpid() == 1 { + verbose("PID 1") + } +} + +func command(n string, args ...string) *exec.Cmd { + return exec.Command(n, args...) +} diff --git a/server/server_other.go b/server/server_other.go index 3034c44d..da9a3be0 100644 --- a/server/server_other.go +++ b/server/server_other.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !linux -// +build !linux +//go:build !linux && !freebsd +// +build !linux,!freebsd package server diff --git a/session/session_freebsd.go b/session/session_freebsd.go new file mode 100644 index 00000000..7216b63d --- /dev/null +++ b/session/session_freebsd.go @@ -0,0 +1,19 @@ +// Copyright 2018-2022 the u-root Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package session + +// Namespace does nothing; no 9p on freebsd yet. +func (s *Session) Namespace() error { + return nil +} + +func osMounts() error { + return nil +} + +// runSetup performs kernel-specific operations for starting a Session. +func runSetup() error { + return nil +} diff --git a/session/session_other.go b/session/session_other.go index f17e68a9..155e8365 100644 --- a/session/session_other.go +++ b/session/session_other.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !linux -// +build !linux +//go:build !linux && !freebsd package session