blob: 9aaf2ddd8c352799216534d26cb89b0c2b38b981 [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
"syscall"
"time"
)
// findLxdProcesses searches for any running LXD daemons by looking at
// the executable being run, which is symlinked at /proc/PID/exe, and
// the commandline at /proc/PID/cmdline. For LXD this will be
// /usr/sbin/lxd and {"/usr/sbin/lxd", "--group", "lxd", "--syslog"}.
func findLxdProcesses() []int {
fileinfos, _ := ioutil.ReadDir("/proc")
// /proc/pid/cmdline contains the command line as a list of null-terminated strings.
cmdline := strings.Join(lxdCmd, "\x00") + "\x00"
var pids []int
for _, file := range fileinfos {
pid, err := strconv.Atoi(file.Name())
if err != nil {
// File/folder name is not an integer, so skip over this path
continue
}
dest, err := os.Readlink("/proc/" + file.Name() + "/exe")
if err != nil {
// The /proc interface unfortunately has a
// race condition in that a process could
// terminate between when we saw this path
// existed and when we try to read it. If this
// happens then we have no idea what the
// process was, but there's obviously no need
// to kill it, so we just skip over
// it. Hopefully that is the only case where
// Readlink can return an error.
continue
}
if dest != "/usr/sbin/lxd" {
continue
}
procCmdline, err := ioutil.ReadFile("/proc/" + file.Name() + "/cmdline")
if err != nil {
// As above, we just skip over this path if there's an error
continue
}
if string(procCmdline) == cmdline {
pids = append(pids, pid)
}
}
return pids
}
// StopLxdIfRunning searches for any running LXD processes and
// terminates them. This is intended to clean up any LXD processes
// that might hang around if tremplin crashes and restarts. This is
// best-effort, since it's difficult to reliably find and stop
// processes with the interface we have.
func (s *tremplinServer) StopLxdIfRunning() error {
// Bail out early in the usual case that we don't need to do anything.
pids := findLxdProcesses()
if len(pids) == 0 {
return nil
}
// Give LXD a chance to exit cleanly, and then SIGKILL it.
// This will leave the containers running and LXD will
// reconnect to them when it restarts.
for _, pid := range pids {
proc, _ := os.FindProcess(pid)
if err := proc.Signal(syscall.SIGTERM); err != nil {
return fmt.Errorf("Failed to request LXD shutdown: %w", err)
}
}
time.Sleep(5 * time.Second)
for _, pid := range findLxdProcesses() {
proc, _ := os.FindProcess(pid)
if err := proc.Kill(); err != nil {
return fmt.Errorf("Failed to kill LXD: %w", err)
}
}
return nil
}