123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- //go:build !windows
- /*
- Copyright The containerd 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 runc
- import (
- "fmt"
- "net"
- "os"
- "path/filepath"
- "github.com/containerd/console"
- "golang.org/x/sys/unix"
- )
- // NewConsoleSocket creates a new unix socket at the provided path to accept a
- // pty master created by runc for use by the container
- func NewConsoleSocket(path string) (*Socket, error) {
- abs, err := filepath.Abs(path)
- if err != nil {
- return nil, err
- }
- addr, err := net.ResolveUnixAddr("unix", abs)
- if err != nil {
- return nil, err
- }
- l, err := net.ListenUnix("unix", addr)
- if err != nil {
- return nil, err
- }
- return &Socket{
- l: l,
- }, nil
- }
- // NewTempConsoleSocket returns a temp console socket for use with a container
- // On Close(), the socket is deleted
- func NewTempConsoleSocket() (*Socket, error) {
- runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
- dir, err := os.MkdirTemp(runtimeDir, "pty")
- if err != nil {
- return nil, err
- }
- abs, err := filepath.Abs(filepath.Join(dir, "pty.sock"))
- if err != nil {
- return nil, err
- }
- addr, err := net.ResolveUnixAddr("unix", abs)
- if err != nil {
- return nil, err
- }
- l, err := net.ListenUnix("unix", addr)
- if err != nil {
- return nil, err
- }
- if runtimeDir != "" {
- if err := os.Chmod(abs, 0o755|os.ModeSticky); err != nil {
- return nil, err
- }
- }
- return &Socket{
- l: l,
- rmdir: true,
- }, nil
- }
- // Socket is a unix socket that accepts the pty master created by runc
- type Socket struct {
- rmdir bool
- l *net.UnixListener
- }
- // Path returns the path to the unix socket on disk
- func (c *Socket) Path() string {
- return c.l.Addr().String()
- }
- // recvFd waits for a file descriptor to be sent over the given AF_UNIX
- // socket. The file name of the remote file descriptor will be recreated
- // locally (it is sent as non-auxiliary data in the same payload).
- func recvFd(socket *net.UnixConn) (*os.File, error) {
- const MaxNameLen = 4096
- oobSpace := unix.CmsgSpace(4)
- name := make([]byte, MaxNameLen)
- oob := make([]byte, oobSpace)
- n, oobn, _, _, err := socket.ReadMsgUnix(name, oob)
- if err != nil {
- return nil, err
- }
- if n >= MaxNameLen || oobn != oobSpace {
- return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn)
- }
- // Truncate.
- name = name[:n]
- oob = oob[:oobn]
- scms, err := unix.ParseSocketControlMessage(oob)
- if err != nil {
- return nil, err
- }
- if len(scms) != 1 {
- return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms))
- }
- scm := scms[0]
- fds, err := unix.ParseUnixRights(&scm)
- if err != nil {
- return nil, err
- }
- if len(fds) != 1 {
- return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds))
- }
- fd := uintptr(fds[0])
- return os.NewFile(fd, string(name)), nil
- }
- // ReceiveMaster blocks until the socket receives the pty master
- func (c *Socket) ReceiveMaster() (console.Console, error) {
- conn, err := c.l.Accept()
- if err != nil {
- return nil, err
- }
- defer conn.Close()
- uc, ok := conn.(*net.UnixConn)
- if !ok {
- return nil, fmt.Errorf("received connection which was not a unix socket")
- }
- f, err := recvFd(uc)
- if err != nil {
- return nil, err
- }
- return console.ConsoleFromFile(f)
- }
- // Close closes the unix socket
- func (c *Socket) Close() error {
- err := c.l.Close()
- if c.rmdir {
- if rerr := os.RemoveAll(filepath.Dir(c.Path())); err == nil {
- err = rerr
- }
- }
- return err
- }
|