mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-03 22:18:39 +00:00
This commit extends the documentation effort by adding detailed explanatory comments to key CLI components and core functionality throughout the cmd/ directory. The changes focus on explaining WHY certain logic is needed, not just WHAT the code does, improving code maintainability and helping developers understand complex business decisions. Key improvements: - Main entry points: Document CLI initialization, logging setup, and cache configuration with reasoning for design decisions - DNS proxy core: Explain DNS proxy constants, data structures, and core processing pipeline for handling DNS queries - Service management: Document service command structure, configuration patterns, and platform-specific service handling - Logging infrastructure: Explain log buffer management, level encoders, and log formatting decisions for different use cases - Metrics and monitoring: Document Prometheus metrics structure, HTTP endpoints, and conditional metric collection for performance - Network handling: Explain Linux-specific network interface filtering, virtual interface detection, and DNS configuration management - Hostname validation: Document RFC1123 compliance and DNS naming standards for system compatibility - Mobile integration: Explain HTTP retry logic, fallback mechanisms, and mobile platform integration patterns - Connection management: Document connection wrapper design to prevent log pollution during process lifecycle Technical details: - Added explanatory comments to 11 additional files in cmd/cli/ - Maintained consistent documentation style and format - Preserved all existing functionality while improving code clarity - Enhanced understanding of complex business logic and platform-specific behavior These comments help future developers understand the reasoning behind complex decisions, making the codebase more maintainable and reducing the risk of incorrect modifications during maintenance.
228 lines
5.7 KiB
Go
228 lines
5.7 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
|
|
"github.com/coreos/go-systemd/v22/unit"
|
|
"github.com/kardianos/service"
|
|
)
|
|
|
|
// newService wraps service.New call to return service.Service
|
|
// wrapper which is suitable for the current platform.
|
|
func newService(i service.Interface, c *service.Config) (service.Service, error) {
|
|
s, err := service.New(i, c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch {
|
|
case s.Platform() == "unix-systemv":
|
|
return &sysV{s}, nil
|
|
case s.Platform() == "linux-systemd":
|
|
return &systemd{s}, nil
|
|
case s.Platform() == "darwin-launchd":
|
|
return newLaunchd(s), nil
|
|
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// sysV wraps a service.Service, and provide start/stop/status command
|
|
// base on "/etc/init.d/<service_name>".
|
|
//
|
|
// Use this on system where "service" command is not available.
|
|
type sysV struct {
|
|
service.Service
|
|
}
|
|
|
|
func (s *sysV) installed() bool {
|
|
fi, err := os.Stat("/etc/init.d/ctrld")
|
|
if err != nil {
|
|
return false
|
|
}
|
|
mode := fi.Mode()
|
|
return mode.IsRegular() && (mode&0111) != 0
|
|
}
|
|
|
|
func (s *sysV) Start() error {
|
|
if !s.installed() {
|
|
return service.ErrNotInstalled
|
|
}
|
|
_, err := exec.Command("/etc/init.d/ctrld", "start").CombinedOutput()
|
|
return err
|
|
}
|
|
|
|
func (s *sysV) Stop() error {
|
|
if !s.installed() {
|
|
return service.ErrNotInstalled
|
|
}
|
|
_, err := exec.Command("/etc/init.d/ctrld", "stop").CombinedOutput()
|
|
return err
|
|
}
|
|
|
|
func (s *sysV) Restart() error {
|
|
if !s.installed() {
|
|
return service.ErrNotInstalled
|
|
}
|
|
// We don't care about error returned by s.Stop,
|
|
// because the service may already be stopped.
|
|
_ = s.Stop()
|
|
return s.Start()
|
|
}
|
|
|
|
func (s *sysV) Status() (service.Status, error) {
|
|
if !s.installed() {
|
|
return service.StatusUnknown, service.ErrNotInstalled
|
|
}
|
|
return unixSystemVServiceStatus()
|
|
}
|
|
|
|
// systemd wraps a service.Service, and provide status command to
|
|
// report the status correctly.
|
|
type systemd struct {
|
|
service.Service
|
|
}
|
|
|
|
func (s *systemd) Status() (service.Status, error) {
|
|
out, _ := exec.Command("systemctl", "status", "ctrld").CombinedOutput()
|
|
if bytes.Contains(out, []byte("/FAILURE)")) {
|
|
return service.StatusStopped, nil
|
|
}
|
|
return s.Service.Status()
|
|
}
|
|
|
|
func (s *systemd) Start() error {
|
|
const systemdUnitFile = "/etc/systemd/system/ctrld.service"
|
|
f, err := os.Open(systemdUnitFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
if opts, change := ensureSystemdKillMode(f); change {
|
|
mode := os.FileMode(0644)
|
|
buf, err := io.ReadAll(unit.Serialize(opts))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := os.WriteFile(systemdUnitFile, buf, mode); err != nil {
|
|
return err
|
|
}
|
|
if out, err := exec.Command("systemctl", "daemon-reload").CombinedOutput(); err != nil {
|
|
return fmt.Errorf("systemctl daemon-reload failed: %w\n%s", err, string(out))
|
|
}
|
|
mainLog.Load().Debug().Msg("set KillMode=process successfully")
|
|
}
|
|
return s.Service.Start()
|
|
}
|
|
|
|
// ensureSystemdKillMode ensure systemd unit file is configured with KillMode=process.
|
|
// This is necessary for running self-upgrade flow.
|
|
func ensureSystemdKillMode(r io.Reader) (opts []*unit.UnitOption, change bool) {
|
|
opts, err := unit.DeserializeOptions(r)
|
|
if err != nil {
|
|
mainLog.Load().Error().Err(err).Msg("failed to deserialize options")
|
|
return
|
|
}
|
|
change = true
|
|
needKillModeOpt := true
|
|
killModeOpt := unit.NewUnitOption("Service", "KillMode", "process")
|
|
for _, opt := range opts {
|
|
if opt.Match(killModeOpt) {
|
|
needKillModeOpt = false
|
|
change = false
|
|
break
|
|
}
|
|
if opt.Section == killModeOpt.Section && opt.Name == killModeOpt.Name {
|
|
opt.Value = killModeOpt.Value
|
|
needKillModeOpt = false
|
|
break
|
|
}
|
|
}
|
|
if needKillModeOpt {
|
|
opts = append(opts, killModeOpt)
|
|
}
|
|
return opts, change
|
|
}
|
|
|
|
// newLaunchd creates a new launchd service wrapper
|
|
func newLaunchd(s service.Service) *launchd {
|
|
return &launchd{
|
|
Service: s,
|
|
statusErrMsg: "Permission denied",
|
|
}
|
|
}
|
|
|
|
// launchd wraps a service.Service, and provide status command to
|
|
// report the status correctly when not running as root on Darwin.
|
|
//
|
|
// TODO: remove this wrapper once https://github.com/kardianos/service/issues/400 fixed.
|
|
type launchd struct {
|
|
service.Service
|
|
statusErrMsg string
|
|
}
|
|
|
|
func (l *launchd) Status() (service.Status, error) {
|
|
if os.Geteuid() != 0 {
|
|
return service.StatusUnknown, errors.New(l.statusErrMsg)
|
|
}
|
|
return l.Service.Status()
|
|
}
|
|
|
|
type task struct {
|
|
f func() error
|
|
abortOnError bool
|
|
Name string
|
|
}
|
|
|
|
// doTasks executes a list of tasks and returns success status
|
|
func doTasks(tasks []task) bool {
|
|
for _, task := range tasks {
|
|
mainLog.Load().Debug().Msgf("Running task %s", task.Name)
|
|
if err := task.f(); err != nil {
|
|
if task.abortOnError {
|
|
mainLog.Load().Error().Msgf("error running task %s: %v", task.Name, err)
|
|
return false
|
|
}
|
|
// if this is darwin stop command, dont print debug
|
|
// since launchctl complains on every start
|
|
if runtime.GOOS != "darwin" || task.Name != "Stop" {
|
|
mainLog.Load().Debug().Msgf("error running task %s: %v", task.Name, err)
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// checkHasElevatedPrivilege checks if the process has elevated privileges and exits if not
|
|
func checkHasElevatedPrivilege() {
|
|
ok, err := hasElevatedPrivilege()
|
|
if err != nil {
|
|
mainLog.Load().Error().Msgf("could not detect user privilege: %v", err)
|
|
return
|
|
}
|
|
if !ok {
|
|
mainLog.Load().Error().Msg("Please relaunch process with admin/root privilege.")
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// unixSystemVServiceStatus checks the status of a Unix System V service
|
|
func unixSystemVServiceStatus() (service.Status, error) {
|
|
out, err := exec.Command("/etc/init.d/ctrld", "status").CombinedOutput()
|
|
if err != nil {
|
|
return service.StatusUnknown, nil
|
|
}
|
|
|
|
switch string(bytes.ToLower(bytes.TrimSpace(out))) {
|
|
case "running":
|
|
return service.StatusRunning, nil
|
|
default:
|
|
return service.StatusStopped, nil
|
|
}
|
|
}
|