| /** |
| # Copyright 2020-2023 NVIDIA CORPORATION |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| **/ |
| |
| package containerd |
| |
| import ( |
| "fmt" |
| "net" |
| "syscall" |
| "time" |
| |
| log "github.com/sirupsen/logrus" |
| ) |
| |
| const ( |
| reloadBackoff = 5 * time.Second |
| maxReloadAttempts = 6 |
| |
| socketMessageToGetPID = "" |
| ) |
| |
| // SignalContainerd sends a SIGHUP signal to the containerd daemon |
| func SignalContainerd(socket string) error { |
| log.Infof("Sending SIGHUP signal to containerd") |
| |
| // Wrap the logic to perform the SIGHUP in a function so we can retry it on failure |
| retriable := func() error { |
| conn, err := net.Dial("unix", socket) |
| if err != nil { |
| return fmt.Errorf("unable to dial: %v", err) |
| } |
| defer conn.Close() |
| |
| sconn, err := conn.(*net.UnixConn).SyscallConn() |
| if err != nil { |
| return fmt.Errorf("unable to get syscall connection: %v", err) |
| } |
| |
| err1 := sconn.Control(func(fd uintptr) { |
| err = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_PASSCRED, 1) |
| }) |
| if err1 != nil { |
| return fmt.Errorf("unable to issue call on socket fd: %v", err1) |
| } |
| if err != nil { |
| return fmt.Errorf("unable to SetsockoptInt on socket fd: %v", err) |
| } |
| |
| _, _, err = conn.(*net.UnixConn).WriteMsgUnix([]byte(socketMessageToGetPID), nil, nil) |
| if err != nil { |
| return fmt.Errorf("unable to WriteMsgUnix on socket fd: %v", err) |
| } |
| |
| oob := make([]byte, 1024) |
| _, oobn, _, _, err := conn.(*net.UnixConn).ReadMsgUnix(nil, oob) |
| if err != nil { |
| return fmt.Errorf("unable to ReadMsgUnix on socket fd: %v", err) |
| } |
| |
| oob = oob[:oobn] |
| scm, err := syscall.ParseSocketControlMessage(oob) |
| if err != nil { |
| return fmt.Errorf("unable to ParseSocketControlMessage from message received on socket fd: %v", err) |
| } |
| |
| ucred, err := syscall.ParseUnixCredentials(&scm[0]) |
| if err != nil { |
| return fmt.Errorf("unable to ParseUnixCredentials from message received on socket fd: %v", err) |
| } |
| |
| err = syscall.Kill(int(ucred.Pid), syscall.SIGHUP) |
| if err != nil { |
| return fmt.Errorf("unable to send SIGHUP to 'containerd' process: %v", err) |
| } |
| |
| return nil |
| } |
| |
| // Try to send a SIGHUP up to maxReloadAttempts times |
| var err error |
| for i := 0; i < maxReloadAttempts; i++ { |
| err = retriable() |
| if err == nil { |
| break |
| } |
| if i == maxReloadAttempts-1 { |
| break |
| } |
| log.Warningf("Error signaling containerd, attempt %v/%v: %v", i+1, maxReloadAttempts, err) |
| time.Sleep(reloadBackoff) |
| } |
| if err != nil { |
| log.Warningf("Max retries reached %v/%v, aborting", maxReloadAttempts, maxReloadAttempts) |
| return err |
| } |
| |
| log.Infof("Successfully signaled containerd") |
| |
| return nil |
| } |