171 lines
3.6 KiB
Go
171 lines
3.6 KiB
Go
// +build linux
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/opencontainers/runc/libcontainer"
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
type notifySocket struct {
|
|
socket *net.UnixConn
|
|
host string
|
|
socketPath string
|
|
}
|
|
|
|
func newNotifySocket(context *cli.Context, notifySocketHost string, id string) *notifySocket {
|
|
if notifySocketHost == "" {
|
|
return nil
|
|
}
|
|
|
|
root := filepath.Join(context.GlobalString("root"), id)
|
|
socketPath := filepath.Join(root, "notify", "notify.sock")
|
|
|
|
notifySocket := ¬ifySocket{
|
|
socket: nil,
|
|
host: notifySocketHost,
|
|
socketPath: socketPath,
|
|
}
|
|
|
|
return notifySocket
|
|
}
|
|
|
|
func (s *notifySocket) Close() error {
|
|
return s.socket.Close()
|
|
}
|
|
|
|
// If systemd is supporting sd_notify protocol, this function will add support
|
|
// for sd_notify protocol from within the container.
|
|
func (s *notifySocket) setupSpec(context *cli.Context, spec *specs.Spec) error {
|
|
pathInContainer := filepath.Join("/run/notify", path.Base(s.socketPath))
|
|
mount := specs.Mount{
|
|
Destination: path.Dir(pathInContainer),
|
|
Source: path.Dir(s.socketPath),
|
|
Options: []string{"bind", "nosuid", "noexec", "nodev", "ro"},
|
|
}
|
|
spec.Mounts = append(spec.Mounts, mount)
|
|
spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", pathInContainer))
|
|
return nil
|
|
}
|
|
|
|
func (s *notifySocket) bindSocket() error {
|
|
addr := net.UnixAddr{
|
|
Name: s.socketPath,
|
|
Net: "unixgram",
|
|
}
|
|
|
|
socket, err := net.ListenUnixgram("unixgram", &addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = os.Chmod(s.socketPath, 0777)
|
|
if err != nil {
|
|
socket.Close()
|
|
return err
|
|
}
|
|
|
|
s.socket = socket
|
|
return nil
|
|
}
|
|
|
|
func (s *notifySocket) setupSocketDirectory() error {
|
|
return os.Mkdir(path.Dir(s.socketPath), 0755)
|
|
}
|
|
|
|
func notifySocketStart(context *cli.Context, notifySocketHost, id string) (*notifySocket, error) {
|
|
notifySocket := newNotifySocket(context, notifySocketHost, id)
|
|
if notifySocket == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
if err := notifySocket.bindSocket(); err != nil {
|
|
return nil, err
|
|
}
|
|
return notifySocket, nil
|
|
}
|
|
|
|
func (n *notifySocket) waitForContainer(container libcontainer.Container) error {
|
|
s, err := container.State()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return n.run(s.InitProcessPid)
|
|
}
|
|
|
|
func (n *notifySocket) run(pid1 int) error {
|
|
if n.socket == nil {
|
|
return nil
|
|
}
|
|
notifySocketHostAddr := net.UnixAddr{Name: n.host, Net: "unixgram"}
|
|
client, err := net.DialUnix("unixgram", nil, ¬ifySocketHostAddr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ticker := time.NewTicker(time.Millisecond * 100)
|
|
defer ticker.Stop()
|
|
|
|
fileChan := make(chan []byte)
|
|
go func() {
|
|
for {
|
|
buf := make([]byte, 4096)
|
|
r, err := n.socket.Read(buf)
|
|
if err != nil {
|
|
return
|
|
}
|
|
got := buf[0:r]
|
|
// systemd-ready sends a single datagram with the state string as payload,
|
|
// so we don't need to worry about partial messages.
|
|
for _, line := range bytes.Split(got, []byte{'\n'}) {
|
|
if bytes.HasPrefix(got, []byte("READY=")) {
|
|
fileChan <- line
|
|
return
|
|
}
|
|
}
|
|
|
|
}
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
_, err := os.Stat(filepath.Join("/proc", strconv.Itoa(pid1)))
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
case b := <-fileChan:
|
|
var out bytes.Buffer
|
|
_, err = out.Write(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = out.Write([]byte{'\n'})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = client.Write(out.Bytes())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// now we can inform systemd to use pid1 as the pid to monitor
|
|
newPid := fmt.Sprintf("MAINPID=%d\n", pid1)
|
|
client.Write([]byte(newPid))
|
|
return nil
|
|
}
|
|
}
|
|
}
|