File: socket.go

package info (click to toggle)
incus 6.0.5-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 24,428 kB
  • sloc: sh: 16,313; ansic: 3,121; python: 457; makefile: 337; ruby: 51; sql: 50; lisp: 6
file content (136 lines) | stat: -rw-r--r-- 3,132 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//go:build linux && cgo

package endpoints

import (
	"errors"
	"fmt"
	"net"
	"os"
	"os/exec"
	"os/user"
	"strconv"

	incus "github.com/lxc/incus/v6/client"
	"github.com/lxc/incus/v6/shared/logger"
	"github.com/lxc/incus/v6/shared/subprocess"
	"github.com/lxc/incus/v6/shared/util"
)

// Bind to the given unix socket path.
func socketUnixListen(path string) (*net.UnixListener, error) {
	addr, err := net.ResolveUnixAddr("unix", path)
	if err != nil {
		return nil, fmt.Errorf("cannot resolve socket address: %w", err)
	}

	listener, err := net.ListenUnix("unix", addr)
	if err != nil {
		return nil, fmt.Errorf("cannot bind socket: %w", err)
	}

	return listener, err
}

// CheckAlreadyRunning checks if the socket at the given path is already
// bound to a running process, and return an error if so.
//
//	FIXME: We should probably rather just try a regular unix socket
//		connection without using the client. However this is the way
//		this logic has historically behaved, so let's keep it like it
//		was.
func CheckAlreadyRunning(path string) error {
	// If socket activated, nothing to do
	pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
	if err == nil {
		if pid == os.Getpid() {
			return nil
		}
	}

	// If there's no socket file at all, there's nothing to do.
	if !util.PathExists(path) {
		return nil
	}

	_, err = incus.ConnectIncusUnix(path, nil)

	// If the connection succeeded it means there's another daemon running.
	if err == nil {
		return errors.New("Incus is already running")
	}

	return nil
}

// Remove any stale socket file at the given path.
func socketUnixRemoveStale(path string) error {
	// If there's no socket file at all, there's nothing to do.
	if !util.PathExists(path) {
		return nil
	}

	logger.Debugf("Detected stale unix socket, deleting")
	err := os.Remove(path)
	if err != nil {
		return fmt.Errorf("could not delete stale local socket: %w", err)
	}

	return nil
}

// Change the file mode of the given unix socket file,.
func socketUnixSetPermissions(path string, mode os.FileMode) error {
	err := os.Chmod(path, mode)
	if err != nil {
		return fmt.Errorf("cannot set permissions on local socket: %w", err)
	}

	return nil
}

// Change the ownership of the given unix socket file,.
func socketUnixSetOwnership(path string, groupName string) error {
	var gid int
	var err error

	if groupName != "" {
		g, err := user.LookupGroup(groupName)
		if err != nil {
			return fmt.Errorf("cannot get group ID of '%s': %w", groupName, err)
		}

		gid, err = strconv.Atoi(g.Gid)
		if err != nil {
			return err
		}
	} else {
		gid = os.Getgid()
	}

	err = os.Chown(path, os.Getuid(), gid)
	if err != nil {
		return fmt.Errorf("cannot change ownership on local socket: %w", err)
	}

	return nil
}

// Set the SELinux label on the socket.
func socketUnixSetLabel(path string, label string) error {
	// Skip if no label requested.
	if label == "" {
		return nil
	}

	// Check if chcon is installed.
	_, err := exec.LookPath("chcon")
	if err != nil {
		return nil
	}

	// Attempt to apply (don't fail as kernel may not support it).
	_, _ = subprocess.RunCommand("chcon", label, path)

	return nil
}