summaryrefslogtreecommitdiff
path: root/src/nslcd_systemd/nslcd_systemd.go
blob: f592279170613ddf8aa9511055cde339f54dc730 (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
129
130
131
132
133
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
	Init() error
	Reload() error
	Close()
}

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.Notice("Error while handling request: %v", err)
	}
}

func Main(backend Backend) uint8 {
	var err error = nil

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

	disable_nss_module()

	err = backend.Init()
	if err != nil {
		logger.Err("Could not initialize backend: %v", err)
		sd.Notify(false, "STOPPING=1")
		return lsb.EXIT_FAILURE
	}
	defer backend.Close()

	socket, err := get_socket()
	if err != nil {
		logger.Err("%v", err)
		sd.Notify(false, "STOPPING=1")
		return lsb.EXIT_NOTRUNNING
	}
	defer socket.Close()
	sock := make(chan *net.UnixConn)
	go func() {
		defer lsb.Recover()
		for {
			conn, err := socket.Accept()
			if err != nil {
				logger.Notice("%v", err)
			}
			if conn != nil {
				sock <- conn.(*net.UnixConn)
			}
		}
	}()

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