Blame snmplib/sd-daemon.c

Packit fcad23
/*
Packit fcad23
 * Systemd integration parts.
Packit fcad23
 *
Packit fcad23
 * Most of this file is directly copied from systemd sources.
Packit fcad23
 * Changes:
Packit fcad23
 * - all exported functions were renamed to have a netsnmp_ prefix
Packit fcad23
 * - all nonexported functions were made static
Packit fcad23
 * - includes were  changed to match Net-SNMP style.
Packit fcad23
 * - removed gcc export macros
Packit fcad23
 * - removed POSIX message queues
Packit fcad23
 * - removed log level macros
Packit fcad23
 * - removed unused functions
Packit fcad23
 * - made SD_LISTEN_FDS_START as it is only used internally
Packit fcad23
 */
Packit fcad23
Packit fcad23
#include <net-snmp/net-snmp-config.h>
Packit fcad23
#include <net-snmp/net-snmp-features.h>
Packit fcad23
#include <net-snmp/types.h>
Packit fcad23
#include <net-snmp/library/snmp_debug.h>
Packit fcad23
Packit fcad23
#ifndef NETSNMP_NO_SYSTEMD
Packit fcad23
Packit fcad23
/***
Packit fcad23
  Copyright 2010 Lennart Poettering
Packit fcad23
Packit fcad23
  Permission is hereby granted, free of charge, to any person
Packit fcad23
  obtaining a copy of this software and associated documentation files
Packit fcad23
  (the "Software"), to deal in the Software without restriction,
Packit fcad23
  including without limitation the rights to use, copy, modify, merge,
Packit fcad23
  publish, distribute, sublicense, and/or sell copies of the Software,
Packit fcad23
  and to permit persons to whom the Software is furnished to do so,
Packit fcad23
  subject to the following conditions:
Packit fcad23
Packit fcad23
  The above copyright notice and this permission notice shall be
Packit fcad23
  included in all copies or substantial portions of the Software.
Packit fcad23
Packit fcad23
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Packit fcad23
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Packit fcad23
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Packit fcad23
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
Packit fcad23
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
Packit fcad23
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
Packit fcad23
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
Packit fcad23
  SOFTWARE.
Packit fcad23
***/
Packit fcad23
Packit fcad23
#ifndef _GNU_SOURCE
Packit fcad23
#define _GNU_SOURCE
Packit fcad23
#endif
Packit fcad23
Packit fcad23
#include <sys/types.h>
Packit fcad23
#include <sys/stat.h>
Packit fcad23
#include <sys/socket.h>
Packit fcad23
#include <sys/un.h>
Packit fcad23
#include <sys/fcntl.h>
Packit fcad23
#include <netinet/in.h>
Packit fcad23
#include <stdlib.h>
Packit fcad23
#include <errno.h>
Packit fcad23
#include <unistd.h>
Packit fcad23
#include <string.h>
Packit fcad23
#include <stdarg.h>
Packit fcad23
#include <stdio.h>
Packit fcad23
#include <stddef.h>
Packit fcad23
#include <limits.h>
Packit fcad23
Packit fcad23
#include <net-snmp/library/sd-daemon.h>
Packit fcad23
Packit fcad23
/* The first passed file descriptor is fd 3 */
Packit fcad23
#define SD_LISTEN_FDS_START 3
Packit fcad23
Packit fcad23
int netsnmp_sd_listen_fds(int unset_environment) {
Packit fcad23
Packit fcad23
        int r, fd;
Packit fcad23
        const char *e;
Packit fcad23
        char *p = NULL;
Packit fcad23
        unsigned long l;
Packit fcad23
Packit fcad23
        if (!(e = getenv("LISTEN_PID"))) {
Packit fcad23
                r = 0;
Packit fcad23
                goto finish;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        errno = 0;
Packit fcad23
        l = strtoul(e, &p, 10);
Packit fcad23
Packit fcad23
        if (errno != 0) {
Packit fcad23
                r = -errno;
Packit fcad23
                goto finish;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        if (!p || *p || l <= 0) {
Packit fcad23
                r = -EINVAL;
Packit fcad23
                goto finish;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        /* Is this for us? */
Packit fcad23
        if (getpid() != (pid_t) l) {
Packit fcad23
                r = 0;
Packit fcad23
                goto finish;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        if (!(e = getenv("LISTEN_FDS"))) {
Packit fcad23
                r = 0;
Packit fcad23
                goto finish;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        errno = 0;
Packit fcad23
        l = strtoul(e, &p, 10);
Packit fcad23
Packit fcad23
        if (errno != 0) {
Packit fcad23
                r = -errno;
Packit fcad23
                goto finish;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        if (!p || *p) {
Packit fcad23
                r = -EINVAL;
Packit fcad23
                goto finish;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
Packit fcad23
                int flags;
Packit fcad23
Packit fcad23
                if ((flags = fcntl(fd, F_GETFD)) < 0) {
Packit fcad23
                        r = -errno;
Packit fcad23
                        goto finish;
Packit fcad23
                }
Packit fcad23
Packit fcad23
                if (flags & FD_CLOEXEC)
Packit fcad23
                        continue;
Packit fcad23
Packit fcad23
                if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
Packit fcad23
                        r = -errno;
Packit fcad23
                        goto finish;
Packit fcad23
                }
Packit fcad23
        }
Packit fcad23
Packit fcad23
        r = (int) l;
Packit fcad23
Packit fcad23
finish:
Packit fcad23
        if (unset_environment) {
Packit fcad23
                unsetenv("LISTEN_PID");
Packit fcad23
                unsetenv("LISTEN_FDS");
Packit fcad23
        }
Packit fcad23
Packit fcad23
        return r;
Packit fcad23
}
Packit fcad23
Packit fcad23
static int sd_is_socket_internal(int fd, int type, int listening) {
Packit fcad23
        struct stat st_fd;
Packit fcad23
Packit fcad23
        if (fd < 0 || type < 0)
Packit fcad23
                return -EINVAL;
Packit fcad23
Packit fcad23
        if (fstat(fd, &st_fd) < 0)
Packit fcad23
                return -errno;
Packit fcad23
Packit fcad23
        if (!S_ISSOCK(st_fd.st_mode))
Packit fcad23
                return 0;
Packit fcad23
Packit fcad23
        if (type != 0) {
Packit fcad23
                int other_type = 0;
Packit fcad23
                socklen_t l = sizeof(other_type);
Packit fcad23
Packit fcad23
                if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
Packit fcad23
                        return -errno;
Packit fcad23
Packit fcad23
                if (l != sizeof(other_type))
Packit fcad23
                        return -EINVAL;
Packit fcad23
Packit fcad23
                if (other_type != type)
Packit fcad23
                        return 0;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        if (listening >= 0) {
Packit fcad23
                int accepting = 0;
Packit fcad23
                socklen_t l = sizeof(accepting);
Packit fcad23
Packit fcad23
                if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
Packit fcad23
                        return -errno;
Packit fcad23
Packit fcad23
                if (l != sizeof(accepting))
Packit fcad23
                        return -EINVAL;
Packit fcad23
Packit fcad23
                if (!accepting != !listening)
Packit fcad23
                        return 0;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        return 1;
Packit fcad23
}
Packit fcad23
Packit fcad23
union sockaddr_union {
Packit fcad23
        struct sockaddr sa;
Packit fcad23
        struct sockaddr_in in4;
Packit fcad23
        struct sockaddr_in6 in6;
Packit fcad23
        struct sockaddr_un un;
Packit fcad23
        struct sockaddr_storage storage;
Packit fcad23
};
Packit fcad23
Packit fcad23
static int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
Packit fcad23
        union sockaddr_union sockaddr;
Packit fcad23
        socklen_t l;
Packit fcad23
        int r;
Packit fcad23
Packit fcad23
        if (family != 0 && family != AF_INET && family != AF_INET6)
Packit fcad23
                return -EINVAL;
Packit fcad23
Packit fcad23
        if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
Packit fcad23
                return r;
Packit fcad23
Packit fcad23
        memset(&sockaddr, 0, sizeof(sockaddr));
Packit fcad23
        l = sizeof(sockaddr);
Packit fcad23
Packit fcad23
        if (getsockname(fd, &sockaddr.sa, &l) < 0)
Packit fcad23
                return -errno;
Packit fcad23
Packit fcad23
        if (l < sizeof(sa_family_t))
Packit fcad23
                return -EINVAL;
Packit fcad23
Packit fcad23
        if (sockaddr.sa.sa_family != AF_INET &&
Packit fcad23
            sockaddr.sa.sa_family != AF_INET6)
Packit fcad23
                return 0;
Packit fcad23
Packit fcad23
        if (family > 0)
Packit fcad23
                if (sockaddr.sa.sa_family != family)
Packit fcad23
                        return 0;
Packit fcad23
Packit fcad23
        if (port > 0) {
Packit fcad23
                if (sockaddr.sa.sa_family == AF_INET) {
Packit fcad23
                        if (l < sizeof(struct sockaddr_in))
Packit fcad23
                                return -EINVAL;
Packit fcad23
Packit fcad23
                        return htons(port) == sockaddr.in4.sin_port;
Packit fcad23
                } else {
Packit fcad23
                        if (l < sizeof(struct sockaddr_in6))
Packit fcad23
                                return -EINVAL;
Packit fcad23
Packit fcad23
                        return htons(port) == sockaddr.in6.sin6_port;
Packit fcad23
                }
Packit fcad23
        }
Packit fcad23
Packit fcad23
        return 1;
Packit fcad23
}
Packit fcad23
Packit fcad23
static int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
Packit fcad23
        union sockaddr_union sockaddr;
Packit fcad23
        socklen_t l;
Packit fcad23
        int r;
Packit fcad23
Packit fcad23
        if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
Packit fcad23
                return r;
Packit fcad23
Packit fcad23
        memset(&sockaddr, 0, sizeof(sockaddr));
Packit fcad23
        l = sizeof(sockaddr);
Packit fcad23
Packit fcad23
        if (getsockname(fd, &sockaddr.sa, &l) < 0)
Packit fcad23
                return -errno;
Packit fcad23
Packit fcad23
        if (l < sizeof(sa_family_t))
Packit fcad23
                return -EINVAL;
Packit fcad23
Packit fcad23
        if (sockaddr.sa.sa_family != AF_UNIX)
Packit fcad23
                return 0;
Packit fcad23
Packit fcad23
        if (path) {
Packit fcad23
                if (length <= 0)
Packit fcad23
                        length = strlen(path);
Packit fcad23
Packit fcad23
                if (length <= 0)
Packit fcad23
                        /* Unnamed socket */
Packit fcad23
                        return l == offsetof(struct sockaddr_un, sun_path);
Packit fcad23
Packit fcad23
                if (path[0])
Packit fcad23
                        /* Normal path socket */
Packit fcad23
                        return
Packit fcad23
                                (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
Packit fcad23
                                memcmp(path, sockaddr.un.sun_path, length+1) == 0;
Packit fcad23
                else
Packit fcad23
                        /* Abstract namespace socket */
Packit fcad23
                        return
Packit fcad23
                                (l == offsetof(struct sockaddr_un, sun_path) + length) &&
Packit fcad23
                                memcmp(path, sockaddr.un.sun_path, length) == 0;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        return 1;
Packit fcad23
}
Packit fcad23
Packit fcad23
int netsnmp_sd_notify(int unset_environment, const char *state) {
Packit fcad23
        int fd = -1, r;
Packit fcad23
        struct msghdr msghdr;
Packit fcad23
        struct iovec iovec;
Packit fcad23
        union sockaddr_union sockaddr;
Packit fcad23
        const char *e;
Packit fcad23
Packit fcad23
        if (!state) {
Packit fcad23
                r = -EINVAL;
Packit fcad23
                goto finish;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        if (!(e = getenv("NOTIFY_SOCKET")))
Packit fcad23
                return 0;
Packit fcad23
Packit fcad23
        /* Must be an abstract socket, or an absolute path */
Packit fcad23
        if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
Packit fcad23
                r = -EINVAL;
Packit fcad23
                goto finish;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
Packit fcad23
                r = -errno;
Packit fcad23
                goto finish;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        memset(&sockaddr, 0, sizeof(sockaddr));
Packit fcad23
        sockaddr.sa.sa_family = AF_UNIX;
Packit fcad23
        strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
Packit fcad23
Packit fcad23
        if (sockaddr.un.sun_path[0] == '@')
Packit fcad23
                sockaddr.un.sun_path[0] = 0;
Packit fcad23
Packit fcad23
        memset(&iovec, 0, sizeof(iovec));
Packit fcad23
        iovec.iov_base = (char *)state;
Packit fcad23
        iovec.iov_len = strlen(state);
Packit fcad23
Packit fcad23
        memset(&msghdr, 0, sizeof(msghdr));
Packit fcad23
        msghdr.msg_name = &sockaddr;
Packit fcad23
        msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
Packit fcad23
Packit fcad23
        if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
Packit fcad23
                msghdr.msg_namelen = sizeof(struct sockaddr_un);
Packit fcad23
Packit fcad23
        msghdr.msg_iov = &iovec;
Packit fcad23
        msghdr.msg_iovlen = 1;
Packit fcad23
Packit fcad23
        if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
Packit fcad23
                r = -errno;
Packit fcad23
                goto finish;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        r = 1;
Packit fcad23
Packit fcad23
finish:
Packit fcad23
        if (unset_environment)
Packit fcad23
                unsetenv("NOTIFY_SOCKET");
Packit fcad23
Packit fcad23
        if (fd >= 0)
Packit fcad23
                close(fd);
Packit fcad23
Packit fcad23
        return r;
Packit fcad23
}
Packit fcad23
Packit fcad23
/* End of original sd-daemon.c from systemd sources */
Packit fcad23
Packit fcad23
int
Packit fcad23
netsnmp_sd_find_inet_socket(int family, int type, int listening, int port)
Packit fcad23
{
Packit fcad23
    int count, fd;
Packit fcad23
Packit fcad23
    count = netsnmp_sd_listen_fds(0);
Packit fcad23
    if (count <= 0) {
Packit fcad23
        DEBUGMSGTL(("systemd:find_inet_socket", "No LISTEN_FDS found.\n"));
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
    DEBUGMSGTL(("systemd:find_inet_socket", "LISTEN_FDS reports %d sockets.\n",
Packit fcad23
            count));
Packit fcad23
Packit fcad23
    for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
Packit fcad23
        int rc = sd_is_socket_inet(fd, family, type, listening, port);
Packit fcad23
        if (rc < 0)
Packit fcad23
            DEBUGMSGTL(("systemd:find_inet_socket",
Packit fcad23
                    "sd_is_socket_inet error: %d\n", rc));
Packit fcad23
        if (rc > 0) {
Packit fcad23
            DEBUGMSGTL(("systemd:find_inet_socket",
Packit fcad23
                    "Found the socket in LISTEN_FDS\n"));
Packit fcad23
            return fd;
Packit fcad23
        }
Packit fcad23
    }
Packit fcad23
    DEBUGMSGTL(("systemd:find_inet_socket", "Socket not found in LISTEN_FDS\n"));
Packit fcad23
    return -1;
Packit fcad23
}
Packit fcad23
Packit fcad23
int
Packit fcad23
netsnmp_sd_find_unix_socket(int type, int listening, const char *path)
Packit fcad23
{
Packit fcad23
    int count, fd;
Packit fcad23
Packit fcad23
    count = netsnmp_sd_listen_fds(0);
Packit fcad23
    if (count <= 0) {
Packit fcad23
        DEBUGMSGTL(("systemd:find_unix_socket", "No LISTEN_FDS found.\n"));
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
    DEBUGMSGTL(("systemd:find_unix_socket", "LISTEN_FDS reports %d sockets.\n",
Packit fcad23
            count));
Packit fcad23
Packit fcad23
    for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
Packit fcad23
        int rc = sd_is_socket_unix(fd, type, listening, path, 0);
Packit fcad23
        if (rc < 0)
Packit fcad23
            DEBUGMSGTL(("systemd:find_unix_socket",
Packit fcad23
                    "sd_is_socket_unix error: %d\n", rc));
Packit fcad23
        if (rc > 0) {
Packit fcad23
            DEBUGMSGTL(("systemd:find_unix_socket",
Packit fcad23
                    "Found the socket in LISTEN_FDS\n"));
Packit fcad23
            return fd;
Packit fcad23
        }
Packit fcad23
    }
Packit fcad23
    DEBUGMSGTL(("systemd:find_unix_socket", "Socket not found in LISTEN_FDS\n"));
Packit fcad23
    return -1;
Packit fcad23
}
Packit fcad23
Packit fcad23
#endif /* ! NETSNMP_NO_SYSTEMD */