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:
parent
0650d875e8
commit
870353f9d1
@ -43,6 +43,7 @@ static int _pthread_create(pthread_t *t, void *(*fun)(void *), void *arg, int st
|
||||
#endif
|
||||
|
||||
static volatile sig_atomic_t _shutdown_requested = 0;
|
||||
static int _systemd_activation = 0;
|
||||
|
||||
static void _exit_handler(int sig __attribute__((unused)))
|
||||
{
|
||||
@ -50,41 +51,148 @@ static void _exit_handler(int sig __attribute__((unused)))
|
||||
}
|
||||
|
||||
#ifdef linux
|
||||
# define OOM_ADJ_FILE "/proc/self/oom_adj"
|
||||
# include <stdio.h>
|
||||
|
||||
/* From linux/oom.h */
|
||||
# define OOM_DISABLE (-17)
|
||||
# define OOM_ADJUST_MIN (-16)
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
struct stat st;
|
||||
|
||||
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");
|
||||
if (!(fp = fopen(oom_adj_path, "w"))) {
|
||||
perror("oom_adj: fopen failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(fp, "%i", val);
|
||||
if (fclose(fp))
|
||||
perror(OOM_ADJ_FILE ": fclose failed");
|
||||
|
||||
if (dm_fclose(fp))
|
||||
perror("oom_adj: fclose failed");
|
||||
|
||||
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
|
||||
|
||||
static int _open_socket(daemon_state s)
|
||||
@ -192,8 +300,14 @@ static void _daemonise(void)
|
||||
else
|
||||
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);
|
||||
}
|
||||
|
||||
if ((open("/dev/null", O_RDONLY) < 0) ||
|
||||
(open("/dev/null", O_WRONLY) < 0) ||
|
||||
@ -328,6 +442,10 @@ void daemon_start(daemon_state s)
|
||||
if (setenv("LANG", "C", 1))
|
||||
perror("Cannot set LANG to C");
|
||||
|
||||
#ifdef linux
|
||||
_systemd_activation = _systemd_handover(&s);
|
||||
#endif
|
||||
|
||||
if (!s.foreground)
|
||||
_daemonise();
|
||||
|
||||
@ -354,11 +472,12 @@ void daemon_start(daemon_state s)
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
#ifdef linux
|
||||
if (s.avoid_oom && !_set_oom_adj(OOM_DISABLE) && !_set_oom_adj(OOM_ADJUST_MIN))
|
||||
syslog(LOG_ERR, "Failed to set oom_adj to protect against OOM killer");
|
||||
/* Systemd has adjusted oom killer for us already */
|
||||
if (s.avoid_oom && !_systemd_activation && !_protect_against_oom_killer())
|
||||
syslog(LOG_ERR, "Failed to protect against OOM killer");
|
||||
#endif
|
||||
|
||||
if (s.socket_path) {
|
||||
if (!_systemd_activation && s.socket_path) {
|
||||
s.socket_fd = _open_socket(s);
|
||||
if (s.socket_fd < 0)
|
||||
failed = 1;
|
||||
|
Loading…
Reference in New Issue
Block a user