Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a runtime directory #426

Merged
merged 15 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/incusd/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -1521,15 +1521,15 @@ func (d *Daemon) init() error {

// Setup seccomp handler
if d.os.SeccompListener {
seccompServer, err := seccomp.NewSeccompServer(d.State(), internalUtil.VarPath("seccomp.socket"), func(pid int32, state *state.State) (seccomp.Instance, error) {
seccompServer, err := seccomp.NewSeccompServer(d.State(), internalUtil.RunPath("seccomp.socket"), func(pid int32, state *state.State) (seccomp.Instance, error) {
return findContainerForPid(pid, state)
})
if err != nil {
return err
}

d.seccomp = seccompServer
logger.Info("Started seccomp handler", logger.Ctx{"path": internalUtil.VarPath("seccomp.socket")})
logger.Info("Started seccomp handler", logger.Ctx{"path": internalUtil.RunPath("seccomp.socket")})
}

// Read the trusted certificates
Expand Down
3 changes: 0 additions & 3 deletions cmd/incusd/instance_logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ var instanceExecOutputsCmd = APIEndpoint{
// type: string
// example: |-
// [
// "/1.0/instances/foo/logs/lxc.conf",
// "/1.0/instances/foo/logs/lxc.log"
// ]
// "403":
Expand Down Expand Up @@ -670,9 +669,7 @@ func validLogFileName(fname string) bool {
* to deal with any escaping or whatever.
*/
return fname == "lxc.log" ||
fname == "lxc.conf" ||
fname == "qemu.log" ||
fname == "qemu.conf" ||
strings.HasPrefix(fname, "migration_") ||
strings.HasPrefix(fname, "snapshot_")
}
Expand Down
58 changes: 58 additions & 0 deletions cmd/incusd/patches.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/lxc/incus/internal/server/db"
dbCluster "github.com/lxc/incus/internal/server/db/cluster"
"github.com/lxc/incus/internal/server/db/query"
"github.com/lxc/incus/internal/server/instance"
"github.com/lxc/incus/internal/server/instance/instancetype"
"github.com/lxc/incus/internal/server/network"
"github.com/lxc/incus/internal/server/node"
Expand Down Expand Up @@ -81,6 +82,7 @@ var patches = []patch{
{name: "snapshots_rename", stage: patchPreDaemonStorage, run: patchSnapshotsRename},
{name: "storage_zfs_unset_invalid_block_settings", stage: patchPostDaemonStorage, run: patchStorageZfsUnsetInvalidBlockSettings},
{name: "storage_zfs_unset_invalid_block_settings_v2", stage: patchPostDaemonStorage, run: patchStorageZfsUnsetInvalidBlockSettingsV2},
{name: "runtime_directory", stage: patchPostDaemonStorage, run: patchRuntimeDirectory},
}

type patch struct {
Expand Down Expand Up @@ -1186,4 +1188,60 @@ func patchStorageZfsUnsetInvalidBlockSettingsV2(_ string, d *Daemon) error {
return nil
}

func patchRuntimeDirectory(name string, d *Daemon) error {
s := d.State()

// Get the list of local instances.
instances, err := instance.LoadNodeAll(s, instancetype.Any)
if err != nil {
return fmt.Errorf("Failed loading local instances: %w", err)
}

for _, inst := range instances {
if !util.PathExists(inst.LogPath()) {
continue
}

err = os.MkdirAll(inst.RunPath(), 0700)
if err != nil && !os.IsExist(err) {
return fmt.Errorf("Failed to create runtime directory for %q in project %q: %w", inst.Name(), inst.Project(), err)
}

files, err := os.ReadDir(inst.LogPath())
if err != nil {
return fmt.Errorf("Failed to list log files for %q in project %q: %w", inst.Name(), inst.Project(), err)
}

for _, fi := range files {
name := fi.Name()

// Keep actual log files where they are.
if strings.HasSuffix(name, ".log") || strings.HasSuffix(name, ".log.old") || strings.HasSuffix(name, ".gz") {
continue
}

info, err := fi.Info()
if err != nil {
return fmt.Errorf("Failed getting file info on %q for instance %q in project %q: %w", name, inst.Name(), inst.Project(), err)
}

if info.Mode().IsRegular() {
// Relocate the file.
_, err := subprocess.RunCommand("mv", filepath.Join(inst.LogPath(), name), inst.RunPath())
if err != nil {
return fmt.Errorf("Failed to relocate runtime file %q for instance %q in project %q: %w", name, inst.Name(), inst.Project(), err)
}
} else {
// For pipes and sockets, we need to use a symlink to avoid breaking the listener.
err := os.Symlink(filepath.Join(inst.LogPath(), name), filepath.Join(inst.RunPath(), name))
if err != nil {
return fmt.Errorf("Failed to symlink runtime file %q for instance %q in project %q: %w", name, inst.Name(), inst.Project(), err)
}
}
}
}

return nil
}

// Patches end here
2 changes: 1 addition & 1 deletion doc/reference/instance_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ Therefore, you should avoid setting any of these keys.

For VM instances, Incus configures QEMU through a configuration file that is passed to QEMU with the `-readconfig` command-line option.
This configuration file is generated for each instance before boot.
It can be found at `/var/log/incus/<instance_name>/qemu.conf`.
It can be found at `/run/incus/<instance_name>/qemu.conf`.

The default configuration works fine for Incus' most common use case: modern UEFI guests with VirtIO devices.
In some situations, however, you might need to override the generated configuration.
Expand Down
1 change: 0 additions & 1 deletion doc/rest-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9055,7 +9055,6 @@ paths:
description: List of endpoints
example: |-
[
"/1.0/instances/foo/logs/lxc.conf",
"/1.0/instances/foo/logs/lxc.log"
]
items:
Expand Down
2 changes: 2 additions & 0 deletions internal/server/apparmor/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type instance interface {
ExpandedConfig() map[string]string
Type() instancetype.Type
LogPath() string
RunPath() string
Path() string
DevicesPath() string
}
Expand Down Expand Up @@ -211,6 +212,7 @@ func instanceProfile(sysOS *sys.OS, inst instance, extraBinaries []string) (stri
"extra_binaries": extraBinaries,
"libraryPath": strings.Split(os.Getenv("LD_LIBRARY_PATH"), ":"),
"logPath": inst.LogPath(),
"runPath": inst.RunPath(),
"name": InstanceProfileName(inst),
"path": path,
"raw": rawContent,
Expand Down
1 change: 1 addition & 0 deletions internal/server/apparmor/instance_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ profile "{{ .name }}" flags=(attach_disconnected,mediate_deleted) {

# Instance specific paths
{{ .logPath }}/** rwk,
{{ .runPath }}/** rwk,
{{ .path }}/** rwk,
{{ .devicesPath }}/** rwk,

Expand Down
6 changes: 6 additions & 0 deletions internal/server/instance/drivers/driver_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,12 @@ func (d *common) LogPath() string {
return internalUtil.LogPath(name)
}

// RunPath returns the instance's runtime path.
func (d *common) RunPath() string {
name := project.Instance(d.project.Name, d.name)
return internalUtil.RunPath(name)
}

// Path returns the instance's path.
func (d *common) Path() string {
return storagePools.InstancePath(d.dbType, d.project.Name, d.name, d.isSnapshot)
Expand Down
39 changes: 27 additions & 12 deletions internal/server/instance/drivers/driver_lxc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@ func (d *lxc) initLXC(config bool) (*liblxc.Container, error) {
// System requirement errors are handled during policy generation instead of here
ok, err := seccomp.InstanceNeedsIntercept(d.state, d)
if err == nil && ok {
err = lxcSetConfigItem(cc, "lxc.seccomp.notify.proxy", fmt.Sprintf("unix:%s", internalUtil.VarPath("seccomp.socket")))
err = lxcSetConfigItem(cc, "lxc.seccomp.notify.proxy", fmt.Sprintf("unix:%s", internalUtil.RunPath("seccomp.socket")))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -2052,6 +2052,11 @@ func (d *lxc) startCommon() (string, []func() error, error) {
return "", nil, err
}

err = os.MkdirAll(d.RunPath(), 0700)
if err != nil {
return "", nil, err
}

err = os.MkdirAll(d.DevicesPath(), 0711)
if err != nil {
return "", nil, err
Expand Down Expand Up @@ -2285,7 +2290,7 @@ func (d *lxc) startCommon() (string, []func() error, error) {
}

// Generate the LXC config
configPath := filepath.Join(d.LogPath(), "lxc.conf")
configPath := filepath.Join(d.RunPath(), "lxc.conf")
err = cc.SaveConfigFile(configPath)
if err != nil {
_ = os.Remove(configPath)
Expand Down Expand Up @@ -3962,6 +3967,16 @@ func (d *lxc) Rename(newName string, applyTemplateTrigger bool) error {
}
}

// Rename the runtime path.
newFullName = project.Instance(d.Project().Name, d.Name())
_ = os.RemoveAll(internalUtil.RunPath(newFullName))
if util.PathExists(d.RunPath()) {
err := os.Rename(d.RunPath(), internalUtil.RunPath(newFullName))
if err != nil {
d.logger.Error("Failed renaming instance", ctxMap)
return fmt.Errorf("Failed renaming instance: %w", err)
}
}
revert := revert.New()
defer revert.Fail()

Expand Down Expand Up @@ -6709,13 +6724,13 @@ func (d *lxc) FileSFTPConn() (net.Conn, error) {
defer spawnUnlock()

// Create any missing directories in case the instance has never been started before.
err = os.MkdirAll(d.LogPath(), 0700)
err = os.MkdirAll(d.RunPath(), 0700)
if err != nil {
return nil, err
}

// Trickery to handle paths > 108 chars.
dirFile, err := os.Open(d.LogPath())
dirFile, err := os.Open(d.RunPath())
if err != nil {
return nil, err
}
Expand All @@ -6728,7 +6743,7 @@ func (d *lxc) FileSFTPConn() (net.Conn, error) {
}

// Attempt to connect on existing socket.
forkfilePath := filepath.Join(d.LogPath(), "forkfile.sock")
forkfilePath := filepath.Join(d.RunPath(), "forkfile.sock")
forkfileConn, err := net.DialUnix("unix", nil, forkfileAddr)
if err == nil {
// Found an existing server.
Expand Down Expand Up @@ -6871,7 +6886,7 @@ func (d *lxc) FileSFTPConn() (net.Conn, error) {
}

// Write PID file.
pidFile := filepath.Join(d.LogPath(), "forkfile.pid")
pidFile := filepath.Join(d.RunPath(), "forkfile.pid")
err = os.WriteFile(pidFile, []byte(fmt.Sprintf("%d\n", forkfile.Process.Pid)), 0600)
if err != nil {
chReady <- fmt.Errorf("Failed to write forkfile PID: %w", err)
Expand Down Expand Up @@ -6951,7 +6966,7 @@ func (d *lxc) stopForkfile(force bool) {
unlock()
}()

content, err := os.ReadFile(filepath.Join(d.LogPath(), "forkfile.pid"))
content, err := os.ReadFile(filepath.Join(d.RunPath(), "forkfile.pid"))
if err != nil {
return
}
Expand Down Expand Up @@ -6985,7 +7000,7 @@ func (d *lxc) Console(protocol string) (*os.File, chan error, error) {
"forkconsole",
project.Instance(d.Project().Name, d.Name()),
d.state.OS.LxcPath,
filepath.Join(d.LogPath(), "lxc.conf"),
filepath.Join(d.RunPath(), "lxc.conf"),
"tty=0",
"escape=-1"}

Expand Down Expand Up @@ -7063,7 +7078,7 @@ func (d *lxc) ConsoleLog(opts liblxc.ConsoleLogOptions) (string, error) {
// Exec executes a command inside the instance.
func (d *lxc) Exec(req api.InstanceExecPost, stdin *os.File, stdout *os.File, stderr *os.File) (instance.Cmd, error) {
// Generate the LXC config if missing.
configPath := filepath.Join(d.LogPath(), "lxc.conf")
configPath := filepath.Join(d.RunPath(), "lxc.conf")
if !util.PathExists(configPath) {
cc, err := d.initLXC(true)
if err != nil {
Expand Down Expand Up @@ -7100,7 +7115,7 @@ func (d *lxc) Exec(req api.InstanceExecPost, stdin *os.File, stdout *os.File, st
"forkexec",
cname,
d.state.OS.LxcPath,
filepath.Join(d.LogPath(), "lxc.conf"),
filepath.Join(d.RunPath(), "lxc.conf"),
req.Cwd,
fmt.Sprintf("%d", req.User),
fmt.Sprintf("%d", req.Group),
Expand Down Expand Up @@ -7592,7 +7607,7 @@ func (d *lxc) insertMountGo(source, target, fstype string, flags int, mntnsPID i

func (d *lxc) insertMountLXC(source, target, fstype string, flags int) error {
cname := project.Instance(d.Project().Name, d.Name())
configPath := filepath.Join(d.LogPath(), "lxc.conf")
configPath := filepath.Join(d.RunPath(), "lxc.conf")
if fstype == "" {
fstype = "none"
}
Expand Down Expand Up @@ -7688,7 +7703,7 @@ func (d *lxc) removeMount(mount string) error {
}

if d.state.OS.LXCFeatures["mount_injection_file"] {
configPath := filepath.Join(d.LogPath(), "lxc.conf")
configPath := filepath.Join(d.RunPath(), "lxc.conf")
cname := project.Instance(d.Project().Name, d.Name())

if !strings.HasPrefix(mount, "/") {
Expand Down
31 changes: 24 additions & 7 deletions internal/server/instance/drivers/driver_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -582,8 +582,8 @@ func (d *qemu) configDriveMountPathClear() error {

// configVirtiofsdPaths returns the path for the socket and PID file to use with config drive virtiofsd process.
func (d *qemu) configVirtiofsdPaths() (string, string) {
sockPath := filepath.Join(d.LogPath(), "virtio-fs.config.sock")
pidPath := filepath.Join(d.LogPath(), "virtiofsd.pid")
sockPath := filepath.Join(d.RunPath(), "virtio-fs.config.sock")
pidPath := filepath.Join(d.RunPath(), "virtiofsd.pid")

return sockPath, pidPath
}
Expand Down Expand Up @@ -1226,6 +1226,12 @@ func (d *qemu) start(stateful bool, op *operationlock.InstanceOperation) error {
return err
}

err = os.MkdirAll(d.RunPath(), 0700)
if err != nil {
op.Done(err)
return err
}

err = os.MkdirAll(d.DevicesPath(), 0711)
if err != nil {
op.Done(err)
Expand Down Expand Up @@ -2546,19 +2552,19 @@ func (d *qemu) deviceDetachNIC(deviceName string) error {
}

func (d *qemu) monitorPath() string {
return filepath.Join(d.LogPath(), "qemu.monitor")
return filepath.Join(d.RunPath(), "qemu.monitor")
}

func (d *qemu) nvramPath() string {
return filepath.Join(d.Path(), "qemu.nvram")
}

func (d *qemu) consolePath() string {
return filepath.Join(d.LogPath(), "qemu.console")
return filepath.Join(d.RunPath(), "qemu.console")
}

func (d *qemu) spicePath() string {
return filepath.Join(d.LogPath(), "qemu.spice")
return filepath.Join(d.RunPath(), "qemu.spice")
}

func (d *qemu) spiceCmdlineConfig() string {
Expand Down Expand Up @@ -3435,7 +3441,7 @@ func (d *qemu) generateQemuConfigFile(cpuInfo *cpuTopology, mountInfo *storagePo
cfg = qemuRawCfgOverride(cfg, d.expandedConfig)
// Write the config file to disk.
sb := qemuStringifyCfg(cfg...)
configPath := filepath.Join(d.LogPath(), "qemu.conf")
configPath := filepath.Join(d.RunPath(), "qemu.conf")
return configPath, monHooks, os.WriteFile(configPath, []byte(sb.String()), 0640)
}

Expand Down Expand Up @@ -4589,7 +4595,7 @@ func (d *qemu) addVmgenDeviceConfig(cfg *[]cfgSection, guid string) error {

// pidFilePath returns the path where the qemu process should write its PID.
func (d *qemu) pidFilePath() string {
return filepath.Join(d.LogPath(), "qemu.pid")
return filepath.Join(d.RunPath(), "qemu.pid")
}

// pid gets the PID of the running qemu process. Returns 0 if PID file or process not found, and -1 if err non-nil.
Expand Down Expand Up @@ -5100,6 +5106,17 @@ func (d *qemu) Rename(newName string, applyTemplateTrigger bool) error {
}
}

// Rename the runtime path.
newFullName = project.Instance(d.Project().Name, d.Name())
_ = os.RemoveAll(internalUtil.RunPath(newFullName))
if util.PathExists(d.RunPath()) {
err := os.Rename(d.RunPath(), internalUtil.RunPath(newFullName))
if err != nil {
d.logger.Error("Failed renaming instance", ctxMap)
return err
}
}

revert := revert.New()
defer revert.Fail()

Expand Down
1 change: 1 addition & 0 deletions internal/server/instance/instance_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ type Instance interface {
LogFilePath() string
ConsoleBufferLogPath() string
LogPath() string
RunPath() string
DevicesPath() string

// Storage.
Expand Down