Compare commits

...

9 Commits

Author SHA1 Message Date
Sasha Koshka
1978e263d6 sahd;ljasl;dkj 2023-06-01 03:44:33 -04:00
Sasha Koshka
7726d732d4 we got some dhrek we got some doneky we got some fienona 2023-06-01 03:37:08 -04:00
Sasha Koshka
32bc44c90f Oops lol 2023-06-01 03:00:15 -04:00
Sasha Koshka
805f42d828 Changed name of router to hn-router 2023-06-01 02:59:18 -04:00
Sasha Koshka
e8349360cc hnctl prints line breaks after errors 2023-06-01 01:01:08 -04:00
Sasha Koshka
4e58df9c9b Fix hnctl 2023-06-01 00:59:03 -04:00
Sasha Koshka
f90421e5db Add gitignore 2023-05-31 22:35:25 -04:00
Sasha Koshka
c9f2c56d65 Services no longer print out errors when they shut down 2023-05-31 22:00:21 -04:00
Sasha Koshka
92b645f34c Routine manager now recovers from panicking goroutines 2023-05-31 18:49:00 -04:00
10 changed files with 164 additions and 92 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/hnctl
/router
/wrench

View File

@@ -5,7 +5,10 @@ package cli
import "os"
import "fmt"
import "flag"
import "os/user"
import "strings"
import "strconv"
import "path/filepath"
// Sayf is like Printf, but prints the program name before the message. This is
// used for printing messages and errors.
@@ -25,6 +28,16 @@ func ServiceUser (service string) string {
return "hn-" + strings.ToLower(service)
}
// DataDir returns the standard Hnakra data directory.
func DataDir () string {
return "/var/hnakra"
}
// ServiceDir returns the standard data directory of a service.
func ServiceDir (service string) string {
return filepath.Join(DataDir(), "services", ServiceUser(service))
}
// NeedRoot halts the program and displays an error if it is not being run as
// root. This should be called whenever an operation takes place that requires
// root privelages.
@@ -35,3 +48,34 @@ func NeedRoot() {
os.Exit(1)
}
}
// MkdirFor makes a directory makes the specified directory (if it doesnt
// already exist) and gives ownership of it to the specified uid and gid.
func MkdirFor (directory string, uid, gid int) error {
err := os.MkdirAll(directory, 0755)
if err != nil { return err }
err = os.Chmod(directory, 0770)
if err != nil { return err }
err = os.Chown(directory, uid, gid)
if err != nil { return err }
return nil
}
// LookupUID returns the uid and gid of the given username, if it exists.
func LookupUID (name string) (uid, gid uint32, err error) {
user, err := user.Lookup(name)
if err != nil {
return 0, 0, err
}
puid, err := strconv.Atoi(user.Uid)
if err != nil {
return 0, 0, err
}
pgid, err := strconv.Atoi(user.Gid)
if err != nil {
return 0, 0, err
}
return uint32(puid), uint32(pgid), nil
}

View File

@@ -10,8 +10,8 @@ import "hnakra/daemon"
import "hnakra/routines"
import "hnakra/router/rcon"
import "hnakra/router/config"
import "hnakra/cmd/router/srvhttps"
import "hnakra/cmd/router/srvhnakra"
import "hnakra/cmd/hn-router/srvhttps"
import "hnakra/cmd/hn-router/srvhnakra"
const banner = "\n" +
" -=\\\n" +

View File

@@ -11,6 +11,8 @@ type Server struct {
underlying net.Listener
Config config.Config
Router *router.Router
running bool
}
func (server *Server) Run () (err error) {
@@ -18,17 +20,25 @@ func (server *Server) Run () (err error) {
"tcp", fmt.Sprint(":", server.Config.RouterPort()),
config.TLSConfigFor(server.Config))
if err != nil { return err }
server.running = true
log.Println(".// router on", server.underlying.Addr())
for {
conn, err := server.underlying.Accept()
if err != nil { return err }
if err != nil {
if server.running {
return err
} else {
return nil
}
}
log.Println("-=E incoming connection from", conn.RemoteAddr())
server.Router.Accept(conn)
}
}
func (server *Server) Shutdown () error {
server.running = false
return server.underlying.Close()
}

View File

@@ -9,6 +9,8 @@ type Server struct {
underlying *http.Server
Config config.Config
Handler http.Handler
running bool
}
func (server *Server) Run () error {
@@ -22,10 +24,17 @@ func (server *Server) Run () error {
Handler: server.Handler,
}
server.running = true
log.Println(".// https on", server.underlying.Addr)
return server.underlying.ListenAndServeTLS("", "")
err := server.underlying.ListenAndServeTLS("", "")
if server.running {
return err
} else {
return nil
}
}
func (server *Server) Shutdown () error {
server.running = false
return server.underlying.Close()
}

View File

