summaryrefslogtreecommitdiff
path: root/src/nslcd_systemd/nslcd_systemd.go
blob: c4511ef7c06b60da0172a0e87f8b3195e279b850 (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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package nslcd_systemd

import (
	"fmt"
	"net"
	"nslcd_proto"
	"os"
	"os/signal"
	sd "sd_daemon"
	"sd_daemon/logger"
	"sd_daemon/lsb"
	"sync"
	"syscall"
)

type Backend interface {
	nslcd_proto.Backend
	Reload() error
}

func get_socket() (socket net.Listener, err error) {
	socket = nil
	err = nil
	fds := sd.ListenFds(true)
	if fds == nil {
		err = fmt.Errorf("failed to aquire sockets from systemd")
		return
	}
	if len(fds) != 1 {
		err = fmt.Errorf("wrong number of sockets from systemd: expected %d but got %d", 1, len(fds))
		return
	}
	socket, err = net.FileListener(fds[0])
	fds[0].Close()
	return
}

func getpeercred(conn *net.UnixConn) (cred nslcd_proto.Ucred, err error) {
	file, err := conn.File()
	if err != nil {
		return
	}
	defer file.Close()
	_cred, err := syscall.GetsockoptUcred(int(file.Fd()), syscall.SOL_SOCKET, syscall.SO_PEERCRED)
	cred = nslcd_proto.Ucred(*_cred)
	return
}

func handler(conn *net.UnixConn, backend nslcd_proto.Backend) {
	defer conn.Close()
	cred, err := getpeercred(conn)
	if err != nil {
		logger.Debug("connection from unknown client")
	} else {
		logger.Debug("connection from pid=%v uid=%v gid=%v",
			cred.Pid, cred.Uid, cred.Gid)
	}
	err = nslcd_proto.HandleRequest(backend, conn, conn, cred)
	if err != nil {
		logger.Debug("Error while handling request: %v", err)
	}
}

func Main(backend Backend) uint8 {
	var err error = nil
	var socket net.Listener = nil
	defer func() {
		if socket != nil {
			socket.Close()
		}
	}()

	socket, err = get_socket()
	if err != nil {
		logger.Err("%s", err.Error())
		sd.Notify(false, "STOPPING=1")
		return lsb.EXIT_NOTRUNNING
	}

	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGTERM, syscall.SIGHUP)

	sock := make(chan *net.UnixConn, 1)
	go func() {
		defer lsb.Recover()
		for {
			conn, err := socket.Accept()
			if err != nil {
				logger.Notice("Accept: %s", err.Error())
			}
			sock <- conn.(*net.UnixConn)
		}
	}()

	exit := lsb.EXIT_SUCCESS
	var wg sync.WaitGroup
	sd.Notify(false, "READY=1")
Loop:
	for {
		select {
		case sig := <-sigs:
			switch sig {
			case syscall.SIGTERM:
				break Loop
			case syscall.SIGHUP:
				sd.Notify(false, "RELOADING=1")
				err := backend.Reload()
				if err != nil {
					logger.Notice("Reload: %s", err.Error())
					exit = lsb.EXIT_NOTRUNNING
					break Loop
				}
				sd.Notify(false, "READY=1")
			}
		case conn := <-sock:
			wg.Add(1)
			go func() {
				defer lsb.Recover()
				defer wg.Done()
				handler(conn, backend)
			}()
		}
	}
	sd.Notify(false, "STOPPING=1")
	wg.Wait()

	return exit
}