1
0
mirror of https://github.com/lxc/incus.git synced 2026-02-05 09:46:19 +01:00
Files
incus/cmd/incus-user/main_daemon.go
Olivier Lechevalier e3f36b1c7d cmd/incus-user: refactor to not use os.Exit and instead close listener
In order to test did the following:

`systemd-socket-activate -l /run/incus-user.sock $PWD/incus-user -d -v 2>&1`

in separate terminal connect and disconnect to the socket:

`nc -U /run/my_app.sock`

Wait and obtained following shutdown log:

```
INFO   [2025-11-24T12:33:45+09:00] Starting up the server
INFO   [2025-11-24T12:34:20+09:00] Daemon has been inactive, shutting down
```

Signed-off-by: Olivier Lechevalier <olivier.lechevalier@gmail.com>
2025-11-24 12:41:11 +09:00

206 lines
4.5 KiB
Go

package main
import (
"errors"
"fmt"
"net"
"os"
"os/user"
"strconv"
"sync"
"time"
"github.com/spf13/cobra"
incus "github.com/lxc/incus/v6/client"
"github.com/lxc/incus/v6/internal/linux"
internalUtil "github.com/lxc/incus/v6/internal/util"
"github.com/lxc/incus/v6/shared/logger"
)
var (
mu sync.RWMutex
connections uint64
transactions uint64
)
var projectNames []string
type cmdDaemon struct {
flagGroup string
}
func (c *cmdDaemon) Command() *cobra.Command {
cmd := &cobra.Command{}
cmd.Use = "incus-user"
cmd.RunE = c.Run
cmd.Flags().StringVar(&c.flagGroup, "group", "", "The group of users that will be allowed to talk to incus-user"+"``")
return cmd
}
func (c *cmdDaemon) Run(cmd *cobra.Command, args []string) error {
// Only root should run this.
if os.Geteuid() != 0 {
return errors.New("This must be run as root")
}
// Create storage.
err := os.MkdirAll(internalUtil.VarPath("users"), 0o700)
if err != nil && !os.IsExist(err) {
return fmt.Errorf("Couldn't create storage: %w", err)
}
// Connect.
logger.Debug("Connecting to the daemon")
client, err := incus.ConnectIncusUnix("", nil)
if err != nil {
return fmt.Errorf("Unable to connect to the daemon: %w", err)
}
cinfo, err := client.GetConnectionInfo()
if err != nil {
return fmt.Errorf("Failed to obtain connection info: %w", err)
}
// Keep track of the socket path we used to successfully connect to the server
serverUnixPath := cinfo.SocketPath
// Validate the configuration.
ok, err := serverIsConfigured(client)
if err != nil {
return fmt.Errorf("Failed to check the configuration: %w", err)
}
if !ok {
logger.Info("Performing initial configuration")
err = serverInitialConfiguration(client)
if err != nil {
return fmt.Errorf("Failed to apply initial configuration: %w", err)
}
}
// Pull the list of projects.
projectNames, err = client.GetProjectNames()
if err != nil {
return fmt.Errorf("Failed to pull project list: %w", err)
}
// Disconnect.
client.Disconnect()
// Setup the unix socket.
listeners := linux.GetSystemdListeners(linux.SystemdListenFDsStart)
if len(listeners) > 1 {
return errors.New("More than one socket-activation FD received")
}
var listener *net.UnixListener
if len(listeners) == 1 {
// Handle socket activation.
unixListener, ok := listeners[0].(*net.UnixListener)
if !ok {
return errors.New("Socket-activation FD isn't a unix socket")
}
listener = unixListener
// Automatically shutdown after inactivity.
go func(unixListener *net.UnixListener) {
for {
time.Sleep(30 * time.Second)
// Check for active connections.
mu.RLock()
if connections > 0 {
mu.RUnlock()
continue
}
// Look for recent activity
oldCount := transactions
mu.RUnlock()
time.Sleep(5 * time.Second)
mu.RLock()
if oldCount == transactions {
mu.RUnlock()
// Daemon has been inactive for 10s, exit.
logger.Info("Daemon has been inactive, shutting down")
err := unixListener.Close()
if err != nil {
logger.Errorf("Failed to close Unix listener: %v", err)
}
return
}
mu.RUnlock()
}
}(listener)
} else {
// Create our own socket.
unixPath := internalUtil.VarPath("unix.socket.user")
err := os.Remove(unixPath)
if err != nil && !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("Failed to delete pre-existing unix socket: %w", err)
}
unixAddr, err := net.ResolveUnixAddr("unix", unixPath)
if err != nil {
return fmt.Errorf("Unable to resolve unix socket: %w", err)
}
server, err := net.ListenUnix("unix", unixAddr)
if err != nil {
return fmt.Errorf("Unable to setup unix socket: %w", err)
}
err = os.Chmod(unixPath, 0o660)
if err != nil {
return fmt.Errorf("Unable to set socket permissions: %w", err)
}
if c.flagGroup != "" {
g, err := user.LookupGroup(c.flagGroup)
if err != nil {
return fmt.Errorf("Cannot get group ID of '%s': %w", c.flagGroup, err)
}
gid, err := strconv.Atoi(g.Gid)
if err != nil {
return err
}
err = os.Chown(unixPath, os.Getuid(), gid)
if err != nil {
return fmt.Errorf("Cannot change ownership on local socket: %w", err)
}
}
server.SetUnlinkOnClose(true)
listener = server
}
// Start accepting requests.
logger.Info("Starting up the server")
for {
// Accept new connection.
conn, err := listener.AcceptUnix()
if err != nil {
if errors.Is(err, net.ErrClosed) {
return nil
}
logger.Errorf("Failed to accept new connection: %v", err)
continue
}
go proxyConnection(conn, serverUnixPath)
}
}