@@ -30,7 +30,7 @@ func main () {
stopService := stopCommand.String("s", "router", "Service to stop")
restartCommand := flag.NewFlagSet("restart", flag.ExitOnError)
restartService := stopCommand.String("s", "router", "Service to restart")
restartService := restartCommand.String("s", "router", "Service to restart")
flag.Parse()
@@ -60,27 +60,27 @@ func execStart (service string) {
pid, err := spawn.PidOf(fullName)
if err == nil && spawn.Running(pid) {
cli.Sayf("service is already running")
cli.Sayf("service is already running\n")
return
}
uid, gid, err := spawn.LookupUID(fullName)
uid, gid, err := cli.LookupUID(fullName)
if err != nil {
cli.Sayf("cannot start service: %v", err)
cli.Sayf("cannot start service: %v\n", err)
os.Exit(1)
}
path, err := exec.LookPath(fullName)
if err != nil {
cli.Sayf("cannot start service: %v", err)
cli.Sayf("cannot start service: %v\n", err)
os.Exit(1)
}
logDir := filepath.Join("/var/log/", fullName)
env := append(os.Environ(), "HNAKRA_LOG_DIR=" + logDir)
err = ensureLogDir(logDir, int(uid), int(gid))
err = cli.MkdirFor(logDir, int(uid), int(gid))
if err != nil {
cli.Sayf("cannot start service: %v", err)
cli.Sayf("cannot start service: %v\n", err)
os.Exit(1)
}
@@ -88,14 +88,14 @@ func execStart (service string) {
// to it
err = ensurePidFile(spawn.PidFile(fullName), int(uid), int(gid))
if err != nil {
cli.Sayf("cannot start service: %v", err)
cli.Sayf("cannot start service: %v\n", err)
os.Exit(1)
}
// spawn the service
pid, err = spawn.Spawn(path, uid, gid, env)
if err != nil {
cli.Sayf("cannot start service: %v", err)
cli.Sayf("cannot start service: %v\n", err)
os.Exit(1)
}
@@ -108,34 +108,23 @@ func execStop (service string) {
pid, err := spawn.PidOf(fullName)
if err != nil || !spawn.Running(pid) {
cli.Sayf("service is not running")
cli.Sayf("service is not running\n")
return
}
process, err := os.FindProcess(pid)
if err != nil {
cli.Sayf("service is not running")
cli.Sayf("service is not running\n")
return
}
err = spawn.KillAndWait(process, 16 * time.Second)
if err != nil {
cli.Sayf("could not stop service: %v", err)
cli.Sayf("could not stop service: %v\n", err)
os.Exit(1)
}
}
func ensureLogDir (directory string, uid, gid int) error {
err := os.MkdirAll(directory, 0755)
if err != nil { return err }
err = os.Chmod(directory, 0770)
if err != nil { return err }
err = os.Chown(directory, uid, gid)
if err != nil { return err }
return nil
}
func ensurePidFile (file string, uid, gid int) error {
pidFile, err := os.Create(file)
if err != nil { return err }

View File

@@ -6,7 +6,6 @@ import "fmt"
import "time"
import "errors"
import "syscall"
import "os/user"
import "strconv"
import "path/filepath"
@@ -45,24 +44,6 @@ func Spawn (path string, uid, gid uint32, env []string, args ...string) (pid int
return process.Pid, process.Release()
}
// LookupUID returns the uid and gid of the given username, if it exists.
func LookupUID (name string) (uid, gid uint32, err error) {
user, err := user.Lookup(name)
if err != nil {
return 0, 0, err
}
puid, err := strconv.Atoi(user.Uid)
if err != nil {
return 0, 0, err
}
pgid, err := strconv.Atoi(user.Gid)
if err != nil {
return 0, 0, err
}
return uint32(puid), uint32(pgid), nil
}
// PidFile returns the path of a pidfile under the specified name. More
// specifically, it returns `/run/<name>.pid`.
func PidFile (name string) string {

View File

@@ -76,6 +76,8 @@ func main () {
delUserCommand := flag.NewFlagSet("deluser", flag.ExitOnError)
delUserService := delUserCommand.String ("s", "router",
"Service to delete the user for")
delUserRmData := delUserCommand.Bool ("D", false,
"Whether to remove the service's data directory")
authCommand := flag.NewFlagSet("auth", flag.ExitOnError)
authService := authCommand.String ("s", "router",
@@ -108,7 +110,7 @@ func main () {
execAdduser(*addUserService)
case "deluser":
delUserCommand.Parse(subCommandArgs)
execDeluser(*delUserService)
execDeluser(*delUserService, *delUserRmData)
case "auth":
authCommand.Parse(subCommandArgs)
execAuth(*authService, *authUser)
@@ -144,55 +146,68 @@ func execHash (cost int, key string) {
func execAdduser (service string) {
fullName := cli.ServiceUser(service)
dataDir := cli.ServiceDir(service)
// BUSYBOX
adduser, err := exec.LookPath("adduser")
if err == nil {
if adduser, err := exec.LookPath("adduser"); err == nil {
// BUSYBOX
addgroup, _ := exec.LookPath("addgroup")
tryCommand (exec.Command(addgroup, fullName, "-S"),
"could not add group")
tryCommand (exec.Command(adduser, fullName, "-SHDG", fullName),
tryCommand (exec.Command (
adduser, fullName, "-SHDG", fullName, "-h", dataDir),
"could not add user")
return
}
// GNU
useradd, err := exec.LookPath("useradd")
if err == nil {
} else if useradd, err := exec.LookPath("useradd"); err == nil {
// GNU
tryCommand (exec.Command (
useradd, fullName, "-rUM",
"--shell", "/sbin/nologin"), "could not add user")
return
"--shell", "/sbin/nologin",
"-d", dataDir), "could not add user")
} else {
cli.Sayf("could not add user: no command adduser or useradd\n")
os.Exit(1)
}
cli.Sayf("could not add user: no command adduser or useradd\n")
os.Exit(1)
// create data directory
uid, gid, err := cli.LookupUID(fullName)
if err != nil {
cli.Sayf("could not create data dir: %v\n", err)
os.Exit(1)
}
err = cli.MkdirFor(dataDir, int(uid), int(gid))
if err != nil {
cli.Sayf("could not create data dir: %v\n", err)
os.Exit(1)
}
}
func execDeluser (service string) {
func execDeluser (service string, rmData bool) {
fullName := cli.ServiceUser(service)
dataDir := cli.ServiceDir(service)
// BUSYBOX
deluser, err := exec.LookPath("deluser")
if err == nil {
if deluser, err := exec.LookPath("deluser"); err == nil {
// BUSYBOX
tryCommand (exec.Command(deluser, fullName, "--remove-home"),
"could not delete user")
return
}
// GNU
userdel, err := exec.LookPath("userdel")
if err == nil {
} else if userdel, err := exec.LookPath("userdel"); err == nil {
// GNU
tryCommand (exec.Command(userdel, fullName, "-r"),
"could not delete user")
groupdel, _ := exec.LookPath("groupdel")
tryCommand (exec.Command(groupdel, fullName),
"could not delete group")
return
} else {
cli.Sayf("could not delete user: no command deluser or userdel\n")
os.Exit(1)
}
cli.Sayf("could not delete user: no command deluser or userdel\n")
os.Exit(1)
// delete data directory
if rmData {
err := os.RemoveAll(dataDir)
if err != nil {
cli.Sayf("could not delete data dir: %v\n", err)
os.Exit(1)
}
}
}
func execAuth (service, user string) {

View File

@@ -6,6 +6,7 @@ import "fmt"
import "log"
import "time"
import "sync"
import "errors"
type routine struct {
run, shutdown func () error
@@ -71,17 +72,16 @@ type Manager struct {
// Run returns only when all routines have exited.
func (manager *Manager) Run () error {
var waitGroup sync.WaitGroup
var errExit error
for _, routine := range manager.Routines {
if routine != nil {
waitGroup.Add(1)
go manager.runRoutine(routine, &waitGroup, &errExit)
go manager.runRoutine(routine, &waitGroup)
}
}
waitGroup.Wait()
return errExit
return nil
}
// Shutdown shuts down all routines in the manager.
@@ -113,21 +113,26 @@ func (manager *Manager) log (message ...any) {
}
}
func (manager *Manager) runRoutine (routine Routine, group *sync.WaitGroup, errExit *error) {
func (manager *Manager) runRoutine (routine Routine, group *sync.WaitGroup) {
defer group.Done()
var err error
for {
lastStart := time.Now()
err := panicWrap(routine.Run)
stopping := false
manager.stoppingMutex.Lock()
stopping = manager.stopping
manager.stoppingMutex.Unlock()
if stopping { break }
// TODO: recover from panics
lastStart := time.Now()
err = routine.Run()
if stopping {
if err == nil {
manager.log("(i) stopped routine")
} else {
manager.log("!!! stopped routine, with error:", err)
}
break
}
if err == nil {
manager.log("(i) routine exited")
break
@@ -142,8 +147,15 @@ func (manager *Manager) runRoutine (routine Routine, group *sync.WaitGroup, errE
manager.log("(i) routine is being restarted")
}
}
if err != nil {
*errExit = err
}
}
func panicWrap (f func () error) (err error) {
defer func () {
if pan := recover(); pan != nil {
err = errors.New(fmt.Sprint(pan))
}
} ()
err = f()
return
}

View File

@@ -19,11 +19,13 @@ type HTTP struct {
Handler http.Handler
conn *Conn
running bool
requests requestManager
}
// Close closes the mount abruptly, interrupting any active connections.
func (htmount *HTTP) Close () error {
htmount.running = false
return htmount.conn.Close()
}
@@ -44,12 +46,19 @@ func (htmount *HTTP) Run (service ServiceInfo) (err error) {
}
htmount.conn, err = Dial(htmount.MountInfo, service)
if err != nil { return }
htmount.running = true
htmount.requests.init()
for {
message, err := htmount.conn.Receive()
if err != nil { return err }
if err != nil {
if htmount.running {
return err
} else {
return nil
}
}
switch message.(type) {
case protocol.MessageHTTPRequest: