summaryrefslogtreecommitdiff
path: root/src/util/fd.go
blob: 0c999729ead7696d2d5ad69ab4c713af0dd93f0c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// Copyright 2015-2016 Luke Shumaker

package util

import (
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"

	sd "lukeshu.com/git/go/libsystemd/sd_daemon"
)

var fdsLock sync.Mutex
var fds = map[int]*os.File{}
var sdFds = map[string]int{}

func init() {
	fds[0] = os.Stdin
	fds[1] = os.Stdout
	fds[2] = os.Stderr
	fromSd := sd.ListenFds(true)
	if fromSd == nil {
		return
	}
	for i, file := range fromSd {
		fds[i+3] = file
		sdFds[file.Name()] = i + 3
	}
}

func FdNameToNum(name string) int {
	switch name {
	case "stdin":
		return 0
	case "stdout":
		return 1
	case "stderr":
		return 2
	case "systemd":
		if len(sdFds) == 0 {
			return -1
		}
		return 3
	default:
		if n, err := strconv.Atoi(name); err == nil {
			if n >= 0 {
				return n
			}
		} else if strings.HasPrefix(name, "systemd:") {
			name = strings.TrimPrefix(name, "systemd")
			n, ok := sdFds[name]
			if ok {
				return n
			} else if n, err := strconv.Atoi(name); err == nil && n < len(sdFds) {
				return n + 3
			}
		}
		return -1
	}
}

func FdFile(fd int) *os.File {
	fdsLock.Lock()
	defer fdsLock.Unlock()
	file, ok := fds[fd]
	if ok {
		return file
	}
	file = os.NewFile(uintptr(fd), fmt.Sprintf("/dev/fd/%d", fd))
	fds[fd] = file
	return file
}

func StreamListener(stype, saddr string) (net.Listener, error) {
	switch stype {
	case "fd":
		return net.FileListener(FdFile(FdNameToNum(saddr)))
	default: /* case "tcp", "tcp4", "tcp6", "unix", "unixpacket": */
		return net.Listen(stype, saddr)
	}
}

func PacketListener(stype, saddr string) (net.PacketConn, error) {
	switch stype {
	case "fd":
		return net.FilePacketConn(FdFile(FdNameToNum(saddr)))
	default: /* case "udp", "udp4", "udp6", "ip", "ip4", "ip6", "unixgram": */
		return net.ListenPacket(stype, saddr)
	}
}

// For completeless, I might want to implement methods for each of
// these:
//  - FIFO
//  - Special
//  - Netlink
//  - MessageQueue
//  - USBFunction