1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-01-03 05:18:29 +03:00

Add support for systemd socket handover for common daemon-server code and also add support for new OOM killer adjustment interface.

This code is already a part of dmeventd, but it's modified slightly to check
sockets instead of FIFOs.
This commit is contained in:
Peter Rajnoha 2012-02-28 13:05:21 +00:00
parent 0650d875e8
commit 870353f9d1

View File

@ -43,6 +43,7 @@ static int _pthread_create(pthread_t *t, void *(*fun)(void *), void *arg, int st
#endif #endif
static volatile sig_atomic_t _shutdown_requested = 0; static volatile sig_atomic_t _shutdown_requested = 0;
static int _systemd_activation = 0;
static void _exit_handler(int sig __attribute__((unused))) static void _exit_handler(int sig __attribute__((unused)))
{ {
@ -50,41 +51,148 @@ static void _exit_handler(int sig __attribute__((unused)))
} }
#ifdef linux #ifdef linux
# define OOM_ADJ_FILE "/proc/self/oom_adj"
# include <stdio.h>
/* From linux/oom.h */ #include <stddef.h>
# define OOM_DISABLE (-17)
# define OOM_ADJUST_MIN (-16)
/* /*
* Protection against OOM killer if kernel supports it * Kernel version 2.6.36 and higher has
* new OOM killer adjustment interface.
*/ */
static int _set_oom_adj(int val) # define OOM_ADJ_FILE_OLD "/proc/self/oom_adj"
# define OOM_ADJ_FILE "/proc/self/oom_score_adj"
/* From linux/oom.h */
/* Old interface */
# define OOM_DISABLE (-17)
# define OOM_ADJUST_MIN (-16)
/* New interface */
# define OOM_SCORE_ADJ_MIN (-1000)
/* Systemd on-demand activation support */
# define SD_LISTEN_PID_ENV_VAR_NAME "LISTEN_PID"
# define SD_LISTEN_FDS_ENV_VAR_NAME "LISTEN_FDS"
# define SD_LISTEN_FDS_START 3
# define SD_FD_SOCKET_SERVER SD_LISTEN_FDS_START
# include <stdio.h>
static int _set_oom_adj(const char *oom_adj_path, int val)
{ {
FILE *fp; FILE *fp;
struct stat st; if (!(fp = fopen(oom_adj_path, "w"))) {
perror("oom_adj: fopen failed");
if (stat(OOM_ADJ_FILE, &st) == -1) {
if (errno == ENOENT)
perror(OOM_ADJ_FILE " not found");
else
perror(OOM_ADJ_FILE ": stat failed");
return 1;
}
if (!(fp = fopen(OOM_ADJ_FILE, "w"))) {
perror(OOM_ADJ_FILE ": fopen failed");
return 0; return 0;
} }
fprintf(fp, "%i", val); fprintf(fp, "%i", val);
if (fclose(fp))
perror(OOM_ADJ_FILE ": fclose failed"); if (dm_fclose(fp))
perror("oom_adj: fclose failed");
return 1; return 1;
} }
/*
* Protection against OOM killer if kernel supports it
*/
static int _protect_against_oom_killer(void)
{
struct stat st;
if (stat(OOM_ADJ_FILE, &st) == -1) {
if (errno != ENOENT)
perror(OOM_ADJ_FILE ": stat failed");
/* Try old oom_adj interface as a fallback */
if (stat(OOM_ADJ_FILE_OLD, &st) == -1) {
if (errno == ENOENT)
perror(OOM_ADJ_FILE_OLD " not found");
else
perror(OOM_ADJ_FILE_OLD ": stat failed");
return 1;
}
return _set_oom_adj(OOM_ADJ_FILE_OLD, OOM_DISABLE) ||
_set_oom_adj(OOM_ADJ_FILE_OLD, OOM_ADJUST_MIN);
}
return _set_oom_adj(OOM_ADJ_FILE, OOM_SCORE_ADJ_MIN);
}
union sockaddr_union {
struct sockaddr sa;
struct sockaddr_un un;
};
static int _handle_preloaded_socket(int fd, const char *path)
{
struct stat st_fd;
union sockaddr_union sockaddr;
int type = 0;
socklen_t len = sizeof(type);
size_t path_len = strlen(path);
if (fd < 0)
return 0;
if (fstat(fd, &st_fd) < 0 || !S_ISSOCK(st_fd.st_mode))
return 0;
if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len) < 0 ||
len != sizeof(type) || type != SOCK_STREAM)
return 0;
memset(&sockaddr, 0, sizeof(sockaddr));
len = sizeof(sockaddr);
if (getsockname(fd, &sockaddr.sa, &len) < 0 ||
len < sizeof(sa_family_t) ||
sockaddr.sa.sa_family != PF_UNIX)
return 0;
if (!(len >= offsetof(struct sockaddr_un, sun_path) + path_len + 1 &&
memcmp(path, sockaddr.un.sun_path, path_len) == 0))
return 0;
return 1;
}
static int _systemd_handover(struct daemon_state *ds)
{
const char *e;
char *p;
unsigned long env_pid, env_listen_fds;
int r = 0;
/* LISTEN_PID must be equal to our PID! */
if (!(e = getenv(SD_LISTEN_PID_ENV_VAR_NAME)))
goto out;
errno = 0;
env_pid = strtoul(e, &p, 10);
if (errno || !p || *p || env_pid <= 0 ||
getpid() != (pid_t) env_pid)
;
/* LISTEN_FDS must be 1 and the fd must be a socket! */
if (!(e = getenv(SD_LISTEN_FDS_ENV_VAR_NAME)))
goto out;
errno = 0;
env_listen_fds = strtoul(e, &p, 10);
if (errno || !p || *p || env_listen_fds != 1)
goto out;
/* Check and handle the socket passed in */
if ((r = _handle_preloaded_socket(SD_FD_SOCKET_SERVER, ds->socket_path)))
ds->socket_fd = SD_FD_SOCKET_SERVER;
out:
unsetenv(SD_LISTEN_PID_ENV_VAR_NAME);
unsetenv(SD_LISTEN_FDS_ENV_VAR_NAME);
return r;
}
#endif #endif
static int _open_socket(daemon_state s) static int _open_socket(daemon_state s)
@ -192,8 +300,14 @@ static void _daemonise(void)
else else
fd = rlim.rlim_cur; fd = rlim.rlim_cur;
for (--fd; fd >= 0; fd--) for (--fd; fd >= 0; fd--) {
#ifdef linux
/* Do not close fds preloaded by systemd! */
if (_systemd_activation && fd == SD_FD_SOCKET_SERVER)
continue;
#endif
close(fd); close(fd);
}
if ((open("/dev/null", O_RDONLY) < 0) || if ((open("/dev/null", O_RDONLY) < 0) ||
(open("/dev/null", O_WRONLY) < 0) || (open("/dev/null", O_WRONLY) < 0) ||
@ -328,6 +442,10 @@ void daemon_start(daemon_state s)
if (setenv("LANG", "C", 1)) if (setenv("LANG", "C", 1))
perror("Cannot set LANG to C"); perror("Cannot set LANG to C");
#ifdef linux
_systemd_activation = _systemd_handover(&s);
#endif
if (!s.foreground) if (!s.foreground)
_daemonise(); _daemonise();
@ -354,11 +472,12 @@ void daemon_start(daemon_state s)
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
#ifdef linux #ifdef linux
if (s.avoid_oom && !_set_oom_adj(OOM_DISABLE) && !_set_oom_adj(OOM_ADJUST_MIN)) /* Systemd has adjusted oom killer for us already */
syslog(LOG_ERR, "Failed to set oom_adj to protect against OOM killer"); if (s.avoid_oom && !_systemd_activation && !_protect_against_oom_killer())
syslog(LOG_ERR, "Failed to protect against OOM killer");
#endif #endif
if (s.socket_path) { if (!_systemd_activation && s.socket_path) {
s.socket_fd = _open_socket(s); s.socket_fd = _open_socket(s);
if (s.socket_fd < 0) if (s.socket_fd < 0)
failed = 1; failed = 1;