// Copyright (C) 2015 Luke Shumaker // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301 USA // Package nslcd_systemd does the legwork for implementing a systemd // socket-activated nslcd server. // // You just need to implement the Backend interface, then pass it to // Main, which will return the exit code for the process. Everything // but the backend is taken care of for you! // // package main // // import "nslcd/systemd" // // func main() { // backend := ... // os.Exit(int(nslcd_systemd.Main(backend))) // } package nslcd_systemd import ( "fmt" "net" "nslcd/proto/server" "os" "os/signal" sd "sd_daemon" "sd_daemon/logger" "sd_daemon/lsb" "sync" "syscall" ) type Backend interface { nslcd_server.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 syscall.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 = *_cred return } func handler(conn *net.UnixConn, backend nslcd_server.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_server.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") }