mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
08459dcf92
This is necessary to ensure that the control server is aware of the new kernel that was installed on restore. PiperOrigin-RevId: 644792458
177 lines
5.0 KiB
Go
177 lines
5.0 KiB
Go
// Copyright 2018 The gVisor Authors.
|
|
//
|
|
// 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 server provides a basic control server interface.
|
|
|
|
Note that no objects are registered by default. Users must provide their own
|
|
implementations of the control interface.
|
|
*/
|
|
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"golang.org/x/sys/unix"
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
|
"gvisor.dev/gvisor/pkg/sync"
|
|
"gvisor.dev/gvisor/pkg/unet"
|
|
"gvisor.dev/gvisor/pkg/urpc"
|
|
)
|
|
|
|
// curUID is the unix user ID of the user that the control server is running as.
|
|
var curUID = os.Getuid()
|
|
|
|
// Server is a basic control server.
|
|
type Server struct {
|
|
// socket is our bound socket.
|
|
socket *unet.ServerSocket
|
|
|
|
// server is our rpc server.
|
|
server atomic.Pointer[urpc.Server]
|
|
|
|
// wg waits for the accept loop to terminate.
|
|
wg sync.WaitGroup
|
|
}
|
|
|
|
// New returns a new bound control server.
|
|
func New(socket *unet.ServerSocket) *Server {
|
|
s := &Server{
|
|
socket: socket,
|
|
}
|
|
s.server.Store(urpc.NewServer())
|
|
return s
|
|
}
|
|
|
|
// ResetServer resets the server, clearing all registered objects. It stops the
|
|
// old server asynchronously.
|
|
func (s *Server) ResetServer() {
|
|
if old := s.server.Swap(urpc.NewServer()); old != nil {
|
|
go old.Stop(0)
|
|
}
|
|
}
|
|
|
|
// FD returns the file descriptor that the server is running on.
|
|
func (s *Server) FD() int {
|
|
return s.socket.FD()
|
|
}
|
|
|
|
// Wait waits for the main server goroutine to exit. This should be
|
|
// called after a call to Serve.
|
|
func (s *Server) Wait() {
|
|
s.wg.Wait()
|
|
}
|
|
|
|
// Stop stops the server. Note that this function should only be called once
|
|
// and the server should not be used afterwards.
|
|
func (s *Server) Stop(timeout time.Duration) {
|
|
s.socket.Close()
|
|
s.Wait()
|
|
|
|
// This will cause existing clients to be terminated safely. If the
|
|
// registered handlers have a Stop callback, it will be called.
|
|
s.server.Load().Stop(timeout)
|
|
}
|
|
|
|
// StartServing starts listening for connect and spawns the main service
|
|
// goroutine for handling incoming control requests. StartServing does not
|
|
// block; to wait for the control server to exit, call Wait.
|
|
func (s *Server) StartServing() error {
|
|
// Actually start listening.
|
|
if err := s.socket.Listen(); err != nil {
|
|
return err
|
|
}
|
|
|
|
s.wg.Add(1)
|
|
go func() { // S/R-SAFE: does not impact state directly.
|
|
s.serve()
|
|
s.wg.Done()
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
|
|
// serve is the body of the main service goroutine. It handles incoming control
|
|
// connections and dispatches requests to registered objects.
|
|
func (s *Server) serve() {
|
|
for {
|
|
// Accept clients.
|
|
conn, err := s.socket.Accept()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Handle the connection non-blockingly.
|
|
s.server.Load().StartHandling(conn)
|
|
}
|
|
}
|
|
|
|
// Register registers a specific control interface with the server.
|
|
func (s *Server) Register(obj any) {
|
|
s.server.Load().Register(obj)
|
|
}
|
|
|
|
// CreateFromFD creates a new control bound to the given 'fd'. It has no
|
|
// registered interfaces and will not start serving until StartServing is
|
|
// called.
|
|
func CreateFromFD(fd int) (*Server, error) {
|
|
socket, err := unet.NewServerSocket(fd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return New(socket), nil
|
|
}
|
|
|
|
// Create creates a new control server with an abstract unix socket
|
|
// with the given address, which must must be unique and a valid
|
|
// abstract socket name.
|
|
func Create(addr string) (*Server, error) {
|
|
socket, err := CreateSocket(addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return CreateFromFD(socket)
|
|
}
|
|
|
|
// CreateSocket creates a socket that can be used with control server,
|
|
// but doesn't start control server. 'addr' must be a valid and unique
|
|
// abstract socket name. Returns socket's FD, -1 in case of error.
|
|
func CreateSocket(addr string) (int, error) {
|
|
if addr[0] != 0 && len(addr) >= linux.UnixPathMax {
|
|
// This is not an abstract socket path. It is a filesystem path.
|
|
// UDS bind fails when the len(socket path) >= UNIX_PATH_MAX. Instead
|
|
// try opening the parent and attempt to shorten the path via procfs.
|
|
dirFD, err := unix.Open(filepath.Dir(addr), unix.O_RDONLY|unix.O_DIRECTORY, 0)
|
|
if err != nil {
|
|
return -1, fmt.Errorf("failed to open parent directory of %q", addr)
|
|
}
|
|
defer unix.Close(dirFD)
|
|
name := filepath.Base(addr)
|
|
addr = fmt.Sprintf("/proc/self/fd/%d/%s", dirFD, name)
|
|
if len(addr) >= linux.UnixPathMax {
|
|
// Urgh... This is just doomed to fail. Ask caller to use a shorter name.
|
|
return -1, fmt.Errorf("socket name %q is too long, use a shorter name", name)
|
|
}
|
|
}
|
|
socket, err := unet.Bind(addr, false)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
return socket.Release()
|
|
}
|