[systemd-commits] 2 commits - Makefile.am src/conf-parser.c src/conf-parser.h src/execute.c src/exit-status.c src/exit-status.h src/hashmap.c src/hashmap.h src/journal src/label.c src/label.h src/log.c src/log.h src/login src/nspawn.c src/ratelimit.c src/ratelimit.h src/set.c src/set.h src/shared src/socket-util.c src/socket-util.h src/strv.c src/strv.h src/udev src/utf8.c src/utf8.h src/util.c src/util.h src/virt.c src/virt.h
Kay Sievers
kay at kemper.freedesktop.org
Tue Apr 10 05:15:15 PDT 2012
Makefile.am | 115
src/conf-parser.c | 852 ------
src/conf-parser.h | 135
src/execute.c | 1
src/exit-status.c | 180 -
src/exit-status.h | 85
src/hashmap.c | 731 -----
src/hashmap.h | 91
src/journal/journald.h | 1
src/label.c | 413 --
src/label.h | 51
src/log.c | 747 -----
src/log.h | 111
src/login/logind.h | 1
src/login/pam-module.c | 1
src/nspawn.c | 1
src/ratelimit.c | 57
src/ratelimit.h | 53
src/set.c | 118
src/set.h | 69
src/shared/audit.c | 118
src/shared/audit.h | 29
src/shared/capability.c | 86
src/shared/capability.h | 27
src/shared/conf-parser.c | 852 ++++++
src/shared/conf-parser.h | 135
src/shared/exit-status.c | 180 +
src/shared/exit-status.h | 85
src/shared/hashmap.c | 731 +++++
src/shared/hashmap.h | 91
src/shared/label.c | 413 ++
src/shared/label.h | 51
src/shared/log.c | 747 +++++
src/shared/log.h | 111
src/shared/ratelimit.c | 57
src/shared/ratelimit.h | 53
src/shared/set.c | 118
src/shared/set.h | 69
src/shared/socket-util.c | 652 ++++
src/shared/socket-util.h | 102
src/shared/strv.c | 690 ++++
src/shared/strv.h | 79
src/shared/utf8.c | 214 +
src/shared/utf8.h | 33
src/shared/util.c | 6123 ++++++++++++++++++++++++++++++++++++++++++++
src/shared/util.h | 536 +++
src/shared/virt.c | 292 ++
src/shared/virt.h | 38
src/socket-util.c | 652 ----
src/socket-util.h | 102
src/strv.c | 690 ----
src/strv.h | 79
src/udev/libudev-monitor.c | 1
src/utf8.c | 214 -
src/utf8.h | 33
src/util.c | 6256 ---------------------------------------------
src/util.h | 544 ---
src/virt.c | 292 --
src/virt.h | 38
59 files changed, 12803 insertions(+), 12623 deletions(-)
New commits:
commit d7832d2c6e0ef5f2839a2296c1cc2fc85c7d9632
Author: Kay Sievers <kay at vrfy.org>
Date: Tue Apr 10 13:39:02 2012 +0200
util: move all to shared/ and split external dependencies in separate internal libraries
Before:
$ ldd /lib/systemd/systemd-timestamp
linux-vdso.so.1 => (0x00007fffb05ff000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f90aac57000)
libcap.so.2 => /lib64/libcap.so.2 (0x00007f90aaa53000)
librt.so.1 => /lib64/librt.so.1 (0x00007f90aa84a000)
libc.so.6 => /lib64/libc.so.6 (0x00007f90aa494000)
/lib64/ld-linux-x86-64.so.2 (0x00007f90aae90000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f90aa290000)
libattr.so.1 => /lib64/libattr.so.1 (0x00007f90aa08a000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f90a9e6e000)
After:
$ ldd systemd-timestamp
linux-vdso.so.1 => (0x00007fff3cbff000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f5eaa1c3000)
librt.so.1 => /lib64/librt.so.1 (0x00007f5ea9fbb000)
libc.so.6 => /lib64/libc.so.6 (0x00007f5ea9c04000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5eaa3fc000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f5ea9a00000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f5ea97e4000)
diff --git a/Makefile.am b/Makefile.am
index 8f56d25..357002b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -120,6 +120,7 @@ AM_CPPFLAGS = \
-DX_SERVER=\"$(bindir)/X\" \
-DUDEVLIBEXECDIR=\""$(libexecdir)/udev"\" \
-I $(top_srcdir)/src \
+ -I $(top_srcdir)/src/shared \
-I $(top_srcdir)/src/readahead \
-I $(top_srcdir)/src/login \
-I $(top_srcdir)/src/journal \
@@ -129,6 +130,7 @@ AM_CPPFLAGS = \
AM_CFLAGS = $(WARNINGFLAGS)
AM_LDFLAGS = $(GCLDFLAGS)
+# ------------------------------------------------------------------------------
if TARGET_GENTOO
AM_CPPFLAGS += \
-DKBD_LOADKEYS=\"/usr/bin/loadkeys\" \
@@ -183,6 +185,7 @@ endif
endif
endif
+# ------------------------------------------------------------------------------
rootbin_PROGRAMS = \
systemctl \
systemd-notify \
@@ -472,42 +475,66 @@ EXTRA_DIST += \
noinst_LTLIBRARIES = \
libsystemd-basic.la \
+ libsystemd-capability.la \
+ libsystemd-audit.la \
libsystemd-core.la
+# ------------------------------------------------------------------------------
libsystemd_basic_la_SOURCES = \
- src/util.c \
- src/util.h \
- src/virt.c \
- src/virt.h \
- src/label.c \
- src/label.h \
- src/hashmap.c \
- src/hashmap.h \
- src/set.c \
- src/set.h \
- src/strv.c \
- src/strv.h \
- src/conf-parser.c \
- src/conf-parser.h \
- src/socket-util.c \
- src/socket-util.h \
- src/log.c \
- src/log.h \
- src/ratelimit.h \
- src/ratelimit.c \
- src/exit-status.c \
- src/exit-status.h \
- src/utf8.c \
- src/utf8.h
+ src/shared/util.c \
+ src/shared/util.h \
+ src/shared/virt.c \
+ src/shared/virt.h \
+ src/shared/label.c \
+ src/shared/label.h \
+ src/shared/hashmap.c \
+ src/shared/hashmap.h \
+ src/shared/set.c \
+ src/shared/set.h \
+ src/shared/strv.c \
+ src/shared/strv.h \
+ src/shared/conf-parser.c \
+ src/shared/conf-parser.h \
+ src/shared/socket-util.c \
+ src/shared/socket-util.h \
+ src/shared/log.c \
+ src/shared/log.h \
+ src/shared/ratelimit.h \
+ src/shared/ratelimit.c \
+ src/shared/exit-status.c \
+ src/shared/exit-status.h \
+ src/shared/utf8.c \
+ src/shared/utf8.h
libsystemd_basic_la_CFLAGS = \
$(AM_CFLAGS) \
$(SELINUX_CFLAGS)
libsystemd_basic_la_LIBADD = \
- $(SELINUX_LIBS) \
+ libsystemd-capability.la \
+ $(SELINUX_LIBS)
+
+# ------------------------------------------------------------------------------
+libsystemd_capability_la_SOURCES = \
+ src/shared/capability.c \
+ src/shared/capability.h
+
+libsystemd_capability_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(CAP_CFLAGS)
+
+libsystemd_capability_la_LIBADD = \
$(CAP_LIBS)
+# ------------------------------------------------------------------------------
+libsystemd_audit_la_SOURCES = \
+ src/shared/audit.c \
+ src/shared/audit.h
+
+libsystemd_audit_la_LIBADD = \
+ libsystemd-capability.la
+
+# ------------------------------------------------------------------------------
libsystemd_core_la_SOURCES = \
src/unit.c \
src/unit.h \
@@ -613,8 +640,8 @@ libsystemd_core_la_SOURCES = \
src/tcpwrap.h \
src/cgroup-attr.c \
src/cgroup-attr.h \
- src/watchdog.c \
- src/watchdog.h \
+ src/watchdog.c \
+ src/watchdog.h \
src/sd-daemon.c \
src/sd-id128.c \
src/macro.h \
@@ -667,6 +694,7 @@ libsystemd_core_la_LIBADD = \
$(CAP_LIBS) \
$(KMOD_LIBS)
+# ------------------------------------------------------------------------------
MANPAGES = \
man/systemd.1 \
man/systemctl.1 \
@@ -741,6 +769,7 @@ EXTRA_DIST += \
$(MANPAGES) \
$(MANPAGES_ALIAS)
+# ------------------------------------------------------------------------------
systemd_SOURCES = \
src/main.c
@@ -751,6 +780,7 @@ systemd_CFLAGS = \
systemd_LDADD = \
libsystemd-core.la
+# ------------------------------------------------------------------------------
test_engine_SOURCES = \
src/test-engine.c
@@ -831,6 +861,7 @@ test_watchdog_SOURCES = \
test_watchdog_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_initctl_SOURCES = \
src/initctl.c \
src/dbus-common.c
@@ -844,6 +875,7 @@ systemd_initctl_LDADD = \
libsystemd-daemon.la \
$(DBUS_LIBS)
+# ------------------------------------------------------------------------------
systemd_update_utmp_SOURCES = \
src/update-utmp.c \
src/dbus-common.c \
@@ -859,6 +891,7 @@ systemd_update_utmp_LDADD = \
$(DBUS_LIBS) \
$(AUDIT_LIBS)
+# ------------------------------------------------------------------------------
systemd_shutdownd_SOURCES = \
src/utmp-wtmp.c \
src/shutdownd.c
@@ -881,6 +914,7 @@ systemd_shutdown_LDADD = \
libsystemd-basic.la \
libudev.la
+# ------------------------------------------------------------------------------
systemd_modules_load_SOURCES = \
src/modules-load.c
@@ -891,12 +925,14 @@ systemd_modules_load_LDADD = \
libsystemd-basic.la \
$(KMOD_LIBS)
+# ------------------------------------------------------------------------------
systemd_tmpfiles_SOURCES = \
src/tmpfiles.c
systemd_tmpfiles_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_machine_id_setup_SOURCES = \
src/machine-id-setup.c \
src/machine-id-main.c \
@@ -905,12 +941,14 @@ systemd_machine_id_setup_SOURCES = \
systemd_machine_id_setup_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_sysctl_SOURCES = \
src/sysctl.c
systemd_sysctl_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_fsck_SOURCES = \
src/fsck.c \
src/dbus-common.c
@@ -924,12 +962,14 @@ systemd_fsck_LDADD = \
libudev.la \
$(DBUS_LIBS)
+# ------------------------------------------------------------------------------
systemd_timestamp_SOURCES = \
src/timestamp.c
systemd_timestamp_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_ac_power_SOURCES = \
src/ac-power.c
@@ -937,12 +977,14 @@ systemd_ac_power_LDADD = \
libsystemd-basic.la \
libudev.la
+# ------------------------------------------------------------------------------
systemd_detect_virt_SOURCES = \
src/detect-virt.c
systemd_detect_virt_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_getty_generator_SOURCES = \
src/getty-generator.c \
src/unit-name.c
@@ -950,20 +992,22 @@ systemd_getty_generator_SOURCES = \
systemd_getty_generator_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_rc_local_generator_SOURCES = \
src/rc-local-generator.c
systemd_rc_local_generator_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_remount_api_vfs_SOURCES = \
src/remount-api-vfs.c \
- src/mount-setup.c \
- src/exit-status.c
+ src/mount-setup.c
systemd_remount_api_vfs_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_cgroups_agent_SOURCES = \
src/cgroups-agent.c \
src/dbus-common.c
@@ -976,6 +1020,7 @@ systemd_cgroups_agent_LDADD = \
libsystemd-basic.la \
$(DBUS_LIBS)
+# ------------------------------------------------------------------------------
systemctl_SOURCES = \
src/systemctl.c \
src/utmp-wtmp.c \
@@ -983,7 +1028,6 @@ systemctl_SOURCES = \
src/path-lookup.c \
src/cgroup-show.c \
src/cgroup-util.c \
- src/exit-status.c \
src/unit-name.c \
src/pager.c \
src/install.c \
@@ -1001,6 +1045,7 @@ systemctl_LDADD = \
libsystemd-id128.la \
$(DBUS_LIBS)
+# ------------------------------------------------------------------------------
systemd_notify_SOURCES = \
src/notify.c \
src/readahead/sd-readahead.c
@@ -1009,6 +1054,7 @@ systemd_notify_LDADD = \
libsystemd-basic.la \
libsystemd-daemon.la
+# ------------------------------------------------------------------------------
systemd_ask_password_SOURCES = \
src/ask-password.c \
src/ask-password-api.c
@@ -1016,12 +1062,14 @@ systemd_ask_password_SOURCES = \
systemd_ask_password_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_reply_password_SOURCES = \
src/reply-password.c
systemd_reply_password_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_cgls_SOURCES = \
src/cgls.c \
src/cgroup-show.c \
@@ -1031,6 +1079,7 @@ systemd_cgls_SOURCES = \
systemd_cgls_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_cgtop_SOURCES = \
src/cgtop.c \
src/cgroup-util.c
@@ -1038,6 +1087,7 @@ systemd_cgtop_SOURCES = \
systemd_cgtop_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_nspawn_SOURCES = \
src/nspawn.c \
src/cgroup-util.c \
@@ -1045,14 +1095,17 @@ systemd_nspawn_SOURCES = \
systemd_nspawn_LDADD = \
libsystemd-basic.la \
+ libsystemd-capability.la \
libsystemd-daemon.la
+# ------------------------------------------------------------------------------
systemd_stdio_bridge_SOURCES = \
src/bridge.c
systemd_stdio_bridge_LDADD = \
libsystemd-basic.la
+# ------------------------------------------------------------------------------
systemd_tty_ask_password_agent_SOURCES = \
src/tty-ask-password-agent.c \
src/ask-password-api.c \
@@ -1844,6 +1897,7 @@ systemd_journald_CFLAGS = \
systemd_journald_LDADD = \
libsystemd-basic.la \
+ libsystemd-audit.la \
libsystemd-daemon.la \
libsystemd-login.la \
$(ACL_LIBS)
@@ -2452,6 +2506,7 @@ systemd_logind_CFLAGS = \
systemd_logind_LDADD = \
libsystemd-basic.la \
+ libsystemd-audit.la \
libsystemd-daemon.la \
libudev.la \
$(DBUS_LIBS) \
diff --git a/src/conf-parser.c b/src/conf-parser.c
deleted file mode 100644
index a9b0113..0000000
--- a/src/conf-parser.c
+++ /dev/null
@@ -1,852 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <assert.h>
-#include <stdlib.h>
-
-#include "conf-parser.h"
-#include "util.h"
-#include "macro.h"
-#include "strv.h"
-#include "log.h"
-#include "utf8.h"
-
-int config_item_table_lookup(
- void *table,
- const char *section,
- const char *lvalue,
- ConfigParserCallback *func,
- int *ltype,
- void **data,
- void *userdata) {
-
- ConfigTableItem *t;
-
- assert(table);
- assert(lvalue);
- assert(func);
- assert(ltype);
- assert(data);
-
- for (t = table; t->lvalue; t++) {
-
- if (!streq(lvalue, t->lvalue))
- continue;
-
- if (!streq_ptr(section, t->section))
- continue;
-
- *func = t->parse;
- *ltype = t->ltype;
- *data = t->data;
- return 1;
- }
-
- return 0;
-}
-
-int config_item_perf_lookup(
- void *table,
- const char *section,
- const char *lvalue,
- ConfigParserCallback *func,
- int *ltype,
- void **data,
- void *userdata) {
-
- ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
- const ConfigPerfItem *p;
-
- assert(table);
- assert(lvalue);
- assert(func);
- assert(ltype);
- assert(data);
-
- if (!section)
- p = lookup(lvalue, strlen(lvalue));
- else {
- char *key;
-
- key = join(section, ".", lvalue, NULL);
- if (!key)
- return -ENOMEM;
-
- p = lookup(key, strlen(key));
- free(key);
- }
-
- if (!p)
- return 0;
-
- *func = p->parse;
- *ltype = p->ltype;
- *data = (uint8_t*) userdata + p->offset;
- return 1;
-}
-
-/* Run the user supplied parser for an assignment */
-static int next_assignment(
- const char *filename,
- unsigned line,
- ConfigItemLookup lookup,
- void *table,
- const char *section,
- const char *lvalue,
- const char *rvalue,
- bool relaxed,
- void *userdata) {
-
- ConfigParserCallback func = NULL;
- int ltype = 0;
- void *data = NULL;
- int r;
-
- assert(filename);
- assert(line > 0);
- assert(lookup);
- assert(lvalue);
- assert(rvalue);
-
- r = lookup(table, section, lvalue, &func, <ype, &data, userdata);
- if (r < 0)
- return r;
-
- if (r > 0) {
- if (func)
- return func(filename, line, section, lvalue, ltype, rvalue, data, userdata);
-
- return 0;
- }
-
- /* Warn about unknown non-extension fields. */
- if (!relaxed && !startswith(lvalue, "X-"))
- log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, section);
-
- return 0;
-}
-
-/* Parse a variable assignment line */
-static int parse_line(
- const char *filename,
- unsigned line,
- const char *sections,
- ConfigItemLookup lookup,
- void *table,
- bool relaxed,
- char **section,
- char *l,
- void *userdata) {
-
- char *e;
-
- assert(filename);
- assert(line > 0);
- assert(lookup);
- assert(l);
-
- l = strstrip(l);
-
- if (!*l)
- return 0;
-
- if (strchr(COMMENTS, *l))
- return 0;
-
- if (startswith(l, ".include ")) {
- char *fn;
- int r;
-
- fn = file_in_same_dir(filename, strstrip(l+9));
- if (!fn)
- return -ENOMEM;
-
- r = config_parse(fn, NULL, sections, lookup, table, relaxed, userdata);
- free(fn);
-
- return r;
- }
-
- if (*l == '[') {
- size_t k;
- char *n;
-
- k = strlen(l);
- assert(k > 0);
-
- if (l[k-1] != ']') {
- log_error("[%s:%u] Invalid section header.", filename, line);
- return -EBADMSG;
- }
-
- n = strndup(l+1, k-2);
- if (!n)
- return -ENOMEM;
-
- if (sections && !nulstr_contains(sections, n)) {
-
- if (!relaxed)
- log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n);
-
- free(n);
- *section = NULL;
- } else {
- free(*section);
- *section = n;
- }
-
- return 0;
- }
-
- if (sections && !*section) {
-
- if (!relaxed)
- log_info("[%s:%u] Assignment outside of section. Ignoring.", filename, line);
-
- return 0;
- }
-
- e = strchr(l, '=');
- if (!e) {
- log_error("[%s:%u] Missing '='.", filename, line);
- return -EBADMSG;
- }
-
- *e = 0;
- e++;
-
- return next_assignment(
- filename,
- line,
- lookup,
- table,
- *section,
- strstrip(l),
- strstrip(e),
- relaxed,
- userdata);
-}
-
-/* Go through the file and parse each line */
-int config_parse(
- const char *filename,
- FILE *f,
- const char *sections,
- ConfigItemLookup lookup,
- void *table,
- bool relaxed,
- void *userdata) {
-
- unsigned line = 0;
- char *section = NULL;
- int r;
- bool ours = false;
- char *continuation = NULL;
-
- assert(filename);
- assert(lookup);
-
- if (!f) {
- f = fopen(filename, "re");
- if (!f) {
- r = -errno;
- log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
- goto finish;
- }
-
- ours = true;
- }
-
- while (!feof(f)) {
- char l[LINE_MAX], *p, *c = NULL, *e;
- bool escaped = false;
-
- if (!fgets(l, sizeof(l), f)) {
- if (feof(f))
- break;
-
- r = -errno;
- log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
- goto finish;
- }
-
- truncate_nl(l);
-
- if (continuation) {
- c = strappend(continuation, l);
- if (!c) {
- r = -ENOMEM;
- goto finish;
- }
-
- free(continuation);
- continuation = NULL;
- p = c;
- } else
- p = l;
-
- for (e = p; *e; e++) {
- if (escaped)
- escaped = false;
- else if (*e == '\\')
- escaped = true;
- }
-
- if (escaped) {
- *(e-1) = ' ';
-
- if (c)
- continuation = c;
- else {
- continuation = strdup(l);
- if (!continuation) {
- r = -ENOMEM;
- goto finish;
- }
- }
-
- continue;
- }
-
- r = parse_line(filename,
- ++line,
- sections,
- lookup,
- table,
- relaxed,
- §ion,
- p,
- userdata);
- free(c);
-
- if (r < 0)
- goto finish;
- }
-
- r = 0;
-
-finish:
- free(section);
- free(continuation);
-
- if (f && ours)
- fclose(f);
-
- return r;
-}
-
-int config_parse_int(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- int *i = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((r = safe_atoi(rvalue, i)) < 0) {
- log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_long(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- long *i = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((r = safe_atoli(rvalue, i)) < 0) {
- log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_uint64(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- uint64_t *u = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((r = safe_atou64(rvalue, u)) < 0) {
- log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_unsigned(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- unsigned *u = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((r = safe_atou(rvalue, u)) < 0) {
- log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
- return r;
- }
-
- return 0;
-}
-
-int config_parse_bytes_size(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- size_t *sz = data;
- off_t o;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) {
- log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- *sz = (size_t) o;
- return 0;
-}
-
-
-int config_parse_bytes_off(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- off_t *bytes = data;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- assert_cc(sizeof(off_t) == sizeof(uint64_t));
-
- if (parse_bytes(rvalue, bytes) < 0) {
- log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_bool(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- int k;
- bool *b = data;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((k = parse_boolean(rvalue)) < 0) {
- log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- *b = !!k;
- return 0;
-}
-
-int config_parse_tristate(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- int k;
- int *b = data;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */
-
- k = parse_boolean(rvalue);
- if (k < 0) {
- log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- *b = !!k;
- return 0;
-}
-
-int config_parse_string(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char **s = data;
- char *n;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- n = cunescape(rvalue);
- if (!n)
- return -ENOMEM;
-
- if (!utf8_is_valid(n)) {
- log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
- free(n);
- return 0;
- }
-
- free(*s);
- if (*n)
- *s = n;
- else {
- free(n);
- *s = NULL;
- }
-
- return 0;
-}
-
-int config_parse_path(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char **s = data;
- char *n;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (!utf8_is_valid(rvalue)) {
- log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
- return 0;
- }
-
- if (!path_is_absolute(rvalue)) {
- log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- n = strdup(rvalue);
- if (!n)
- return -ENOMEM;
-
- path_kill_slashes(n);
-
- free(*s);
- *s = n;
-
- return 0;
-}
-
-int config_parse_strv(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char*** sv = data;
- char **n;
- char *w;
- unsigned k;
- size_t l;
- char *state;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- k = strv_length(*sv);
- FOREACH_WORD_QUOTED(w, l, rvalue, state)
- k++;
-
- n = new(char*, k+1);
- if (!n)
- return -ENOMEM;
-
- if (*sv)
- for (k = 0; (*sv)[k]; k++)
- n[k] = (*sv)[k];
- else
- k = 0;
-
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- n[k] = cunescape_length(w, l);
- if (!n[k]) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (!utf8_is_valid(n[k])) {
- log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
- free(n[k]);
- continue;
- }
-
- k++;
- }
-
- n[k] = NULL;
- free(*sv);
- *sv = n;
-
- return 0;
-
-fail:
- for (; k > 0; k--)
- free(n[k-1]);
- free(n);
-
- return r;
-}
-
-int config_parse_path_strv(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char*** sv = data;
- char **n;
- char *w;
- unsigned k;
- size_t l;
- char *state;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- k = strv_length(*sv);
- FOREACH_WORD_QUOTED(w, l, rvalue, state)
- k++;
-
- n = new(char*, k+1);
- if (!n)
- return -ENOMEM;
-
- k = 0;
- if (*sv)
- for (; (*sv)[k]; k++)
- n[k] = (*sv)[k];
-
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- n[k] = strndup(w, l);
- if (!n[k]) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (!utf8_is_valid(n[k])) {
- log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
- free(n[k]);
- continue;
- }
-
- if (!path_is_absolute(n[k])) {
- log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
- free(n[k]);
- continue;
- }
-
- path_kill_slashes(n[k]);
- k++;
- }
-
- n[k] = NULL;
- free(*sv);
- *sv = n;
-
- return 0;
-
-fail:
- for (; k > 0; k--)
- free(n[k-1]);
- free(n);
-
- return r;
-}
-
-int config_parse_usec(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- usec_t *usec = data;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (parse_usec(rvalue, usec) < 0) {
- log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_mode(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- mode_t *m = data;
- long l;
- char *x = NULL;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- errno = 0;
- l = strtol(rvalue, &x, 8);
- if (!x || *x || errno) {
- log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (l < 0000 || l > 07777) {
- log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- *m = (mode_t) l;
- return 0;
-}
diff --git a/src/conf-parser.h b/src/conf-parser.h
deleted file mode 100644
index be7d708..0000000
--- a/src/conf-parser.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooconfparserhfoo
-#define fooconfparserhfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdio.h>
-#include <stdbool.h>
-
-/* An abstract parser for simple, line based, shallow configuration
- * files consisting of variable assignments only. */
-
-/* Prototype for a parser for a specific configuration setting */
-typedef int (*ConfigParserCallback)(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata);
-
-/* Wraps information for parsing a specific configuration variable, to
- * be stored in a simple array */
-typedef struct ConfigTableItem {
- const char *section; /* Section */
- const char *lvalue; /* Name of the variable */
- ConfigParserCallback parse; /* Function that is called to parse the variable's value */
- int ltype; /* Distinguish different variables passed to the same callback */
- void *data; /* Where to store the variable's data */
-} ConfigTableItem;
-
-/* Wraps information for parsing a specific configuration variable, to
- * ve srored in a gperf perfect hashtable */
-typedef struct ConfigPerfItem {
- const char *section_and_lvalue; /* Section + "." + name of the variable */
- ConfigParserCallback parse; /* Function that is called to parse the variable's value */
- int ltype; /* Distinguish different variables passed to the same callback */
- size_t offset; /* Offset where to store data, from the beginning of userdata */
-} ConfigPerfItem;
-
-/* Prototype for a low-level gperf lookup function */
-typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, unsigned length);
-
-/* Prototype for a generic high-level lookup function */
-typedef int (*ConfigItemLookup)(
- void *table,
- const char *section,
- const char *lvalue,
- ConfigParserCallback *func,
- int *ltype,
- void **data,
- void *userdata);
-
-/* Linear table search implementation of ConfigItemLookup, based on
- * ConfigTableItem arrays */
-int config_item_table_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
-
-/* gperf implementation of ConfigItemLookup, based on gperf
- * ConfigPerfItem tables */
-int config_item_perf_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
-
-int config_parse(
- const char *filename,
- FILE *f,
- const char *sections, /* nulstr */
- ConfigItemLookup lookup,
- void *table,
- bool relaxed,
- void *userdata);
-
-/* Generic parsers */
-int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_long(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_uint64(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bytes_size(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bytes_off(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_tristate(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_path_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_usec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-
-#define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \
- int function( \
- const char *filename, \
- unsigned line, \
- const char *section, \
- const char *lvalue, \
- int ltype, \
- const char *rvalue, \
- void *data, \
- void *userdata) { \
- \
- type *i = data, x; \
- \
- assert(filename); \
- assert(lvalue); \
- assert(rvalue); \
- assert(data); \
- \
- if ((x = name##_from_string(rvalue)) < 0) { \
- log_error("[%s:%u] " msg ", ignoring: %s", filename, line, rvalue); \
- return 0; \
- } \
- \
- *i = x; \
- \
- return 0; \
- }
-
-#endif
diff --git a/src/execute.c b/src/execute.c
index dab4856..179ca7e 100644
--- a/src/execute.c
+++ b/src/execute.c
@@ -45,6 +45,7 @@
#include "execute.h"
#include "strv.h"
#include "macro.h"
+#include "capability.h"
#include "util.h"
#include "log.h"
#include "ioprio.h"
diff --git a/src/exit-status.c b/src/exit-status.c
deleted file mode 100644
index ab8907d..0000000
--- a/src/exit-status.c
+++ /dev/null
@@ -1,180 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdlib.h>
-#include <sys/wait.h>
-
-#include "exit-status.h"
-
-const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
-
- /* We cast to int here, so that -Wenum doesn't complain that
- * EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */
-
- switch ((int) status) {
-
- case EXIT_SUCCESS:
- return "SUCCESS";
-
- case EXIT_FAILURE:
- return "FAILURE";
- }
-
-
- if (level == EXIT_STATUS_SYSTEMD || level == EXIT_STATUS_LSB) {
- switch ((int) status) {
-
- case EXIT_CHDIR:
- return "CHDIR";
-
- case EXIT_NICE:
- return "NICE";
-
- case EXIT_FDS:
- return "FDS";
-
- case EXIT_EXEC:
- return "EXEC";
-
- case EXIT_MEMORY:
- return "MEMORY";
-
- case EXIT_LIMITS:
- return "LIMITS";
-
- case EXIT_OOM_ADJUST:
- return "OOM_ADJUST";
-
- case EXIT_SIGNAL_MASK:
- return "SIGNAL_MASK";
-
- case EXIT_STDIN:
- return "STDIN";
-
- case EXIT_STDOUT:
- return "STDOUT";
-
- case EXIT_CHROOT:
- return "CHROOT";
-
- case EXIT_IOPRIO:
- return "IOPRIO";
-
- case EXIT_TIMERSLACK:
- return "TIMERSLACK";
-
- case EXIT_SECUREBITS:
- return "SECUREBITS";
-
- case EXIT_SETSCHEDULER:
- return "SETSCHEDULER";
-
- case EXIT_CPUAFFINITY:
- return "CPUAFFINITY";
-
- case EXIT_GROUP:
- return "GROUP";
-
- case EXIT_USER:
- return "USER";
-
- case EXIT_CAPABILITIES:
- return "CAPABILITIES";
-
- case EXIT_CGROUP:
- return "CGROUP";
-
- case EXIT_SETSID:
- return "SETSID";
-
- case EXIT_CONFIRM:
- return "CONFIRM";
-
- case EXIT_STDERR:
- return "STDERR";
-
- case EXIT_TCPWRAP:
- return "TCPWRAP";
-
- case EXIT_PAM:
- return "PAM";
-
- case EXIT_NETWORK:
- return "NETWORK";
-
- case EXIT_NAMESPACE:
- return "NAMESPACE";
- }
- }
-
- if (level == EXIT_STATUS_LSB) {
- switch ((int) status) {
-
- case EXIT_INVALIDARGUMENT:
- return "INVALIDARGUMENT";
-
- case EXIT_NOTIMPLEMENTED:
- return "NOTIMPLEMENTED";
-
- case EXIT_NOPERMISSION:
- return "NOPERMISSION";
-
- case EXIT_NOTINSTALLED:
- return "NOTINSSTALLED";
-
- case EXIT_NOTCONFIGURED:
- return "NOTCONFIGURED";
-
- case EXIT_NOTRUNNING:
- return "NOTRUNNING";
- }
- }
-
- return NULL;
-}
-
-
-bool is_clean_exit(int code, int status) {
-
- if (code == CLD_EXITED)
- return status == 0;
-
- /* If a daemon does not implement handlers for some of the
- * signals that's not considered an unclean shutdown */
- if (code == CLD_KILLED)
- return
- status == SIGHUP ||
- status == SIGINT ||
- status == SIGTERM ||
- status == SIGPIPE;
-
- return false;
-}
-
-bool is_clean_exit_lsb(int code, int status) {
-
- if (is_clean_exit(code, status))
- return true;
-
- return
- code == CLD_EXITED &&
- (status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED);
-}
diff --git a/src/exit-status.h b/src/exit-status.h
deleted file mode 100644
index 44ef879..0000000
--- a/src/exit-status.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooexitstatushfoo
-#define fooexitstatushfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-
-typedef enum ExitStatus {
- /* EXIT_SUCCESS defined by libc */
- /* EXIT_FAILURE defined by libc */
- EXIT_INVALIDARGUMENT = 2,
- EXIT_NOTIMPLEMENTED = 3,
- EXIT_NOPERMISSION = 4,
- EXIT_NOTINSTALLED = 5,
- EXIT_NOTCONFIGURED = 6,
- EXIT_NOTRUNNING = 7,
-
- /* The LSB suggests that error codes >= 200 are "reserved". We
- * use them here under the assumption that they hence are
- * unused by init scripts.
- *
- * http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */
-
- EXIT_CHDIR = 200,
- EXIT_NICE,
- EXIT_FDS,
- EXIT_EXEC,
- EXIT_MEMORY,
- EXIT_LIMITS,
- EXIT_OOM_ADJUST,
- EXIT_SIGNAL_MASK,
- EXIT_STDIN,
- EXIT_STDOUT,
- EXIT_CHROOT, /* 210 */
- EXIT_IOPRIO,
- EXIT_TIMERSLACK,
- EXIT_SECUREBITS,
- EXIT_SETSCHEDULER,
- EXIT_CPUAFFINITY,
- EXIT_GROUP,
- EXIT_USER,
- EXIT_CAPABILITIES,
- EXIT_CGROUP,
- EXIT_SETSID, /* 220 */
- EXIT_CONFIRM,
- EXIT_STDERR,
- EXIT_TCPWRAP,
- EXIT_PAM,
- EXIT_NETWORK,
- EXIT_NAMESPACE
-
-} ExitStatus;
-
-typedef enum ExitStatusLevel {
- EXIT_STATUS_MINIMAL,
- EXIT_STATUS_SYSTEMD,
- EXIT_STATUS_LSB,
- EXIT_STATUS_FULL = EXIT_STATUS_LSB
-} ExitStatusLevel;
-
-const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level);
-
-bool is_clean_exit(int code, int status);
-bool is_clean_exit_lsb(int code, int status);
-
-#endif
diff --git a/src/hashmap.c b/src/hashmap.c
deleted file mode 100644
index 6928118..0000000
--- a/src/hashmap.c
+++ /dev/null
@@ -1,731 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include "util.h"
-#include "hashmap.h"
-#include "macro.h"
-
-#define NBUCKETS 127
-
-struct hashmap_entry {
- const void *key;
- void *value;
- struct hashmap_entry *bucket_next, *bucket_previous;
- struct hashmap_entry *iterate_next, *iterate_previous;
-};
-
-struct Hashmap {
- hash_func_t hash_func;
- compare_func_t compare_func;
-
- struct hashmap_entry *iterate_list_head, *iterate_list_tail;
- unsigned n_entries;
-
- bool from_pool;
-};
-
-#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
-
-struct pool {
- struct pool *next;
- unsigned n_tiles;
- unsigned n_used;
-};
-
-static struct pool *first_hashmap_pool = NULL;
-static void *first_hashmap_tile = NULL;
-
-static struct pool *first_entry_pool = NULL;
-static void *first_entry_tile = NULL;
-
-static void* allocate_tile(struct pool **first_pool, void **first_tile, size_t tile_size) {
- unsigned i;
-
- if (*first_tile) {
- void *r;
-
- r = *first_tile;
- *first_tile = * (void**) (*first_tile);
- return r;
- }
-
- if (_unlikely_(!*first_pool) || _unlikely_((*first_pool)->n_used >= (*first_pool)->n_tiles)) {
- unsigned n;
- size_t size;
- struct pool *p;
-
- n = *first_pool ? (*first_pool)->n_tiles : 0;
- n = MAX(512U, n * 2);
- size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*tile_size);
- n = (size - ALIGN(sizeof(struct pool))) / tile_size;
-
- p = malloc(size);
- if (!p)
- return NULL;
-
- p->next = *first_pool;
- p->n_tiles = n;
- p->n_used = 0;
-
- *first_pool = p;
- }
-
- i = (*first_pool)->n_used++;
-
- return ((uint8_t*) (*first_pool)) + ALIGN(sizeof(struct pool)) + i*tile_size;
-}
-
-static void deallocate_tile(void **first_tile, void *p) {
- * (void**) p = *first_tile;
- *first_tile = p;
-}
-
-#ifndef __OPTIMIZE__
-
-static void drop_pool(struct pool *p) {
- while (p) {
- struct pool *n;
- n = p->next;
- free(p);
- p = n;
- }
-}
-
-__attribute__((destructor)) static void cleanup_pool(void) {
- /* Be nice to valgrind */
-
- drop_pool(first_hashmap_pool);
- drop_pool(first_entry_pool);
-}
-
-#endif
-
-unsigned string_hash_func(const void *p) {
- unsigned hash = 5381;
- const signed char *c;
-
- /* DJB's hash function */
-
- for (c = p; *c; c++)
- hash = (hash << 5) + hash + (unsigned) *c;
-
- return hash;
-}
-
-int string_compare_func(const void *a, const void *b) {
- return strcmp(a, b);
-}
-
-unsigned trivial_hash_func(const void *p) {
- return PTR_TO_UINT(p);
-}
-
-int trivial_compare_func(const void *a, const void *b) {
- return a < b ? -1 : (a > b ? 1 : 0);
-}
-
-Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
- bool b;
- Hashmap *h;
- size_t size;
-
- b = is_main_thread();
-
- size = ALIGN(sizeof(Hashmap)) + NBUCKETS * sizeof(struct hashmap_entry*);
-
- if (b) {
- h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size);
- if (!h)
- return NULL;
-
- memset(h, 0, size);
- } else {
- h = malloc0(size);
-
- if (!h)
- return NULL;
- }
-
- h->hash_func = hash_func ? hash_func : trivial_hash_func;
- h->compare_func = compare_func ? compare_func : trivial_compare_func;
-
- h->n_entries = 0;
- h->iterate_list_head = h->iterate_list_tail = NULL;
-
- h->from_pool = b;
-
- return h;
-}
-
-int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) {
- assert(h);
-
- if (*h)
- return 0;
-
- if (!(*h = hashmap_new(hash_func, compare_func)))
- return -ENOMEM;
-
- return 0;
-}
-
-static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
- assert(h);
- assert(e);
-
- /* Insert into hash table */
- e->bucket_next = BY_HASH(h)[hash];
- e->bucket_previous = NULL;
- if (BY_HASH(h)[hash])
- BY_HASH(h)[hash]->bucket_previous = e;
- BY_HASH(h)[hash] = e;
-
- /* Insert into iteration list */
- e->iterate_previous = h->iterate_list_tail;
- e->iterate_next = NULL;
- if (h->iterate_list_tail) {
- assert(h->iterate_list_head);
- h->iterate_list_tail->iterate_next = e;
- } else {
- assert(!h->iterate_list_head);
- h->iterate_list_head = e;
- }
- h->iterate_list_tail = e;
-
- h->n_entries++;
- assert(h->n_entries >= 1);
-}
-
-static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
- assert(h);
- assert(e);
-
- /* Remove from iteration list */
- if (e->iterate_next)
- e->iterate_next->iterate_previous = e->iterate_previous;
- else
- h->iterate_list_tail = e->iterate_previous;
-
- if (e->iterate_previous)
- e->iterate_previous->iterate_next = e->iterate_next;
- else
- h->iterate_list_head = e->iterate_next;
-
- /* Remove from hash table bucket list */
- if (e->bucket_next)
- e->bucket_next->bucket_previous = e->bucket_previous;
-
- if (e->bucket_previous)
- e->bucket_previous->bucket_next = e->bucket_next;
- else
- BY_HASH(h)[hash] = e->bucket_next;
-
- assert(h->n_entries >= 1);
- h->n_entries--;
-}
-
-static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
- unsigned hash;
-
- assert(h);
- assert(e);
-
- hash = h->hash_func(e->key) % NBUCKETS;
-
- unlink_entry(h, e, hash);
-
- if (h->from_pool)
- deallocate_tile(&first_entry_tile, e);
- else
- free(e);
-}
-
-void hashmap_free(Hashmap*h) {
-
- if (!h)
- return;
-
- hashmap_clear(h);
-
- if (h->from_pool)
- deallocate_tile(&first_hashmap_tile, h);
- else
- free(h);
-}
-
-void hashmap_free_free(Hashmap *h) {
- void *p;
-
- while ((p = hashmap_steal_first(h)))
- free(p);
-
- hashmap_free(h);
-}
-
-void hashmap_clear(Hashmap *h) {
- if (!h)
- return;
-
- while (h->iterate_list_head)
- remove_entry(h, h->iterate_list_head);
-}
-
-static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
- struct hashmap_entry *e;
- assert(h);
- assert(hash < NBUCKETS);
-
- for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
- if (h->compare_func(e->key, key) == 0)
- return e;
-
- return NULL;
-}
-
-int hashmap_put(Hashmap *h, const void *key, void *value) {
- struct hashmap_entry *e;
- unsigned hash;
-
- assert(h);
-
- hash = h->hash_func(key) % NBUCKETS;
-
- if ((e = hash_scan(h, hash, key))) {
-
- if (e->value == value)
- return 0;
-
- return -EEXIST;
- }
-
- if (h->from_pool)
- e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry));
- else
- e = new(struct hashmap_entry, 1);
-
- if (!e)
- return -ENOMEM;
-
- e->key = key;
- e->value = value;
-
- link_entry(h, e, hash);
-
- return 1;
-}
-
-int hashmap_replace(Hashmap *h, const void *key, void *value) {
- struct hashmap_entry *e;
- unsigned hash;
-
- assert(h);
-
- hash = h->hash_func(key) % NBUCKETS;
-
- if ((e = hash_scan(h, hash, key))) {
- e->key = key;
- e->value = value;
- return 0;
- }
-
- return hashmap_put(h, key, value);
-}
-
-void* hashmap_get(Hashmap *h, const void *key) {
- unsigned hash;
- struct hashmap_entry *e;
-
- if (!h)
- return NULL;
-
- hash = h->hash_func(key) % NBUCKETS;
-
- if (!(e = hash_scan(h, hash, key)))
- return NULL;
-
- return e->value;
-}
-
-void* hashmap_remove(Hashmap *h, const void *key) {
- struct hashmap_entry *e;
- unsigned hash;
- void *data;
-
- if (!h)
- return NULL;
-
- hash = h->hash_func(key) % NBUCKETS;
-
- if (!(e = hash_scan(h, hash, key)))
- return NULL;
-
- data = e->value;
- remove_entry(h, e);
-
- return data;
-}
-
-int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
- struct hashmap_entry *e;
- unsigned old_hash, new_hash;
-
- if (!h)
- return -ENOENT;
-
- old_hash = h->hash_func(old_key) % NBUCKETS;
- if (!(e = hash_scan(h, old_hash, old_key)))
- return -ENOENT;
-
- new_hash = h->hash_func(new_key) % NBUCKETS;
- if (hash_scan(h, new_hash, new_key))
- return -EEXIST;
-
- unlink_entry(h, e, old_hash);
-
- e->key = new_key;
- e->value = value;
-
- link_entry(h, e, new_hash);
-
- return 0;
-}
-
-int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
- struct hashmap_entry *e, *k;
- unsigned old_hash, new_hash;
-
- if (!h)
- return -ENOENT;
-
- old_hash = h->hash_func(old_key) % NBUCKETS;
- if (!(e = hash_scan(h, old_hash, old_key)))
- return -ENOENT;
-
- new_hash = h->hash_func(new_key) % NBUCKETS;
-
- if ((k = hash_scan(h, new_hash, new_key)))
- if (e != k)
- remove_entry(h, k);
-
- unlink_entry(h, e, old_hash);
-
- e->key = new_key;
- e->value = value;
-
- link_entry(h, e, new_hash);
-
- return 0;
-}
-
-void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
- struct hashmap_entry *e;
- unsigned hash;
-
- if (!h)
- return NULL;
-
- hash = h->hash_func(key) % NBUCKETS;
-
- if (!(e = hash_scan(h, hash, key)))
- return NULL;
-
- if (e->value != value)
- return NULL;
-
- remove_entry(h, e);
-
- return value;
-}
-
-void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
- struct hashmap_entry *e;
-
- assert(i);
-
- if (!h)
- goto at_end;
-
- if (*i == ITERATOR_LAST)
- goto at_end;
-
- if (*i == ITERATOR_FIRST && !h->iterate_list_head)
- goto at_end;
-
- e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
-
- if (e->iterate_next)
- *i = (Iterator) e->iterate_next;
- else
- *i = ITERATOR_LAST;
-
- if (key)
- *key = e->key;
-
- return e->value;
-
-at_end:
- *i = ITERATOR_LAST;
-
- if (key)
- *key = NULL;
-
- return NULL;
-}
-
-void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
- struct hashmap_entry *e;
-
- assert(i);
-
- if (!h)
- goto at_beginning;
-
- if (*i == ITERATOR_FIRST)
- goto at_beginning;
-
- if (*i == ITERATOR_LAST && !h->iterate_list_tail)
- goto at_beginning;
-
- e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
-
- if (e->iterate_previous)
- *i = (Iterator) e->iterate_previous;
- else
- *i = ITERATOR_FIRST;
-
- if (key)
- *key = e->key;
-
- return e->value;
-
-at_beginning:
- *i = ITERATOR_FIRST;
-
- if (key)
- *key = NULL;
-
- return NULL;
-}
-
-void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
- unsigned hash;
- struct hashmap_entry *e;
-
- if (!h)
- return NULL;
-
- hash = h->hash_func(key) % NBUCKETS;
-
- if (!(e = hash_scan(h, hash, key)))
- return NULL;
-
- *i = (Iterator) e;
-
- return e->value;
-}
-
-void* hashmap_first(Hashmap *h) {
-
- if (!h)
- return NULL;
-
- if (!h->iterate_list_head)
- return NULL;
-
- return h->iterate_list_head->value;
-}
-
-void* hashmap_first_key(Hashmap *h) {
-
- if (!h)
- return NULL;
-
- if (!h->iterate_list_head)
- return NULL;
-
- return (void*) h->iterate_list_head->key;
-}
-
-void* hashmap_last(Hashmap *h) {
-
- if (!h)
- return NULL;
-
- if (!h->iterate_list_tail)
- return NULL;
-
- return h->iterate_list_tail->value;
-}
-
-void* hashmap_steal_first(Hashmap *h) {
- void *data;
-
- if (!h)
- return NULL;
-
- if (!h->iterate_list_head)
- return NULL;
-
- data = h->iterate_list_head->value;
- remove_entry(h, h->iterate_list_head);
-
- return data;
-}
-
-void* hashmap_steal_first_key(Hashmap *h) {
- void *key;
-
- if (!h)
- return NULL;
-
- if (!h->iterate_list_head)
- return NULL;
-
- key = (void*) h->iterate_list_head->key;
- remove_entry(h, h->iterate_list_head);
-
- return key;
-}
-
-unsigned hashmap_size(Hashmap *h) {
-
- if (!h)
- return 0;
-
- return h->n_entries;
-}
-
-bool hashmap_isempty(Hashmap *h) {
-
- if (!h)
- return true;
-
- return h->n_entries == 0;
-}
-
-int hashmap_merge(Hashmap *h, Hashmap *other) {
- struct hashmap_entry *e;
-
- assert(h);
-
- if (!other)
- return 0;
-
- for (e = other->iterate_list_head; e; e = e->iterate_next) {
- int r;
-
- if ((r = hashmap_put(h, e->key, e->value)) < 0)
- if (r != -EEXIST)
- return r;
- }
-
- return 0;
-}
-
-void hashmap_move(Hashmap *h, Hashmap *other) {
- struct hashmap_entry *e, *n;
-
- assert(h);
-
- /* The same as hashmap_merge(), but every new item from other
- * is moved to h. This function is guaranteed to succeed. */
-
- if (!other)
- return;
-
- for (e = other->iterate_list_head; e; e = n) {
- unsigned h_hash, other_hash;
-
- n = e->iterate_next;
-
- h_hash = h->hash_func(e->key) % NBUCKETS;
-
- if (hash_scan(h, h_hash, e->key))
- continue;
-
- other_hash = other->hash_func(e->key) % NBUCKETS;
-
- unlink_entry(other, e, other_hash);
- link_entry(h, e, h_hash);
- }
-}
-
-int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
- unsigned h_hash, other_hash;
- struct hashmap_entry *e;
-
- if (!other)
- return 0;
-
- assert(h);
-
- h_hash = h->hash_func(key) % NBUCKETS;
- if (hash_scan(h, h_hash, key))
- return -EEXIST;
-
- other_hash = other->hash_func(key) % NBUCKETS;
- if (!(e = hash_scan(other, other_hash, key)))
- return -ENOENT;
-
- unlink_entry(other, e, other_hash);
- link_entry(h, e, h_hash);
-
- return 0;
-}
-
-Hashmap *hashmap_copy(Hashmap *h) {
- Hashmap *copy;
-
- assert(h);
-
- if (!(copy = hashmap_new(h->hash_func, h->compare_func)))
- return NULL;
-
- if (hashmap_merge(copy, h) < 0) {
- hashmap_free(copy);
- return NULL;
- }
-
- return copy;
-}
-
-char **hashmap_get_strv(Hashmap *h) {
- char **sv;
- Iterator it;
- char *item;
- int n;
-
- sv = new(char*, h->n_entries+1);
- if (!sv)
- return NULL;
-
- n = 0;
- HASHMAP_FOREACH(item, h, it)
- sv[n++] = item;
- sv[n] = NULL;
-
- return sv;
-}
diff --git a/src/hashmap.h b/src/hashmap.h
deleted file mode 100644
index ab4363a..0000000
--- a/src/hashmap.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foohashmaphfoo
-#define foohashmaphfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-
-/* Pretty straightforward hash table implementation. As a minor
- * optimization a NULL hashmap object will be treated as empty hashmap
- * for all read operations. That way it is not necessary to
- * instantiate an object for each Hashmap use. */
-
-typedef struct Hashmap Hashmap;
-typedef struct _IteratorStruct _IteratorStruct;
-typedef _IteratorStruct* Iterator;
-
-#define ITERATOR_FIRST ((Iterator) 0)
-#define ITERATOR_LAST ((Iterator) -1)
-
-typedef unsigned (*hash_func_t)(const void *p);
-typedef int (*compare_func_t)(const void *a, const void *b);
-
-unsigned string_hash_func(const void *p);
-int string_compare_func(const void *a, const void *b);
-
-unsigned trivial_hash_func(const void *p);
-int trivial_compare_func(const void *a, const void *b);
-
-Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func);
-void hashmap_free(Hashmap *h);
-void hashmap_free_free(Hashmap *h);
-Hashmap *hashmap_copy(Hashmap *h);
-int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func);
-
-int hashmap_put(Hashmap *h, const void *key, void *value);
-int hashmap_replace(Hashmap *h, const void *key, void *value);
-void* hashmap_get(Hashmap *h, const void *key);
-void* hashmap_remove(Hashmap *h, const void *key);
-void* hashmap_remove_value(Hashmap *h, const void *key, void *value);
-int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
-int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value);
-
-int hashmap_merge(Hashmap *h, Hashmap *other);
-void hashmap_move(Hashmap *h, Hashmap *other);
-int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key);
-
-unsigned hashmap_size(Hashmap *h);
-bool hashmap_isempty(Hashmap *h);
-
-void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key);
-void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key);
-void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i);
-
-void hashmap_clear(Hashmap *h);
-void *hashmap_steal_first(Hashmap *h);
-void *hashmap_steal_first_key(Hashmap *h);
-void* hashmap_first(Hashmap *h);
-void* hashmap_first_key(Hashmap *h);
-void* hashmap_last(Hashmap *h);
-
-char **hashmap_get_strv(Hashmap *h);
-
-#define HASHMAP_FOREACH(e, h, i) \
- for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); (e); (e) = hashmap_iterate((h), &(i), NULL))
-
-#define HASHMAP_FOREACH_KEY(e, k, h, i) \
- for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); (e); (e) = hashmap_iterate((h), &(i), (const void**) &(k)))
-
-#define HASHMAP_FOREACH_BACKWARDS(e, h, i) \
- for ((i) = ITERATOR_LAST, (e) = hashmap_iterate_backwards((h), &(i), NULL); (e); (e) = hashmap_iterate_backwards((h), &(i), NULL))
-
-#endif
diff --git a/src/journal/journald.h b/src/journal/journald.h
index 6160991..7840fd5 100644
--- a/src/journal/journald.h
+++ b/src/journal/journald.h
@@ -29,6 +29,7 @@
#include "journal-file.h"
#include "hashmap.h"
#include "util.h"
+#include "audit.h"
#include "journal-rate-limit.h"
#include "list.h"
diff --git a/src/label.c b/src/label.c
deleted file mode 100644
index 2c887a0..0000000
--- a/src/label.c
+++ /dev/null
@@ -1,413 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <malloc.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include "label.h"
-#include "util.h"
-
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-
-static struct selabel_handle *label_hnd = NULL;
-
-static int use_selinux_cached = -1;
-
-static inline bool use_selinux(void) {
-
- if (use_selinux_cached < 0)
- use_selinux_cached = is_selinux_enabled() > 0;
-
- return use_selinux_cached;
-}
-
-void label_retest_selinux(void) {
- use_selinux_cached = -1;
-}
-
-#endif
-
-int label_init(void) {
- int r = 0;
-
-#ifdef HAVE_SELINUX
- usec_t before_timestamp, after_timestamp;
- struct mallinfo before_mallinfo, after_mallinfo;
-
- if (!use_selinux())
- return 0;
-
- if (label_hnd)
- return 0;
-
- before_mallinfo = mallinfo();
- before_timestamp = now(CLOCK_MONOTONIC);
-
- label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
- if (!label_hnd) {
- log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
- "Failed to initialize SELinux context: %m");
- r = security_getenforce() == 1 ? -errno : 0;
- } else {
- char timespan[FORMAT_TIMESPAN_MAX];
- int l;
-
- after_timestamp = now(CLOCK_MONOTONIC);
- after_mallinfo = mallinfo();
-
- l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
-
- log_info("Successfully loaded SELinux database in %s, size on heap is %iK.",
- format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp),
- (l+1023)/1024);
- }
-#endif
-
- return r;
-}
-
-int label_fix(const char *path, bool ignore_enoent) {
- int r = 0;
-
-#ifdef HAVE_SELINUX
- struct stat st;
- security_context_t fcon;
-
- if (!use_selinux() || !label_hnd)
- return 0;
-
- r = lstat(path, &st);
- if (r == 0) {
- r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
-
- /* If there's no label to set, then exit without warning */
- if (r < 0 && errno == ENOENT)
- return 0;
-
- if (r == 0) {
- r = lsetfilecon(path, fcon);
- freecon(fcon);
-
- /* If the FS doesn't support labels, then exit without warning */
- if (r < 0 && errno == ENOTSUP)
- return 0;
- }
- }
-
- if (r < 0) {
- /* Ignore ENOENT in some cases */
- if (ignore_enoent && errno == ENOENT)
- return 0;
-
- log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
- "Unable to fix label of %s: %m", path);
- r = security_getenforce() == 1 ? -errno : 0;
- }
-#endif
-
- return r;
-}
-
-void label_finish(void) {
-
-#ifdef HAVE_SELINUX
- if (use_selinux() && label_hnd)
- selabel_close(label_hnd);
-#endif
-}
-
-int label_get_create_label_from_exe(const char *exe, char **label) {
-
- int r = 0;
-
-#ifdef HAVE_SELINUX
- security_context_t mycon = NULL, fcon = NULL;
- security_class_t sclass;
-
- if (!use_selinux()) {
- *label = NULL;
- return 0;
- }
-
- r = getcon(&mycon);
- if (r < 0)
- goto fail;
-
- r = getfilecon(exe, &fcon);
- if (r < 0)
- goto fail;
-
- sclass = string_to_security_class("process");
- r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
- if (r == 0)
- log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
-
-fail:
- if (r < 0 && security_getenforce() == 1)
- r = -errno;
-
- freecon(mycon);
- freecon(fcon);
-#endif
-
- return r;
-}
-
-int label_fifofile_set(const char *path) {
- int r = 0;
-
-#ifdef HAVE_SELINUX
- security_context_t filecon = NULL;
-
- if (!use_selinux() || !label_hnd)
- return 0;
-
- r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFIFO);
- if (r < 0)
- r = -errno;
- else if (r == 0) {
- r = setfscreatecon(filecon);
- if (r < 0) {
- log_error("Failed to set SELinux file context on %s: %m", path);
- r = -errno;
- }
-
- freecon(filecon);
- }
-
- if (r < 0 && security_getenforce() == 0)
- r = 0;
-#endif
-
- return r;
-}
-
-int label_symlinkfile_set(const char *path) {
- int r = 0;
-
-#ifdef HAVE_SELINUX
- security_context_t filecon = NULL;
-
- if (!use_selinux() || !label_hnd)
- return 0;
-
- r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFLNK);
- if (r < 0)
- r = -errno;
- else if (r == 0) {
- r = setfscreatecon(filecon);
- if (r < 0) {
- log_error("Failed to set SELinux file context on %s: %m", path);
- r = -errno;
- }
-
- freecon(filecon);
- }
-
- if (r < 0 && security_getenforce() == 0)
- r = 0;
-#endif
-
- return r;
-}
-
-int label_socket_set(const char *label) {
-
-#ifdef HAVE_SELINUX
- if (!use_selinux())
- return 0;
-
- if (setsockcreatecon((security_context_t) label) < 0) {
- log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
- "Failed to set SELinux context (%s) on socket: %m", label);
-
- if (security_getenforce() == 1)
- return -errno;
- }
-#endif
-
- return 0;
-}
-
-void label_file_clear(void) {
-
-#ifdef HAVE_SELINUX
- if (!use_selinux())
- return;
-
- setfscreatecon(NULL);
-#endif
-}
-
-void label_socket_clear(void) {
-
-#ifdef HAVE_SELINUX
- if (!use_selinux())
- return;
-
- setsockcreatecon(NULL);
-#endif
-}
-
-void label_free(const char *label) {
-
-#ifdef HAVE_SELINUX
- if (!use_selinux())
- return;
-
- freecon((security_context_t) label);
-#endif
-}
-
-int label_mkdir(const char *path, mode_t mode) {
-
- /* Creates a directory and labels it according to the SELinux policy */
-
-#ifdef HAVE_SELINUX
- int r;
- security_context_t fcon = NULL;
-
- if (!use_selinux() || !label_hnd)
- goto skipped;
-
- if (path_is_absolute(path))
- r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
- else {
- char *newpath;
-
- newpath = path_make_absolute_cwd(path);
- if (!newpath)
- return -ENOMEM;
-
- r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
- free(newpath);
- }
-
- if (r == 0)
- r = setfscreatecon(fcon);
-
- if (r < 0 && errno != ENOENT) {
- log_error("Failed to set security context %s for %s: %m", fcon, path);
-
- if (security_getenforce() == 1) {
- r = -errno;
- goto finish;
- }
- }
-
- r = mkdir(path, mode);
- if (r < 0)
- r = -errno;
-
-finish:
- setfscreatecon(NULL);
- freecon(fcon);
-
- return r;
-
-skipped:
-#endif
- return mkdir(path, mode) < 0 ? -errno : 0;
-}
-
-int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
-
- /* Binds a socket and label its file system object according to the SELinux policy */
-
-#ifdef HAVE_SELINUX
- int r;
- security_context_t fcon = NULL;
- const struct sockaddr_un *un;
- char *path = NULL;
-
- assert(fd >= 0);
- assert(addr);
- assert(addrlen >= sizeof(sa_family_t));
-
- if (!use_selinux() || !label_hnd)
- goto skipped;
-
- /* Filter out non-local sockets */
- if (addr->sa_family != AF_UNIX)
- goto skipped;
-
- /* Filter out anonymous sockets */
- if (addrlen < sizeof(sa_family_t) + 1)
- goto skipped;
-
- /* Filter out abstract namespace sockets */
- un = (const struct sockaddr_un*) addr;
- if (un->sun_path[0] == 0)
- goto skipped;
-
- path = strndup(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
- if (!path)
- return -ENOMEM;
-
- if (path_is_absolute(path))
- r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
- else {
- char *newpath;
-
- newpath = path_make_absolute_cwd(path);
-
- if (!newpath) {
- free(path);
- return -ENOMEM;
- }
-
- r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
- free(newpath);
- }
-
- if (r == 0)
- r = setfscreatecon(fcon);
-
- if (r < 0 && errno != ENOENT) {
- log_error("Failed to set security context %s for %s: %m", fcon, path);
-
- if (security_getenforce() == 1) {
- r = -errno;
- goto finish;
- }
- }
-
- r = bind(fd, addr, addrlen);
- if (r < 0)
- r = -errno;
-
-finish:
- setfscreatecon(NULL);
- freecon(fcon);
- free(path);
-
- return r;
-
-skipped:
-#endif
- return bind(fd, addr, addrlen) < 0 ? -errno : 0;
-}
diff --git a/src/label.h b/src/label.h
deleted file mode 100644
index ead4483..0000000
--- a/src/label.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foolabelhfoo
-#define foolabelhfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/types.h>
-#include <stdbool.h>
-#include <sys/socket.h>
-
-int label_init(void);
-void label_finish(void);
-
-int label_fix(const char *path, bool ignore_enoent);
-
-int label_socket_set(const char *label);
-void label_socket_clear(void);
-
-int label_fifofile_set(const char *path);
-int label_symlinkfile_set(const char *path);
-void label_file_clear(void);
-
-void label_free(const char *label);
-
-int label_get_create_label_from_exe(const char *exe, char **label);
-
-int label_mkdir(const char *path, mode_t mode);
-
-void label_retest_selinux(void);
-
-int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen);
-
-#endif
diff --git a/src/log.c b/src/log.c
deleted file mode 100644
index 9fffc1d..0000000
--- a/src/log.c
+++ /dev/null
@@ -1,747 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <stddef.h>
-
-#include "log.h"
-#include "util.h"
-#include "macro.h"
-#include "socket-util.h"
-
-#define SNDBUF_SIZE (8*1024*1024)
-
-static LogTarget log_target = LOG_TARGET_CONSOLE;
-static int log_max_level = LOG_INFO;
-static int log_facility = LOG_DAEMON;
-
-static int console_fd = STDERR_FILENO;
-static int syslog_fd = -1;
-static int kmsg_fd = -1;
-static int journal_fd = -1;
-
-static bool syslog_is_stream = false;
-
-static bool show_color = false;
-static bool show_location = false;
-
-/* Akin to glibc's __abort_msg; which is private and we hence cannot
- * use here. */
-static char *log_abort_msg = NULL;
-
-void log_close_console(void) {
-
- if (console_fd < 0)
- return;
-
- if (getpid() == 1) {
- if (console_fd >= 3)
- close_nointr_nofail(console_fd);
-
- console_fd = -1;
- }
-}
-
-static int log_open_console(void) {
-
- if (console_fd >= 0)
- return 0;
-
- if (getpid() == 1) {
-
- console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
- if (console_fd < 0) {
- log_error("Failed to open /dev/console for logging: %s", strerror(-console_fd));
- return console_fd;
- }
-
- log_debug("Successfully opened /dev/console for logging.");
- } else
- console_fd = STDERR_FILENO;
-
- return 0;
-}
-
-void log_close_kmsg(void) {
-
- if (kmsg_fd < 0)
- return;
-
- close_nointr_nofail(kmsg_fd);
- kmsg_fd = -1;
-}
-
-static int log_open_kmsg(void) {
-
- if (kmsg_fd >= 0)
- return 0;
-
- kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC);
- if (kmsg_fd < 0) {
- log_error("Failed to open /dev/kmsg for logging: %s", strerror(errno));
- return -errno;
- }
-
- log_debug("Successfully opened /dev/kmsg for logging.");
-
- return 0;
-}
-
-void log_close_syslog(void) {
-
- if (syslog_fd < 0)
- return;
-
- close_nointr_nofail(syslog_fd);
- syslog_fd = -1;
-}
-
-static int create_log_socket(int type) {
- int fd;
-
- /* All output to the syslog/journal fds we do asynchronously,
- * and if the buffers are full we just drop the messages */
-
- fd = socket(AF_UNIX, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (fd < 0)
- return -errno;
-
- fd_inc_sndbuf(fd, SNDBUF_SIZE);
-
- return fd;
-}
-
-static int log_open_syslog(void) {
- union sockaddr_union sa;
- int r;
-
- if (syslog_fd >= 0)
- return 0;
-
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
-
- syslog_fd = create_log_socket(SOCK_DGRAM);
- if (syslog_fd < 0) {
- r = syslog_fd;
- goto fail;
- }
-
- if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
- close_nointr_nofail(syslog_fd);
-
- /* Some legacy syslog systems still use stream
- * sockets. They really shouldn't. But what can we
- * do... */
- syslog_fd = create_log_socket(SOCK_STREAM);
- if (syslog_fd < 0) {
- r = syslog_fd;
- goto fail;
- }
-
- if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
- r = -errno;
- goto fail;
- }
-
- syslog_is_stream = true;
- } else
- syslog_is_stream = false;
-
- log_debug("Successfully opened syslog for logging.");
-
- return 0;
-
-fail:
- log_close_syslog();
- log_debug("Failed to open syslog for logging: %s", strerror(-r));
- return r;
-}
-
-void log_close_journal(void) {
-
- if (journal_fd < 0)
- return;
-
- close_nointr_nofail(journal_fd);
- journal_fd = -1;
-}
-
-static int log_open_journal(void) {
- union sockaddr_union sa;
- int r;
-
- if (journal_fd >= 0)
- return 0;
-
- journal_fd = create_log_socket(SOCK_DGRAM);
- if (journal_fd < 0) {
- r = journal_fd;
- goto fail;
- }
-
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
-
- if (connect(journal_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
- r = -errno;
- goto fail;
- }
-
- log_debug("Successfully opened journal for logging.");
-
- return 0;
-
-fail:
- log_close_journal();
- log_debug("Failed to open journal for logging: %s", strerror(-r));
- return r;
-}
-
-int log_open(void) {
- int r;
-
- /* If we don't use the console we close it here, to not get
- * killed by SAK. If we don't use syslog we close it here so
- * that we are not confused by somebody deleting the socket in
- * the fs. If we don't use /dev/kmsg we still keep it open,
- * because there is no reason to close it. */
-
- if (log_target == LOG_TARGET_NULL) {
- log_close_journal();
- log_close_syslog();
- log_close_console();
- return 0;
- }
-
- if (log_target != LOG_TARGET_AUTO ||
- getpid() == 1 ||
- isatty(STDERR_FILENO) <= 0) {
-
- if (log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
- log_target == LOG_TARGET_JOURNAL) {
- r = log_open_journal();
- if (r >= 0) {
- log_close_syslog();
- log_close_console();
- return r;
- }
- }
-
- if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_SYSLOG) {
- r = log_open_syslog();
- if (r >= 0) {
- log_close_journal();
- log_close_console();
- return r;
- }
- }
-
- if (log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
- log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_KMSG) {
- r = log_open_kmsg();
- if (r >= 0) {
- log_close_journal();
- log_close_syslog();
- log_close_console();
- return r;
- }
- }
- }
-
- log_close_journal();
- log_close_syslog();
-
- /* Get the real /dev/console if we are PID=1, hence reopen */
- log_close_console();
- return log_open_console();
-}
-
-void log_set_target(LogTarget target) {
- assert(target >= 0);
- assert(target < _LOG_TARGET_MAX);
-
- log_target = target;
-}
-
-void log_close(void) {
- log_close_journal();
- log_close_syslog();
- log_close_kmsg();
- log_close_console();
-}
-
-void log_forget_fds(void) {
- console_fd = kmsg_fd = syslog_fd = journal_fd = -1;
-}
-
-void log_set_max_level(int level) {
- assert((level & LOG_PRIMASK) == level);
-
- log_max_level = level;
-}
-
-void log_set_facility(int facility) {
- log_facility = facility;
-}
-
-static int write_to_console(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *buffer) {
-
- char location[64];
- struct iovec iovec[5];
- unsigned n = 0;
- bool highlight;
-
- if (console_fd < 0)
- return 0;
-
- highlight = LOG_PRI(level) <= LOG_ERR && show_color;
-
- zero(iovec);
-
- if (show_location) {
- snprintf(location, sizeof(location), "(%s:%u) ", file, line);
- char_array_0(location);
- IOVEC_SET_STRING(iovec[n++], location);
- }
-
- if (highlight)
- IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED_ON);
- IOVEC_SET_STRING(iovec[n++], buffer);
- if (highlight)
- IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_OFF);
- IOVEC_SET_STRING(iovec[n++], "\n");
-
- if (writev(console_fd, iovec, n) < 0)
- return -errno;
-
- return 1;
-}
-
-static int write_to_syslog(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *buffer) {
-
- char header_priority[16], header_time[64], header_pid[16];
- struct iovec iovec[5];
- struct msghdr msghdr;
- time_t t;
- struct tm *tm;
-
- if (syslog_fd < 0)
- return 0;
-
- snprintf(header_priority, sizeof(header_priority), "<%i>", level);
- char_array_0(header_priority);
-
- t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
- if (!(tm = localtime(&t)))
- return -EINVAL;
-
- if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
- return -EINVAL;
-
- snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
- char_array_0(header_pid);
-
- zero(iovec);
- IOVEC_SET_STRING(iovec[0], header_priority);
- IOVEC_SET_STRING(iovec[1], header_time);
- IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
- IOVEC_SET_STRING(iovec[3], header_pid);
- IOVEC_SET_STRING(iovec[4], buffer);
-
- /* When using syslog via SOCK_STREAM separate the messages by NUL chars */
- if (syslog_is_stream)
- iovec[4].iov_len++;
-
- zero(msghdr);
- msghdr.msg_iov = iovec;
- msghdr.msg_iovlen = ELEMENTSOF(iovec);
-
- for (;;) {
- ssize_t n;
-
- n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL);
- if (n < 0)
- return -errno;
-
- if (!syslog_is_stream ||
- (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec)))
- break;
-
- IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n);
- }
-
- return 1;
-}
-
-static int write_to_kmsg(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *buffer) {
-
- char header_priority[16], header_pid[16];
- struct iovec iovec[5];
-
- if (kmsg_fd < 0)
- return 0;
-
- snprintf(header_priority, sizeof(header_priority), "<%i>", level);
- char_array_0(header_priority);
-
- snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
- char_array_0(header_pid);
-
- zero(iovec);
- IOVEC_SET_STRING(iovec[0], header_priority);
- IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
- IOVEC_SET_STRING(iovec[2], header_pid);
- IOVEC_SET_STRING(iovec[3], buffer);
- IOVEC_SET_STRING(iovec[4], "\n");
-
- if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
- return -errno;
-
- return 1;
-}
-
-static int write_to_journal(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *buffer) {
-
- char header[LINE_MAX];
- struct iovec iovec[3];
- struct msghdr mh;
-
- if (journal_fd < 0)
- return 0;
-
- snprintf(header, sizeof(header),
- "PRIORITY=%i\n"
- "SYSLOG_FACILITY=%i\n"
- "CODE_FILE=%s\n"
- "CODE_LINE=%i\n"
- "CODE_FUNCTION=%s\n"
- "MESSAGE=",
- LOG_PRI(level),
- LOG_FAC(level),
- file,
- line,
- func);
-
- char_array_0(header);
-
- zero(iovec);
- IOVEC_SET_STRING(iovec[0], header);
- IOVEC_SET_STRING(iovec[1], buffer);
- IOVEC_SET_STRING(iovec[2], "\n");
-
- zero(mh);
- mh.msg_iov = iovec;
- mh.msg_iovlen = ELEMENTSOF(iovec);
-
- if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0)
- return -errno;
-
- return 1;
-}
-
-static int log_dispatch(
- int level,
- const char*file,
- int line,
- const char *func,
- char *buffer) {
-
- int r = 0;
-
- if (log_target == LOG_TARGET_NULL)
- return 0;
-
- /* Patch in LOG_DAEMON facility if necessary */
- if ((level & LOG_FACMASK) == 0)
- level = log_facility | LOG_PRI(level);
-
- do {
- char *e;
- int k = 0;
-
- buffer += strspn(buffer, NEWLINE);
-
- if (buffer[0] == 0)
- break;
-
- if ((e = strpbrk(buffer, NEWLINE)))
- *(e++) = 0;
-
- if (log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
- log_target == LOG_TARGET_JOURNAL) {
-
- k = write_to_journal(level, file, line, func, buffer);
- if (k < 0) {
- if (k != -EAGAIN)
- log_close_journal();
- log_open_kmsg();
- } else if (k > 0)
- r++;
- }
-
- if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_SYSLOG) {
-
- k = write_to_syslog(level, file, line, func, buffer);
- if (k < 0) {
- if (k != -EAGAIN)
- log_close_syslog();
- log_open_kmsg();
- } else if (k > 0)
- r++;
- }
-
- if (k <= 0 &&
- (log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_KMSG)) {
-
- k = write_to_kmsg(level, file, line, func, buffer);
- if (k < 0) {
- log_close_kmsg();
- log_open_console();
- } else if (k > 0)
- r++;
- }
-
- if (k <= 0) {
- k = write_to_console(level, file, line, func, buffer);
- if (k < 0)
- return k;
- }
-
- buffer = e;
- } while (buffer);
-
- return r;
-}
-
-int log_dump_internal(
- int level,
- const char*file,
- int line,
- const char *func,
- char *buffer) {
-
- int saved_errno, r;
-
- /* This modifies the buffer... */
-
- if (_likely_(LOG_PRI(level) > log_max_level))
- return 0;
-
- saved_errno = errno;
- r = log_dispatch(level, file, line, func, buffer);
- errno = saved_errno;
-
- return r;
-}
-
-int log_metav(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *format,
- va_list ap) {
-
- char buffer[LINE_MAX];
- int saved_errno, r;
-
- if (_likely_(LOG_PRI(level) > log_max_level))
- return 0;
-
- saved_errno = errno;
- vsnprintf(buffer, sizeof(buffer), format, ap);
- char_array_0(buffer);
-
- r = log_dispatch(level, file, line, func, buffer);
- errno = saved_errno;
-
- return r;
-}
-
-int log_meta(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *format, ...) {
-
- int r;
- va_list ap;
-
- va_start(ap, format);
- r = log_metav(level, file, line, func, format, ap);
- va_end(ap);
-
- return r;
-}
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
-_noreturn_ static void log_assert(const char *text, const char *file, int line, const char *func, const char *format) {
- static char buffer[LINE_MAX];
-
- snprintf(buffer, sizeof(buffer), format, text, file, line, func);
-
- char_array_0(buffer);
- log_abort_msg = buffer;
-
- log_dispatch(LOG_CRIT, file, line, func, buffer);
- abort();
-}
-#pragma GCC diagnostic pop
-
-_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func) {
- log_assert(text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.");
-}
-
-_noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) {
- log_assert(text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.");
-}
-
-int log_set_target_from_string(const char *e) {
- LogTarget t;
-
- t = log_target_from_string(e);
- if (t < 0)
- return -EINVAL;
-
- log_set_target(t);
- return 0;
-}
-
-int log_set_max_level_from_string(const char *e) {
- int t;
-
- t = log_level_from_string(e);
- if (t < 0)
- return t;
-
- log_set_max_level(t);
- return 0;
-}
-
-void log_parse_environment(void) {
- const char *e;
-
- if ((e = getenv("SYSTEMD_LOG_TARGET")))
- if (log_set_target_from_string(e) < 0)
- log_warning("Failed to parse log target %s. Ignoring.", e);
-
- if ((e = getenv("SYSTEMD_LOG_LEVEL")))
- if (log_set_max_level_from_string(e) < 0)
- log_warning("Failed to parse log level %s. Ignoring.", e);
-
- if ((e = getenv("SYSTEMD_LOG_COLOR")))
- if (log_show_color_from_string(e) < 0)
- log_warning("Failed to parse bool %s. Ignoring.", e);
-
- if ((e = getenv("SYSTEMD_LOG_LOCATION")))
- if (log_show_location_from_string(e) < 0)
- log_warning("Failed to parse bool %s. Ignoring.", e);
-}
-
-LogTarget log_get_target(void) {
- return log_target;
-}
-
-int log_get_max_level(void) {
- return log_max_level;
-}
-
-void log_show_color(bool b) {
- show_color = b;
-}
-
-void log_show_location(bool b) {
- show_location = b;
-}
-
-int log_show_color_from_string(const char *e) {
- int t;
-
- t = parse_boolean(e);
- if (t < 0)
- return t;
-
- log_show_color(t);
- return 0;
-}
-
-int log_show_location_from_string(const char *e) {
- int t;
-
- t = parse_boolean(e);
- if (t < 0)
- return t;
-
- log_show_location(t);
- return 0;
-}
-
-static const char *const log_target_table[] = {
- [LOG_TARGET_CONSOLE] = "console",
- [LOG_TARGET_KMSG] = "kmsg",
- [LOG_TARGET_JOURNAL] = "journal",
- [LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg",
- [LOG_TARGET_SYSLOG] = "syslog",
- [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
- [LOG_TARGET_AUTO] = "auto",
- [LOG_TARGET_NULL] = "null"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
diff --git a/src/log.h b/src/log.h
deleted file mode 100644
index 3283808..0000000
--- a/src/log.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foologhfoo
-#define foologhfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <syslog.h>
-#include <stdbool.h>
-#include <stdarg.h>
-
-#include "macro.h"
-
-typedef enum LogTarget{
- LOG_TARGET_CONSOLE,
- LOG_TARGET_KMSG,
- LOG_TARGET_JOURNAL,
- LOG_TARGET_JOURNAL_OR_KMSG,
- LOG_TARGET_SYSLOG,
- LOG_TARGET_SYSLOG_OR_KMSG,
- LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
- LOG_TARGET_NULL,
- _LOG_TARGET_MAX,
- _LOG_TARGET_INVALID = -1
-} LogTarget;
-
-void log_set_target(LogTarget target);
-void log_set_max_level(int level);
-void log_set_facility(int facility);
-
-int log_set_target_from_string(const char *e);
-int log_set_max_level_from_string(const char *e);
-
-void log_show_color(bool b);
-void log_show_location(bool b);
-
-int log_show_color_from_string(const char *e);
-int log_show_location_from_string(const char *e);
-
-LogTarget log_get_target(void);
-int log_get_max_level(void);
-
-int log_open(void);
-void log_close(void);
-void log_forget_fds(void);
-
-void log_close_syslog(void);
-void log_close_journal(void);
-void log_close_kmsg(void);
-void log_close_console(void);
-
-void log_parse_environment(void);
-
-int log_meta(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *format, ...) _printf_attr_(5,6);
-
-int log_metav(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *format,
- va_list ap);
-
-_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func);
-_noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func);
-
-/* This modifies the buffer passed! */
-int log_dump_internal(
- int level,
- const char*file,
- int line,
- const char *func,
- char *buffer);
-
-#define log_full(level, ...) log_meta(level, __FILE__, __LINE__, __func__, __VA_ARGS__)
-
-#define log_debug(...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__)
-#define log_info(...) log_meta(LOG_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__)
-#define log_notice(...) log_meta(LOG_NOTICE, __FILE__, __LINE__, __func__, __VA_ARGS__)
-#define log_warning(...) log_meta(LOG_WARNING, __FILE__, __LINE__, __func__, __VA_ARGS__)
-#define log_error(...) log_meta(LOG_ERR, __FILE__, __LINE__, __func__, __VA_ARGS__)
-
-/* This modifies the buffer passed! */
-#define log_dump(level, buffer) log_dump_internal(level, __FILE__, __LINE__, __func__, buffer)
-
-const char *log_target_to_string(LogTarget target);
-LogTarget log_target_from_string(const char *s);
-
-#endif
diff --git a/src/login/logind.h b/src/login/logind.h
index a4b4602..9586793 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -28,6 +28,7 @@
#include <libudev.h>
#include "util.h"
+#include "audit.h"
#include "list.h"
#include "hashmap.h"
#include "cgroup-util.h"
diff --git a/src/login/pam-module.c b/src/login/pam-module.c
index 4106d2b..1edb91c 100644
--- a/src/login/pam-module.c
+++ b/src/login/pam-module.c
@@ -35,6 +35,7 @@
#include <systemd/sd-daemon.h>
#include "util.h"
+#include "audit.h"
#include "macro.h"
#include "strv.h"
#include "dbus-common.h"
diff --git a/src/nspawn.c b/src/nspawn.c
index 6f5a9d9..66910bc 100644
--- a/src/nspawn.c
+++ b/src/nspawn.c
@@ -43,6 +43,7 @@
#include "log.h"
#include "util.h"
+#include "audit.h"
#include "missing.h"
#include "cgroup-util.h"
#include "strv.h"
diff --git a/src/ratelimit.c b/src/ratelimit.c
deleted file mode 100644
index 93157c7..0000000
--- a/src/ratelimit.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-
-#include "ratelimit.h"
-#include "log.h"
-
-/* Modelled after Linux' lib/ratelimit.c by Dave Young
- * <hidave.darkstar at gmail.com>, which is licensed GPLv2. */
-
-bool ratelimit_test(RateLimit *r) {
- usec_t ts;
-
- assert(r);
-
- if (r->interval <= 0 || r->burst <= 0)
- return true;
-
- ts = now(CLOCK_MONOTONIC);
-
- if (r->begin <= 0 ||
- r->begin + r->interval < ts) {
- r->begin = ts;
-
- /* Reset counter */
- r->num = 0;
- goto good;
- }
-
- if (r->num <= r->burst)
- goto good;
-
- return false;
-
-good:
- r->num++;
- return true;
-}
diff --git a/src/ratelimit.h b/src/ratelimit.h
deleted file mode 100644
index a6443e7..0000000
--- a/src/ratelimit.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooratelimithfoo
-#define fooratelimithfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-
-typedef struct RateLimit {
- usec_t interval;
- usec_t begin;
- unsigned burst;
- unsigned num;
-} RateLimit;
-
-#define RATELIMIT_DEFINE(_name, _interval, _burst) \
- RateLimit _name = { \
- .interval = (_interval), \
- .burst = (_burst), \
- .num = 0, \
- .begin = 0 \
- }
-
-#define RATELIMIT_INIT(v, _interval, _burst) \
- do { \
- RateLimit *_r = &(v); \
- _r->interval = (_interval); \
- _r->burst = (_burst); \
- _r->num = 0; \
- _r->begin = 0; \
- } while (false)
-
-bool ratelimit_test(RateLimit *r);
-
-#endif
diff --git a/src/set.c b/src/set.c
deleted file mode 100644
index 097b9d3..0000000
--- a/src/set.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdlib.h>
-
-#include "set.h"
-#include "hashmap.h"
-
-#define MAKE_SET(h) ((Set*) (h))
-#define MAKE_HASHMAP(s) ((Hashmap*) (s))
-
-/* For now this is not much more than a wrapper around a hashmap */
-
-Set *set_new(hash_func_t hash_func, compare_func_t compare_func) {
- return MAKE_SET(hashmap_new(hash_func, compare_func));
-}
-
-void set_free(Set* s) {
- hashmap_free(MAKE_HASHMAP(s));
-}
-
-void set_free_free(Set *s) {
- hashmap_free_free(MAKE_HASHMAP(s));
-}
-
-int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func) {
- return hashmap_ensure_allocated((Hashmap**) s, hash_func, compare_func);
-}
-
-int set_put(Set *s, void *value) {
- return hashmap_put(MAKE_HASHMAP(s), value, value);
-}
-
-int set_replace(Set *s, void *value) {
- return hashmap_replace(MAKE_HASHMAP(s), value, value);
-}
-
-void *set_get(Set *s, void *value) {
- return hashmap_get(MAKE_HASHMAP(s), value);
-}
-
-void *set_remove(Set *s, void *value) {
- return hashmap_remove(MAKE_HASHMAP(s), value);
-}
-
-int set_remove_and_put(Set *s, void *old_value, void *new_value) {
- return hashmap_remove_and_put(MAKE_HASHMAP(s), old_value, new_value, new_value);
-}
-
-unsigned set_size(Set *s) {
- return hashmap_size(MAKE_HASHMAP(s));
-}
-
-bool set_isempty(Set *s) {
- return hashmap_isempty(MAKE_HASHMAP(s));
-}
-
-void *set_iterate(Set *s, Iterator *i) {
- return hashmap_iterate(MAKE_HASHMAP(s), i, NULL);
-}
-
-void *set_iterate_backwards(Set *s, Iterator *i) {
- return hashmap_iterate_backwards(MAKE_HASHMAP(s), i, NULL);
-}
-
-void *set_iterate_skip(Set *s, void *value, Iterator *i) {
- return hashmap_iterate_skip(MAKE_HASHMAP(s), value, i);
-}
-
-void *set_steal_first(Set *s) {
- return hashmap_steal_first(MAKE_HASHMAP(s));
-}
-
-void* set_first(Set *s) {
- return hashmap_first(MAKE_HASHMAP(s));
-}
-
-void* set_last(Set *s) {
- return hashmap_last(MAKE_HASHMAP(s));
-}
-
-int set_merge(Set *s, Set *other) {
- return hashmap_merge(MAKE_HASHMAP(s), MAKE_HASHMAP(other));
-}
-
-void set_move(Set *s, Set *other) {
- return hashmap_move(MAKE_HASHMAP(s), MAKE_HASHMAP(other));
-}
-
-int set_move_one(Set *s, Set *other, void *value) {
- return hashmap_move_one(MAKE_HASHMAP(s), MAKE_HASHMAP(other), value);
-}
-
-Set* set_copy(Set *s) {
- return MAKE_SET(hashmap_copy(MAKE_HASHMAP(s)));
-}
-
-void set_clear(Set *s) {
- hashmap_clear(MAKE_HASHMAP(s));
-}
diff --git a/src/set.h b/src/set.h
deleted file mode 100644
index 885780c..0000000
--- a/src/set.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foosethfoo
-#define foosethfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/* Pretty straightforward set implementation. Internally based on the
- * hashmap. That means that as a minor optimization a NULL set
- * object will be treated as empty set for all read
- * operations. That way it is not necessary to instantiate an object
- * for each set use. */
-
-#include "hashmap.h"
-
-typedef struct Set Set;
-
-Set *set_new(hash_func_t hash_func, compare_func_t compare_func);
-void set_free(Set* s);
-void set_free_free(Set *s);
-Set* set_copy(Set *s);
-int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func);
-
-int set_put(Set *s, void *value);
-int set_replace(Set *s, void *value);
-void *set_get(Set *s, void *value);
-void *set_remove(Set *s, void *value);
-int set_remove_and_put(Set *s, void *old_value, void *new_value);
-
-int set_merge(Set *s, Set *other);
-void set_move(Set *s, Set *other);
-int set_move_one(Set *s, Set *other, void *value);
-
-unsigned set_size(Set *s);
-bool set_isempty(Set *s);
-
-void *set_iterate(Set *s, Iterator *i);
-void *set_iterate_backwards(Set *s, Iterator *i);
-void *set_iterate_skip(Set *s, void *value, Iterator *i);
-
-void set_clear(Set *s);
-void *set_steal_first(Set *s);
-void* set_first(Set *s);
-void* set_last(Set *s);
-
-#define SET_FOREACH(e, s, i) \
- for ((i) = ITERATOR_FIRST, (e) = set_iterate((s), &(i)); (e); (e) = set_iterate((s), &(i)))
-
-#define SET_FOREACH_BACKWARDS(e, s, i) \
- for ((i) = ITERATOR_LAST, (e) = set_iterate_backwards((s), &(i)); (e); (e) = set_iterate_backwards((s), &(i)))
-
-#endif
diff --git a/src/shared/audit.c b/src/shared/audit.c
new file mode 100644
index 0000000..3850059
--- /dev/null
+++ b/src/shared/audit.c
@@ -0,0 +1,118 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/prctl.h>
+#include <sys/capability.h>
+
+#include "macro.h"
+#include "audit.h"
+#include "util.h"
+#include "log.h"
+
+int audit_session_from_pid(pid_t pid, uint32_t *id) {
+ char *s;
+ uint32_t u;
+ int r;
+
+ assert(id);
+
+ if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
+ return -ENOENT;
+
+ if (pid == 0)
+ r = read_one_line_file("/proc/self/sessionid", &s);
+ else {
+ char *p;
+
+ if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ r = read_one_line_file(p, &s);
+ free(p);
+ }
+
+ if (r < 0)
+ return r;
+
+ r = safe_atou32(s, &u);
+ free(s);
+
+ if (r < 0)
+ return r;
+
+ if (u == (uint32_t) -1 || u <= 0)
+ return -ENOENT;
+
+ *id = u;
+ return 0;
+}
+
+int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
+ char *s;
+ uid_t u;
+ int r;
+
+ assert(uid);
+
+ /* Only use audit login uid if we are executed with sufficient
+ * capabilities so that pam_loginuid could do its job. If we
+ * are lacking the CAP_AUDIT_CONTROL capabality we most likely
+ * are being run in a container and /proc/self/loginuid is
+ * useless since it probably contains a uid of the host
+ * system. */
+
+ if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
+ return -ENOENT;
+
+ if (pid == 0)
+ r = read_one_line_file("/proc/self/loginuid", &s);
+ else {
+ char *p;
+
+ if (asprintf(&p, "/proc/%lu/loginuid", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ r = read_one_line_file(p, &s);
+ free(p);
+ }
+
+ if (r < 0)
+ return r;
+
+ r = parse_uid(s, &u);
+ free(s);
+
+ if (r < 0)
+ return r;
+
+ if (u == (uid_t) -1)
+ return -ENOENT;
+
+ *uid = (uid_t) u;
+ return 0;
+}
diff --git a/src/shared/audit.h b/src/shared/audit.h
new file mode 100644
index 0000000..3529046
--- /dev/null
+++ b/src/shared/audit.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooaudithfoo
+#define fooaudithfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "capability.h"
+
+int audit_session_from_pid(pid_t pid, uint32_t *id);
+int audit_loginuid_from_pid(pid_t pid, uid_t *uid);
+#endif
diff --git a/src/shared/capability.c b/src/shared/capability.c
new file mode 100644
index 0000000..b800215
--- /dev/null
+++ b/src/shared/capability.c
@@ -0,0 +1,86 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+
+#include "macro.h"
+#include "capability.h"
+#include "util.h"
+#include "log.h"
+
+int have_effective_cap(int value) {
+ cap_t cap;
+ cap_flag_value_t fv;
+ int r;
+
+ if (!(cap = cap_get_proc()))
+ return -errno;
+
+ if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
+ r = -errno;
+ else
+ r = fv == CAP_SET;
+
+ cap_free(cap);
+ return r;
+}
+
+unsigned long cap_last_cap(void) {
+ static __thread unsigned long saved;
+ static __thread bool valid = false;
+ unsigned long p;
+
+ if (valid)
+ return saved;
+
+ p = (unsigned long) CAP_LAST_CAP;
+
+ if (prctl(PR_CAPBSET_READ, p) < 0) {
+
+ /* Hmm, look downwards, until we find one that
+ * works */
+ for (p--; p > 0; p --)
+ if (prctl(PR_CAPBSET_READ, p) >= 0)
+ break;
+
+ } else {
+
+ /* Hmm, look upwards, until we find one that doesn't
+ * work */
+ for (;; p++)
+ if (prctl(PR_CAPBSET_READ, p+1) < 0)
+ break;
+ }
+
+ saved = p;
+ valid = true;
+
+ return p;
+}
diff --git a/src/shared/capability.h b/src/shared/capability.h
new file mode 100644
index 0000000..ab7e40b
--- /dev/null
+++ b/src/shared/capability.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foocapabilityhfoo
+#define foocapabilityhfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+unsigned long cap_last_cap(void);
+int have_effective_cap(int value);
+#endif
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
new file mode 100644
index 0000000..a9b0113
--- /dev/null
+++ b/src/shared/conf-parser.c
@@ -0,0 +1,852 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "conf-parser.h"
+#include "util.h"
+#include "macro.h"
+#include "strv.h"
+#include "log.h"
+#include "utf8.h"
+
+int config_item_table_lookup(
+ void *table,
+ const char *section,
+ const char *lvalue,
+ ConfigParserCallback *func,
+ int *ltype,
+ void **data,
+ void *userdata) {
+
+ ConfigTableItem *t;
+
+ assert(table);
+ assert(lvalue);
+ assert(func);
+ assert(ltype);
+ assert(data);
+
+ for (t = table; t->lvalue; t++) {
+
+ if (!streq(lvalue, t->lvalue))
+ continue;
+
+ if (!streq_ptr(section, t->section))
+ continue;
+
+ *func = t->parse;
+ *ltype = t->ltype;
+ *data = t->data;
+ return 1;
+ }
+
+ return 0;
+}
+
+int config_item_perf_lookup(
+ void *table,
+ const char *section,
+ const char *lvalue,
+ ConfigParserCallback *func,
+ int *ltype,
+ void **data,
+ void *userdata) {
+
+ ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
+ const ConfigPerfItem *p;
+
+ assert(table);
+ assert(lvalue);
+ assert(func);
+ assert(ltype);
+ assert(data);
+
+ if (!section)
+ p = lookup(lvalue, strlen(lvalue));
+ else {
+ char *key;
+
+ key = join(section, ".", lvalue, NULL);
+ if (!key)
+ return -ENOMEM;
+
+ p = lookup(key, strlen(key));
+ free(key);
+ }
+
+ if (!p)
+ return 0;
+
+ *func = p->parse;
+ *ltype = p->ltype;
+ *data = (uint8_t*) userdata + p->offset;
+ return 1;
+}
+
+/* Run the user supplied parser for an assignment */
+static int next_assignment(
+ const char *filename,
+ unsigned line,
+ ConfigItemLookup lookup,
+ void *table,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ bool relaxed,
+ void *userdata) {
+
+ ConfigParserCallback func = NULL;
+ int ltype = 0;
+ void *data = NULL;
+ int r;
+
+ assert(filename);
+ assert(line > 0);
+ assert(lookup);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = lookup(table, section, lvalue, &func, <ype, &data, userdata);
+ if (r < 0)
+ return r;
+
+ if (r > 0) {
+ if (func)
+ return func(filename, line, section, lvalue, ltype, rvalue, data, userdata);
+
+ return 0;
+ }
+
+ /* Warn about unknown non-extension fields. */
+ if (!relaxed && !startswith(lvalue, "X-"))
+ log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, section);
+
+ return 0;
+}
+
+/* Parse a variable assignment line */
+static int parse_line(
+ const char *filename,
+ unsigned line,
+ const char *sections,
+ ConfigItemLookup lookup,
+ void *table,
+ bool relaxed,
+ char **section,
+ char *l,
+ void *userdata) {
+
+ char *e;
+
+ assert(filename);
+ assert(line > 0);
+ assert(lookup);
+ assert(l);
+
+ l = strstrip(l);
+
+ if (!*l)
+ return 0;
+
+ if (strchr(COMMENTS, *l))
+ return 0;
+
+ if (startswith(l, ".include ")) {
+ char *fn;
+ int r;
+
+ fn = file_in_same_dir(filename, strstrip(l+9));
+ if (!fn)
+ return -ENOMEM;
+
+ r = config_parse(fn, NULL, sections, lookup, table, relaxed, userdata);
+ free(fn);
+
+ return r;
+ }
+
+ if (*l == '[') {
+ size_t k;
+ char *n;
+
+ k = strlen(l);
+ assert(k > 0);
+
+ if (l[k-1] != ']') {
+ log_error("[%s:%u] Invalid section header.", filename, line);
+ return -EBADMSG;
+ }
+
+ n = strndup(l+1, k-2);
+ if (!n)
+ return -ENOMEM;
+
+ if (sections && !nulstr_contains(sections, n)) {
+
+ if (!relaxed)
+ log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n);
+
+ free(n);
+ *section = NULL;
+ } else {
+ free(*section);
+ *section = n;
+ }
+
+ return 0;
+ }
+
+ if (sections && !*section) {
+
+ if (!relaxed)
+ log_info("[%s:%u] Assignment outside of section. Ignoring.", filename, line);
+
+ return 0;
+ }
+
+ e = strchr(l, '=');
+ if (!e) {
+ log_error("[%s:%u] Missing '='.", filename, line);
+ return -EBADMSG;
+ }
+
+ *e = 0;
+ e++;
+
+ return next_assignment(
+ filename,
+ line,
+ lookup,
+ table,
+ *section,
+ strstrip(l),
+ strstrip(e),
+ relaxed,
+ userdata);
+}
+
+/* Go through the file and parse each line */
+int config_parse(
+ const char *filename,
+ FILE *f,
+ const char *sections,
+ ConfigItemLookup lookup,
+ void *table,
+ bool relaxed,
+ void *userdata) {
+
+ unsigned line = 0;
+ char *section = NULL;
+ int r;
+ bool ours = false;
+ char *continuation = NULL;
+
+ assert(filename);
+ assert(lookup);
+
+ if (!f) {
+ f = fopen(filename, "re");
+ if (!f) {
+ r = -errno;
+ log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
+ goto finish;
+ }
+
+ ours = true;
+ }
+
+ while (!feof(f)) {
+ char l[LINE_MAX], *p, *c = NULL, *e;
+ bool escaped = false;
+
+ if (!fgets(l, sizeof(l), f)) {
+ if (feof(f))
+ break;
+
+ r = -errno;
+ log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
+ goto finish;
+ }
+
+ truncate_nl(l);
+
+ if (continuation) {
+ c = strappend(continuation, l);
+ if (!c) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ free(continuation);
+ continuation = NULL;
+ p = c;
+ } else
+ p = l;
+
+ for (e = p; *e; e++) {
+ if (escaped)
+ escaped = false;
+ else if (*e == '\\')
+ escaped = true;
+ }
+
+ if (escaped) {
+ *(e-1) = ' ';
+
+ if (c)
+ continuation = c;
+ else {
+ continuation = strdup(l);
+ if (!continuation) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ }
+
+ continue;
+ }
+
+ r = parse_line(filename,
+ ++line,
+ sections,
+ lookup,
+ table,
+ relaxed,
+ §ion,
+ p,
+ userdata);
+ free(c);
+
+ if (r < 0)
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ free(section);
+ free(continuation);
+
+ if (f && ours)
+ fclose(f);
+
+ return r;
+}
+
+int config_parse_int(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int *i = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((r = safe_atoi(rvalue, i)) < 0) {
+ log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_long(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ long *i = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((r = safe_atoli(rvalue, i)) < 0) {
+ log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_uint64(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ uint64_t *u = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((r = safe_atou64(rvalue, u)) < 0) {
+ log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_unsigned(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ unsigned *u = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((r = safe_atou(rvalue, u)) < 0) {
+ log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
+ return r;
+ }
+
+ return 0;
+}
+
+int config_parse_bytes_size(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ size_t *sz = data;
+ off_t o;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) {
+ log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ *sz = (size_t) o;
+ return 0;
+}
+
+
+int config_parse_bytes_off(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ off_t *bytes = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ assert_cc(sizeof(off_t) == sizeof(uint64_t));
+
+ if (parse_bytes(rvalue, bytes) < 0) {
+ log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_bool(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int k;
+ bool *b = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((k = parse_boolean(rvalue)) < 0) {
+ log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ *b = !!k;
+ return 0;
+}
+
+int config_parse_tristate(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int k;
+ int *b = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */
+
+ k = parse_boolean(rvalue);
+ if (k < 0) {
+ log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ *b = !!k;
+ return 0;
+}
+
+int config_parse_string(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char **s = data;
+ char *n;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ n = cunescape(rvalue);
+ if (!n)
+ return -ENOMEM;
+
+ if (!utf8_is_valid(n)) {
+ log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+ free(n);
+ return 0;
+ }
+
+ free(*s);
+ if (*n)
+ *s = n;
+ else {
+ free(n);
+ *s = NULL;
+ }
+
+ return 0;
+}
+
+int config_parse_path(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char **s = data;
+ char *n;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (!utf8_is_valid(rvalue)) {
+ log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (!path_is_absolute(rvalue)) {
+ log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ n = strdup(rvalue);
+ if (!n)
+ return -ENOMEM;
+
+ path_kill_slashes(n);
+
+ free(*s);
+ *s = n;
+
+ return 0;
+}
+
+int config_parse_strv(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char*** sv = data;
+ char **n;
+ char *w;
+ unsigned k;
+ size_t l;
+ char *state;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ k = strv_length(*sv);
+ FOREACH_WORD_QUOTED(w, l, rvalue, state)
+ k++;
+
+ n = new(char*, k+1);
+ if (!n)
+ return -ENOMEM;
+
+ if (*sv)
+ for (k = 0; (*sv)[k]; k++)
+ n[k] = (*sv)[k];
+ else
+ k = 0;
+
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ n[k] = cunescape_length(w, l);
+ if (!n[k]) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!utf8_is_valid(n[k])) {
+ log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+ free(n[k]);
+ continue;
+ }
+
+ k++;
+ }
+
+ n[k] = NULL;
+ free(*sv);
+ *sv = n;
+
+ return 0;
+
+fail:
+ for (; k > 0; k--)
+ free(n[k-1]);
+ free(n);
+
+ return r;
+}
+
+int config_parse_path_strv(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char*** sv = data;
+ char **n;
+ char *w;
+ unsigned k;
+ size_t l;
+ char *state;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ k = strv_length(*sv);
+ FOREACH_WORD_QUOTED(w, l, rvalue, state)
+ k++;
+
+ n = new(char*, k+1);
+ if (!n)
+ return -ENOMEM;
+
+ k = 0;
+ if (*sv)
+ for (; (*sv)[k]; k++)
+ n[k] = (*sv)[k];
+
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ n[k] = strndup(w, l);
+ if (!n[k]) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!utf8_is_valid(n[k])) {
+ log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+ free(n[k]);
+ continue;
+ }
+
+ if (!path_is_absolute(n[k])) {
+ log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
+ free(n[k]);
+ continue;
+ }
+
+ path_kill_slashes(n[k]);
+ k++;
+ }
+
+ n[k] = NULL;
+ free(*sv);
+ *sv = n;
+
+ return 0;
+
+fail:
+ for (; k > 0; k--)
+ free(n[k-1]);
+ free(n);
+
+ return r;
+}
+
+int config_parse_usec(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ usec_t *usec = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (parse_usec(rvalue, usec) < 0) {
+ log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_mode(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ mode_t *m = data;
+ long l;
+ char *x = NULL;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ errno = 0;
+ l = strtol(rvalue, &x, 8);
+ if (!x || *x || errno) {
+ log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (l < 0000 || l > 07777) {
+ log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ *m = (mode_t) l;
+ return 0;
+}
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
new file mode 100644
index 0000000..be7d708
--- /dev/null
+++ b/src/shared/conf-parser.h
@@ -0,0 +1,135 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooconfparserhfoo
+#define fooconfparserhfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <stdbool.h>
+
+/* An abstract parser for simple, line based, shallow configuration
+ * files consisting of variable assignments only. */
+
+/* Prototype for a parser for a specific configuration setting */
+typedef int (*ConfigParserCallback)(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata);
+
+/* Wraps information for parsing a specific configuration variable, to
+ * be stored in a simple array */
+typedef struct ConfigTableItem {
+ const char *section; /* Section */
+ const char *lvalue; /* Name of the variable */
+ ConfigParserCallback parse; /* Function that is called to parse the variable's value */
+ int ltype; /* Distinguish different variables passed to the same callback */
+ void *data; /* Where to store the variable's data */
+} ConfigTableItem;
+
+/* Wraps information for parsing a specific configuration variable, to
+ * ve srored in a gperf perfect hashtable */
+typedef struct ConfigPerfItem {
+ const char *section_and_lvalue; /* Section + "." + name of the variable */
+ ConfigParserCallback parse; /* Function that is called to parse the variable's value */
+ int ltype; /* Distinguish different variables passed to the same callback */
+ size_t offset; /* Offset where to store data, from the beginning of userdata */
+} ConfigPerfItem;
+
+/* Prototype for a low-level gperf lookup function */
+typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, unsigned length);
+
+/* Prototype for a generic high-level lookup function */
+typedef int (*ConfigItemLookup)(
+ void *table,
+ const char *section,
+ const char *lvalue,
+ ConfigParserCallback *func,
+ int *ltype,
+ void **data,
+ void *userdata);
+
+/* Linear table search implementation of ConfigItemLookup, based on
+ * ConfigTableItem arrays */
+int config_item_table_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
+
+/* gperf implementation of ConfigItemLookup, based on gperf
+ * ConfigPerfItem tables */
+int config_item_perf_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
+
+int config_parse(
+ const char *filename,
+ FILE *f,
+ const char *sections, /* nulstr */
+ ConfigItemLookup lookup,
+ void *table,
+ bool relaxed,
+ void *userdata);
+
+/* Generic parsers */
+int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_long(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_uint64(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bytes_size(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bytes_off(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_tristate(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_path_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_usec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+
+#define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \
+ int function( \
+ const char *filename, \
+ unsigned line, \
+ const char *section, \
+ const char *lvalue, \
+ int ltype, \
+ const char *rvalue, \
+ void *data, \
+ void *userdata) { \
+ \
+ type *i = data, x; \
+ \
+ assert(filename); \
+ assert(lvalue); \
+ assert(rvalue); \
+ assert(data); \
+ \
+ if ((x = name##_from_string(rvalue)) < 0) { \
+ log_error("[%s:%u] " msg ", ignoring: %s", filename, line, rvalue); \
+ return 0; \
+ } \
+ \
+ *i = x; \
+ \
+ return 0; \
+ }
+
+#endif
diff --git a/src/shared/exit-status.c b/src/shared/exit-status.c
new file mode 100644
index 0000000..ab8907d
--- /dev/null
+++ b/src/shared/exit-status.c
@@ -0,0 +1,180 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include "exit-status.h"
+
+const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
+
+ /* We cast to int here, so that -Wenum doesn't complain that
+ * EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */
+
+ switch ((int) status) {
+
+ case EXIT_SUCCESS:
+ return "SUCCESS";
+
+ case EXIT_FAILURE:
+ return "FAILURE";
+ }
+
+
+ if (level == EXIT_STATUS_SYSTEMD || level == EXIT_STATUS_LSB) {
+ switch ((int) status) {
+
+ case EXIT_CHDIR:
+ return "CHDIR";
+
+ case EXIT_NICE:
+ return "NICE";
+
+ case EXIT_FDS:
+ return "FDS";
+
+ case EXIT_EXEC:
+ return "EXEC";
+
+ case EXIT_MEMORY:
+ return "MEMORY";
+
+ case EXIT_LIMITS:
+ return "LIMITS";
+
+ case EXIT_OOM_ADJUST:
+ return "OOM_ADJUST";
+
+ case EXIT_SIGNAL_MASK:
+ return "SIGNAL_MASK";
+
+ case EXIT_STDIN:
+ return "STDIN";
+
+ case EXIT_STDOUT:
+ return "STDOUT";
+
+ case EXIT_CHROOT:
+ return "CHROOT";
+
+ case EXIT_IOPRIO:
+ return "IOPRIO";
+
+ case EXIT_TIMERSLACK:
+ return "TIMERSLACK";
+
+ case EXIT_SECUREBITS:
+ return "SECUREBITS";
+
+ case EXIT_SETSCHEDULER:
+ return "SETSCHEDULER";
+
+ case EXIT_CPUAFFINITY:
+ return "CPUAFFINITY";
+
+ case EXIT_GROUP:
+ return "GROUP";
+
+ case EXIT_USER:
+ return "USER";
+
+ case EXIT_CAPABILITIES:
+ return "CAPABILITIES";
+
+ case EXIT_CGROUP:
+ return "CGROUP";
+
+ case EXIT_SETSID:
+ return "SETSID";
+
+ case EXIT_CONFIRM:
+ return "CONFIRM";
+
+ case EXIT_STDERR:
+ return "STDERR";
+
+ case EXIT_TCPWRAP:
+ return "TCPWRAP";
+
+ case EXIT_PAM:
+ return "PAM";
+
+ case EXIT_NETWORK:
+ return "NETWORK";
+
+ case EXIT_NAMESPACE:
+ return "NAMESPACE";
+ }
+ }
+
+ if (level == EXIT_STATUS_LSB) {
+ switch ((int) status) {
+
+ case EXIT_INVALIDARGUMENT:
+ return "INVALIDARGUMENT";
+
+ case EXIT_NOTIMPLEMENTED:
+ return "NOTIMPLEMENTED";
+
+ case EXIT_NOPERMISSION:
+ return "NOPERMISSION";
+
+ case EXIT_NOTINSTALLED:
+ return "NOTINSSTALLED";
+
+ case EXIT_NOTCONFIGURED:
+ return "NOTCONFIGURED";
+
+ case EXIT_NOTRUNNING:
+ return "NOTRUNNING";
+ }
+ }
+
+ return NULL;
+}
+
+
+bool is_clean_exit(int code, int status) {
+
+ if (code == CLD_EXITED)
+ return status == 0;
+
+ /* If a daemon does not implement handlers for some of the
+ * signals that's not considered an unclean shutdown */
+ if (code == CLD_KILLED)
+ return
+ status == SIGHUP ||
+ status == SIGINT ||
+ status == SIGTERM ||
+ status == SIGPIPE;
+
+ return false;
+}
+
+bool is_clean_exit_lsb(int code, int status) {
+
+ if (is_clean_exit(code, status))
+ return true;
+
+ return
+ code == CLD_EXITED &&
+ (status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED);
+}
diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h
new file mode 100644
index 0000000..44ef879
--- /dev/null
+++ b/src/shared/exit-status.h
@@ -0,0 +1,85 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooexitstatushfoo
+#define fooexitstatushfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+typedef enum ExitStatus {
+ /* EXIT_SUCCESS defined by libc */
+ /* EXIT_FAILURE defined by libc */
+ EXIT_INVALIDARGUMENT = 2,
+ EXIT_NOTIMPLEMENTED = 3,
+ EXIT_NOPERMISSION = 4,
+ EXIT_NOTINSTALLED = 5,
+ EXIT_NOTCONFIGURED = 6,
+ EXIT_NOTRUNNING = 7,
+
+ /* The LSB suggests that error codes >= 200 are "reserved". We
+ * use them here under the assumption that they hence are
+ * unused by init scripts.
+ *
+ * http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */
+
+ EXIT_CHDIR = 200,
+ EXIT_NICE,
+ EXIT_FDS,
+ EXIT_EXEC,
+ EXIT_MEMORY,
+ EXIT_LIMITS,
+ EXIT_OOM_ADJUST,
+ EXIT_SIGNAL_MASK,
+ EXIT_STDIN,
+ EXIT_STDOUT,
+ EXIT_CHROOT, /* 210 */
+ EXIT_IOPRIO,
+ EXIT_TIMERSLACK,
+ EXIT_SECUREBITS,
+ EXIT_SETSCHEDULER,
+ EXIT_CPUAFFINITY,
+ EXIT_GROUP,
+ EXIT_USER,
+ EXIT_CAPABILITIES,
+ EXIT_CGROUP,
+ EXIT_SETSID, /* 220 */
+ EXIT_CONFIRM,
+ EXIT_STDERR,
+ EXIT_TCPWRAP,
+ EXIT_PAM,
+ EXIT_NETWORK,
+ EXIT_NAMESPACE
+
+} ExitStatus;
+
+typedef enum ExitStatusLevel {
+ EXIT_STATUS_MINIMAL,
+ EXIT_STATUS_SYSTEMD,
+ EXIT_STATUS_LSB,
+ EXIT_STATUS_FULL = EXIT_STATUS_LSB
+} ExitStatusLevel;
+
+const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level);
+
+bool is_clean_exit(int code, int status);
+bool is_clean_exit_lsb(int code, int status);
+
+#endif
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c
new file mode 100644
index 0000000..6928118
--- /dev/null
+++ b/src/shared/hashmap.c
@@ -0,0 +1,731 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "util.h"
+#include "hashmap.h"
+#include "macro.h"
+
+#define NBUCKETS 127
+
+struct hashmap_entry {
+ const void *key;
+ void *value;
+ struct hashmap_entry *bucket_next, *bucket_previous;
+ struct hashmap_entry *iterate_next, *iterate_previous;
+};
+
+struct Hashmap {
+ hash_func_t hash_func;
+ compare_func_t compare_func;
+
+ struct hashmap_entry *iterate_list_head, *iterate_list_tail;
+ unsigned n_entries;
+
+ bool from_pool;
+};
+
+#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
+
+struct pool {
+ struct pool *next;
+ unsigned n_tiles;
+ unsigned n_used;
+};
+
+static struct pool *first_hashmap_pool = NULL;
+static void *first_hashmap_tile = NULL;
+
+static struct pool *first_entry_pool = NULL;
+static void *first_entry_tile = NULL;
+
+static void* allocate_tile(struct pool **first_pool, void **first_tile, size_t tile_size) {
+ unsigned i;
+
+ if (*first_tile) {
+ void *r;
+
+ r = *first_tile;
+ *first_tile = * (void**) (*first_tile);
+ return r;
+ }
+
+ if (_unlikely_(!*first_pool) || _unlikely_((*first_pool)->n_used >= (*first_pool)->n_tiles)) {
+ unsigned n;
+ size_t size;
+ struct pool *p;
+
+ n = *first_pool ? (*first_pool)->n_tiles : 0;
+ n = MAX(512U, n * 2);
+ size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*tile_size);
+ n = (size - ALIGN(sizeof(struct pool))) / tile_size;
+
+ p = malloc(size);
+ if (!p)
+ return NULL;
+
+ p->next = *first_pool;
+ p->n_tiles = n;
+ p->n_used = 0;
+
+ *first_pool = p;
+ }
+
+ i = (*first_pool)->n_used++;
+
+ return ((uint8_t*) (*first_pool)) + ALIGN(sizeof(struct pool)) + i*tile_size;
+}
+
+static void deallocate_tile(void **first_tile, void *p) {
+ * (void**) p = *first_tile;
+ *first_tile = p;
+}
+
+#ifndef __OPTIMIZE__
+
+static void drop_pool(struct pool *p) {
+ while (p) {
+ struct pool *n;
+ n = p->next;
+ free(p);
+ p = n;
+ }
+}
+
+__attribute__((destructor)) static void cleanup_pool(void) {
+ /* Be nice to valgrind */
+
+ drop_pool(first_hashmap_pool);
+ drop_pool(first_entry_pool);
+}
+
+#endif
+
+unsigned string_hash_func(const void *p) {
+ unsigned hash = 5381;
+ const signed char *c;
+
+ /* DJB's hash function */
+
+ for (c = p; *c; c++)
+ hash = (hash << 5) + hash + (unsigned) *c;
+
+ return hash;
+}
+
+int string_compare_func(const void *a, const void *b) {
+ return strcmp(a, b);
+}
+
+unsigned trivial_hash_func(const void *p) {
+ return PTR_TO_UINT(p);
+}
+
+int trivial_compare_func(const void *a, const void *b) {
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
+ bool b;
+ Hashmap *h;
+ size_t size;
+
+ b = is_main_thread();
+
+ size = ALIGN(sizeof(Hashmap)) + NBUCKETS * sizeof(struct hashmap_entry*);
+
+ if (b) {
+ h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size);
+ if (!h)
+ return NULL;
+
+ memset(h, 0, size);
+ } else {
+ h = malloc0(size);
+
+ if (!h)
+ return NULL;
+ }
+
+ h->hash_func = hash_func ? hash_func : trivial_hash_func;
+ h->compare_func = compare_func ? compare_func : trivial_compare_func;
+
+ h->n_entries = 0;
+ h->iterate_list_head = h->iterate_list_tail = NULL;
+
+ h->from_pool = b;
+
+ return h;
+}
+
+int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) {
+ assert(h);
+
+ if (*h)
+ return 0;
+
+ if (!(*h = hashmap_new(hash_func, compare_func)))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
+ assert(h);
+ assert(e);
+
+ /* Insert into hash table */
+ e->bucket_next = BY_HASH(h)[hash];
+ e->bucket_previous = NULL;
+ if (BY_HASH(h)[hash])
+ BY_HASH(h)[hash]->bucket_previous = e;
+ BY_HASH(h)[hash] = e;
+
+ /* Insert into iteration list */
+ e->iterate_previous = h->iterate_list_tail;
+ e->iterate_next = NULL;
+ if (h->iterate_list_tail) {
+ assert(h->iterate_list_head);
+ h->iterate_list_tail->iterate_next = e;
+ } else {
+ assert(!h->iterate_list_head);
+ h->iterate_list_head = e;
+ }
+ h->iterate_list_tail = e;
+
+ h->n_entries++;
+ assert(h->n_entries >= 1);
+}
+
+static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
+ assert(h);
+ assert(e);
+
+ /* Remove from iteration list */
+ if (e->iterate_next)
+ e->iterate_next->iterate_previous = e->iterate_previous;
+ else
+ h->iterate_list_tail = e->iterate_previous;
+
+ if (e->iterate_previous)
+ e->iterate_previous->iterate_next = e->iterate_next;
+ else
+ h->iterate_list_head = e->iterate_next;
+
+ /* Remove from hash table bucket list */
+ if (e->bucket_next)
+ e->bucket_next->bucket_previous = e->bucket_previous;
+
+ if (e->bucket_previous)
+ e->bucket_previous->bucket_next = e->bucket_next;
+ else
+ BY_HASH(h)[hash] = e->bucket_next;
+
+ assert(h->n_entries >= 1);
+ h->n_entries--;
+}
+
+static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
+ unsigned hash;
+
+ assert(h);
+ assert(e);
+
+ hash = h->hash_func(e->key) % NBUCKETS;
+
+ unlink_entry(h, e, hash);
+
+ if (h->from_pool)
+ deallocate_tile(&first_entry_tile, e);
+ else
+ free(e);
+}
+
+void hashmap_free(Hashmap*h) {
+
+ if (!h)
+ return;
+
+ hashmap_clear(h);
+
+ if (h->from_pool)
+ deallocate_tile(&first_hashmap_tile, h);
+ else
+ free(h);
+}
+
+void hashmap_free_free(Hashmap *h) {
+ void *p;
+
+ while ((p = hashmap_steal_first(h)))
+ free(p);
+
+ hashmap_free(h);
+}
+
+void hashmap_clear(Hashmap *h) {
+ if (!h)
+ return;
+
+ while (h->iterate_list_head)
+ remove_entry(h, h->iterate_list_head);
+}
+
+static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
+ struct hashmap_entry *e;
+ assert(h);
+ assert(hash < NBUCKETS);
+
+ for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
+ if (h->compare_func(e->key, key) == 0)
+ return e;
+
+ return NULL;
+}
+
+int hashmap_put(Hashmap *h, const void *key, void *value) {
+ struct hashmap_entry *e;
+ unsigned hash;
+
+ assert(h);
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if ((e = hash_scan(h, hash, key))) {
+
+ if (e->value == value)
+ return 0;
+
+ return -EEXIST;
+ }
+
+ if (h->from_pool)
+ e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry));
+ else
+ e = new(struct hashmap_entry, 1);
+
+ if (!e)
+ return -ENOMEM;
+
+ e->key = key;
+ e->value = value;
+
+ link_entry(h, e, hash);
+
+ return 1;
+}
+
+int hashmap_replace(Hashmap *h, const void *key, void *value) {
+ struct hashmap_entry *e;
+ unsigned hash;
+
+ assert(h);
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if ((e = hash_scan(h, hash, key))) {
+ e->key = key;
+ e->value = value;
+ return 0;
+ }
+
+ return hashmap_put(h, key, value);
+}
+
+void* hashmap_get(Hashmap *h, const void *key) {
+ unsigned hash;
+ struct hashmap_entry *e;
+
+ if (!h)
+ return NULL;
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if (!(e = hash_scan(h, hash, key)))
+ return NULL;
+
+ return e->value;
+}
+
+void* hashmap_remove(Hashmap *h, const void *key) {
+ struct hashmap_entry *e;
+ unsigned hash;
+ void *data;
+
+ if (!h)
+ return NULL;
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if (!(e = hash_scan(h, hash, key)))
+ return NULL;
+
+ data = e->value;
+ remove_entry(h, e);
+
+ return data;
+}
+
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
+ struct hashmap_entry *e;
+ unsigned old_hash, new_hash;
+
+ if (!h)
+ return -ENOENT;
+
+ old_hash = h->hash_func(old_key) % NBUCKETS;
+ if (!(e = hash_scan(h, old_hash, old_key)))
+ return -ENOENT;
+
+ new_hash = h->hash_func(new_key) % NBUCKETS;
+ if (hash_scan(h, new_hash, new_key))
+ return -EEXIST;
+
+ unlink_entry(h, e, old_hash);
+
+ e->key = new_key;
+ e->value = value;
+
+ link_entry(h, e, new_hash);
+
+ return 0;
+}
+
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
+ struct hashmap_entry *e, *k;
+ unsigned old_hash, new_hash;
+
+ if (!h)
+ return -ENOENT;
+
+ old_hash = h->hash_func(old_key) % NBUCKETS;
+ if (!(e = hash_scan(h, old_hash, old_key)))
+ return -ENOENT;
+
+ new_hash = h->hash_func(new_key) % NBUCKETS;
+
+ if ((k = hash_scan(h, new_hash, new_key)))
+ if (e != k)
+ remove_entry(h, k);
+
+ unlink_entry(h, e, old_hash);
+
+ e->key = new_key;
+ e->value = value;
+
+ link_entry(h, e, new_hash);
+
+ return 0;
+}
+
+void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
+ struct hashmap_entry *e;
+ unsigned hash;
+
+ if (!h)
+ return NULL;
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if (!(e = hash_scan(h, hash, key)))
+ return NULL;
+
+ if (e->value != value)
+ return NULL;
+
+ remove_entry(h, e);
+
+ return value;
+}
+
+void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
+ struct hashmap_entry *e;
+
+ assert(i);
+
+ if (!h)
+ goto at_end;
+
+ if (*i == ITERATOR_LAST)
+ goto at_end;
+
+ if (*i == ITERATOR_FIRST && !h->iterate_list_head)
+ goto at_end;
+
+ e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
+
+ if (e->iterate_next)
+ *i = (Iterator) e->iterate_next;
+ else
+ *i = ITERATOR_LAST;
+
+ if (key)
+ *key = e->key;
+
+ return e->value;
+
+at_end:
+ *i = ITERATOR_LAST;
+
+ if (key)
+ *key = NULL;
+
+ return NULL;
+}
+
+void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
+ struct hashmap_entry *e;
+
+ assert(i);
+
+ if (!h)
+ goto at_beginning;
+
+ if (*i == ITERATOR_FIRST)
+ goto at_beginning;
+
+ if (*i == ITERATOR_LAST && !h->iterate_list_tail)
+ goto at_beginning;
+
+ e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
+
+ if (e->iterate_previous)
+ *i = (Iterator) e->iterate_previous;
+ else
+ *i = ITERATOR_FIRST;
+
+ if (key)
+ *key = e->key;
+
+ return e->value;
+
+at_beginning:
+ *i = ITERATOR_FIRST;
+
+ if (key)
+ *key = NULL;
+
+ return NULL;
+}
+
+void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
+ unsigned hash;
+ struct hashmap_entry *e;
+
+ if (!h)
+ return NULL;
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if (!(e = hash_scan(h, hash, key)))
+ return NULL;
+
+ *i = (Iterator) e;
+
+ return e->value;
+}
+
+void* hashmap_first(Hashmap *h) {
+
+ if (!h)
+ return NULL;
+
+ if (!h->iterate_list_head)
+ return NULL;
+
+ return h->iterate_list_head->value;
+}
+
+void* hashmap_first_key(Hashmap *h) {
+
+ if (!h)
+ return NULL;
+
+ if (!h->iterate_list_head)
+ return NULL;
+
+ return (void*) h->iterate_list_head->key;
+}
+
+void* hashmap_last(Hashmap *h) {
+
+ if (!h)
+ return NULL;
+
+ if (!h->iterate_list_tail)
+ return NULL;
+
+ return h->iterate_list_tail->value;
+}
+
+void* hashmap_steal_first(Hashmap *h) {
+ void *data;
+
+ if (!h)
+ return NULL;
+
+ if (!h->iterate_list_head)
+ return NULL;
+
+ data = h->iterate_list_head->value;
+ remove_entry(h, h->iterate_list_head);
+
+ return data;
+}
+
+void* hashmap_steal_first_key(Hashmap *h) {
+ void *key;
+
+ if (!h)
+ return NULL;
+
+ if (!h->iterate_list_head)
+ return NULL;
+
+ key = (void*) h->iterate_list_head->key;
+ remove_entry(h, h->iterate_list_head);
+
+ return key;
+}
+
+unsigned hashmap_size(Hashmap *h) {
+
+ if (!h)
+ return 0;
+
+ return h->n_entries;
+}
+
+bool hashmap_isempty(Hashmap *h) {
+
+ if (!h)
+ return true;
+
+ return h->n_entries == 0;
+}
+
+int hashmap_merge(Hashmap *h, Hashmap *other) {
+ struct hashmap_entry *e;
+
+ assert(h);
+
+ if (!other)
+ return 0;
+
+ for (e = other->iterate_list_head; e; e = e->iterate_next) {
+ int r;
+
+ if ((r = hashmap_put(h, e->key, e->value)) < 0)
+ if (r != -EEXIST)
+ return r;
+ }
+
+ return 0;
+}
+
+void hashmap_move(Hashmap *h, Hashmap *other) {
+ struct hashmap_entry *e, *n;
+
+ assert(h);
+
+ /* The same as hashmap_merge(), but every new item from other
+ * is moved to h. This function is guaranteed to succeed. */
+
+ if (!other)
+ return;
+
+ for (e = other->iterate_list_head; e; e = n) {
+ unsigned h_hash, other_hash;
+
+ n = e->iterate_next;
+
+ h_hash = h->hash_func(e->key) % NBUCKETS;
+
+ if (hash_scan(h, h_hash, e->key))
+ continue;
+
+ other_hash = other->hash_func(e->key) % NBUCKETS;
+
+ unlink_entry(other, e, other_hash);
+ link_entry(h, e, h_hash);
+ }
+}
+
+int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
+ unsigned h_hash, other_hash;
+ struct hashmap_entry *e;
+
+ if (!other)
+ return 0;
+
+ assert(h);
+
+ h_hash = h->hash_func(key) % NBUCKETS;
+ if (hash_scan(h, h_hash, key))
+ return -EEXIST;
+
+ other_hash = other->hash_func(key) % NBUCKETS;
+ if (!(e = hash_scan(other, other_hash, key)))
+ return -ENOENT;
+
+ unlink_entry(other, e, other_hash);
+ link_entry(h, e, h_hash);
+
+ return 0;
+}
+
+Hashmap *hashmap_copy(Hashmap *h) {
+ Hashmap *copy;
+
+ assert(h);
+
+ if (!(copy = hashmap_new(h->hash_func, h->compare_func)))
+ return NULL;
+
+ if (hashmap_merge(copy, h) < 0) {
+ hashmap_free(copy);
+ return NULL;
+ }
+
+ return copy;
+}
+
+char **hashmap_get_strv(Hashmap *h) {
+ char **sv;
+ Iterator it;
+ char *item;
+ int n;
+
+ sv = new(char*, h->n_entries+1);
+ if (!sv)
+ return NULL;
+
+ n = 0;
+ HASHMAP_FOREACH(item, h, it)
+ sv[n++] = item;
+ sv[n] = NULL;
+
+ return sv;
+}
diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
new file mode 100644
index 0000000..ab4363a
--- /dev/null
+++ b/src/shared/hashmap.h
@@ -0,0 +1,91 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foohashmaphfoo
+#define foohashmaphfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+/* Pretty straightforward hash table implementation. As a minor
+ * optimization a NULL hashmap object will be treated as empty hashmap
+ * for all read operations. That way it is not necessary to
+ * instantiate an object for each Hashmap use. */
+
+typedef struct Hashmap Hashmap;
+typedef struct _IteratorStruct _IteratorStruct;
+typedef _IteratorStruct* Iterator;
+
+#define ITERATOR_FIRST ((Iterator) 0)
+#define ITERATOR_LAST ((Iterator) -1)
+
+typedef unsigned (*hash_func_t)(const void *p);
+typedef int (*compare_func_t)(const void *a, const void *b);
+
+unsigned string_hash_func(const void *p);
+int string_compare_func(const void *a, const void *b);
+
+unsigned trivial_hash_func(const void *p);
+int trivial_compare_func(const void *a, const void *b);
+
+Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func);
+void hashmap_free(Hashmap *h);
+void hashmap_free_free(Hashmap *h);
+Hashmap *hashmap_copy(Hashmap *h);
+int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func);
+
+int hashmap_put(Hashmap *h, const void *key, void *value);
+int hashmap_replace(Hashmap *h, const void *key, void *value);
+void* hashmap_get(Hashmap *h, const void *key);
+void* hashmap_remove(Hashmap *h, const void *key);
+void* hashmap_remove_value(Hashmap *h, const void *key, void *value);
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value);
+
+int hashmap_merge(Hashmap *h, Hashmap *other);
+void hashmap_move(Hashmap *h, Hashmap *other);
+int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key);
+
+unsigned hashmap_size(Hashmap *h);
+bool hashmap_isempty(Hashmap *h);
+
+void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key);
+void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key);
+void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i);
+
+void hashmap_clear(Hashmap *h);
+void *hashmap_steal_first(Hashmap *h);
+void *hashmap_steal_first_key(Hashmap *h);
+void* hashmap_first(Hashmap *h);
+void* hashmap_first_key(Hashmap *h);
+void* hashmap_last(Hashmap *h);
+
+char **hashmap_get_strv(Hashmap *h);
+
+#define HASHMAP_FOREACH(e, h, i) \
+ for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); (e); (e) = hashmap_iterate((h), &(i), NULL))
+
+#define HASHMAP_FOREACH_KEY(e, k, h, i) \
+ for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); (e); (e) = hashmap_iterate((h), &(i), (const void**) &(k)))
+
+#define HASHMAP_FOREACH_BACKWARDS(e, h, i) \
+ for ((i) = ITERATOR_LAST, (e) = hashmap_iterate_backwards((h), &(i), NULL); (e); (e) = hashmap_iterate_backwards((h), &(i), NULL))
+
+#endif
diff --git a/src/shared/label.c b/src/shared/label.c
new file mode 100644
index 0000000..2c887a0
--- /dev/null
+++ b/src/shared/label.c
@@ -0,0 +1,413 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "label.h"
+#include "util.h"
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+static struct selabel_handle *label_hnd = NULL;
+
+static int use_selinux_cached = -1;
+
+static inline bool use_selinux(void) {
+
+ if (use_selinux_cached < 0)
+ use_selinux_cached = is_selinux_enabled() > 0;
+
+ return use_selinux_cached;
+}
+
+void label_retest_selinux(void) {
+ use_selinux_cached = -1;
+}
+
+#endif
+
+int label_init(void) {
+ int r = 0;
+
+#ifdef HAVE_SELINUX
+ usec_t before_timestamp, after_timestamp;
+ struct mallinfo before_mallinfo, after_mallinfo;
+
+ if (!use_selinux())
+ return 0;
+
+ if (label_hnd)
+ return 0;
+
+ before_mallinfo = mallinfo();
+ before_timestamp = now(CLOCK_MONOTONIC);
+
+ label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
+ if (!label_hnd) {
+ log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
+ "Failed to initialize SELinux context: %m");
+ r = security_getenforce() == 1 ? -errno : 0;
+ } else {
+ char timespan[FORMAT_TIMESPAN_MAX];
+ int l;
+
+ after_timestamp = now(CLOCK_MONOTONIC);
+ after_mallinfo = mallinfo();
+
+ l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
+
+ log_info("Successfully loaded SELinux database in %s, size on heap is %iK.",
+ format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp),
+ (l+1023)/1024);
+ }
+#endif
+
+ return r;
+}
+
+int label_fix(const char *path, bool ignore_enoent) {
+ int r = 0;
+
+#ifdef HAVE_SELINUX
+ struct stat st;
+ security_context_t fcon;
+
+ if (!use_selinux() || !label_hnd)
+ return 0;
+
+ r = lstat(path, &st);
+ if (r == 0) {
+ r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
+
+ /* If there's no label to set, then exit without warning */
+ if (r < 0 && errno == ENOENT)
+ return 0;
+
+ if (r == 0) {
+ r = lsetfilecon(path, fcon);
+ freecon(fcon);
+
+ /* If the FS doesn't support labels, then exit without warning */
+ if (r < 0 && errno == ENOTSUP)
+ return 0;
+ }
+ }
+
+ if (r < 0) {
+ /* Ignore ENOENT in some cases */
+ if (ignore_enoent && errno == ENOENT)
+ return 0;
+
+ log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
+ "Unable to fix label of %s: %m", path);
+ r = security_getenforce() == 1 ? -errno : 0;
+ }
+#endif
+
+ return r;
+}
+
+void label_finish(void) {
+
+#ifdef HAVE_SELINUX
+ if (use_selinux() && label_hnd)
+ selabel_close(label_hnd);
+#endif
+}
+
+int label_get_create_label_from_exe(const char *exe, char **label) {
+
+ int r = 0;
+
+#ifdef HAVE_SELINUX
+ security_context_t mycon = NULL, fcon = NULL;
+ security_class_t sclass;
+
+ if (!use_selinux()) {
+ *label = NULL;
+ return 0;
+ }
+
+ r = getcon(&mycon);
+ if (r < 0)
+ goto fail;
+
+ r = getfilecon(exe, &fcon);
+ if (r < 0)
+ goto fail;
+
+ sclass = string_to_security_class("process");
+ r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
+ if (r == 0)
+ log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
+
+fail:
+ if (r < 0 && security_getenforce() == 1)
+ r = -errno;
+
+ freecon(mycon);
+ freecon(fcon);
+#endif
+
+ return r;
+}
+
+int label_fifofile_set(const char *path) {
+ int r = 0;
+
+#ifdef HAVE_SELINUX
+ security_context_t filecon = NULL;
+
+ if (!use_selinux() || !label_hnd)
+ return 0;
+
+ r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFIFO);
+ if (r < 0)
+ r = -errno;
+ else if (r == 0) {
+ r = setfscreatecon(filecon);
+ if (r < 0) {
+ log_error("Failed to set SELinux file context on %s: %m", path);
+ r = -errno;
+ }
+
+ freecon(filecon);
+ }
+
+ if (r < 0 && security_getenforce() == 0)
+ r = 0;
+#endif
+
+ return r;
+}
+
+int label_symlinkfile_set(const char *path) {
+ int r = 0;
+
+#ifdef HAVE_SELINUX
+ security_context_t filecon = NULL;
+
+ if (!use_selinux() || !label_hnd)
+ return 0;
+
+ r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFLNK);
+ if (r < 0)
+ r = -errno;
+ else if (r == 0) {
+ r = setfscreatecon(filecon);
+ if (r < 0) {
+ log_error("Failed to set SELinux file context on %s: %m", path);
+ r = -errno;
+ }
+
+ freecon(filecon);
+ }
+
+ if (r < 0 && security_getenforce() == 0)
+ r = 0;
+#endif
+
+ return r;
+}
+
+int label_socket_set(const char *label) {
+
+#ifdef HAVE_SELINUX
+ if (!use_selinux())
+ return 0;
+
+ if (setsockcreatecon((security_context_t) label) < 0) {
+ log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
+ "Failed to set SELinux context (%s) on socket: %m", label);
+
+ if (security_getenforce() == 1)
+ return -errno;
+ }
+#endif
+
+ return 0;
+}
+
+void label_file_clear(void) {
+
+#ifdef HAVE_SELINUX
+ if (!use_selinux())
+ return;
+
+ setfscreatecon(NULL);
+#endif
+}
+
+void label_socket_clear(void) {
+
+#ifdef HAVE_SELINUX
+ if (!use_selinux())
+ return;
+
+ setsockcreatecon(NULL);
+#endif
+}
+
+void label_free(const char *label) {
+
+#ifdef HAVE_SELINUX
+ if (!use_selinux())
+ return;
+
+ freecon((security_context_t) label);
+#endif
+}
+
+int label_mkdir(const char *path, mode_t mode) {
+
+ /* Creates a directory and labels it according to the SELinux policy */
+
+#ifdef HAVE_SELINUX
+ int r;
+ security_context_t fcon = NULL;
+
+ if (!use_selinux() || !label_hnd)
+ goto skipped;
+
+ if (path_is_absolute(path))
+ r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
+ else {
+ char *newpath;
+
+ newpath = path_make_absolute_cwd(path);
+ if (!newpath)
+ return -ENOMEM;
+
+ r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
+ free(newpath);
+ }
+
+ if (r == 0)
+ r = setfscreatecon(fcon);
+
+ if (r < 0 && errno != ENOENT) {
+ log_error("Failed to set security context %s for %s: %m", fcon, path);
+
+ if (security_getenforce() == 1) {
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ r = mkdir(path, mode);
+ if (r < 0)
+ r = -errno;
+
+finish:
+ setfscreatecon(NULL);
+ freecon(fcon);
+
+ return r;
+
+skipped:
+#endif
+ return mkdir(path, mode) < 0 ? -errno : 0;
+}
+
+int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
+
+ /* Binds a socket and label its file system object according to the SELinux policy */
+
+#ifdef HAVE_SELINUX
+ int r;
+ security_context_t fcon = NULL;
+ const struct sockaddr_un *un;
+ char *path = NULL;
+
+ assert(fd >= 0);
+ assert(addr);
+ assert(addrlen >= sizeof(sa_family_t));
+
+ if (!use_selinux() || !label_hnd)
+ goto skipped;
+
+ /* Filter out non-local sockets */
+ if (addr->sa_family != AF_UNIX)
+ goto skipped;
+
+ /* Filter out anonymous sockets */
+ if (addrlen < sizeof(sa_family_t) + 1)
+ goto skipped;
+
+ /* Filter out abstract namespace sockets */
+ un = (const struct sockaddr_un*) addr;
+ if (un->sun_path[0] == 0)
+ goto skipped;
+
+ path = strndup(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
+ if (!path)
+ return -ENOMEM;
+
+ if (path_is_absolute(path))
+ r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
+ else {
+ char *newpath;
+
+ newpath = path_make_absolute_cwd(path);
+
+ if (!newpath) {
+ free(path);
+ return -ENOMEM;
+ }
+
+ r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
+ free(newpath);
+ }
+
+ if (r == 0)
+ r = setfscreatecon(fcon);
+
+ if (r < 0 && errno != ENOENT) {
+ log_error("Failed to set security context %s for %s: %m", fcon, path);
+
+ if (security_getenforce() == 1) {
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ r = bind(fd, addr, addrlen);
+ if (r < 0)
+ r = -errno;
+
+finish:
+ setfscreatecon(NULL);
+ freecon(fcon);
+ free(path);
+
+ return r;
+
+skipped:
+#endif
+ return bind(fd, addr, addrlen) < 0 ? -errno : 0;
+}
diff --git a/src/shared/label.h b/src/shared/label.h
new file mode 100644
index 0000000..ead4483
--- /dev/null
+++ b/src/shared/label.h
@@ -0,0 +1,51 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foolabelhfoo
+#define foolabelhfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+
+int label_init(void);
+void label_finish(void);
+
+int label_fix(const char *path, bool ignore_enoent);
+
+int label_socket_set(const char *label);
+void label_socket_clear(void);
+
+int label_fifofile_set(const char *path);
+int label_symlinkfile_set(const char *path);
+void label_file_clear(void);
+
+void label_free(const char *label);
+
+int label_get_create_label_from_exe(const char *exe, char **label);
+
+int label_mkdir(const char *path, mode_t mode);
+
+void label_retest_selinux(void);
+
+int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen);
+
+#endif
diff --git a/src/shared/log.c b/src/shared/log.c
new file mode 100644
index 0000000..9fffc1d
--- /dev/null
+++ b/src/shared/log.c
@@ -0,0 +1,747 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stddef.h>
+
+#include "log.h"
+#include "util.h"
+#include "macro.h"
+#include "socket-util.h"
+
+#define SNDBUF_SIZE (8*1024*1024)
+
+static LogTarget log_target = LOG_TARGET_CONSOLE;
+static int log_max_level = LOG_INFO;
+static int log_facility = LOG_DAEMON;
+
+static int console_fd = STDERR_FILENO;
+static int syslog_fd = -1;
+static int kmsg_fd = -1;
+static int journal_fd = -1;
+
+static bool syslog_is_stream = false;
+
+static bool show_color = false;
+static bool show_location = false;
+
+/* Akin to glibc's __abort_msg; which is private and we hence cannot
+ * use here. */
+static char *log_abort_msg = NULL;
+
+void log_close_console(void) {
+
+ if (console_fd < 0)
+ return;
+
+ if (getpid() == 1) {
+ if (console_fd >= 3)
+ close_nointr_nofail(console_fd);
+
+ console_fd = -1;
+ }
+}
+
+static int log_open_console(void) {
+
+ if (console_fd >= 0)
+ return 0;
+
+ if (getpid() == 1) {
+
+ console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (console_fd < 0) {
+ log_error("Failed to open /dev/console for logging: %s", strerror(-console_fd));
+ return console_fd;
+ }
+
+ log_debug("Successfully opened /dev/console for logging.");
+ } else
+ console_fd = STDERR_FILENO;
+
+ return 0;
+}
+
+void log_close_kmsg(void) {
+
+ if (kmsg_fd < 0)
+ return;
+
+ close_nointr_nofail(kmsg_fd);
+ kmsg_fd = -1;
+}
+
+static int log_open_kmsg(void) {
+
+ if (kmsg_fd >= 0)
+ return 0;
+
+ kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (kmsg_fd < 0) {
+ log_error("Failed to open /dev/kmsg for logging: %s", strerror(errno));
+ return -errno;
+ }
+
+ log_debug("Successfully opened /dev/kmsg for logging.");
+
+ return 0;
+}
+
+void log_close_syslog(void) {
+
+ if (syslog_fd < 0)
+ return;
+
+ close_nointr_nofail(syslog_fd);
+ syslog_fd = -1;
+}
+
+static int create_log_socket(int type) {
+ int fd;
+
+ /* All output to the syslog/journal fds we do asynchronously,
+ * and if the buffers are full we just drop the messages */
+
+ fd = socket(AF_UNIX, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0)
+ return -errno;
+
+ fd_inc_sndbuf(fd, SNDBUF_SIZE);
+
+ return fd;
+}
+
+static int log_open_syslog(void) {
+ union sockaddr_union sa;
+ int r;
+
+ if (syslog_fd >= 0)
+ return 0;
+
+ zero(sa);
+ sa.un.sun_family = AF_UNIX;
+ strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
+
+ syslog_fd = create_log_socket(SOCK_DGRAM);
+ if (syslog_fd < 0) {
+ r = syslog_fd;
+ goto fail;
+ }
+
+ if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
+ close_nointr_nofail(syslog_fd);
+
+ /* Some legacy syslog systems still use stream
+ * sockets. They really shouldn't. But what can we
+ * do... */
+ syslog_fd = create_log_socket(SOCK_STREAM);
+ if (syslog_fd < 0) {
+ r = syslog_fd;
+ goto fail;
+ }
+
+ if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ syslog_is_stream = true;
+ } else
+ syslog_is_stream = false;
+
+ log_debug("Successfully opened syslog for logging.");
+
+ return 0;
+
+fail:
+ log_close_syslog();
+ log_debug("Failed to open syslog for logging: %s", strerror(-r));
+ return r;
+}
+
+void log_close_journal(void) {
+
+ if (journal_fd < 0)
+ return;
+
+ close_nointr_nofail(journal_fd);
+ journal_fd = -1;
+}
+
+static int log_open_journal(void) {
+ union sockaddr_union sa;
+ int r;
+
+ if (journal_fd >= 0)
+ return 0;
+
+ journal_fd = create_log_socket(SOCK_DGRAM);
+ if (journal_fd < 0) {
+ r = journal_fd;
+ goto fail;
+ }
+
+ zero(sa);
+ sa.un.sun_family = AF_UNIX;
+ strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
+
+ if (connect(journal_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ log_debug("Successfully opened journal for logging.");
+
+ return 0;
+
+fail:
+ log_close_journal();
+ log_debug("Failed to open journal for logging: %s", strerror(-r));
+ return r;
+}
+
+int log_open(void) {
+ int r;
+
+ /* If we don't use the console we close it here, to not get
+ * killed by SAK. If we don't use syslog we close it here so
+ * that we are not confused by somebody deleting the socket in
+ * the fs. If we don't use /dev/kmsg we still keep it open,
+ * because there is no reason to close it. */
+
+ if (log_target == LOG_TARGET_NULL) {
+ log_close_journal();
+ log_close_syslog();
+ log_close_console();
+ return 0;
+ }
+
+ if (log_target != LOG_TARGET_AUTO ||
+ getpid() == 1 ||
+ isatty(STDERR_FILENO) <= 0) {
+
+ if (log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+ log_target == LOG_TARGET_JOURNAL) {
+ r = log_open_journal();
+ if (r >= 0) {
+ log_close_syslog();
+ log_close_console();
+ return r;
+ }
+ }
+
+ if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_SYSLOG) {
+ r = log_open_syslog();
+ if (r >= 0) {
+ log_close_journal();
+ log_close_console();
+ return r;
+ }
+ }
+
+ if (log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+ log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_KMSG) {
+ r = log_open_kmsg();
+ if (r >= 0) {
+ log_close_journal();
+ log_close_syslog();
+ log_close_console();
+ return r;
+ }
+ }
+ }
+
+ log_close_journal();
+ log_close_syslog();
+
+ /* Get the real /dev/console if we are PID=1, hence reopen */
+ log_close_console();
+ return log_open_console();
+}
+
+void log_set_target(LogTarget target) {
+ assert(target >= 0);
+ assert(target < _LOG_TARGET_MAX);
+
+ log_target = target;
+}
+
+void log_close(void) {
+ log_close_journal();
+ log_close_syslog();
+ log_close_kmsg();
+ log_close_console();
+}
+
+void log_forget_fds(void) {
+ console_fd = kmsg_fd = syslog_fd = journal_fd = -1;
+}
+
+void log_set_max_level(int level) {
+ assert((level & LOG_PRIMASK) == level);
+
+ log_max_level = level;
+}
+
+void log_set_facility(int facility) {
+ log_facility = facility;
+}
+
+static int write_to_console(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ const char *buffer) {
+
+ char location[64];
+ struct iovec iovec[5];
+ unsigned n = 0;
+ bool highlight;
+
+ if (console_fd < 0)
+ return 0;
+
+ highlight = LOG_PRI(level) <= LOG_ERR && show_color;
+
+ zero(iovec);
+
+ if (show_location) {
+ snprintf(location, sizeof(location), "(%s:%u) ", file, line);
+ char_array_0(location);
+ IOVEC_SET_STRING(iovec[n++], location);
+ }
+
+ if (highlight)
+ IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED_ON);
+ IOVEC_SET_STRING(iovec[n++], buffer);
+ if (highlight)
+ IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_OFF);
+ IOVEC_SET_STRING(iovec[n++], "\n");
+
+ if (writev(console_fd, iovec, n) < 0)
+ return -errno;
+
+ return 1;
+}
+
+static int write_to_syslog(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ const char *buffer) {
+
+ char header_priority[16], header_time[64], header_pid[16];
+ struct iovec iovec[5];
+ struct msghdr msghdr;
+ time_t t;
+ struct tm *tm;
+
+ if (syslog_fd < 0)
+ return 0;
+
+ snprintf(header_priority, sizeof(header_priority), "<%i>", level);
+ char_array_0(header_priority);
+
+ t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
+ if (!(tm = localtime(&t)))
+ return -EINVAL;
+
+ if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
+ return -EINVAL;
+
+ snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
+ char_array_0(header_pid);
+
+ zero(iovec);
+ IOVEC_SET_STRING(iovec[0], header_priority);
+ IOVEC_SET_STRING(iovec[1], header_time);
+ IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
+ IOVEC_SET_STRING(iovec[3], header_pid);
+ IOVEC_SET_STRING(iovec[4], buffer);
+
+ /* When using syslog via SOCK_STREAM separate the messages by NUL chars */
+ if (syslog_is_stream)
+ iovec[4].iov_len++;
+
+ zero(msghdr);
+ msghdr.msg_iov = iovec;
+ msghdr.msg_iovlen = ELEMENTSOF(iovec);
+
+ for (;;) {
+ ssize_t n;
+
+ n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL);
+ if (n < 0)
+ return -errno;
+
+ if (!syslog_is_stream ||
+ (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec)))
+ break;
+
+ IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n);
+ }
+
+ return 1;
+}
+
+static int write_to_kmsg(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ const char *buffer) {
+
+ char header_priority[16], header_pid[16];
+ struct iovec iovec[5];
+
+ if (kmsg_fd < 0)
+ return 0;
+
+ snprintf(header_priority, sizeof(header_priority), "<%i>", level);
+ char_array_0(header_priority);
+
+ snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
+ char_array_0(header_pid);
+
+ zero(iovec);
+ IOVEC_SET_STRING(iovec[0], header_priority);
+ IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
+ IOVEC_SET_STRING(iovec[2], header_pid);
+ IOVEC_SET_STRING(iovec[3], buffer);
+ IOVEC_SET_STRING(iovec[4], "\n");
+
+ if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
+ return -errno;
+
+ return 1;
+}
+
+static int write_to_journal(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ const char *buffer) {
+
+ char header[LINE_MAX];
+ struct iovec iovec[3];
+ struct msghdr mh;
+
+ if (journal_fd < 0)
+ return 0;
+
+ snprintf(header, sizeof(header),
+ "PRIORITY=%i\n"
+ "SYSLOG_FACILITY=%i\n"
+ "CODE_FILE=%s\n"
+ "CODE_LINE=%i\n"
+ "CODE_FUNCTION=%s\n"
+ "MESSAGE=",
+ LOG_PRI(level),
+ LOG_FAC(level),
+ file,
+ line,
+ func);
+
+ char_array_0(header);
+
+ zero(iovec);
+ IOVEC_SET_STRING(iovec[0], header);
+ IOVEC_SET_STRING(iovec[1], buffer);
+ IOVEC_SET_STRING(iovec[2], "\n");
+
+ zero(mh);
+ mh.msg_iov = iovec;
+ mh.msg_iovlen = ELEMENTSOF(iovec);
+
+ if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0)
+ return -errno;
+
+ return 1;
+}
+
+static int log_dispatch(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ char *buffer) {
+
+ int r = 0;
+
+ if (log_target == LOG_TARGET_NULL)
+ return 0;
+
+ /* Patch in LOG_DAEMON facility if necessary */
+ if ((level & LOG_FACMASK) == 0)
+ level = log_facility | LOG_PRI(level);
+
+ do {
+ char *e;
+ int k = 0;
+
+ buffer += strspn(buffer, NEWLINE);
+
+ if (buffer[0] == 0)
+ break;
+
+ if ((e = strpbrk(buffer, NEWLINE)))
+ *(e++) = 0;
+
+ if (log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+ log_target == LOG_TARGET_JOURNAL) {
+
+ k = write_to_journal(level, file, line, func, buffer);
+ if (k < 0) {
+ if (k != -EAGAIN)
+ log_close_journal();
+ log_open_kmsg();
+ } else if (k > 0)
+ r++;
+ }
+
+ if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_SYSLOG) {
+
+ k = write_to_syslog(level, file, line, func, buffer);
+ if (k < 0) {
+ if (k != -EAGAIN)
+ log_close_syslog();
+ log_open_kmsg();
+ } else if (k > 0)
+ r++;
+ }
+
+ if (k <= 0 &&
+ (log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_KMSG)) {
+
+ k = write_to_kmsg(level, file, line, func, buffer);
+ if (k < 0) {
+ log_close_kmsg();
+ log_open_console();
+ } else if (k > 0)
+ r++;
+ }
+
+ if (k <= 0) {
+ k = write_to_console(level, file, line, func, buffer);
+ if (k < 0)
+ return k;
+ }
+
+ buffer = e;
+ } while (buffer);
+
+ return r;
+}
+
+int log_dump_internal(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ char *buffer) {
+
+ int saved_errno, r;
+
+ /* This modifies the buffer... */
+
+ if (_likely_(LOG_PRI(level) > log_max_level))
+ return 0;
+
+ saved_errno = errno;
+ r = log_dispatch(level, file, line, func, buffer);
+ errno = saved_errno;
+
+ return r;
+}
+
+int log_metav(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ const char *format,
+ va_list ap) {
+
+ char buffer[LINE_MAX];
+ int saved_errno, r;
+
+ if (_likely_(LOG_PRI(level) > log_max_level))
+ return 0;
+
+ saved_errno = errno;
+ vsnprintf(buffer, sizeof(buffer), format, ap);
+ char_array_0(buffer);
+
+ r = log_dispatch(level, file, line, func, buffer);
+ errno = saved_errno;
+
+ return r;
+}
+
+int log_meta(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ const char *format, ...) {
+
+ int r;
+ va_list ap;
+
+ va_start(ap, format);
+ r = log_metav(level, file, line, func, format, ap);
+ va_end(ap);
+
+ return r;
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+_noreturn_ static void log_assert(const char *text, const char *file, int line, const char *func, const char *format) {
+ static char buffer[LINE_MAX];
+
+ snprintf(buffer, sizeof(buffer), format, text, file, line, func);
+
+ char_array_0(buffer);
+ log_abort_msg = buffer;
+
+ log_dispatch(LOG_CRIT, file, line, func, buffer);
+ abort();
+}
+#pragma GCC diagnostic pop
+
+_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func) {
+ log_assert(text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.");
+}
+
+_noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) {
+ log_assert(text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.");
+}
+
+int log_set_target_from_string(const char *e) {
+ LogTarget t;
+
+ t = log_target_from_string(e);
+ if (t < 0)
+ return -EINVAL;
+
+ log_set_target(t);
+ return 0;
+}
+
+int log_set_max_level_from_string(const char *e) {
+ int t;
+
+ t = log_level_from_string(e);
+ if (t < 0)
+ return t;
+
+ log_set_max_level(t);
+ return 0;
+}
+
+void log_parse_environment(void) {
+ const char *e;
+
+ if ((e = getenv("SYSTEMD_LOG_TARGET")))
+ if (log_set_target_from_string(e) < 0)
+ log_warning("Failed to parse log target %s. Ignoring.", e);
+
+ if ((e = getenv("SYSTEMD_LOG_LEVEL")))
+ if (log_set_max_level_from_string(e) < 0)
+ log_warning("Failed to parse log level %s. Ignoring.", e);
+
+ if ((e = getenv("SYSTEMD_LOG_COLOR")))
+ if (log_show_color_from_string(e) < 0)
+ log_warning("Failed to parse bool %s. Ignoring.", e);
+
+ if ((e = getenv("SYSTEMD_LOG_LOCATION")))
+ if (log_show_location_from_string(e) < 0)
+ log_warning("Failed to parse bool %s. Ignoring.", e);
+}
+
+LogTarget log_get_target(void) {
+ return log_target;
+}
+
+int log_get_max_level(void) {
+ return log_max_level;
+}
+
+void log_show_color(bool b) {
+ show_color = b;
+}
+
+void log_show_location(bool b) {
+ show_location = b;
+}
+
+int log_show_color_from_string(const char *e) {
+ int t;
+
+ t = parse_boolean(e);
+ if (t < 0)
+ return t;
+
+ log_show_color(t);
+ return 0;
+}
+
+int log_show_location_from_string(const char *e) {
+ int t;
+
+ t = parse_boolean(e);
+ if (t < 0)
+ return t;
+
+ log_show_location(t);
+ return 0;
+}
+
+static const char *const log_target_table[] = {
+ [LOG_TARGET_CONSOLE] = "console",
+ [LOG_TARGET_KMSG] = "kmsg",
+ [LOG_TARGET_JOURNAL] = "journal",
+ [LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg",
+ [LOG_TARGET_SYSLOG] = "syslog",
+ [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
+ [LOG_TARGET_AUTO] = "auto",
+ [LOG_TARGET_NULL] = "null"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
diff --git a/src/shared/log.h b/src/shared/log.h
new file mode 100644
index 0000000..3283808
--- /dev/null
+++ b/src/shared/log.h
@@ -0,0 +1,111 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foologhfoo
+#define foologhfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <syslog.h>
+#include <stdbool.h>
+#include <stdarg.h>
+
+#include "macro.h"
+
+typedef enum LogTarget{
+ LOG_TARGET_CONSOLE,
+ LOG_TARGET_KMSG,
+ LOG_TARGET_JOURNAL,
+ LOG_TARGET_JOURNAL_OR_KMSG,
+ LOG_TARGET_SYSLOG,
+ LOG_TARGET_SYSLOG_OR_KMSG,
+ LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
+ LOG_TARGET_NULL,
+ _LOG_TARGET_MAX,
+ _LOG_TARGET_INVALID = -1
+} LogTarget;
+
+void log_set_target(LogTarget target);
+void log_set_max_level(int level);
+void log_set_facility(int facility);
+
+int log_set_target_from_string(const char *e);
+int log_set_max_level_from_string(const char *e);
+
+void log_show_color(bool b);
+void log_show_location(bool b);
+
+int log_show_color_from_string(const char *e);
+int log_show_location_from_string(const char *e);
+
+LogTarget log_get_target(void);
+int log_get_max_level(void);
+
+int log_open(void);
+void log_close(void);
+void log_forget_fds(void);
+
+void log_close_syslog(void);
+void log_close_journal(void);
+void log_close_kmsg(void);
+void log_close_console(void);
+
+void log_parse_environment(void);
+
+int log_meta(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ const char *format, ...) _printf_attr_(5,6);
+
+int log_metav(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ const char *format,
+ va_list ap);
+
+_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func);
+_noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func);
+
+/* This modifies the buffer passed! */
+int log_dump_internal(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ char *buffer);
+
+#define log_full(level, ...) log_meta(level, __FILE__, __LINE__, __func__, __VA_ARGS__)
+
+#define log_debug(...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define log_info(...) log_meta(LOG_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define log_notice(...) log_meta(LOG_NOTICE, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define log_warning(...) log_meta(LOG_WARNING, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define log_error(...) log_meta(LOG_ERR, __FILE__, __LINE__, __func__, __VA_ARGS__)
+
+/* This modifies the buffer passed! */
+#define log_dump(level, buffer) log_dump_internal(level, __FILE__, __LINE__, __func__, buffer)
+
+const char *log_target_to_string(LogTarget target);
+LogTarget log_target_from_string(const char *s);
+
+#endif
diff --git a/src/shared/ratelimit.c b/src/shared/ratelimit.c
new file mode 100644
index 0000000..93157c7
--- /dev/null
+++ b/src/shared/ratelimit.c
@@ -0,0 +1,57 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+
+#include "ratelimit.h"
+#include "log.h"
+
+/* Modelled after Linux' lib/ratelimit.c by Dave Young
+ * <hidave.darkstar at gmail.com>, which is licensed GPLv2. */
+
+bool ratelimit_test(RateLimit *r) {
+ usec_t ts;
+
+ assert(r);
+
+ if (r->interval <= 0 || r->burst <= 0)
+ return true;
+
+ ts = now(CLOCK_MONOTONIC);
+
+ if (r->begin <= 0 ||
+ r->begin + r->interval < ts) {
+ r->begin = ts;
+
+ /* Reset counter */
+ r->num = 0;
+ goto good;
+ }
+
+ if (r->num <= r->burst)
+ goto good;
+
+ return false;
+
+good:
+ r->num++;
+ return true;
+}
diff --git a/src/shared/ratelimit.h b/src/shared/ratelimit.h
new file mode 100644
index 0000000..a6443e7
--- /dev/null
+++ b/src/shared/ratelimit.h
@@ -0,0 +1,53 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooratelimithfoo
+#define fooratelimithfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+
+typedef struct RateLimit {
+ usec_t interval;
+ usec_t begin;
+ unsigned burst;
+ unsigned num;
+} RateLimit;
+
+#define RATELIMIT_DEFINE(_name, _interval, _burst) \
+ RateLimit _name = { \
+ .interval = (_interval), \
+ .burst = (_burst), \
+ .num = 0, \
+ .begin = 0 \
+ }
+
+#define RATELIMIT_INIT(v, _interval, _burst) \
+ do { \
+ RateLimit *_r = &(v); \
+ _r->interval = (_interval); \
+ _r->burst = (_burst); \
+ _r->num = 0; \
+ _r->begin = 0; \
+ } while (false)
+
+bool ratelimit_test(RateLimit *r);
+
+#endif
diff --git a/src/shared/set.c b/src/shared/set.c
new file mode 100644
index 0000000..097b9d3
--- /dev/null
+++ b/src/shared/set.c
@@ -0,0 +1,118 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+
+#include "set.h"
+#include "hashmap.h"
+
+#define MAKE_SET(h) ((Set*) (h))
+#define MAKE_HASHMAP(s) ((Hashmap*) (s))
+
+/* For now this is not much more than a wrapper around a hashmap */
+
+Set *set_new(hash_func_t hash_func, compare_func_t compare_func) {
+ return MAKE_SET(hashmap_new(hash_func, compare_func));
+}
+
+void set_free(Set* s) {
+ hashmap_free(MAKE_HASHMAP(s));
+}
+
+void set_free_free(Set *s) {
+ hashmap_free_free(MAKE_HASHMAP(s));
+}
+
+int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func) {
+ return hashmap_ensure_allocated((Hashmap**) s, hash_func, compare_func);
+}
+
+int set_put(Set *s, void *value) {
+ return hashmap_put(MAKE_HASHMAP(s), value, value);
+}
+
+int set_replace(Set *s, void *value) {
+ return hashmap_replace(MAKE_HASHMAP(s), value, value);
+}
+
+void *set_get(Set *s, void *value) {
+ return hashmap_get(MAKE_HASHMAP(s), value);
+}
+
+void *set_remove(Set *s, void *value) {
+ return hashmap_remove(MAKE_HASHMAP(s), value);
+}
+
+int set_remove_and_put(Set *s, void *old_value, void *new_value) {
+ return hashmap_remove_and_put(MAKE_HASHMAP(s), old_value, new_value, new_value);
+}
+
+unsigned set_size(Set *s) {
+ return hashmap_size(MAKE_HASHMAP(s));
+}
+
+bool set_isempty(Set *s) {
+ return hashmap_isempty(MAKE_HASHMAP(s));
+}
+
+void *set_iterate(Set *s, Iterator *i) {
+ return hashmap_iterate(MAKE_HASHMAP(s), i, NULL);
+}
+
+void *set_iterate_backwards(Set *s, Iterator *i) {
+ return hashmap_iterate_backwards(MAKE_HASHMAP(s), i, NULL);
+}
+
+void *set_iterate_skip(Set *s, void *value, Iterator *i) {
+ return hashmap_iterate_skip(MAKE_HASHMAP(s), value, i);
+}
+
+void *set_steal_first(Set *s) {
+ return hashmap_steal_first(MAKE_HASHMAP(s));
+}
+
+void* set_first(Set *s) {
+ return hashmap_first(MAKE_HASHMAP(s));
+}
+
+void* set_last(Set *s) {
+ return hashmap_last(MAKE_HASHMAP(s));
+}
+
+int set_merge(Set *s, Set *other) {
+ return hashmap_merge(MAKE_HASHMAP(s), MAKE_HASHMAP(other));
+}
+
+void set_move(Set *s, Set *other) {
+ return hashmap_move(MAKE_HASHMAP(s), MAKE_HASHMAP(other));
+}
+
+int set_move_one(Set *s, Set *other, void *value) {
+ return hashmap_move_one(MAKE_HASHMAP(s), MAKE_HASHMAP(other), value);
+}
+
+Set* set_copy(Set *s) {
+ return MAKE_SET(hashmap_copy(MAKE_HASHMAP(s)));
+}
+
+void set_clear(Set *s) {
+ hashmap_clear(MAKE_HASHMAP(s));
+}
diff --git a/src/shared/set.h b/src/shared/set.h
new file mode 100644
index 0000000..885780c
--- /dev/null
+++ b/src/shared/set.h
@@ -0,0 +1,69 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosethfoo
+#define foosethfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* Pretty straightforward set implementation. Internally based on the
+ * hashmap. That means that as a minor optimization a NULL set
+ * object will be treated as empty set for all read
+ * operations. That way it is not necessary to instantiate an object
+ * for each set use. */
+
+#include "hashmap.h"
+
+typedef struct Set Set;
+
+Set *set_new(hash_func_t hash_func, compare_func_t compare_func);
+void set_free(Set* s);
+void set_free_free(Set *s);
+Set* set_copy(Set *s);
+int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func);
+
+int set_put(Set *s, void *value);
+int set_replace(Set *s, void *value);
+void *set_get(Set *s, void *value);
+void *set_remove(Set *s, void *value);
+int set_remove_and_put(Set *s, void *old_value, void *new_value);
+
+int set_merge(Set *s, Set *other);
+void set_move(Set *s, Set *other);
+int set_move_one(Set *s, Set *other, void *value);
+
+unsigned set_size(Set *s);
+bool set_isempty(Set *s);
+
+void *set_iterate(Set *s, Iterator *i);
+void *set_iterate_backwards(Set *s, Iterator *i);
+void *set_iterate_skip(Set *s, void *value, Iterator *i);
+
+void set_clear(Set *s);
+void *set_steal_first(Set *s);
+void* set_first(Set *s);
+void* set_last(Set *s);
+
+#define SET_FOREACH(e, s, i) \
+ for ((i) = ITERATOR_FIRST, (e) = set_iterate((s), &(i)); (e); (e) = set_iterate((s), &(i)))
+
+#define SET_FOREACH_BACKWARDS(e, s, i) \
+ for ((i) = ITERATOR_LAST, (e) = set_iterate_backwards((s), &(i)); (e); (e) = set_iterate_backwards((s), &(i)))
+
+#endif
diff --git a/src/shared/socket-util.c b/src/shared/socket-util.c
new file mode 100644
index 0000000..acc4d33
--- /dev/null
+++ b/src/shared/socket-util.c
@@ -0,0 +1,652 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stddef.h>
+#include <sys/ioctl.h>
+
+#include "macro.h"
+#include "util.h"
+#include "socket-util.h"
+#include "missing.h"
+#include "label.h"
+
+int socket_address_parse(SocketAddress *a, const char *s) {
+ int r;
+ char *e, *n;
+ unsigned u;
+
+ assert(a);
+ assert(s);
+
+ zero(*a);
+ a->type = SOCK_STREAM;
+
+ if (*s == '[') {
+ /* IPv6 in [x:.....:z]:p notation */
+
+ if (!socket_ipv6_is_supported()) {
+ log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
+ return -EAFNOSUPPORT;
+ }
+
+ if (!(e = strchr(s+1, ']')))
+ return -EINVAL;
+
+ if (!(n = strndup(s+1, e-s-1)))
+ return -ENOMEM;
+
+ errno = 0;
+ if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) {
+ free(n);
+ return errno != 0 ? -errno : -EINVAL;
+ }
+
+ free(n);
+
+ e++;
+ if (*e != ':')
+ return -EINVAL;
+
+ e++;
+ if ((r = safe_atou(e, &u)) < 0)
+ return r;
+
+ if (u <= 0 || u > 0xFFFF)
+ return -EINVAL;
+
+ a->sockaddr.in6.sin6_family = AF_INET6;
+ a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+ a->size = sizeof(struct sockaddr_in6);
+
+ } else if (*s == '/') {
+ /* AF_UNIX socket */
+
+ size_t l;
+
+ l = strlen(s);
+ if (l >= sizeof(a->sockaddr.un.sun_path))
+ return -EINVAL;
+
+ a->sockaddr.un.sun_family = AF_UNIX;
+ memcpy(a->sockaddr.un.sun_path, s, l);
+ a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
+
+ } else if (*s == '@') {
+ /* Abstract AF_UNIX socket */
+ size_t l;
+
+ l = strlen(s+1);
+ if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
+ return -EINVAL;
+
+ a->sockaddr.un.sun_family = AF_UNIX;
+ memcpy(a->sockaddr.un.sun_path+1, s+1, l);
+ a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
+
+ } else {
+
+ if ((e = strchr(s, ':'))) {
+
+ if ((r = safe_atou(e+1, &u)) < 0)
+ return r;
+
+ if (u <= 0 || u > 0xFFFF)
+ return -EINVAL;
+
+ if (!(n = strndup(s, e-s)))
+ return -ENOMEM;
+
+ /* IPv4 in w.x.y.z:p notation? */
+ if ((r = inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr)) < 0) {
+ free(n);
+ return -errno;
+ }
+
+ if (r > 0) {
+ /* Gotcha, it's a traditional IPv4 address */
+ free(n);
+
+ a->sockaddr.in4.sin_family = AF_INET;
+ a->sockaddr.in4.sin_port = htons((uint16_t) u);
+ a->size = sizeof(struct sockaddr_in);
+ } else {
+ unsigned idx;
+
+ if (strlen(n) > IF_NAMESIZE-1) {
+ free(n);
+ return -EINVAL;
+ }
+
+ /* Uh, our last resort, an interface name */
+ idx = if_nametoindex(n);
+ free(n);
+
+ if (idx == 0)
+ return -EINVAL;
+
+ if (!socket_ipv6_is_supported()) {
+ log_warning("Binding to interface is not available since kernel does not support IPv6.");
+ return -EAFNOSUPPORT;
+ }
+
+ a->sockaddr.in6.sin6_family = AF_INET6;
+ a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+ a->sockaddr.in6.sin6_scope_id = idx;
+ a->sockaddr.in6.sin6_addr = in6addr_any;
+ a->size = sizeof(struct sockaddr_in6);
+ }
+ } else {
+
+ /* Just a port */
+ if ((r = safe_atou(s, &u)) < 0)
+ return r;
+
+ if (u <= 0 || u > 0xFFFF)
+ return -EINVAL;
+
+ if (socket_ipv6_is_supported()) {
+ a->sockaddr.in6.sin6_family = AF_INET6;
+ a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+ a->sockaddr.in6.sin6_addr = in6addr_any;
+ a->size = sizeof(struct sockaddr_in6);
+ } else {
+ a->sockaddr.in4.sin_family = AF_INET;
+ a->sockaddr.in4.sin_port = htons((uint16_t) u);
+ a->sockaddr.in4.sin_addr.s_addr = INADDR_ANY;
+ a->size = sizeof(struct sockaddr_in);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int socket_address_parse_netlink(SocketAddress *a, const char *s) {
+ int family;
+ unsigned group = 0;
+ char* sfamily = NULL;
+ assert(a);
+ assert(s);
+
+ zero(*a);
+ a->type = SOCK_RAW;
+
+ errno = 0;
+ if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
+ return errno ? -errno : -EINVAL;
+
+ if ((family = netlink_family_from_string(sfamily)) < 0)
+ if (safe_atoi(sfamily, &family) < 0) {
+ free(sfamily);
+ return -EINVAL;
+ }
+
+ free(sfamily);
+
+ a->sockaddr.nl.nl_family = AF_NETLINK;
+ a->sockaddr.nl.nl_groups = group;
+
+ a->type = SOCK_RAW;
+ a->size = sizeof(struct sockaddr_nl);
+ a->protocol = family;
+
+ return 0;
+}
+
+int socket_address_verify(const SocketAddress *a) {
+ assert(a);
+
+ switch (socket_address_family(a)) {
+
+ case AF_INET:
+ if (a->size != sizeof(struct sockaddr_in))
+ return -EINVAL;
+
+ if (a->sockaddr.in4.sin_port == 0)
+ return -EINVAL;
+
+ if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+ return -EINVAL;
+
+ return 0;
+
+ case AF_INET6:
+ if (a->size != sizeof(struct sockaddr_in6))
+ return -EINVAL;
+
+ if (a->sockaddr.in6.sin6_port == 0)
+ return -EINVAL;
+
+ if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+ return -EINVAL;
+
+ return 0;
+
+ case AF_UNIX:
+ if (a->size < offsetof(struct sockaddr_un, sun_path))
+ return -EINVAL;
+
+ if (a->size > offsetof(struct sockaddr_un, sun_path)) {
+
+ if (a->sockaddr.un.sun_path[0] != 0) {
+ char *e;
+
+ /* path */
+ if (!(e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path))))
+ return -EINVAL;
+
+ if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
+ return -EINVAL;
+ }
+ }
+
+ if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
+ return -EINVAL;
+
+ return 0;
+
+ case AF_NETLINK:
+
+ if (a->size != sizeof(struct sockaddr_nl))
+ return -EINVAL;
+
+ if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
+ return -EINVAL;
+
+ return 0;
+
+ default:
+ return -EAFNOSUPPORT;
+ }
+}
+
+int socket_address_print(const SocketAddress *a, char **p) {
+ int r;
+ assert(a);
+ assert(p);
+
+ if ((r = socket_address_verify(a)) < 0)
+ return r;
+
+ switch (socket_address_family(a)) {
+
+ case AF_INET: {
+ char *ret;
+
+ if (!(ret = new(char, INET_ADDRSTRLEN+1+5+1)))
+ return -ENOMEM;
+
+ if (!inet_ntop(AF_INET, &a->sockaddr.in4.sin_addr, ret, INET_ADDRSTRLEN)) {
+ free(ret);
+ return -errno;
+ }
+
+ sprintf(strchr(ret, 0), ":%u", ntohs(a->sockaddr.in4.sin_port));
+ *p = ret;
+ return 0;
+ }
+
+ case AF_INET6: {
+ char *ret;
+
+ if (!(ret = new(char, 1+INET6_ADDRSTRLEN+2+5+1)))
+ return -ENOMEM;
+
+ ret[0] = '[';
+ if (!inet_ntop(AF_INET6, &a->sockaddr.in6.sin6_addr, ret+1, INET6_ADDRSTRLEN)) {
+ free(ret);
+ return -errno;
+ }
+
+ sprintf(strchr(ret, 0), "]:%u", ntohs(a->sockaddr.in6.sin6_port));
+ *p = ret;
+ return 0;
+ }
+
+ case AF_UNIX: {
+ char *ret;
+
+ if (a->size <= offsetof(struct sockaddr_un, sun_path)) {
+
+ if (!(ret = strdup("<unnamed>")))
+ return -ENOMEM;
+
+ } else if (a->sockaddr.un.sun_path[0] == 0) {
+ /* abstract */
+
+ /* FIXME: We assume we can print the
+ * socket path here and that it hasn't
+ * more than one NUL byte. That is
+ * actually an invalid assumption */
+
+ if (!(ret = new(char, sizeof(a->sockaddr.un.sun_path)+1)))
+ return -ENOMEM;
+
+ ret[0] = '@';
+ memcpy(ret+1, a->sockaddr.un.sun_path+1, sizeof(a->sockaddr.un.sun_path)-1);
+ ret[sizeof(a->sockaddr.un.sun_path)] = 0;
+
+ } else {
+
+ if (!(ret = strdup(a->sockaddr.un.sun_path)))
+ return -ENOMEM;
+ }
+
+ *p = ret;
+ return 0;
+ }
+
+ case AF_NETLINK: {
+ const char *sfamily;
+
+ if ((sfamily = netlink_family_to_string(a->protocol)))
+ r = asprintf(p, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
+ else
+ r = asprintf(p, "%i %u", a->protocol, a->sockaddr.nl.nl_groups);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ return 0;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+int socket_address_listen(
+ const SocketAddress *a,
+ int backlog,
+ SocketAddressBindIPv6Only only,
+ const char *bind_to_device,
+ bool free_bind,
+ bool transparent,
+ mode_t directory_mode,
+ mode_t socket_mode,
+ const char *label,
+ int *ret) {
+
+ int r, fd, one;
+ assert(a);
+ assert(ret);
+
+ if ((r = socket_address_verify(a)) < 0)
+ return r;
+
+ if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
+ return -EAFNOSUPPORT;
+
+ r = label_socket_set(label);
+ if (r < 0)
+ return r;
+
+ fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, a->protocol);
+ r = fd < 0 ? -errno : 0;
+
+ label_socket_clear();
+
+ if (r < 0)
+ return r;
+
+ if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
+ int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
+ goto fail;
+ }
+
+ if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) {
+ if (bind_to_device)
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
+ goto fail;
+
+ if (free_bind) {
+ one = 1;
+ if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
+ log_warning("IP_FREEBIND failed: %m");
+ }
+
+ if (transparent) {
+ one = 1;
+ if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
+ log_warning("IP_TRANSPARENT failed: %m");
+ }
+ }
+
+ one = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
+ goto fail;
+
+ if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
+ mode_t old_mask;
+
+ /* Create parents */
+ mkdir_parents(a->sockaddr.un.sun_path, directory_mode);
+
+ /* Enforce the right access mode for the socket*/
+ old_mask = umask(~ socket_mode);
+
+ /* Include the original umask in our mask */
+ umask(~socket_mode | old_mask);
+
+ r = label_bind(fd, &a->sockaddr.sa, a->size);
+
+ if (r < 0 && errno == EADDRINUSE) {
+ /* Unlink and try again */
+ unlink(a->sockaddr.un.sun_path);
+ r = bind(fd, &a->sockaddr.sa, a->size);
+ }
+
+ umask(old_mask);
+ } else
+ r = bind(fd, &a->sockaddr.sa, a->size);
+
+ if (r < 0)
+ goto fail;
+
+ if (socket_address_can_accept(a))
+ if (listen(fd, backlog) < 0)
+ goto fail;
+
+ *ret = fd;
+ return 0;
+
+fail:
+ r = -errno;
+ close_nointr_nofail(fd);
+ return r;
+}
+
+bool socket_address_can_accept(const SocketAddress *a) {
+ assert(a);
+
+ return
+ a->type == SOCK_STREAM ||
+ a->type == SOCK_SEQPACKET;
+}
+
+bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
+ assert(a);
+ assert(b);
+
+ /* Invalid addresses are unequal to all */
+ if (socket_address_verify(a) < 0 ||
+ socket_address_verify(b) < 0)
+ return false;
+
+ if (a->type != b->type)
+ return false;
+
+ if (a->size != b->size)
+ return false;
+
+ if (socket_address_family(a) != socket_address_family(b))
+ return false;
+
+ switch (socket_address_family(a)) {
+
+ case AF_INET:
+ if (a->sockaddr.in4.sin_addr.s_addr != b->sockaddr.in4.sin_addr.s_addr)
+ return false;
+
+ if (a->sockaddr.in4.sin_port != b->sockaddr.in4.sin_port)
+ return false;
+
+ break;
+
+ case AF_INET6:
+ if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
+ return false;
+
+ if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
+ return false;
+
+ break;
+
+ case AF_UNIX:
+
+ if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
+ return false;
+
+ if (a->sockaddr.un.sun_path[0]) {
+ if (strncmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
+ return false;
+ } else {
+ if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
+ return false;
+ }
+
+ break;
+
+ case AF_NETLINK:
+
+ if (a->protocol != b->protocol)
+ return false;
+
+ if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
+ return false;
+
+ break;
+
+ default:
+ /* Cannot compare, so we assume the addresses are different */
+ return false;
+ }
+
+ return true;
+}
+
+bool socket_address_is(const SocketAddress *a, const char *s, int type) {
+ struct SocketAddress b;
+
+ assert(a);
+ assert(s);
+
+ if (socket_address_parse(&b, s) < 0)
+ return false;
+
+ b.type = type;
+
+ return socket_address_equal(a, &b);
+}
+
+bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
+ struct SocketAddress b;
+
+ assert(a);
+ assert(s);
+
+ if (socket_address_parse_netlink(&b, s) < 0)
+ return false;
+
+ return socket_address_equal(a, &b);
+}
+
+bool socket_address_needs_mount(const SocketAddress *a, const char *prefix) {
+ assert(a);
+
+ if (socket_address_family(a) != AF_UNIX)
+ return false;
+
+ if (a->sockaddr.un.sun_path[0] == 0)
+ return false;
+
+ return path_startswith(a->sockaddr.un.sun_path, prefix);
+}
+
+bool socket_ipv6_is_supported(void) {
+ char *l = 0;
+ bool enabled;
+
+ if (access("/sys/module/ipv6", F_OK) != 0)
+ return 0;
+
+ /* If we can't check "disable" parameter, assume enabled */
+ if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
+ return 1;
+
+ /* If module was loaded with disable=1 no IPv6 available */
+ enabled = l[0] == '0';
+ free(l);
+
+ return enabled;
+}
+
+static const char* const netlink_family_table[] = {
+ [NETLINK_ROUTE] = "route",
+ [NETLINK_FIREWALL] = "firewall",
+ [NETLINK_INET_DIAG] = "inet-diag",
+ [NETLINK_NFLOG] = "nflog",
+ [NETLINK_XFRM] = "xfrm",
+ [NETLINK_SELINUX] = "selinux",
+ [NETLINK_ISCSI] = "iscsi",
+ [NETLINK_AUDIT] = "audit",
+ [NETLINK_FIB_LOOKUP] = "fib-lookup",
+ [NETLINK_CONNECTOR] = "connector",
+ [NETLINK_NETFILTER] = "netfilter",
+ [NETLINK_IP6_FW] = "ip6-fw",
+ [NETLINK_DNRTMSG] = "dnrtmsg",
+ [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
+ [NETLINK_GENERIC] = "generic",
+ [NETLINK_SCSITRANSPORT] = "scsitransport",
+ [NETLINK_ECRYPTFS] = "ecryptfs"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(netlink_family, int);
+
+static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
+ [SOCKET_ADDRESS_DEFAULT] = "default",
+ [SOCKET_ADDRESS_BOTH] = "both",
+ [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
diff --git a/src/shared/socket-util.h b/src/shared/socket-util.h
new file mode 100644
index 0000000..8ccbd37
--- /dev/null
+++ b/src/shared/socket-util.h
@@ -0,0 +1,102 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosocketutilhfoo
+#define foosocketutilhfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <net/if.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+
+#include "macro.h"
+#include "util.h"
+
+union sockaddr_union {
+ struct sockaddr sa;
+ struct sockaddr_in in4;
+ struct sockaddr_in6 in6;
+ struct sockaddr_un un;
+ struct sockaddr_nl nl;
+ struct sockaddr_storage storage;
+};
+
+typedef struct SocketAddress {
+ union sockaddr_union sockaddr;
+
+ /* We store the size here explicitly due to the weird
+ * sockaddr_un semantics for abstract sockets */
+ socklen_t size;
+
+ /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */
+ int type;
+
+ /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */
+ int protocol;
+} SocketAddress;
+
+typedef enum SocketAddressBindIPv6Only {
+ SOCKET_ADDRESS_DEFAULT,
+ SOCKET_ADDRESS_BOTH,
+ SOCKET_ADDRESS_IPV6_ONLY,
+ _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX,
+ _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1
+} SocketAddressBindIPv6Only;
+
+#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
+
+int socket_address_parse(SocketAddress *a, const char *s);
+int socket_address_parse_netlink(SocketAddress *a, const char *s);
+int socket_address_print(const SocketAddress *a, char **p);
+int socket_address_verify(const SocketAddress *a);
+
+bool socket_address_can_accept(const SocketAddress *a);
+
+int socket_address_listen(
+ const SocketAddress *a,
+ int backlog,
+ SocketAddressBindIPv6Only only,
+ const char *bind_to_device,
+ bool free_bind,
+ bool transparent,
+ mode_t directory_mode,
+ mode_t socket_mode,
+ const char *label,
+ int *ret);
+
+bool socket_address_is(const SocketAddress *a, const char *s, int type);
+bool socket_address_is_netlink(const SocketAddress *a, const char *s);
+
+bool socket_address_equal(const SocketAddress *a, const SocketAddress *b);
+
+bool socket_address_needs_mount(const SocketAddress *a, const char *prefix);
+
+const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b);
+SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s);
+
+const char* netlink_family_to_string(int b);
+int netlink_family_from_string(const char *s);
+
+bool socket_ipv6_is_supported(void);
+
+#endif
diff --git a/src/shared/strv.c b/src/shared/strv.c
new file mode 100644
index 0000000..bb309d9
--- /dev/null
+++ b/src/shared/strv.c
@@ -0,0 +1,690 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include "util.h"
+#include "strv.h"
+
+char *strv_find(char **l, const char *name) {
+ char **i;
+
+ assert(name);
+
+ STRV_FOREACH(i, l)
+ if (streq(*i, name))
+ return *i;
+
+ return NULL;
+}
+
+char *strv_find_prefix(char **l, const char *name) {
+ char **i;
+
+ assert(name);
+
+ STRV_FOREACH(i, l)
+ if (startswith(*i, name))
+ return *i;
+
+ return NULL;
+}
+
+void strv_free(char **l) {
+ char **k;
+
+ if (!l)
+ return;
+
+ for (k = l; *k; k++)
+ free(*k);
+
+ free(l);
+}
+
+char **strv_copy(char **l) {
+ char **r, **k;
+
+ k = r = new(char*, strv_length(l)+1);
+ if (!k)
+ return NULL;
+
+ if (l)
+ for (; *l; k++, l++)
+ if (!(*k = strdup(*l)))
+ goto fail;
+
+ *k = NULL;
+ return r;
+
+fail:
+ for (k--; k >= r; k--)
+ free(*k);
+
+ free(r);
+
+ return NULL;
+}
+
+unsigned strv_length(char **l) {
+ unsigned n = 0;
+
+ if (!l)
+ return 0;
+
+ for (; *l; l++)
+ n++;
+
+ return n;
+}
+
+char **strv_new_ap(const char *x, va_list ap) {
+ const char *s;
+ char **a;
+ unsigned n = 0, i = 0;
+ va_list aq;
+
+ if (x) {
+ n = 1;
+
+ va_copy(aq, ap);
+ while (va_arg(aq, const char*))
+ n++;
+ va_end(aq);
+ }
+
+ if (!(a = new(char*, n+1)))
+ return NULL;
+
+ if (x) {
+ if (!(a[i] = strdup(x))) {
+ free(a);
+ return NULL;
+ }
+
+ i++;
+
+ while ((s = va_arg(ap, const char*))) {
+ if (!(a[i] = strdup(s)))
+ goto fail;
+
+ i++;
+ }
+ }
+
+ a[i] = NULL;
+
+ return a;
+
+fail:
+
+ for (; i > 0; i--)
+ if (a[i-1])
+ free(a[i-1]);
+
+ free(a);
+
+ return NULL;
+}
+
+char **strv_new(const char *x, ...) {
+ char **r;
+ va_list ap;
+
+ va_start(ap, x);
+ r = strv_new_ap(x, ap);
+ va_end(ap);
+
+ return r;
+}
+
+char **strv_merge(char **a, char **b) {
+ char **r, **k;
+
+ if (!a)
+ return strv_copy(b);
+
+ if (!b)
+ return strv_copy(a);
+
+ if (!(r = new(char*, strv_length(a)+strv_length(b)+1)))
+ return NULL;
+
+ for (k = r; *a; k++, a++)
+ if (!(*k = strdup(*a)))
+ goto fail;
+ for (; *b; k++, b++)
+ if (!(*k = strdup(*b)))
+ goto fail;
+
+ *k = NULL;
+ return r;
+
+fail:
+ for (k--; k >= r; k--)
+ free(*k);
+
+ free(r);
+
+ return NULL;
+}
+
+char **strv_merge_concat(char **a, char **b, const char *suffix) {
+ char **r, **k;
+
+ /* Like strv_merge(), but appends suffix to all strings in b, before adding */
+
+ if (!b)
+ return strv_copy(a);
+
+ r = new(char*, strv_length(a) + strv_length(b) + 1);
+ if (!r)
+ return NULL;
+
+ k = r;
+ if (a)
+ for (; *a; k++, a++) {
+ *k = strdup(*a);
+ if (!*k)
+ goto fail;
+ }
+
+ for (; *b; k++, b++) {
+ *k = strappend(*b, suffix);
+ if (!*k)
+ goto fail;
+ }
+
+ *k = NULL;
+ return r;
+
+fail:
+ for (k--; k >= r; k--)
+ free(*k);
+
+ free(r);
+
+ return NULL;
+
+}
+
+char **strv_split(const char *s, const char *separator) {
+ char *state;
+ char *w;
+ size_t l;
+ unsigned n, i;
+ char **r;
+
+ assert(s);
+
+ n = 0;
+ FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
+ n++;
+
+ if (!(r = new(char*, n+1)))
+ return NULL;
+
+ i = 0;
+ FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
+ if (!(r[i++] = strndup(w, l))) {
+ strv_free(r);
+ return NULL;
+ }
+
+ r[i] = NULL;
+ return r;
+}
+
+char **strv_split_quoted(const char *s) {
+ char *state;
+ char *w;
+ size_t l;
+ unsigned n, i;
+ char **r;
+
+ assert(s);
+
+ n = 0;
+ FOREACH_WORD_QUOTED(w, l, s, state)
+ n++;
+
+ if (!(r = new(char*, n+1)))
+ return NULL;
+
+ i = 0;
+ FOREACH_WORD_QUOTED(w, l, s, state)
+ if (!(r[i++] = cunescape_length(w, l))) {
+ strv_free(r);
+ return NULL;
+ }
+
+ r[i] = NULL;
+ return r;
+}
+
+char *strv_join(char **l, const char *separator) {
+ char *r, *e;
+ char **s;
+ size_t n, k;
+
+ if (!separator)
+ separator = " ";
+
+ k = strlen(separator);
+
+ n = 0;
+ STRV_FOREACH(s, l) {
+ if (n != 0)
+ n += k;
+ n += strlen(*s);
+ }
+
+ if (!(r = new(char, n+1)))
+ return NULL;
+
+ e = r;
+ STRV_FOREACH(s, l) {
+ if (e != r)
+ e = stpcpy(e, separator);
+
+ e = stpcpy(e, *s);
+ }
+
+ *e = 0;
+
+ return r;
+}
+
+char **strv_append(char **l, const char *s) {
+ char **r, **k;
+
+ if (!l)
+ return strv_new(s, NULL);
+
+ if (!s)
+ return strv_copy(l);
+
+ r = new(char*, strv_length(l)+2);
+ if (!r)
+ return NULL;
+
+ for (k = r; *l; k++, l++)
+ if (!(*k = strdup(*l)))
+ goto fail;
+
+ if (!(*(k++) = strdup(s)))
+ goto fail;
+
+ *k = NULL;
+ return r;
+
+fail:
+ for (k--; k >= r; k--)
+ free(*k);
+
+ free(r);
+
+ return NULL;
+}
+
+char **strv_uniq(char **l) {
+ char **i;
+
+ /* Drops duplicate entries. The first identical string will be
+ * kept, the others dropped */
+
+ STRV_FOREACH(i, l)
+ strv_remove(i+1, *i);
+
+ return l;
+}
+
+char **strv_remove(char **l, const char *s) {
+ char **f, **t;
+
+ if (!l)
+ return NULL;
+
+ assert(s);
+
+ /* Drops every occurrence of s in the string list, edits
+ * in-place. */
+
+ for (f = t = l; *f; f++) {
+
+ if (streq(*f, s)) {
+ free(*f);
+ continue;
+ }
+
+ *(t++) = *f;
+ }
+
+ *t = NULL;
+ return l;
+}
+
+static int env_append(char **r, char ***k, char **a) {
+ assert(r);
+ assert(k);
+
+ if (!a)
+ return 0;
+
+ /* Add the entries of a to *k unless they already exist in *r
+ * in which case they are overridden instead. This assumes
+ * there is enough space in the r array. */
+
+ for (; *a; a++) {
+ char **j;
+ size_t n;
+
+ n = strcspn(*a, "=");
+
+ if ((*a)[n] == '=')
+ n++;
+
+ for (j = r; j < *k; j++)
+ if (strncmp(*j, *a, n) == 0)
+ break;
+
+ if (j >= *k)
+ (*k)++;
+ else
+ free(*j);
+
+ if (!(*j = strdup(*a)))
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+char **strv_env_merge(unsigned n_lists, ...) {
+ size_t n = 0;
+ char **l, **k, **r;
+ va_list ap;
+ unsigned i;
+
+ /* Merges an arbitrary number of environment sets */
+
+ va_start(ap, n_lists);
+ for (i = 0; i < n_lists; i++) {
+ l = va_arg(ap, char**);
+ n += strv_length(l);
+ }
+ va_end(ap);
+
+ if (!(r = new(char*, n+1)))
+ return NULL;
+
+ k = r;
+
+ va_start(ap, n_lists);
+ for (i = 0; i < n_lists; i++) {
+ l = va_arg(ap, char**);
+ if (env_append(r, &k, l) < 0)
+ goto fail;
+ }
+ va_end(ap);
+
+ *k = NULL;
+
+ return r;
+
+fail:
+ va_end(ap);
+
+ for (k--; k >= r; k--)
+ free(*k);
+
+ free(r);
+
+ return NULL;
+}
+
+static bool env_match(const char *t, const char *pattern) {
+ assert(t);
+ assert(pattern);
+
+ /* pattern a matches string a
+ * a matches a=
+ * a matches a=b
+ * a= matches a=
+ * a=b matches a=b
+ * a= does not match a
+ * a=b does not match a=
+ * a=b does not match a
+ * a=b does not match a=c */
+
+ if (streq(t, pattern))
+ return true;
+
+ if (!strchr(pattern, '=')) {
+ size_t l = strlen(pattern);
+
+ return strncmp(t, pattern, l) == 0 && t[l] == '=';
+ }
+
+ return false;
+}
+
+char **strv_env_delete(char **x, unsigned n_lists, ...) {
+ size_t n, i = 0;
+ char **k, **r;
+ va_list ap;
+
+ /* Deletes every entry from x that is mentioned in the other
+ * string lists */
+
+ n = strv_length(x);
+
+ r = new(char*, n+1);
+ if (!r)
+ return NULL;
+
+ STRV_FOREACH(k, x) {
+ unsigned v;
+
+ va_start(ap, n_lists);
+ for (v = 0; v < n_lists; v++) {
+ char **l, **j;
+
+ l = va_arg(ap, char**);
+ STRV_FOREACH(j, l)
+ if (env_match(*k, *j))
+ goto skip;
+ }
+ va_end(ap);
+
+ r[i] = strdup(*k);
+ if (!r[i]) {
+ strv_free(r);
+ return NULL;
+ }
+
+ i++;
+ continue;
+
+ skip:
+ va_end(ap);
+ }
+
+ r[i] = NULL;
+
+ assert(i <= n);
+
+ return r;
+}
+
+char **strv_env_unset(char **l, const char *p) {
+
+ char **f, **t;
+
+ if (!l)
+ return NULL;
+
+ assert(p);
+
+ /* Drops every occurrence of the env var setting p in the
+ * string list. edits in-place. */
+
+ for (f = t = l; *f; f++) {
+
+ if (env_match(*f, p)) {
+ free(*f);
+ continue;
+ }
+
+ *(t++) = *f;
+ }
+
+ *t = NULL;
+ return l;
+}
+
+char **strv_env_set(char **x, const char *p) {
+
+ char **k, **r;
+ char* m[2] = { (char*) p, NULL };
+
+ /* Overrides the env var setting of p, returns a new copy */
+
+ if (!(r = new(char*, strv_length(x)+2)))
+ return NULL;
+
+ k = r;
+ if (env_append(r, &k, x) < 0)
+ goto fail;
+
+ if (env_append(r, &k, m) < 0)
+ goto fail;
+
+ *k = NULL;
+
+ return r;
+
+fail:
+ for (k--; k >= r; k--)
+ free(*k);
+
+ free(r);
+
+ return NULL;
+
+}
+
+char *strv_env_get_with_length(char **l, const char *name, size_t k) {
+ char **i;
+
+ assert(name);
+
+ STRV_FOREACH(i, l)
+ if (strncmp(*i, name, k) == 0 &&
+ (*i)[k] == '=')
+ return *i + k + 1;
+
+ return NULL;
+}
+
+char *strv_env_get(char **l, const char *name) {
+ return strv_env_get_with_length(l, name, strlen(name));
+}
+
+char **strv_env_clean(char **l) {
+ char **r, **ret;
+
+ for (r = ret = l; *l; l++) {
+ const char *equal;
+
+ equal = strchr(*l, '=');
+
+ if (equal && equal[1] == 0) {
+ free(*l);
+ continue;
+ }
+
+ *(r++) = *l;
+ }
+
+ *r = NULL;
+
+ return ret;
+}
+
+char **strv_parse_nulstr(const char *s, size_t l) {
+ const char *p;
+ unsigned c = 0, i = 0;
+ char **v;
+
+ assert(s || l <= 0);
+
+ if (l <= 0)
+ return strv_new(NULL, NULL);
+
+ for (p = s; p < s + l; p++)
+ if (*p == 0)
+ c++;
+
+ if (s[l-1] != 0)
+ c++;
+
+ if (!(v = new0(char*, c+1)))
+ return NULL;
+
+ p = s;
+ while (p < s + l) {
+ const char *e;
+
+ e = memchr(p, 0, s + l - p);
+
+ if (!(v[i++] = strndup(p, e ? e - p : s + l - p))) {
+ strv_free(v);
+ return NULL;
+ }
+
+ if (!e)
+ break;
+
+ p = e + 1;
+ }
+
+ assert(i == c);
+
+ return v;
+}
+
+bool strv_overlap(char **a, char **b) {
+ char **i, **j;
+
+ STRV_FOREACH(i, a) {
+ STRV_FOREACH(j, b) {
+ if (streq(*i, *j))
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/src/shared/strv.h b/src/shared/strv.h
new file mode 100644
index 0000000..d038c9f
--- /dev/null
+++ b/src/shared/strv.h
@@ -0,0 +1,79 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foostrvhfoo
+#define foostrvhfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "macro.h"
+
+char *strv_find(char **l, const char *name);
+char *strv_find_prefix(char **l, const char *name);
+
+void strv_free(char **l);
+char **strv_copy(char **l) _malloc_;
+unsigned strv_length(char **l);
+
+char **strv_merge(char **a, char **b);
+char **strv_merge_concat(char **a, char **b, const char *suffix);
+char **strv_append(char **l, const char *s);
+
+char **strv_remove(char **l, const char *s);
+char **strv_uniq(char **l);
+
+#define strv_contains(l, s) (!!strv_find((l), (s)))
+
+char **strv_new(const char *x, ...) _sentinel_ _malloc_;
+char **strv_new_ap(const char *x, va_list ap) _malloc_;
+
+static inline bool strv_isempty(char **l) {
+ return !l || !*l;
+}
+
+char **strv_split(const char *s, const char *separator) _malloc_;
+char **strv_split_quoted(const char *s) _malloc_;
+
+char *strv_join(char **l, const char *separator) _malloc_;
+
+char **strv_env_merge(unsigned n_lists, ...);
+char **strv_env_delete(char **x, unsigned n_lists, ...);
+
+char **strv_env_set(char **x, const char *p);
+char **strv_env_unset(char **l, const char *p);
+
+char *strv_env_get_with_length(char **l, const char *name, size_t k);
+char *strv_env_get(char **x, const char *n);
+
+char **strv_env_clean(char **l);
+
+char **strv_parse_nulstr(const char *s, size_t l);
+
+bool strv_overlap(char **a, char **b);
+
+#define STRV_FOREACH(s, l) \
+ for ((s) = (l); (s) && *(s); (s)++)
+
+#define STRV_FOREACH_BACKWARDS(s, l) \
+ for (; (l) && ((s) >= (l)); (s)--)
+
+#endif
diff --git a/src/shared/utf8.c b/src/shared/utf8.c
new file mode 100644
index 0000000..11619dc
--- /dev/null
+++ b/src/shared/utf8.c
@@ -0,0 +1,214 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This file is based on the GLIB utf8 validation functions. The
+ * original license text follows. */
+
+/* gutf8.c - Operations on UTF-8 strings.
+ *
+ * Copyright (C) 1999 Tom Tromey
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "utf8.h"
+
+#define FILTER_CHAR '_'
+
+static inline bool is_unicode_valid(uint32_t ch) {
+
+ if (ch >= 0x110000) /* End of unicode space */
+ return false;
+ if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
+ return false;
+ if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
+ return false;
+ if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
+ return false;
+
+ return true;
+}
+
+static inline bool is_continuation_char(uint8_t ch) {
+ if ((ch & 0xc0) != 0x80) /* 10xxxxxx */
+ return false;
+ return true;
+}
+
+static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) {
+ *u_ch <<= 6;
+ *u_ch |= ch & 0x3f;
+}
+
+static char* utf8_validate(const char *str, char *output) {
+ uint32_t val = 0;
+ uint32_t min = 0;
+ const uint8_t *p, *last;
+ int size;
+ uint8_t *o;
+
+ assert(str);
+
+ o = (uint8_t*) output;
+ for (p = (const uint8_t*) str; *p; p++) {
+ if (*p < 128) {
+ if (o)
+ *o = *p;
+ } else {
+ last = p;
+
+ if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
+ size = 2;
+ min = 128;
+ val = (uint32_t) (*p & 0x1e);
+ goto ONE_REMAINING;
+ } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
+ size = 3;
+ min = (1 << 11);
+ val = (uint32_t) (*p & 0x0f);
+ goto TWO_REMAINING;
+ } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
+ size = 4;
+ min = (1 << 16);
+ val = (uint32_t) (*p & 0x07);
+ } else
+ goto error;
+
+ p++;
+ if (!is_continuation_char(*p))
+ goto error;
+ merge_continuation_char(&val, *p);
+
+ TWO_REMAINING:
+ p++;
+ if (!is_continuation_char(*p))
+ goto error;
+ merge_continuation_char(&val, *p);
+
+ ONE_REMAINING:
+ p++;
+ if (!is_continuation_char(*p))
+ goto error;
+ merge_continuation_char(&val, *p);
+
+ if (val < min)
+ goto error;
+
+ if (!is_unicode_valid(val))
+ goto error;
+
+ if (o) {
+ memcpy(o, last, (size_t) size);
+ o += size;
+ }
+
+ continue;
+
+ error:
+ if (o) {
+ *o = FILTER_CHAR;
+ p = last; /* We retry at the next character */
+ } else
+ goto failure;
+ }
+
+ if (o)
+ o++;
+ }
+
+ if (o) {
+ *o = '\0';
+ return output;
+ }
+
+ return (char*) str;
+
+failure:
+ return NULL;
+}
+
+char* utf8_is_valid (const char *str) {
+ return utf8_validate(str, NULL);
+}
+
+char* utf8_filter (const char *str) {
+ char *new_str;
+
+ assert(str);
+
+ new_str = malloc(strlen(str) + 1);
+ if (!new_str)
+ return NULL;
+
+ return utf8_validate(str, new_str);
+}
+
+char *ascii_is_valid(const char *str) {
+ const char *p;
+
+ assert(str);
+
+ for (p = str; *p; p++)
+ if ((unsigned char) *p >= 128)
+ return NULL;
+
+ return (char*) str;
+}
+
+char *ascii_filter(const char *str) {
+ char *r, *s, *d;
+ size_t l;
+
+ assert(str);
+
+ l = strlen(str);
+ r = malloc(l + 1);
+ if (!r)
+ return NULL;
+
+ for (s = r, d = r; *s; s++)
+ if ((unsigned char) *s < 128)
+ *(d++) = *s;
+
+ *d = 0;
+
+ return r;
+}
diff --git a/src/shared/utf8.h b/src/shared/utf8.h
new file mode 100644
index 0000000..9a72bec
--- /dev/null
+++ b/src/shared/utf8.h
@@ -0,0 +1,33 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooutf8hfoo
+#define fooutf8hfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "macro.h"
+
+char *utf8_is_valid(const char *s) _pure_;
+char *ascii_is_valid(const char *s) _pure_;
+
+char *utf8_filter(const char *s);
+char *ascii_filter(const char *s);
+
+#endif
diff --git a/src/shared/util.c b/src/shared/util.c
new file mode 100644
index 0000000..563853f
--- /dev/null
+++ b/src/shared/util.c
@@ -0,0 +1,6123 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <linux/sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <linux/vt.h>
+#include <linux/tiocl.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <sys/inotify.h>
+#include <sys/poll.h>
+#include <libgen.h>
+#include <ctype.h>
+#include <sys/prctl.h>
+#include <sys/utsname.h>
+#include <pwd.h>
+#include <netinet/ip.h>
+#include <linux/kd.h>
+#include <dlfcn.h>
+#include <sys/wait.h>
+#include <sys/capability.h>
+#include <sys/time.h>
+#include <linux/rtc.h>
+#include <glob.h>
+#include <grp.h>
+#include <sys/mman.h>
+
+#include "macro.h"
+#include "util.h"
+#include "ioprio.h"
+#include "missing.h"
+#include "log.h"
+#include "strv.h"
+#include "label.h"
+#include "exit-status.h"
+#include "hashmap.h"
+
+int saved_argc = 0;
+char **saved_argv = NULL;
+
+size_t page_size(void) {
+ static __thread size_t pgsz = 0;
+ long r;
+
+ if (_likely_(pgsz > 0))
+ return pgsz;
+
+ assert_se((r = sysconf(_SC_PAGESIZE)) > 0);
+
+ pgsz = (size_t) r;
+
+ return pgsz;
+}
+
+bool streq_ptr(const char *a, const char *b) {
+
+ /* Like streq(), but tries to make sense of NULL pointers */
+
+ if (a && b)
+ return streq(a, b);
+
+ if (!a && !b)
+ return true;
+
+ return false;
+}
+
+usec_t now(clockid_t clock_id) {
+ struct timespec ts;
+
+ assert_se(clock_gettime(clock_id, &ts) == 0);
+
+ return timespec_load(&ts);
+}
+
+dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
+ assert(ts);
+
+ ts->realtime = now(CLOCK_REALTIME);
+ ts->monotonic = now(CLOCK_MONOTONIC);
+
+ return ts;
+}
+
+dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
+ int64_t delta;
+ assert(ts);
+
+ ts->realtime = u;
+
+ if (u == 0)
+ ts->monotonic = 0;
+ else {
+ delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
+
+ ts->monotonic = now(CLOCK_MONOTONIC);
+
+ if ((int64_t) ts->monotonic > delta)
+ ts->monotonic -= delta;
+ else
+ ts->monotonic = 0;
+ }
+
+ return ts;
+}
+
+usec_t timespec_load(const struct timespec *ts) {
+ assert(ts);
+
+ return
+ (usec_t) ts->tv_sec * USEC_PER_SEC +
+ (usec_t) ts->tv_nsec / NSEC_PER_USEC;
+}
+
+struct timespec *timespec_store(struct timespec *ts, usec_t u) {
+ assert(ts);
+
+ ts->tv_sec = (time_t) (u / USEC_PER_SEC);
+ ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
+
+ return ts;
+}
+
+usec_t timeval_load(const struct timeval *tv) {
+ assert(tv);
+
+ return
+ (usec_t) tv->tv_sec * USEC_PER_SEC +
+ (usec_t) tv->tv_usec;
+}
+
+struct timeval *timeval_store(struct timeval *tv, usec_t u) {
+ assert(tv);
+
+ tv->tv_sec = (time_t) (u / USEC_PER_SEC);
+ tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
+
+ return tv;
+}
+
+bool endswith(const char *s, const char *postfix) {
+ size_t sl, pl;
+
+ assert(s);
+ assert(postfix);
+
+ sl = strlen(s);
+ pl = strlen(postfix);
+
+ if (pl == 0)
+ return true;
+
+ if (sl < pl)
+ return false;
+
+ return memcmp(s + sl - pl, postfix, pl) == 0;
+}
+
+bool startswith(const char *s, const char *prefix) {
+ size_t sl, pl;
+
+ assert(s);
+ assert(prefix);
+
+ sl = strlen(s);
+ pl = strlen(prefix);
+
+ if (pl == 0)
+ return true;
+
+ if (sl < pl)
+ return false;
+
+ return memcmp(s, prefix, pl) == 0;
+}
+
+bool startswith_no_case(const char *s, const char *prefix) {
+ size_t sl, pl;
+ unsigned i;
+
+ assert(s);
+ assert(prefix);
+
+ sl = strlen(s);
+ pl = strlen(prefix);
+
+ if (pl == 0)
+ return true;
+
+ if (sl < pl)
+ return false;
+
+ for(i = 0; i < pl; ++i) {
+ if (tolower(s[i]) != tolower(prefix[i]))
+ return false;
+ }
+
+ return true;
+}
+
+bool first_word(const char *s, const char *word) {
+ size_t sl, wl;
+
+ assert(s);
+ assert(word);
+
+ sl = strlen(s);
+ wl = strlen(word);
+
+ if (sl < wl)
+ return false;
+
+ if (wl == 0)
+ return true;
+
+ if (memcmp(s, word, wl) != 0)
+ return false;
+
+ return s[wl] == 0 ||
+ strchr(WHITESPACE, s[wl]);
+}
+
+int close_nointr(int fd) {
+ assert(fd >= 0);
+
+ for (;;) {
+ int r;
+
+ r = close(fd);
+ if (r >= 0)
+ return r;
+
+ if (errno != EINTR)
+ return -errno;
+ }
+}
+
+void close_nointr_nofail(int fd) {
+ int saved_errno = errno;
+
+ /* like close_nointr() but cannot fail, and guarantees errno
+ * is unchanged */
+
+ assert_se(close_nointr(fd) == 0);
+
+ errno = saved_errno;
+}
+
+void close_many(const int fds[], unsigned n_fd) {
+ unsigned i;
+
+ for (i = 0; i < n_fd; i++)
+ close_nointr_nofail(fds[i]);
+}
+
+int parse_boolean(const char *v) {
+ assert(v);
+
+ if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
+ return 1;
+ else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
+ return 0;
+
+ return -EINVAL;
+}
+
+int parse_pid(const char *s, pid_t* ret_pid) {
+ unsigned long ul = 0;
+ pid_t pid;
+ int r;
+
+ assert(s);
+ assert(ret_pid);
+
+ if ((r = safe_atolu(s, &ul)) < 0)
+ return r;
+
+ pid = (pid_t) ul;
+
+ if ((unsigned long) pid != ul)
+ return -ERANGE;
+
+ if (pid <= 0)
+ return -ERANGE;
+
+ *ret_pid = pid;
+ return 0;
+}
+
+int parse_uid(const char *s, uid_t* ret_uid) {
+ unsigned long ul = 0;
+ uid_t uid;
+ int r;
+
+ assert(s);
+ assert(ret_uid);
+
+ if ((r = safe_atolu(s, &ul)) < 0)
+ return r;
+
+ uid = (uid_t) ul;
+
+ if ((unsigned long) uid != ul)
+ return -ERANGE;
+
+ *ret_uid = uid;
+ return 0;
+}
+
+int safe_atou(const char *s, unsigned *ret_u) {
+ char *x = NULL;
+ unsigned long l;
+
+ assert(s);
+ assert(ret_u);
+
+ errno = 0;
+ l = strtoul(s, &x, 0);
+
+ if (!x || *x || errno)
+ return errno ? -errno : -EINVAL;
+
+ if ((unsigned long) (unsigned) l != l)
+ return -ERANGE;
+
+ *ret_u = (unsigned) l;
+ return 0;
+}
+
+int safe_atoi(const char *s, int *ret_i) {
+ char *x = NULL;
+ long l;
+
+ assert(s);
+ assert(ret_i);
+
+ errno = 0;
+ l = strtol(s, &x, 0);
+
+ if (!x || *x || errno)
+ return errno ? -errno : -EINVAL;
+
+ if ((long) (int) l != l)
+ return -ERANGE;
+
+ *ret_i = (int) l;
+ return 0;
+}
+
+int safe_atollu(const char *s, long long unsigned *ret_llu) {
+ char *x = NULL;
+ unsigned long long l;
+
+ assert(s);
+ assert(ret_llu);
+
+ errno = 0;
+ l = strtoull(s, &x, 0);
+
+ if (!x || *x || errno)
+ return errno ? -errno : -EINVAL;
+
+ *ret_llu = l;
+ return 0;
+}
+
+int safe_atolli(const char *s, long long int *ret_lli) {
+ char *x = NULL;
+ long long l;
+
+ assert(s);
+ assert(ret_lli);
+
+ errno = 0;
+ l = strtoll(s, &x, 0);
+
+ if (!x || *x || errno)
+ return errno ? -errno : -EINVAL;
+
+ *ret_lli = l;
+ return 0;
+}
+
+/* Split a string into words. */
+char *split(const char *c, size_t *l, const char *separator, char **state) {
+ char *current;
+
+ current = *state ? *state : (char*) c;
+
+ if (!*current || *c == 0)
+ return NULL;
+
+ current += strspn(current, separator);
+ *l = strcspn(current, separator);
+ *state = current+*l;
+
+ return (char*) current;
+}
+
+/* Split a string into words, but consider strings enclosed in '' and
+ * "" as words even if they include spaces. */
+char *split_quoted(const char *c, size_t *l, char **state) {
+ char *current, *e;
+ bool escaped = false;
+
+ current = *state ? *state : (char*) c;
+
+ if (!*current || *c == 0)
+ return NULL;
+
+ current += strspn(current, WHITESPACE);
+
+ if (*current == '\'') {
+ current ++;
+
+ for (e = current; *e; e++) {
+ if (escaped)
+ escaped = false;
+ else if (*e == '\\')
+ escaped = true;
+ else if (*e == '\'')
+ break;
+ }
+
+ *l = e-current;
+ *state = *e == 0 ? e : e+1;
+ } else if (*current == '\"') {
+ current ++;
+
+ for (e = current; *e; e++) {
+ if (escaped)
+ escaped = false;
+ else if (*e == '\\')
+ escaped = true;
+ else if (*e == '\"')
+ break;
+ }
+
+ *l = e-current;
+ *state = *e == 0 ? e : e+1;
+ } else {
+ for (e = current; *e; e++) {
+ if (escaped)
+ escaped = false;
+ else if (*e == '\\')
+ escaped = true;
+ else if (strchr(WHITESPACE, *e))
+ break;
+ }
+ *l = e-current;
+ *state = e;
+ }
+
+ return (char*) current;
+}
+
+char **split_path_and_make_absolute(const char *p) {
+ char **l;
+ assert(p);
+
+ if (!(l = strv_split(p, ":")))
+ return NULL;
+
+ if (!strv_path_make_absolute_cwd(l)) {
+ strv_free(l);
+ return NULL;
+ }
+
+ return l;
+}
+
+int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
+ int r;
+ FILE *f;
+ char fn[PATH_MAX], line[LINE_MAX], *p;
+ long unsigned ppid;
+
+ assert(pid > 0);
+ assert(_ppid);
+
+ assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
+ char_array_0(fn);
+
+ if (!(f = fopen(fn, "re")))
+ return -errno;
+
+ if (!(fgets(line, sizeof(line), f))) {
+ r = feof(f) ? -EIO : -errno;
+ fclose(f);
+ return r;
+ }
+
+ fclose(f);
+
+ /* Let's skip the pid and comm fields. The latter is enclosed
+ * in () but does not escape any () in its value, so let's
+ * skip over it manually */
+
+ if (!(p = strrchr(line, ')')))
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " "
+ "%*c " /* state */
+ "%lu ", /* ppid */
+ &ppid) != 1)
+ return -EIO;
+
+ if ((long unsigned) (pid_t) ppid != ppid)
+ return -ERANGE;
+
+ *_ppid = (pid_t) ppid;
+
+ return 0;
+}
+
+int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
+ int r;
+ FILE *f;
+ char fn[PATH_MAX], line[LINE_MAX], *p;
+
+ assert(pid > 0);
+ assert(st);
+
+ assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
+ char_array_0(fn);
+
+ if (!(f = fopen(fn, "re")))
+ return -errno;
+
+ if (!(fgets(line, sizeof(line), f))) {
+ r = feof(f) ? -EIO : -errno;
+ fclose(f);
+ return r;
+ }
+
+ fclose(f);
+
+ /* Let's skip the pid and comm fields. The latter is enclosed
+ * in () but does not escape any () in its value, so let's
+ * skip over it manually */
+
+ if (!(p = strrchr(line, ')')))
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " "
+ "%*c " /* state */
+ "%*d " /* ppid */
+ "%*d " /* pgrp */
+ "%*d " /* session */
+ "%*d " /* tty_nr */
+ "%*d " /* tpgid */
+ "%*u " /* flags */
+ "%*u " /* minflt */
+ "%*u " /* cminflt */
+ "%*u " /* majflt */
+ "%*u " /* cmajflt */
+ "%*u " /* utime */
+ "%*u " /* stime */
+ "%*d " /* cutime */
+ "%*d " /* cstime */
+ "%*d " /* priority */
+ "%*d " /* nice */
+ "%*d " /* num_threads */
+ "%*d " /* itrealvalue */
+ "%llu " /* starttime */,
+ st) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+int write_one_line_file(const char *fn, const char *line) {
+ FILE *f;
+ int r;
+
+ assert(fn);
+ assert(line);
+
+ if (!(f = fopen(fn, "we")))
+ return -errno;
+
+ errno = 0;
+ if (fputs(line, f) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (!endswith(line, "\n"))
+ fputc('\n', f);
+
+ fflush(f);
+
+ if (ferror(f)) {
+ if (errno != 0)
+ r = -errno;
+ else
+ r = -EIO;
+ } else
+ r = 0;
+
+finish:
+ fclose(f);
+ return r;
+}
+
+int fchmod_umask(int fd, mode_t m) {
+ mode_t u;
+ int r;
+
+ u = umask(0777);
+ r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
+ umask(u);
+
+ return r;
+}
+
+int write_one_line_file_atomic(const char *fn, const char *line) {
+ FILE *f;
+ int r;
+ char *p;
+
+ assert(fn);
+ assert(line);
+
+ r = fopen_temporary(fn, &f, &p);
+ if (r < 0)
+ return r;
+
+ fchmod_umask(fileno(f), 0644);
+
+ errno = 0;
+ if (fputs(line, f) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (!endswith(line, "\n"))
+ fputc('\n', f);
+
+ fflush(f);
+
+ if (ferror(f)) {
+ if (errno != 0)
+ r = -errno;
+ else
+ r = -EIO;
+ } else {
+ if (rename(p, fn) < 0)
+ r = -errno;
+ else
+ r = 0;
+ }
+
+finish:
+ if (r < 0)
+ unlink(p);
+
+ fclose(f);
+ free(p);
+
+ return r;
+}
+
+int read_one_line_file(const char *fn, char **line) {
+ FILE *f;
+ int r;
+ char t[LINE_MAX], *c;
+
+ assert(fn);
+ assert(line);
+
+ f = fopen(fn, "re");
+ if (!f)
+ return -errno;
+
+ if (!fgets(t, sizeof(t), f)) {
+
+ if (ferror(f)) {
+ r = -errno;
+ goto finish;
+ }
+
+ t[0] = 0;
+ }
+
+ c = strdup(t);
+ if (!c) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ truncate_nl(c);
+
+ *line = c;
+ r = 0;
+
+finish:
+ fclose(f);
+ return r;
+}
+
+int read_full_file(const char *fn, char **contents, size_t *size) {
+ FILE *f;
+ int r;
+ size_t n, l;
+ char *buf = NULL;
+ struct stat st;
+
+ if (!(f = fopen(fn, "re")))
+ return -errno;
+
+ if (fstat(fileno(f), &st) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ /* Safety check */
+ if (st.st_size > 4*1024*1024) {
+ r = -E2BIG;
+ goto finish;
+ }
+
+ n = st.st_size > 0 ? st.st_size : LINE_MAX;
+ l = 0;
+
+ for (;;) {
+ char *t;
+ size_t k;
+
+ if (!(t = realloc(buf, n+1))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ buf = t;
+ k = fread(buf + l, 1, n - l, f);
+
+ if (k <= 0) {
+ if (ferror(f)) {
+ r = -errno;
+ goto finish;
+ }
+
+ break;
+ }
+
+ l += k;
+ n *= 2;
+
+ /* Safety check */
+ if (n > 4*1024*1024) {
+ r = -E2BIG;
+ goto finish;
+ }
+ }
+
+ buf[l] = 0;
+ *contents = buf;
+ buf = NULL;
+
+ if (size)
+ *size = l;
+
+ r = 0;
+
+finish:
+ fclose(f);
+ free(buf);
+
+ return r;
+}
+
+int parse_env_file(
+ const char *fname,
+ const char *separator, ...) {
+
+ int r = 0;
+ char *contents = NULL, *p;
+
+ assert(fname);
+ assert(separator);
+
+ if ((r = read_full_file(fname, &contents, NULL)) < 0)
+ return r;
+
+ p = contents;
+ for (;;) {
+ const char *key = NULL;
+
+ p += strspn(p, separator);
+ p += strspn(p, WHITESPACE);
+
+ if (!*p)
+ break;
+
+ if (!strchr(COMMENTS, *p)) {
+ va_list ap;
+ char **value;
+
+ va_start(ap, separator);
+ while ((key = va_arg(ap, char *))) {
+ size_t n;
+ char *v;
+
+ value = va_arg(ap, char **);
+
+ n = strlen(key);
+ if (strncmp(p, key, n) != 0 ||
+ p[n] != '=')
+ continue;
+
+ p += n + 1;
+ n = strcspn(p, separator);
+
+ if (n >= 2 &&
+ strchr(QUOTES, p[0]) &&
+ p[n-1] == p[0])
+ v = strndup(p+1, n-2);
+ else
+ v = strndup(p, n);
+
+ if (!v) {
+ r = -ENOMEM;
+ va_end(ap);
+ goto fail;
+ }
+
+ if (v[0] == '\0') {
+ /* return empty value strings as NULL */
+ free(v);
+ v = NULL;
+ }
+
+ free(*value);
+ *value = v;
+
+ p += n;
+
+ r ++;
+ break;
+ }
+ va_end(ap);
+ }
+
+ if (!key)
+ p += strcspn(p, separator);
+ }
+
+fail:
+ free(contents);
+ return r;
+}
+
+int load_env_file(
+ const char *fname,
+ char ***rl) {
+
+ FILE *f;
+ char **m = NULL;
+ int r;
+
+ assert(fname);
+ assert(rl);
+
+ if (!(f = fopen(fname, "re")))
+ return -errno;
+
+ while (!feof(f)) {
+ char l[LINE_MAX], *p, *u;
+ char **t;
+
+ if (!fgets(l, sizeof(l), f)) {
+ if (feof(f))
+ break;
+
+ r = -errno;
+ goto finish;
+ }
+
+ p = strstrip(l);
+
+ if (!*p)
+ continue;
+
+ if (strchr(COMMENTS, *p))
+ continue;
+
+ if (!(u = normalize_env_assignment(p))) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ t = strv_append(m, u);
+ free(u);
+
+ if (!t) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ strv_free(m);
+ m = t;
+ }
+
+ r = 0;
+
+ *rl = m;
+ m = NULL;
+
+finish:
+ if (f)
+ fclose(f);
+
+ strv_free(m);
+
+ return r;
+}
+
+int write_env_file(const char *fname, char **l) {
+ char **i, *p;
+ FILE *f;
+ int r;
+
+ r = fopen_temporary(fname, &f, &p);
+ if (r < 0)
+ return r;
+
+ fchmod_umask(fileno(f), 0644);
+
+ errno = 0;
+ STRV_FOREACH(i, l) {
+ fputs(*i, f);
+ fputc('\n', f);
+ }
+
+ fflush(f);
+
+ if (ferror(f)) {
+ if (errno != 0)
+ r = -errno;
+ else
+ r = -EIO;
+ } else {
+ if (rename(p, fname) < 0)
+ r = -errno;
+ else
+ r = 0;
+ }
+
+ if (r < 0)
+ unlink(p);
+
+ fclose(f);
+ free(p);
+
+ return r;
+}
+
+char *truncate_nl(char *s) {
+ assert(s);
+
+ s[strcspn(s, NEWLINE)] = 0;
+ return s;
+}
+
+int get_process_comm(pid_t pid, char **name) {
+ int r;
+
+ assert(name);
+
+ if (pid == 0)
+ r = read_one_line_file("/proc/self/comm", name);
+ else {
+ char *p;
+ if (asprintf(&p, "/proc/%lu/comm", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ r = read_one_line_file(p, name);
+ free(p);
+ }
+
+ return r;
+}
+
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
+ char *r, *k;
+ int c;
+ bool space = false;
+ size_t left;
+ FILE *f;
+
+ assert(max_length > 0);
+ assert(line);
+
+ if (pid == 0)
+ f = fopen("/proc/self/cmdline", "re");
+ else {
+ char *p;
+ if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ f = fopen(p, "re");
+ free(p);
+ }
+
+ if (!f)
+ return -errno;
+
+ r = new(char, max_length);
+ if (!r) {
+ fclose(f);
+ return -ENOMEM;
+ }
+
+ k = r;
+ left = max_length;
+ while ((c = getc(f)) != EOF) {
+
+ if (isprint(c)) {
+ if (space) {
+ if (left <= 4)
+ break;
+
+ *(k++) = ' ';
+ left--;
+ space = false;
+ }
+
+ if (left <= 4)
+ break;
+
+ *(k++) = (char) c;
+ left--;
+ } else
+ space = true;
+ }
+
+ if (left <= 4) {
+ size_t n = MIN(left-1, 3U);
+ memcpy(k, "...", n);
+ k[n] = 0;
+ } else
+ *k = 0;
+
+ fclose(f);
+
+ /* Kernel threads have no argv[] */
+ if (r[0] == 0) {
+ char *t;
+ int h;
+
+ free(r);
+
+ if (!comm_fallback)
+ return -ENOENT;
+
+ h = get_process_comm(pid, &t);
+ if (h < 0)
+ return h;
+
+ r = join("[", t, "]", NULL);
+ free(t);
+
+ if (!r)
+ return -ENOMEM;
+ }
+
+ *line = r;
+ return 0;
+}
+
+int is_kernel_thread(pid_t pid) {
+ char *p;
+ size_t count;
+ char c;
+ bool eof;
+ FILE *f;
+
+ if (pid == 0)
+ return 0;
+
+ if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ f = fopen(p, "re");
+ free(p);
+
+ if (!f)
+ return -errno;
+
+ count = fread(&c, 1, 1, f);
+ eof = feof(f);
+ fclose(f);
+
+ /* Kernel threads have an empty cmdline */
+
+ if (count <= 0)
+ return eof ? 1 : -errno;
+
+ return 0;
+}
+
+int get_process_exe(pid_t pid, char **name) {
+ int r;
+
+ assert(name);
+
+ if (pid == 0)
+ r = readlink_malloc("/proc/self/exe", name);
+ else {
+ char *p;
+ if (asprintf(&p, "/proc/%lu/exe", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ r = readlink_malloc(p, name);
+ free(p);
+ }
+
+ return r;
+}
+
+int get_process_uid(pid_t pid, uid_t *uid) {
+ char *p;
+ FILE *f;
+ int r;
+
+ assert(uid);
+
+ if (pid == 0)
+ return getuid();
+
+ if (asprintf(&p, "/proc/%lu/status", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ f = fopen(p, "re");
+ free(p);
+
+ if (!f)
+ return -errno;
+
+ while (!feof(f)) {
+ char line[LINE_MAX], *l;
+
+ if (!fgets(line, sizeof(line), f)) {
+ if (feof(f))
+ break;
+
+ r = -errno;
+ goto finish;
+ }
+
+ l = strstrip(line);
+
+ if (startswith(l, "Uid:")) {
+ l += 4;
+ l += strspn(l, WHITESPACE);
+
+ l[strcspn(l, WHITESPACE)] = 0;
+
+ r = parse_uid(l, uid);
+ goto finish;
+ }
+ }
+
+ r = -EIO;
+
+finish:
+ fclose(f);
+
+ return r;
+}
+
+char *strnappend(const char *s, const char *suffix, size_t b) {
+ size_t a;
+ char *r;
+
+ if (!s && !suffix)
+ return strdup("");
+
+ if (!s)
+ return strndup(suffix, b);
+
+ if (!suffix)
+ return strdup(s);
+
+ assert(s);
+ assert(suffix);
+
+ a = strlen(s);
+
+ if (!(r = new(char, a+b+1)))
+ return NULL;
+
+ memcpy(r, s, a);
+ memcpy(r+a, suffix, b);
+ r[a+b] = 0;
+
+ return r;
+}
+
+char *strappend(const char *s, const char *suffix) {
+ return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
+}
+
+int readlink_malloc(const char *p, char **r) {
+ size_t l = 100;
+
+ assert(p);
+ assert(r);
+
+ for (;;) {
+ char *c;
+ ssize_t n;
+
+ if (!(c = new(char, l)))
+ return -ENOMEM;
+
+ if ((n = readlink(p, c, l-1)) < 0) {
+ int ret = -errno;
+ free(c);
+ return ret;
+ }
+
+ if ((size_t) n < l-1) {
+ c[n] = 0;
+ *r = c;
+ return 0;
+ }
+
+ free(c);
+ l *= 2;
+ }
+}
+
+int readlink_and_make_absolute(const char *p, char **r) {
+ char *target, *k;
+ int j;
+
+ assert(p);
+ assert(r);
+
+ if ((j = readlink_malloc(p, &target)) < 0)
+ return j;
+
+ k = file_in_same_dir(p, target);
+ free(target);
+
+ if (!k)
+ return -ENOMEM;
+
+ *r = k;
+ return 0;
+}
+
+int readlink_and_canonicalize(const char *p, char **r) {
+ char *t, *s;
+ int j;
+
+ assert(p);
+ assert(r);
+
+ j = readlink_and_make_absolute(p, &t);
+ if (j < 0)
+ return j;
+
+ s = canonicalize_file_name(t);
+ if (s) {
+ free(t);
+ *r = s;
+ } else
+ *r = t;
+
+ path_kill_slashes(*r);
+
+ return 0;
+}
+
+int parent_of_path(const char *path, char **_r) {
+ const char *e, *a = NULL, *b = NULL, *p;
+ char *r;
+ bool slash = false;
+
+ assert(path);
+ assert(_r);
+
+ if (!*path)
+ return -EINVAL;
+
+ for (e = path; *e; e++) {
+
+ if (!slash && *e == '/') {
+ a = b;
+ b = e;
+ slash = true;
+ } else if (slash && *e != '/')
+ slash = false;
+ }
+
+ if (*(e-1) == '/')
+ p = a;
+ else
+ p = b;
+
+ if (!p)
+ return -EINVAL;
+
+ if (p == path)
+ r = strdup("/");
+ else
+ r = strndup(path, p-path);
+
+ if (!r)
+ return -ENOMEM;
+
+ *_r = r;
+ return 0;
+}
+
+
+char *file_name_from_path(const char *p) {
+ char *r;
+
+ assert(p);
+
+ if ((r = strrchr(p, '/')))
+ return r + 1;
+
+ return (char*) p;
+}
+
+bool path_is_absolute(const char *p) {
+ assert(p);
+
+ return p[0] == '/';
+}
+
+bool is_path(const char *p) {
+
+ return !!strchr(p, '/');
+}
+
+char *path_make_absolute(const char *p, const char *prefix) {
+ assert(p);
+
+ /* Makes every item in the list an absolute path by prepending
+ * the prefix, if specified and necessary */
+
+ if (path_is_absolute(p) || !prefix)
+ return strdup(p);
+
+ return join(prefix, "/", p, NULL);
+}
+
+char *path_make_absolute_cwd(const char *p) {
+ char *cwd, *r;
+
+ assert(p);
+
+ /* Similar to path_make_absolute(), but prefixes with the
+ * current working directory. */
+
+ if (path_is_absolute(p))
+ return strdup(p);
+
+ if (!(cwd = get_current_dir_name()))
+ return NULL;
+
+ r = path_make_absolute(p, cwd);
+ free(cwd);
+
+ return r;
+}
+
+char **strv_path_make_absolute_cwd(char **l) {
+ char **s;
+
+ /* Goes through every item in the string list and makes it
+ * absolute. This works in place and won't rollback any
+ * changes on failure. */
+
+ STRV_FOREACH(s, l) {
+ char *t;
+
+ if (!(t = path_make_absolute_cwd(*s)))
+ return NULL;
+
+ free(*s);
+ *s = t;
+ }
+
+ return l;
+}
+
+char **strv_path_canonicalize(char **l) {
+ char **s;
+ unsigned k = 0;
+ bool enomem = false;
+
+ if (strv_isempty(l))
+ return l;
+
+ /* Goes through every item in the string list and canonicalize
+ * the path. This works in place and won't rollback any
+ * changes on failure. */
+
+ STRV_FOREACH(s, l) {
+ char *t, *u;
+
+ t = path_make_absolute_cwd(*s);
+ free(*s);
+
+ if (!t) {
+ enomem = true;
+ continue;
+ }
+
+ errno = 0;
+ u = canonicalize_file_name(t);
+ free(t);
+
+ if (!u) {
+ if (errno == ENOMEM || !errno)
+ enomem = true;
+
+ continue;
+ }
+
+ l[k++] = u;
+ }
+
+ l[k] = NULL;
+
+ if (enomem)
+ return NULL;
+
+ return l;
+}
+
+char **strv_path_remove_empty(char **l) {
+ char **f, **t;
+
+ if (!l)
+ return NULL;
+
+ for (f = t = l; *f; f++) {
+
+ if (dir_is_empty(*f) > 0) {
+ free(*f);
+ continue;
+ }
+
+ *(t++) = *f;
+ }
+
+ *t = NULL;
+ return l;
+}
+
+int reset_all_signal_handlers(void) {
+ int sig;
+
+ for (sig = 1; sig < _NSIG; sig++) {
+ struct sigaction sa;
+
+ if (sig == SIGKILL || sig == SIGSTOP)
+ continue;
+
+ zero(sa);
+ sa.sa_handler = SIG_DFL;
+ sa.sa_flags = SA_RESTART;
+
+ /* On Linux the first two RT signals are reserved by
+ * glibc, and sigaction() will return EINVAL for them. */
+ if ((sigaction(sig, &sa, NULL) < 0))
+ if (errno != EINVAL)
+ return -errno;
+ }
+
+ return 0;
+}
+
+char *strstrip(char *s) {
+ char *e;
+
+ /* Drops trailing whitespace. Modifies the string in
+ * place. Returns pointer to first non-space character */
+
+ s += strspn(s, WHITESPACE);
+
+ for (e = strchr(s, 0); e > s; e --)
+ if (!strchr(WHITESPACE, e[-1]))
+ break;
+
+ *e = 0;
+
+ return s;
+}
+
+char *delete_chars(char *s, const char *bad) {
+ char *f, *t;
+
+ /* Drops all whitespace, regardless where in the string */
+
+ for (f = s, t = s; *f; f++) {
+ if (strchr(bad, *f))
+ continue;
+
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ return s;
+}
+
+bool in_charset(const char *s, const char* charset) {
+ const char *i;
+
+ assert(s);
+ assert(charset);
+
+ for (i = s; *i; i++)
+ if (!strchr(charset, *i))
+ return false;
+
+ return true;
+}
+
+char *file_in_same_dir(const char *path, const char *filename) {
+ char *e, *r;
+ size_t k;
+
+ assert(path);
+ assert(filename);
+
+ /* This removes the last component of path and appends
+ * filename, unless the latter is absolute anyway or the
+ * former isn't */
+
+ if (path_is_absolute(filename))
+ return strdup(filename);
+
+ if (!(e = strrchr(path, '/')))
+ return strdup(filename);
+
+ k = strlen(filename);
+ if (!(r = new(char, e-path+1+k+1)))
+ return NULL;
+
+ memcpy(r, path, e-path+1);
+ memcpy(r+(e-path)+1, filename, k+1);
+
+ return r;
+}
+
+int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+ struct stat st;
+
+ if (label_mkdir(path, mode) >= 0)
+ if (chmod_and_chown(path, mode, uid, gid) < 0)
+ return -errno;
+
+ if (lstat(path, &st) < 0)
+ return -errno;
+
+ if ((st.st_mode & 0777) != mode ||
+ st.st_uid != uid ||
+ st.st_gid != gid ||
+ !S_ISDIR(st.st_mode)) {
+ errno = EEXIST;
+ return -errno;
+ }
+
+ return 0;
+}
+
+
+int mkdir_parents(const char *path, mode_t mode) {
+ const char *p, *e;
+
+ assert(path);
+
+ /* Creates every parent directory in the path except the last
+ * component. */
+
+ p = path + strspn(path, "/");
+ for (;;) {
+ int r;
+ char *t;
+
+ e = p + strcspn(p, "/");
+ p = e + strspn(e, "/");
+
+ /* Is this the last component? If so, then we're
+ * done */
+ if (*p == 0)
+ return 0;
+
+ if (!(t = strndup(path, e - path)))
+ return -ENOMEM;
+
+ r = label_mkdir(t, mode);
+ free(t);
+
+ if (r < 0 && errno != EEXIST)
+ return -errno;
+ }
+}
+
+int mkdir_p(const char *path, mode_t mode) {
+ int r;
+
+ /* Like mkdir -p */
+
+ if ((r = mkdir_parents(path, mode)) < 0)
+ return r;
+
+ if (label_mkdir(path, mode) < 0 && errno != EEXIST)
+ return -errno;
+
+ return 0;
+}
+
+int rmdir_parents(const char *path, const char *stop) {
+ size_t l;
+ int r = 0;
+
+ assert(path);
+ assert(stop);
+
+ l = strlen(path);
+
+ /* Skip trailing slashes */
+ while (l > 0 && path[l-1] == '/')
+ l--;
+
+ while (l > 0) {
+ char *t;
+
+ /* Skip last component */
+ while (l > 0 && path[l-1] != '/')
+ l--;
+
+ /* Skip trailing slashes */
+ while (l > 0 && path[l-1] == '/')
+ l--;
+
+ if (l <= 0)
+ break;
+
+ if (!(t = strndup(path, l)))
+ return -ENOMEM;
+
+ if (path_startswith(stop, t)) {
+ free(t);
+ return 0;
+ }
+
+ r = rmdir(t);
+ free(t);
+
+ if (r < 0)
+ if (errno != ENOENT)
+ return -errno;
+ }
+
+ return 0;
+}
+
+
+char hexchar(int x) {
+ static const char table[16] = "0123456789abcdef";
+
+ return table[x & 15];
+}
+
+int unhexchar(char c) {
+
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+
+ return -1;
+}
+
+char octchar(int x) {
+ return '0' + (x & 7);
+}
+
+int unoctchar(char c) {
+
+ if (c >= '0' && c <= '7')
+ return c - '0';
+
+ return -1;
+}
+
+char decchar(int x) {
+ return '0' + (x % 10);
+}
+
+int undecchar(char c) {
+
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ return -1;
+}
+
+char *cescape(const char *s) {
+ char *r, *t;
+ const char *f;
+
+ assert(s);
+
+ /* Does C style string escaping. */
+
+ if (!(r = new(char, strlen(s)*4 + 1)))
+ return NULL;
+
+ for (f = s, t = r; *f; f++)
+
+ switch (*f) {
+
+ case '\a':
+ *(t++) = '\\';
+ *(t++) = 'a';
+ break;
+ case '\b':
+ *(t++) = '\\';
+ *(t++) = 'b';
+ break;
+ case '\f':
+ *(t++) = '\\';
+ *(t++) = 'f';
+ break;
+ case '\n':
+ *(t++) = '\\';
+ *(t++) = 'n';
+ break;
+ case '\r':
+ *(t++) = '\\';
+ *(t++) = 'r';
+ break;
+ case '\t':
+ *(t++) = '\\';
+ *(t++) = 't';
+ break;
+ case '\v':
+ *(t++) = '\\';
+ *(t++) = 'v';
+ break;
+ case '\\':
+ *(t++) = '\\';
+ *(t++) = '\\';
+ break;
+ case '"':
+ *(t++) = '\\';
+ *(t++) = '"';
+ break;
+ case '\'':
+ *(t++) = '\\';
+ *(t++) = '\'';
+ break;
+
+ default:
+ /* For special chars we prefer octal over
+ * hexadecimal encoding, simply because glib's
+ * g_strescape() does the same */
+ if ((*f < ' ') || (*f >= 127)) {
+ *(t++) = '\\';
+ *(t++) = octchar((unsigned char) *f >> 6);
+ *(t++) = octchar((unsigned char) *f >> 3);
+ *(t++) = octchar((unsigned char) *f);
+ } else
+ *(t++) = *f;
+ break;
+ }
+
+ *t = 0;
+
+ return r;
+}
+
+char *cunescape_length(const char *s, size_t length) {
+ char *r, *t;
+ const char *f;
+
+ assert(s);
+
+ /* Undoes C style string escaping */
+
+ r = new(char, length+1);
+ if (!r)
+ return r;
+
+ for (f = s, t = r; f < s + length; f++) {
+
+ if (*f != '\\') {
+ *(t++) = *f;
+ continue;
+ }
+
+ f++;
+
+ switch (*f) {
+
+ case 'a':
+ *(t++) = '\a';
+ break;
+ case 'b':
+ *(t++) = '\b';
+ break;
+ case 'f':
+ *(t++) = '\f';
+ break;
+ case 'n':
+ *(t++) = '\n';
+ break;
+ case 'r':
+ *(t++) = '\r';
+ break;
+ case 't':
+ *(t++) = '\t';
+ break;
+ case 'v':
+ *(t++) = '\v';
+ break;
+ case '\\':
+ *(t++) = '\\';
+ break;
+ case '"':
+ *(t++) = '"';
+ break;
+ case '\'':
+ *(t++) = '\'';
+ break;
+
+ case 's':
+ /* This is an extension of the XDG syntax files */
+ *(t++) = ' ';
+ break;
+
+ case 'x': {
+ /* hexadecimal encoding */
+ int a, b;
+
+ a = unhexchar(f[1]);
+ b = unhexchar(f[2]);
+
+ if (a < 0 || b < 0) {
+ /* Invalid escape code, let's take it literal then */
+ *(t++) = '\\';
+ *(t++) = 'x';
+ } else {
+ *(t++) = (char) ((a << 4) | b);
+ f += 2;
+ }
+
+ break;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7': {
+ /* octal encoding */
+ int a, b, c;
+
+ a = unoctchar(f[0]);
+ b = unoctchar(f[1]);
+ c = unoctchar(f[2]);
+
+ if (a < 0 || b < 0 || c < 0) {
+ /* Invalid escape code, let's take it literal then */
+ *(t++) = '\\';
+ *(t++) = f[0];
+ } else {
+ *(t++) = (char) ((a << 6) | (b << 3) | c);
+ f += 2;
+ }
+
+ break;
+ }
+
+ case 0:
+ /* premature end of string.*/
+ *(t++) = '\\';
+ goto finish;
+
+ default:
+ /* Invalid escape code, let's take it literal then */
+ *(t++) = '\\';
+ *(t++) = *f;
+ break;
+ }
+ }
+
+finish:
+ *t = 0;
+ return r;
+}
+
+char *cunescape(const char *s) {
+ return cunescape_length(s, strlen(s));
+}
+
+char *xescape(const char *s, const char *bad) {
+ char *r, *t;
+ const char *f;
+
+ /* Escapes all chars in bad, in addition to \ and all special
+ * chars, in \xFF style escaping. May be reversed with
+ * cunescape. */
+
+ if (!(r = new(char, strlen(s)*4+1)))
+ return NULL;
+
+ for (f = s, t = r; *f; f++) {
+
+ if ((*f < ' ') || (*f >= 127) ||
+ (*f == '\\') || strchr(bad, *f)) {
+ *(t++) = '\\';
+ *(t++) = 'x';
+ *(t++) = hexchar(*f >> 4);
+ *(t++) = hexchar(*f);
+ } else
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ return r;
+}
+
+char *bus_path_escape(const char *s) {
+ char *r, *t;
+ const char *f;
+
+ assert(s);
+
+ /* Escapes all chars that D-Bus' object path cannot deal
+ * with. Can be reverse with bus_path_unescape() */
+
+ if (!(r = new(char, strlen(s)*3+1)))
+ return NULL;
+
+ for (f = s, t = r; *f; f++) {
+
+ if (!(*f >= 'A' && *f <= 'Z') &&
+ !(*f >= 'a' && *f <= 'z') &&
+ !(*f >= '0' && *f <= '9')) {
+ *(t++) = '_';
+ *(t++) = hexchar(*f >> 4);
+ *(t++) = hexchar(*f);
+ } else
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ return r;
+}
+
+char *bus_path_unescape(const char *f) {
+ char *r, *t;
+
+ assert(f);
+
+ if (!(r = strdup(f)))
+ return NULL;
+
+ for (t = r; *f; f++) {
+
+ if (*f == '_') {
+ int a, b;
+
+ if ((a = unhexchar(f[1])) < 0 ||
+ (b = unhexchar(f[2])) < 0) {
+ /* Invalid escape code, let's take it literal then */
+ *(t++) = '_';
+ } else {
+ *(t++) = (char) ((a << 4) | b);
+ f += 2;
+ }
+ } else
+ *(t++) = *f;
+ }
+
+ *t = 0;
+
+ return r;
+}
+
+char *path_kill_slashes(char *path) {
+ char *f, *t;
+ bool slash = false;
+
+ /* Removes redundant inner and trailing slashes. Modifies the
+ * passed string in-place.
+ *
+ * ///foo///bar/ becomes /foo/bar
+ */
+
+ for (f = path, t = path; *f; f++) {
+
+ if (*f == '/') {
+ slash = true;
+ continue;
+ }
+
+ if (slash) {
+ slash = false;
+ *(t++) = '/';
+ }
+
+ *(t++) = *f;
+ }
+
+ /* Special rule, if we are talking of the root directory, a
+ trailing slash is good */
+
+ if (t == path && slash)
+ *(t++) = '/';
+
+ *t = 0;
+ return path;
+}
+
+bool path_startswith(const char *path, const char *prefix) {
+ assert(path);
+ assert(prefix);
+
+ if ((path[0] == '/') != (prefix[0] == '/'))
+ return false;
+
+ for (;;) {
+ size_t a, b;
+
+ path += strspn(path, "/");
+ prefix += strspn(prefix, "/");
+
+ if (*prefix == 0)
+ return true;
+
+ if (*path == 0)
+ return false;
+
+ a = strcspn(path, "/");
+ b = strcspn(prefix, "/");
+
+ if (a != b)
+ return false;
+
+ if (memcmp(path, prefix, a) != 0)
+ return false;
+
+ path += a;
+ prefix += b;
+ }
+}
+
+bool path_equal(const char *a, const char *b) {
+ assert(a);
+ assert(b);
+
+ if ((a[0] == '/') != (b[0] == '/'))
+ return false;
+
+ for (;;) {
+ size_t j, k;
+
+ a += strspn(a, "/");
+ b += strspn(b, "/");
+
+ if (*a == 0 && *b == 0)
+ return true;
+
+ if (*a == 0 || *b == 0)
+ return false;
+
+ j = strcspn(a, "/");
+ k = strcspn(b, "/");
+
+ if (j != k)
+ return false;
+
+ if (memcmp(a, b, j) != 0)
+ return false;
+
+ a += j;
+ b += k;
+ }
+}
+
+char *ascii_strlower(char *t) {
+ char *p;
+
+ assert(t);
+
+ for (p = t; *p; p++)
+ if (*p >= 'A' && *p <= 'Z')
+ *p = *p - 'A' + 'a';
+
+ return t;
+}
+
+bool ignore_file(const char *filename) {
+ assert(filename);
+
+ return
+ filename[0] == '.' ||
+ streq(filename, "lost+found") ||
+ streq(filename, "aquota.user") ||
+ streq(filename, "aquota.group") ||
+ endswith(filename, "~") ||
+ endswith(filename, ".rpmnew") ||
+ endswith(filename, ".rpmsave") ||
+ endswith(filename, ".rpmorig") ||
+ endswith(filename, ".dpkg-old") ||
+ endswith(filename, ".dpkg-new") ||
+ endswith(filename, ".swp");
+}
+
+int fd_nonblock(int fd, bool nonblock) {
+ int flags;
+
+ assert(fd >= 0);
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
+ return -errno;
+
+ if (nonblock)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+
+ if (fcntl(fd, F_SETFL, flags) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int fd_cloexec(int fd, bool cloexec) {
+ int flags;
+
+ assert(fd >= 0);
+
+ if ((flags = fcntl(fd, F_GETFD, 0)) < 0)
+ return -errno;
+
+ if (cloexec)
+ flags |= FD_CLOEXEC;
+ else
+ flags &= ~FD_CLOEXEC;
+
+ if (fcntl(fd, F_SETFD, flags) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
+ unsigned i;
+
+ assert(n_fdset == 0 || fdset);
+
+ for (i = 0; i < n_fdset; i++)
+ if (fdset[i] == fd)
+ return true;
+
+ return false;
+}
+
+int close_all_fds(const int except[], unsigned n_except) {
+ DIR *d;
+ struct dirent *de;
+ int r = 0;
+
+ assert(n_except == 0 || except);
+
+ d = opendir("/proc/self/fd");
+ if (!d) {
+ int fd;
+ struct rlimit rl;
+
+ /* When /proc isn't available (for example in chroots)
+ * the fallback is brute forcing through the fd
+ * table */
+
+ assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
+ for (fd = 3; fd < (int) rl.rlim_max; fd ++) {
+
+ if (fd_in_set(fd, except, n_except))
+ continue;
+
+ if (close_nointr(fd) < 0)
+ if (errno != EBADF && r == 0)
+ r = -errno;
+ }
+
+ return r;
+ }
+
+ while ((de = readdir(d))) {
+ int fd = -1;
+
+ if (ignore_file(de->d_name))
+ continue;
+
+ if (safe_atoi(de->d_name, &fd) < 0)
+ /* Let's better ignore this, just in case */
+ continue;
+
+ if (fd < 3)
+ continue;
+
+ if (fd == dirfd(d))
+ continue;
+
+ if (fd_in_set(fd, except, n_except))
+ continue;
+
+ if (close_nointr(fd) < 0) {
+ /* Valgrind has its own FD and doesn't want to have it closed */
+ if (errno != EBADF && r == 0)
+ r = -errno;
+ }
+ }
+
+ closedir(d);
+ return r;
+}
+
+bool chars_intersect(const char *a, const char *b) {
+ const char *p;
+
+ /* Returns true if any of the chars in a are in b. */
+ for (p = a; *p; p++)
+ if (strchr(b, *p))
+ return true;
+
+ return false;
+}
+
+char *format_timestamp(char *buf, size_t l, usec_t t) {
+ struct tm tm;
+ time_t sec;
+
+ assert(buf);
+ assert(l > 0);
+
+ if (t <= 0)
+ return NULL;
+
+ sec = (time_t) (t / USEC_PER_SEC);
+
+ if (strftime(buf, l, "%a, %d %b %Y %H:%M:%S %z", localtime_r(&sec, &tm)) <= 0)
+ return NULL;
+
+ return buf;
+}
+
+char *format_timestamp_pretty(char *buf, size_t l, usec_t t) {
+ usec_t n, d;
+
+ n = now(CLOCK_REALTIME);
+
+ if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
+ return NULL;
+
+ d = n - t;
+
+ if (d >= USEC_PER_YEAR)
+ snprintf(buf, l, "%llu years and %llu months ago",
+ (unsigned long long) (d / USEC_PER_YEAR),
+ (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
+ else if (d >= USEC_PER_MONTH)
+ snprintf(buf, l, "%llu months and %llu days ago",
+ (unsigned long long) (d / USEC_PER_MONTH),
+ (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
+ else if (d >= USEC_PER_WEEK)
+ snprintf(buf, l, "%llu weeks and %llu days ago",
+ (unsigned long long) (d / USEC_PER_WEEK),
+ (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
+ else if (d >= 2*USEC_PER_DAY)
+ snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
+ else if (d >= 25*USEC_PER_HOUR)
+ snprintf(buf, l, "1 day and %lluh ago",
+ (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
+ else if (d >= 6*USEC_PER_HOUR)
+ snprintf(buf, l, "%lluh ago",
+ (unsigned long long) (d / USEC_PER_HOUR));
+ else if (d >= USEC_PER_HOUR)
+ snprintf(buf, l, "%lluh %llumin ago",
+ (unsigned long long) (d / USEC_PER_HOUR),
+ (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
+ else if (d >= 5*USEC_PER_MINUTE)
+ snprintf(buf, l, "%llumin ago",
+ (unsigned long long) (d / USEC_PER_MINUTE));
+ else if (d >= USEC_PER_MINUTE)
+ snprintf(buf, l, "%llumin %llus ago",
+ (unsigned long long) (d / USEC_PER_MINUTE),
+ (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
+ else if (d >= USEC_PER_SEC)
+ snprintf(buf, l, "%llus ago",
+ (unsigned long long) (d / USEC_PER_SEC));
+ else if (d >= USEC_PER_MSEC)
+ snprintf(buf, l, "%llums ago",
+ (unsigned long long) (d / USEC_PER_MSEC));
+ else if (d > 0)
+ snprintf(buf, l, "%lluus ago",
+ (unsigned long long) d);
+ else
+ snprintf(buf, l, "now");
+
+ buf[l-1] = 0;
+ return buf;
+}
+
+char *format_timespan(char *buf, size_t l, usec_t t) {
+ static const struct {
+ const char *suffix;
+ usec_t usec;
+ } table[] = {
+ { "w", USEC_PER_WEEK },
+ { "d", USEC_PER_DAY },
+ { "h", USEC_PER_HOUR },
+ { "min", USEC_PER_MINUTE },
+ { "s", USEC_PER_SEC },
+ { "ms", USEC_PER_MSEC },
+ { "us", 1 },
+ };
+
+ unsigned i;
+ char *p = buf;
+
+ assert(buf);
+ assert(l > 0);
+
+ if (t == (usec_t) -1)
+ return NULL;
+
+ if (t == 0) {
+ snprintf(p, l, "0");
+ p[l-1] = 0;
+ return p;
+ }
+
+ /* The result of this function can be parsed with parse_usec */
+
+ for (i = 0; i < ELEMENTSOF(table); i++) {
+ int k;
+ size_t n;
+
+ if (t < table[i].usec)
+ continue;
+
+ if (l <= 1)
+ break;
+
+ k = snprintf(p, l, "%s%llu%s", p > buf ? " " : "", (unsigned long long) (t / table[i].usec), table[i].suffix);
+ n = MIN((size_t) k, l);
+
+ l -= n;
+ p += n;
+
+ t %= table[i].usec;
+ }
+
+ *p = 0;
+
+ return buf;
+}
+
+bool fstype_is_network(const char *fstype) {
+ static const char * const table[] = {
+ "cifs",
+ "smbfs",
+ "ncpfs",
+ "nfs",
+ "nfs4",
+ "gfs",
+ "gfs2"
+ };
+
+ unsigned i;
+
+ for (i = 0; i < ELEMENTSOF(table); i++)
+ if (streq(table[i], fstype))
+ return true;
+
+ return false;
+}
+
+int chvt(int vt) {
+ int fd, r = 0;
+
+ if ((fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
+ return -errno;
+
+ if (vt < 0) {
+ int tiocl[2] = {
+ TIOCL_GETKMSGREDIRECT,
+ 0
+ };
+
+ if (ioctl(fd, TIOCLINUX, tiocl) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ vt = tiocl[0] <= 0 ? 1 : tiocl[0];
+ }
+
+ if (ioctl(fd, VT_ACTIVATE, vt) < 0)
+ r = -errno;
+
+fail:
+ close_nointr_nofail(fd);
+ return r;
+}
+
+int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
+ struct termios old_termios, new_termios;
+ char c;
+ char line[LINE_MAX];
+
+ assert(f);
+ assert(ret);
+
+ if (tcgetattr(fileno(f), &old_termios) >= 0) {
+ new_termios = old_termios;
+
+ new_termios.c_lflag &= ~ICANON;
+ new_termios.c_cc[VMIN] = 1;
+ new_termios.c_cc[VTIME] = 0;
+
+ if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
+ size_t k;
+
+ if (t != (usec_t) -1) {
+ if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
+ tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+ return -ETIMEDOUT;
+ }
+ }
+
+ k = fread(&c, 1, 1, f);
+
+ tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+
+ if (k <= 0)
+ return -EIO;
+
+ if (need_nl)
+ *need_nl = c != '\n';
+
+ *ret = c;
+ return 0;
+ }
+ }
+
+ if (t != (usec_t) -1)
+ if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
+ return -ETIMEDOUT;
+
+ if (!fgets(line, sizeof(line), f))
+ return -EIO;
+
+ truncate_nl(line);
+
+ if (strlen(line) != 1)
+ return -EBADMSG;
+
+ if (need_nl)
+ *need_nl = false;
+
+ *ret = line[0];
+ return 0;
+}
+
+int ask(char *ret, const char *replies, const char *text, ...) {
+ bool on_tty;
+
+ assert(ret);
+ assert(replies);
+ assert(text);
+
+ on_tty = isatty(STDOUT_FILENO);
+
+ for (;;) {
+ va_list ap;
+ char c;
+ int r;
+ bool need_nl = true;
+
+ if (on_tty)
+ fputs(ANSI_HIGHLIGHT_ON, stdout);
+
+ va_start(ap, text);
+ vprintf(text, ap);
+ va_end(ap);
+
+ if (on_tty)
+ fputs(ANSI_HIGHLIGHT_OFF, stdout);
+
+ fflush(stdout);
+
+ r = read_one_char(stdin, &c, (usec_t) -1, &need_nl);
+ if (r < 0) {
+
+ if (r == -EBADMSG) {
+ puts("Bad input, please try again.");
+ continue;
+ }
+
+ putchar('\n');
+ return r;
+ }
+
+ if (need_nl)
+ putchar('\n');
+
+ if (strchr(replies, c)) {
+ *ret = c;
+ return 0;
+ }
+
+ puts("Read unexpected character, please try again.");
+ }
+}
+
+int reset_terminal_fd(int fd, bool switch_to_text) {
+ struct termios termios;
+ int r = 0;
+
+ /* Set terminal to some sane defaults */
+
+ assert(fd >= 0);
+
+ /* We leave locked terminal attributes untouched, so that
+ * Plymouth may set whatever it wants to set, and we don't
+ * interfere with that. */
+
+ /* Disable exclusive mode, just in case */
+ ioctl(fd, TIOCNXCL);
+
+ /* Switch to text mode */
+ if (switch_to_text)
+ ioctl(fd, KDSETMODE, KD_TEXT);
+
+ /* Enable console unicode mode */
+ ioctl(fd, KDSKBMODE, K_UNICODE);
+
+ if (tcgetattr(fd, &termios) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ /* We only reset the stuff that matters to the software. How
+ * hardware is set up we don't touch assuming that somebody
+ * else will do that for us */
+
+ termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
+ termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
+ termios.c_oflag |= ONLCR;
+ termios.c_cflag |= CREAD;
+ termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
+
+ termios.c_cc[VINTR] = 03; /* ^C */
+ termios.c_cc[VQUIT] = 034; /* ^\ */
+ termios.c_cc[VERASE] = 0177;
+ termios.c_cc[VKILL] = 025; /* ^X */
+ termios.c_cc[VEOF] = 04; /* ^D */
+ termios.c_cc[VSTART] = 021; /* ^Q */
+ termios.c_cc[VSTOP] = 023; /* ^S */
+ termios.c_cc[VSUSP] = 032; /* ^Z */
+ termios.c_cc[VLNEXT] = 026; /* ^V */
+ termios.c_cc[VWERASE] = 027; /* ^W */
+ termios.c_cc[VREPRINT] = 022; /* ^R */
+ termios.c_cc[VEOL] = 0;
+ termios.c_cc[VEOL2] = 0;
+
+ termios.c_cc[VTIME] = 0;
+ termios.c_cc[VMIN] = 1;
+
+ if (tcsetattr(fd, TCSANOW, &termios) < 0)
+ r = -errno;
+
+finish:
+ /* Just in case, flush all crap out */
+ tcflush(fd, TCIOFLUSH);
+
+ return r;
+}
+
+int reset_terminal(const char *name) {
+ int fd, r;
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ r = reset_terminal_fd(fd, true);
+ close_nointr_nofail(fd);
+
+ return r;
+}
+
+int open_terminal(const char *name, int mode) {
+ int fd, r;
+ unsigned c = 0;
+
+ /*
+ * If a TTY is in the process of being closed opening it might
+ * cause EIO. This is horribly awful, but unlikely to be
+ * changed in the kernel. Hence we work around this problem by
+ * retrying a couple of times.
+ *
+ * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
+ */
+
+ for (;;) {
+ if ((fd = open(name, mode)) >= 0)
+ break;
+
+ if (errno != EIO)
+ return -errno;
+
+ if (c >= 20)
+ return -errno;
+
+ usleep(50 * USEC_PER_MSEC);
+ c++;
+ }
+
+ if (fd < 0)
+ return -errno;
+
+ if ((r = isatty(fd)) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ if (!r) {
+ close_nointr_nofail(fd);
+ return -ENOTTY;
+ }
+
+ return fd;
+}
+
+int flush_fd(int fd) {
+ struct pollfd pollfd;
+
+ zero(pollfd);
+ pollfd.fd = fd;
+ pollfd.events = POLLIN;
+
+ for (;;) {
+ char buf[LINE_MAX];
+ ssize_t l;
+ int r;
+
+ if ((r = poll(&pollfd, 1, 0)) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ return -errno;
+ }
+
+ if (r == 0)
+ return 0;
+
+ if ((l = read(fd, buf, sizeof(buf))) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN)
+ return 0;
+
+ return -errno;
+ }
+
+ if (l <= 0)
+ return 0;
+ }
+}
+
+int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm) {
+ int fd = -1, notify = -1, r, wd = -1;
+
+ assert(name);
+
+ /* We use inotify to be notified when the tty is closed. We
+ * create the watch before checking if we can actually acquire
+ * it, so that we don't lose any event.
+ *
+ * Note: strictly speaking this actually watches for the
+ * device being closed, it does *not* really watch whether a
+ * tty loses its controlling process. However, unless some
+ * rogue process uses TIOCNOTTY on /dev/tty *after* closing
+ * its tty otherwise this will not become a problem. As long
+ * as the administrator makes sure not configure any service
+ * on the same tty as an untrusted user this should not be a
+ * problem. (Which he probably should not do anyway.) */
+
+ if (!fail && !force) {
+ if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if ((wd = inotify_add_watch(notify, name, IN_CLOSE)) < 0) {
+ r = -errno;
+ goto fail;
+ }
+ }
+
+ for (;;) {
+ if (notify >= 0)
+ if ((r = flush_fd(notify)) < 0)
+ goto fail;
+
+ /* We pass here O_NOCTTY only so that we can check the return
+ * value TIOCSCTTY and have a reliable way to figure out if we
+ * successfully became the controlling process of the tty */
+ if ((fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
+ return fd;
+
+ /* First, try to get the tty */
+ r = ioctl(fd, TIOCSCTTY, force);
+
+ /* Sometimes it makes sense to ignore TIOCSCTTY
+ * returning EPERM, i.e. when very likely we already
+ * are have this controlling terminal. */
+ if (r < 0 && errno == EPERM && ignore_tiocstty_eperm)
+ r = 0;
+
+ if (r < 0 && (force || fail || errno != EPERM)) {
+ r = -errno;
+ goto fail;
+ }
+
+ if (r >= 0)
+ break;
+
+ assert(!fail);
+ assert(!force);
+ assert(notify >= 0);
+
+ for (;;) {
+ uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
+ ssize_t l;
+ struct inotify_event *e;
+
+ if ((l = read(notify, inotify_buffer, sizeof(inotify_buffer))) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ r = -errno;
+ goto fail;
+ }
+
+ e = (struct inotify_event*) inotify_buffer;
+
+ while (l > 0) {
+ size_t step;
+
+ if (e->wd != wd || !(e->mask & IN_CLOSE)) {
+ r = -EIO;
+ goto fail;
+ }
+
+ step = sizeof(struct inotify_event) + e->len;
+ assert(step <= (size_t) l);
+
+ e = (struct inotify_event*) ((uint8_t*) e + step);
+ l -= step;
+ }
+
+ break;
+ }
+
+ /* We close the tty fd here since if the old session
+ * ended our handle will be dead. It's important that
+ * we do this after sleeping, so that we don't enter
+ * an endless loop. */
+ close_nointr_nofail(fd);
+ }
+
+ if (notify >= 0)
+ close_nointr_nofail(notify);
+
+ r = reset_terminal_fd(fd, true);
+ if (r < 0)
+ log_warning("Failed to reset terminal: %s", strerror(-r));
+
+ return fd;
+
+fail:
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ if (notify >= 0)
+ close_nointr_nofail(notify);
+
+ return r;
+}
+
+int release_terminal(void) {
+ int r = 0, fd;
+ struct sigaction sa_old, sa_new;
+
+ if ((fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC)) < 0)
+ return -errno;
+
+ /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
+ * by our own TIOCNOTTY */
+
+ zero(sa_new);
+ sa_new.sa_handler = SIG_IGN;
+ sa_new.sa_flags = SA_RESTART;
+ assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
+
+ if (ioctl(fd, TIOCNOTTY) < 0)
+ r = -errno;
+
+ assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
+
+ close_nointr_nofail(fd);
+ return r;
+}
+
+int sigaction_many(const struct sigaction *sa, ...) {
+ va_list ap;
+ int r = 0, sig;
+
+ va_start(ap, sa);
+ while ((sig = va_arg(ap, int)) > 0)
+ if (sigaction(sig, sa, NULL) < 0)
+ r = -errno;
+ va_end(ap);
+
+ return r;
+}
+
+int ignore_signals(int sig, ...) {
+ struct sigaction sa;
+ va_list ap;
+ int r = 0;
+
+ zero(sa);
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_RESTART;
+
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+
+ va_start(ap, sig);
+ while ((sig = va_arg(ap, int)) > 0)
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+ va_end(ap);
+
+ return r;
+}
+
+int default_signals(int sig, ...) {
+ struct sigaction sa;
+ va_list ap;
+ int r = 0;
+
+ zero(sa);
+ sa.sa_handler = SIG_DFL;
+ sa.sa_flags = SA_RESTART;
+
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+
+ va_start(ap, sig);
+ while ((sig = va_arg(ap, int)) > 0)
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+ va_end(ap);
+
+ return r;
+}
+
+int close_pipe(int p[]) {
+ int a = 0, b = 0;
+
+ assert(p);
+
+ if (p[0] >= 0) {
+ a = close_nointr(p[0]);
+ p[0] = -1;
+ }
+
+ if (p[1] >= 0) {
+ b = close_nointr(p[1]);
+ p[1] = -1;
+ }
+
+ return a < 0 ? a : b;
+}
+
+ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
+ uint8_t *p;
+ ssize_t n = 0;
+
+ assert(fd >= 0);
+ assert(buf);
+
+ p = buf;
+
+ while (nbytes > 0) {
+ ssize_t k;
+
+ if ((k = read(fd, p, nbytes)) <= 0) {
+
+ if (k < 0 && errno == EINTR)
+ continue;
+
+ if (k < 0 && errno == EAGAIN && do_poll) {
+ struct pollfd pollfd;
+
+ zero(pollfd);
+ pollfd.fd = fd;
+ pollfd.events = POLLIN;
+
+ if (poll(&pollfd, 1, -1) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ return n > 0 ? n : -errno;
+ }
+
+ if (pollfd.revents != POLLIN)
+ return n > 0 ? n : -EIO;
+
+ continue;
+ }
+
+ return n > 0 ? n : (k < 0 ? -errno : 0);
+ }
+
+ p += k;
+ nbytes -= k;
+ n += k;
+ }
+
+ return n;
+}
+
+ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
+ const uint8_t *p;
+ ssize_t n = 0;
+
+ assert(fd >= 0);
+ assert(buf);
+
+ p = buf;
+
+ while (nbytes > 0) {
+ ssize_t k;
+
+ k = write(fd, p, nbytes);
+ if (k <= 0) {
+
+ if (k < 0 && errno == EINTR)
+ continue;
+
+ if (k < 0 && errno == EAGAIN && do_poll) {
+ struct pollfd pollfd;
+
+ zero(pollfd);
+ pollfd.fd = fd;
+ pollfd.events = POLLOUT;
+
+ if (poll(&pollfd, 1, -1) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ return n > 0 ? n : -errno;
+ }
+
+ if (pollfd.revents != POLLOUT)
+ return n > 0 ? n : -EIO;
+
+ continue;
+ }
+
+ return n > 0 ? n : (k < 0 ? -errno : 0);
+ }
+
+ p += k;
+ nbytes -= k;
+ n += k;
+ }
+
+ return n;
+}
+
+int path_is_mount_point(const char *t, bool allow_symlink) {
+ struct stat a, b;
+ char *parent;
+ int r;
+
+ if (allow_symlink)
+ r = stat(t, &a);
+ else
+ r = lstat(t, &a);
+
+ if (r < 0) {
+ if (errno == ENOENT)
+ return 0;
+
+ return -errno;
+ }
+
+ r = parent_of_path(t, &parent);
+ if (r < 0)
+ return r;
+
+ r = lstat(parent, &b);
+ free(parent);
+
+ if (r < 0)
+ return -errno;
+
+ return a.st_dev != b.st_dev;
+}
+
+int parse_usec(const char *t, usec_t *usec) {
+ static const struct {
+ const char *suffix;
+ usec_t usec;
+ } table[] = {
+ { "sec", USEC_PER_SEC },
+ { "s", USEC_PER_SEC },
+ { "min", USEC_PER_MINUTE },
+ { "hr", USEC_PER_HOUR },
+ { "h", USEC_PER_HOUR },
+ { "d", USEC_PER_DAY },
+ { "w", USEC_PER_WEEK },
+ { "msec", USEC_PER_MSEC },
+ { "ms", USEC_PER_MSEC },
+ { "m", USEC_PER_MINUTE },
+ { "usec", 1ULL },
+ { "us", 1ULL },
+ { "", USEC_PER_SEC },
+ };
+
+ const char *p;
+ usec_t r = 0;
+
+ assert(t);
+ assert(usec);
+
+ p = t;
+ do {
+ long long l;
+ char *e;
+ unsigned i;
+
+ errno = 0;
+ l = strtoll(p, &e, 10);
+
+ if (errno != 0)
+ return -errno;
+
+ if (l < 0)
+ return -ERANGE;
+
+ if (e == p)
+ return -EINVAL;
+
+ e += strspn(e, WHITESPACE);
+
+ for (i = 0; i < ELEMENTSOF(table); i++)
+ if (startswith(e, table[i].suffix)) {
+ r += (usec_t) l * table[i].usec;
+ p = e + strlen(table[i].suffix);
+ break;
+ }
+
+ if (i >= ELEMENTSOF(table))
+ return -EINVAL;
+
+ } while (*p != 0);
+
+ *usec = r;
+
+ return 0;
+}
+
+int parse_bytes(const char *t, off_t *bytes) {
+ static const struct {
+ const char *suffix;
+ off_t factor;
+ } table[] = {
+ { "B", 1 },
+ { "K", 1024ULL },
+ { "M", 1024ULL*1024ULL },
+ { "G", 1024ULL*1024ULL*1024ULL },
+ { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+ { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "", 1 },
+ };
+
+ const char *p;
+ off_t r = 0;
+
+ assert(t);
+ assert(bytes);
+
+ p = t;
+ do {
+ long long l;
+ char *e;
+ unsigned i;
+
+ errno = 0;
+ l = strtoll(p, &e, 10);
+
+ if (errno != 0)
+ return -errno;
+
+ if (l < 0)
+ return -ERANGE;
+
+ if (e == p)
+ return -EINVAL;
+
+ e += strspn(e, WHITESPACE);
+
+ for (i = 0; i < ELEMENTSOF(table); i++)
+ if (startswith(e, table[i].suffix)) {
+ r += (off_t) l * table[i].factor;
+ p = e + strlen(table[i].suffix);
+ break;
+ }
+
+ if (i >= ELEMENTSOF(table))
+ return -EINVAL;
+
+ } while (*p != 0);
+
+ *bytes = r;
+
+ return 0;
+}
+
+int make_stdio(int fd) {
+ int r, s, t;
+
+ assert(fd >= 0);
+
+ r = dup2(fd, STDIN_FILENO);
+ s = dup2(fd, STDOUT_FILENO);
+ t = dup2(fd, STDERR_FILENO);
+
+ if (fd >= 3)
+ close_nointr_nofail(fd);
+
+ if (r < 0 || s < 0 || t < 0)
+ return -errno;
+
+ fd_cloexec(STDIN_FILENO, false);
+ fd_cloexec(STDOUT_FILENO, false);
+ fd_cloexec(STDERR_FILENO, false);
+
+ return 0;
+}
+
+int make_null_stdio(void) {
+ int null_fd;
+
+ if ((null_fd = open("/dev/null", O_RDWR|O_NOCTTY)) < 0)
+ return -errno;
+
+ return make_stdio(null_fd);
+}
+
+bool is_device_path(const char *path) {
+
+ /* Returns true on paths that refer to a device, either in
+ * sysfs or in /dev */
+
+ return
+ path_startswith(path, "/dev/") ||
+ path_startswith(path, "/sys/");
+}
+
+int dir_is_empty(const char *path) {
+ DIR *d;
+ int r;
+ struct dirent buf, *de;
+
+ if (!(d = opendir(path)))
+ return -errno;
+
+ for (;;) {
+ if ((r = readdir_r(d, &buf, &de)) > 0) {
+ r = -r;
+ break;
+ }
+
+ if (!de) {
+ r = 1;
+ break;
+ }
+
+ if (!ignore_file(de->d_name)) {
+ r = 0;
+ break;
+ }
+ }
+
+ closedir(d);
+ return r;
+}
+
+unsigned long long random_ull(void) {
+ int fd;
+ uint64_t ull;
+ ssize_t r;
+
+ if ((fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0)
+ goto fallback;
+
+ r = loop_read(fd, &ull, sizeof(ull), true);
+ close_nointr_nofail(fd);
+
+ if (r != sizeof(ull))
+ goto fallback;
+
+ return ull;
+
+fallback:
+ return random() * RAND_MAX + random();
+}
+
+void rename_process(const char name[8]) {
+ assert(name);
+
+ /* This is a like a poor man's setproctitle(). It changes the
+ * comm field, argv[0], and also the glibc's internally used
+ * name of the process. For the first one a limit of 16 chars
+ * applies, to the second one usually one of 10 (i.e. length
+ * of "/sbin/init"), to the third one one of 7 (i.e. length of
+ * "systemd"). If you pass a longer string it will be
+ * truncated */
+
+ prctl(PR_SET_NAME, name);
+
+ if (program_invocation_name)
+ strncpy(program_invocation_name, name, strlen(program_invocation_name));
+
+ if (saved_argc > 0) {
+ int i;
+
+ if (saved_argv[0])
+ strncpy(saved_argv[0], name, strlen(saved_argv[0]));
+
+ for (i = 1; i < saved_argc; i++) {
+ if (!saved_argv[i])
+ break;
+
+ memset(saved_argv[i], 0, strlen(saved_argv[i]));
+ }
+ }
+}
+
+void sigset_add_many(sigset_t *ss, ...) {
+ va_list ap;
+ int sig;
+
+ assert(ss);
+
+ va_start(ap, ss);
+ while ((sig = va_arg(ap, int)) > 0)
+ assert_se(sigaddset(ss, sig) == 0);
+ va_end(ap);
+}
+
+char* gethostname_malloc(void) {
+ struct utsname u;
+
+ assert_se(uname(&u) >= 0);
+
+ if (u.nodename[0])
+ return strdup(u.nodename);
+
+ return strdup(u.sysname);
+}
+
+char* getlogname_malloc(void) {
+ uid_t uid;
+ long bufsize;
+ char *buf, *name;
+ struct passwd pwbuf, *pw = NULL;
+ struct stat st;
+
+ if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
+ uid = st.st_uid;
+ else
+ uid = getuid();
+
+ /* Shortcut things to avoid NSS lookups */
+ if (uid == 0)
+ return strdup("root");
+
+ if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) <= 0)
+ bufsize = 4096;
+
+ if (!(buf = malloc(bufsize)))
+ return NULL;
+
+ if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw) {
+ name = strdup(pw->pw_name);
+ free(buf);
+ return name;
+ }
+
+ free(buf);
+
+ if (asprintf(&name, "%lu", (unsigned long) uid) < 0)
+ return NULL;
+
+ return name;
+}
+
+int getttyname_malloc(int fd, char **r) {
+ char path[PATH_MAX], *c;
+ int k;
+
+ assert(r);
+
+ if ((k = ttyname_r(fd, path, sizeof(path))) != 0)
+ return -k;
+
+ char_array_0(path);
+
+ if (!(c = strdup(startswith(path, "/dev/") ? path + 5 : path)))
+ return -ENOMEM;
+
+ *r = c;
+ return 0;
+}
+
+int getttyname_harder(int fd, char **r) {
+ int k;
+ char *s;
+
+ if ((k = getttyname_malloc(fd, &s)) < 0)
+ return k;
+
+ if (streq(s, "tty")) {
+ free(s);
+ return get_ctty(0, NULL, r);
+ }
+
+ *r = s;
+ return 0;
+}
+
+int get_ctty_devnr(pid_t pid, dev_t *d) {
+ int k;
+ char line[LINE_MAX], *p, *fn;
+ unsigned long ttynr;
+ FILE *f;
+
+ if (asprintf(&fn, "/proc/%lu/stat", (unsigned long) (pid <= 0 ? getpid() : pid)) < 0)
+ return -ENOMEM;
+
+ f = fopen(fn, "re");
+ free(fn);
+ if (!f)
+ return -errno;
+
+ if (!fgets(line, sizeof(line), f)) {
+ k = feof(f) ? -EIO : -errno;
+ fclose(f);
+ return k;
+ }
+
+ fclose(f);
+
+ p = strrchr(line, ')');
+ if (!p)
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " "
+ "%*c " /* state */
+ "%*d " /* ppid */
+ "%*d " /* pgrp */
+ "%*d " /* session */
+ "%lu ", /* ttynr */
+ &ttynr) != 1)
+ return -EIO;
+
+ *d = (dev_t) ttynr;
+ return 0;
+}
+
+int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
+ int k;
+ char fn[PATH_MAX], *s, *b, *p;
+ dev_t devnr;
+
+ assert(r);
+
+ k = get_ctty_devnr(pid, &devnr);
+ if (k < 0)
+ return k;
+
+ snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr));
+ char_array_0(fn);
+
+ if ((k = readlink_malloc(fn, &s)) < 0) {
+
+ if (k != -ENOENT)
+ return k;
+
+ /* This is an ugly hack */
+ if (major(devnr) == 136) {
+ if (asprintf(&b, "pts/%lu", (unsigned long) minor(devnr)) < 0)
+ return -ENOMEM;
+
+ *r = b;
+ if (_devnr)
+ *_devnr = devnr;
+
+ return 0;
+ }
+
+ /* Probably something like the ptys which have no
+ * symlink in /dev/char. Let's return something
+ * vaguely useful. */
+
+ if (!(b = strdup(fn + 5)))
+ return -ENOMEM;
+
+ *r = b;
+ if (_devnr)
+ *_devnr = devnr;
+
+ return 0;
+ }
+
+ if (startswith(s, "/dev/"))
+ p = s + 5;
+ else if (startswith(s, "../"))
+ p = s + 3;
+ else
+ p = s;
+
+ b = strdup(p);
+ free(s);
+
+ if (!b)
+ return -ENOMEM;
+
+ *r = b;
+ if (_devnr)
+ *_devnr = devnr;
+
+ return 0;
+}
+
+static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
+ DIR *d;
+ int ret = 0;
+
+ assert(fd >= 0);
+
+ /* This returns the first error we run into, but nevertheless
+ * tries to go on */
+
+ if (!(d = fdopendir(fd))) {
+ close_nointr_nofail(fd);
+
+ return errno == ENOENT ? 0 : -errno;
+ }
+
+ for (;;) {
+ struct dirent buf, *de;
+ bool is_dir, keep_around = false;
+ int r;
+
+ if ((r = readdir_r(d, &buf, &de)) != 0) {
+ if (ret == 0)
+ ret = -r;
+ break;
+ }
+
+ if (!de)
+ break;
+
+ if (streq(de->d_name, ".") || streq(de->d_name, ".."))
+ continue;
+
+ if (de->d_type == DT_UNKNOWN) {
+ struct stat st;
+
+ if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ continue;
+ }
+
+ if (honour_sticky)
+ keep_around =
+ (st.st_uid == 0 || st.st_uid == getuid()) &&
+ (st.st_mode & S_ISVTX);
+
+ is_dir = S_ISDIR(st.st_mode);
+
+ } else {
+ if (honour_sticky) {
+ struct stat st;
+
+ if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ continue;
+ }
+
+ keep_around =
+ (st.st_uid == 0 || st.st_uid == getuid()) &&
+ (st.st_mode & S_ISVTX);
+ }
+
+ is_dir = de->d_type == DT_DIR;
+ }
+
+ if (is_dir) {
+ int subdir_fd;
+
+ subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+ if (subdir_fd < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ continue;
+ }
+
+ if ((r = rm_rf_children(subdir_fd, only_dirs, honour_sticky)) < 0) {
+ if (ret == 0)
+ ret = r;
+ }
+
+ if (!keep_around)
+ if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ }
+
+ } else if (!only_dirs && !keep_around) {
+
+ if (unlinkat(fd, de->d_name, 0) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ }
+ }
+ }
+
+ closedir(d);
+
+ return ret;
+}
+
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+ int fd;
+ int r;
+
+ assert(path);
+
+ if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) {
+
+ if (errno != ENOTDIR)
+ return -errno;
+
+ if (delete_root && !only_dirs)
+ if (unlink(path) < 0)
+ return -errno;
+
+ return 0;
+ }
+
+ r = rm_rf_children(fd, only_dirs, honour_sticky);
+
+ if (delete_root) {
+
+ if (honour_sticky && file_is_priv_sticky(path) > 0)
+ return r;
+
+ if (rmdir(path) < 0 && errno != ENOENT) {
+ if (r == 0)
+ r = -errno;
+ }
+ }
+
+ return r;
+}
+
+int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+ assert(path);
+
+ /* Under the assumption that we are running privileged we
+ * first change the access mode and only then hand out
+ * ownership to avoid a window where access is too open. */
+
+ if (mode != (mode_t) -1)
+ if (chmod(path, mode) < 0)
+ return -errno;
+
+ if (uid != (uid_t) -1 || gid != (gid_t) -1)
+ if (chown(path, uid, gid) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
+ assert(fd >= 0);
+
+ /* Under the assumption that we are running privileged we
+ * first change the access mode and only then hand out
+ * ownership to avoid a window where access is too open. */
+
+ if (fchmod(fd, mode) < 0)
+ return -errno;
+
+ if (fchown(fd, uid, gid) < 0)
+ return -errno;
+
+ return 0;
+}
+
+cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
+ cpu_set_t *r;
+ unsigned n = 1024;
+
+ /* Allocates the cpuset in the right size */
+
+ for (;;) {
+ if (!(r = CPU_ALLOC(n)))
+ return NULL;
+
+ if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), r) >= 0) {
+ CPU_ZERO_S(CPU_ALLOC_SIZE(n), r);
+
+ if (ncpus)
+ *ncpus = n;
+
+ return r;
+ }
+
+ CPU_FREE(r);
+
+ if (errno != EINVAL)
+ return NULL;
+
+ n *= 2;
+ }
+}
+
+void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap) {
+ char *s = NULL, *spaces = NULL, *e;
+ int fd = -1, c;
+ size_t emax, sl, left;
+ struct iovec iovec[5];
+ int n = 0;
+
+ assert(format);
+
+ /* This independent of logging, as status messages are
+ * optional and go exclusively to the console. */
+
+ if (vasprintf(&s, format, ap) < 0)
+ goto finish;
+
+ fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ goto finish;
+
+ if (ellipse) {
+ c = fd_columns(fd);
+ if (c <= 0)
+ c = 80;
+
+ if (status) {
+ sl = 2 + 6 + 1; /* " [" status "]" */
+ emax = (size_t) c > sl ? c - sl - 1 : 0;
+ } else
+ emax = c - 1;
+
+ e = ellipsize(s, emax, 75);
+ if (e) {
+ free(s);
+ s = e;
+ }
+ }
+
+ zero(iovec);
+ IOVEC_SET_STRING(iovec[n++], s);
+
+ if (ellipse) {
+ sl = strlen(s);
+ left = emax > sl ? emax - sl : 0;
+ if (left > 0) {
+ spaces = malloc(left);
+ if (spaces) {
+ memset(spaces, ' ', left);
+ iovec[n].iov_base = spaces;
+ iovec[n].iov_len = left;
+ n++;
+ }
+ }
+ }
+
+ if (status) {
+ IOVEC_SET_STRING(iovec[n++], " [");
+ IOVEC_SET_STRING(iovec[n++], status);
+ IOVEC_SET_STRING(iovec[n++], "]\n");
+ } else
+ IOVEC_SET_STRING(iovec[n++], "\n");
+
+ writev(fd, iovec, n);
+
+finish:
+ free(s);
+ free(spaces);
+
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+}
+
+void status_printf(const char *status, bool ellipse, const char *format, ...) {
+ va_list ap;
+
+ assert(format);
+
+ va_start(ap, format);
+ status_vprintf(status, ellipse, format, ap);
+ va_end(ap);
+}
+
+void status_welcome(void) {
+ char *pretty_name = NULL, *ansi_color = NULL;
+ const char *const_pretty = NULL, *const_color = NULL;
+ int r;
+
+ if ((r = parse_env_file("/etc/os-release", NEWLINE,
+ "PRETTY_NAME", &pretty_name,
+ "ANSI_COLOR", &ansi_color,
+ NULL)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/os-release: %s", strerror(-r));
+ }
+
+ if (!pretty_name && !const_pretty)
+ const_pretty = "Linux";
+
+ if (!ansi_color && !const_color)
+ const_color = "1";
+
+ status_printf(NULL,
+ false,
+ "\nWelcome to \x1B[%sm%s\x1B[0m!\n",
+ const_color ? const_color : ansi_color,
+ const_pretty ? const_pretty : pretty_name);
+
+ free(ansi_color);
+ free(pretty_name);
+}
+
+char *replace_env(const char *format, char **env) {
+ enum {
+ WORD,
+ CURLY,
+ VARIABLE
+ } state = WORD;
+
+ const char *e, *word = format;
+ char *r = NULL, *k;
+
+ assert(format);
+
+ for (e = format; *e; e ++) {
+
+ switch (state) {
+
+ case WORD:
+ if (*e == '$')
+ state = CURLY;
+ break;
+
+ case CURLY:
+ if (*e == '{') {
+ if (!(k = strnappend(r, word, e-word-1)))
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e-1;
+ state = VARIABLE;
+
+ } else if (*e == '$') {
+ if (!(k = strnappend(r, word, e-word)))
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e+1;
+ state = WORD;
+ } else
+ state = WORD;
+ break;
+
+ case VARIABLE:
+ if (*e == '}') {
+ const char *t;
+
+ if (!(t = strv_env_get_with_length(env, word+2, e-word-2)))
+ t = "";
+
+ if (!(k = strappend(r, t)))
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e+1;
+ state = WORD;
+ }
+ break;
+ }
+ }
+
+ if (!(k = strnappend(r, word, e-word)))
+ goto fail;
+
+ free(r);
+ return k;
+
+fail:
+ free(r);
+ return NULL;
+}
+
+char **replace_env_argv(char **argv, char **env) {
+ char **r, **i;
+ unsigned k = 0, l = 0;
+
+ l = strv_length(argv);
+
+ if (!(r = new(char*, l+1)))
+ return NULL;
+
+ STRV_FOREACH(i, argv) {
+
+ /* If $FOO appears as single word, replace it by the split up variable */
+ if ((*i)[0] == '$' && (*i)[1] != '{') {
+ char *e;
+ char **w, **m;
+ unsigned q;
+
+ if ((e = strv_env_get(env, *i+1))) {
+
+ if (!(m = strv_split_quoted(e))) {
+ r[k] = NULL;
+ strv_free(r);
+ return NULL;
+ }
+ } else
+ m = NULL;
+
+ q = strv_length(m);
+ l = l + q - 1;
+
+ if (!(w = realloc(r, sizeof(char*) * (l+1)))) {
+ r[k] = NULL;
+ strv_free(r);
+ strv_free(m);
+ return NULL;
+ }
+
+ r = w;
+ if (m) {
+ memcpy(r + k, m, q * sizeof(char*));
+ free(m);
+ }
+
+ k += q;
+ continue;
+ }
+
+ /* If ${FOO} appears as part of a word, replace it by the variable as-is */
+ if (!(r[k++] = replace_env(*i, env))) {
+ strv_free(r);
+ return NULL;
+ }
+ }
+
+ r[k] = NULL;
+ return r;
+}
+
+int fd_columns(int fd) {
+ struct winsize ws;
+ zero(ws);
+
+ if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+ return -errno;
+
+ if (ws.ws_col <= 0)
+ return -EIO;
+
+ return ws.ws_col;
+}
+
+unsigned columns(void) {
+ static __thread int parsed_columns = 0;
+ const char *e;
+
+ if (_likely_(parsed_columns > 0))
+ return parsed_columns;
+
+ e = getenv("COLUMNS");
+ if (e)
+ parsed_columns = atoi(e);
+
+ if (parsed_columns <= 0)
+ parsed_columns = fd_columns(STDOUT_FILENO);
+
+ if (parsed_columns <= 0)
+ parsed_columns = 80;
+
+ return parsed_columns;
+}
+
+int fd_lines(int fd) {
+ struct winsize ws;
+ zero(ws);
+
+ if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+ return -errno;
+
+ if (ws.ws_row <= 0)
+ return -EIO;
+
+ return ws.ws_row;
+}
+
+unsigned lines(void) {
+ static __thread int parsed_lines = 0;
+ const char *e;
+
+ if (_likely_(parsed_lines > 0))
+ return parsed_lines;
+
+ e = getenv("LINES");
+ if (e)
+ parsed_lines = atoi(e);
+
+ if (parsed_lines <= 0)
+ parsed_lines = fd_lines(STDOUT_FILENO);
+
+ if (parsed_lines <= 0)
+ parsed_lines = 25;
+
+ return parsed_lines;
+}
+
+int running_in_chroot(void) {
+ struct stat a, b;
+
+ zero(a);
+ zero(b);
+
+ /* Only works as root */
+
+ if (stat("/proc/1/root", &a) < 0)
+ return -errno;
+
+ if (stat("/", &b) < 0)
+ return -errno;
+
+ return
+ a.st_dev != b.st_dev ||
+ a.st_ino != b.st_ino;
+}
+
+char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+ size_t x;
+ char *r;
+
+ assert(s);
+ assert(percent <= 100);
+ assert(new_length >= 3);
+
+ if (old_length <= 3 || old_length <= new_length)
+ return strndup(s, old_length);
+
+ r = new0(char, new_length+1);
+ if (!r)
+ return r;
+
+ x = (new_length * percent) / 100;
+
+ if (x > new_length - 3)
+ x = new_length - 3;
+
+ memcpy(r, s, x);
+ r[x] = '.';
+ r[x+1] = '.';
+ r[x+2] = '.';
+ memcpy(r + x + 3,
+ s + old_length - (new_length - x - 3),
+ new_length - x - 3);
+
+ return r;
+}
+
+char *ellipsize(const char *s, size_t length, unsigned percent) {
+ return ellipsize_mem(s, strlen(s), length, percent);
+}
+
+int touch(const char *path) {
+ int fd;
+
+ assert(path);
+
+ if ((fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644)) < 0)
+ return -errno;
+
+ close_nointr_nofail(fd);
+ return 0;
+}
+
+char *unquote(const char *s, const char* quotes) {
+ size_t l;
+ assert(s);
+
+ l = strlen(s);
+ if (l < 2)
+ return strdup(s);
+
+ if (strchr(quotes, s[0]) && s[l-1] == s[0])
+ return strndup(s+1, l-2);
+
+ return strdup(s);
+}
+
+char *normalize_env_assignment(const char *s) {
+ char *name, *value, *p, *r;
+
+ p = strchr(s, '=');
+
+ if (!p) {
+ if (!(r = strdup(s)))
+ return NULL;
+
+ return strstrip(r);
+ }
+
+ if (!(name = strndup(s, p - s)))
+ return NULL;
+
+ if (!(p = strdup(p+1))) {
+ free(name);
+ return NULL;
+ }
+
+ value = unquote(strstrip(p), QUOTES);
+ free(p);
+
+ if (!value) {
+ free(name);
+ return NULL;
+ }
+
+ if (asprintf(&r, "%s=%s", name, value) < 0)
+ r = NULL;
+
+ free(value);
+ free(name);
+
+ return r;
+}
+
+int wait_for_terminate(pid_t pid, siginfo_t *status) {
+ siginfo_t dummy;
+
+ assert(pid >= 1);
+
+ if (!status)
+ status = &dummy;
+
+ for (;;) {
+ zero(*status);
+
+ if (waitid(P_PID, pid, status, WEXITED) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ return -errno;
+ }
+
+ return 0;
+ }
+}
+
+int wait_for_terminate_and_warn(const char *name, pid_t pid) {
+ int r;
+ siginfo_t status;
+
+ assert(name);
+ assert(pid > 1);
+
+ if ((r = wait_for_terminate(pid, &status)) < 0) {
+ log_warning("Failed to wait for %s: %s", name, strerror(-r));
+ return r;
+ }
+
+ if (status.si_code == CLD_EXITED) {
+ if (status.si_status != 0) {
+ log_warning("%s failed with error code %i.", name, status.si_status);
+ return status.si_status;
+ }
+
+ log_debug("%s succeeded.", name);
+ return 0;
+
+ } else if (status.si_code == CLD_KILLED ||
+ status.si_code == CLD_DUMPED) {
+
+ log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
+ return -EPROTO;
+ }
+
+ log_warning("%s failed due to unknown reason.", name);
+ return -EPROTO;
+
+}
+
+_noreturn_ void freeze(void) {
+
+ /* Make sure nobody waits for us on a socket anymore */
+ close_all_fds(NULL, 0);
+
+ sync();
+
+ for (;;)
+ pause();
+}
+
+bool null_or_empty(struct stat *st) {
+ assert(st);
+
+ if (S_ISREG(st->st_mode) && st->st_size <= 0)
+ return true;
+
+ if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
+ return true;
+
+ return false;
+}
+
+int null_or_empty_path(const char *fn) {
+ struct stat st;
+
+ assert(fn);
+
+ if (stat(fn, &st) < 0)
+ return -errno;
+
+ return null_or_empty(&st);
+}
+
+DIR *xopendirat(int fd, const char *name, int flags) {
+ int nfd;
+ DIR *d;
+
+ if ((nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags)) < 0)
+ return NULL;
+
+ if (!(d = fdopendir(nfd))) {
+ close_nointr_nofail(nfd);
+ return NULL;
+ }
+
+ return d;
+}
+
+int signal_from_string_try_harder(const char *s) {
+ int signo;
+ assert(s);
+
+ if ((signo = signal_from_string(s)) <= 0)
+ if (startswith(s, "SIG"))
+ return signal_from_string(s+3);
+
+ return signo;
+}
+
+void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
+
+ assert(f);
+ assert(name);
+ assert(t);
+
+ if (!dual_timestamp_is_set(t))
+ return;
+
+ fprintf(f, "%s=%llu %llu\n",
+ name,
+ (unsigned long long) t->realtime,
+ (unsigned long long) t->monotonic);
+}
+
+void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
+ unsigned long long a, b;
+
+ assert(value);
+ assert(t);
+
+ if (sscanf(value, "%lli %llu", &a, &b) != 2)
+ log_debug("Failed to parse finish timestamp value %s", value);
+ else {
+ t->realtime = a;
+ t->monotonic = b;
+ }
+}
+
+char *fstab_node_to_udev_node(const char *p) {
+ char *dn, *t, *u;
+ int r;
+
+ /* FIXME: to follow udev's logic 100% we need to leave valid
+ * UTF8 chars unescaped */
+
+ if (startswith(p, "LABEL=")) {
+
+ if (!(u = unquote(p+6, "\"\'")))
+ return NULL;
+
+ t = xescape(u, "/ ");
+ free(u);
+
+ if (!t)
+ return NULL;
+
+ r = asprintf(&dn, "/dev/disk/by-label/%s", t);
+ free(t);
+
+ if (r < 0)
+ return NULL;
+
+ return dn;
+ }
+
+ if (startswith(p, "UUID=")) {
+
+ if (!(u = unquote(p+5, "\"\'")))
+ return NULL;
+
+ t = xescape(u, "/ ");
+ free(u);
+
+ if (!t)
+ return NULL;
+
+ r = asprintf(&dn, "/dev/disk/by-uuid/%s", t);
+ free(t);
+
+ if (r < 0)
+ return NULL;
+
+ return dn;
+ }
+
+ return strdup(p);
+}
+
+void filter_environ(const char *prefix) {
+ int i, j;
+ assert(prefix);
+
+ if (!environ)
+ return;
+
+ for (i = 0, j = 0; environ[i]; i++) {
+
+ if (startswith(environ[i], prefix))
+ continue;
+
+ environ[j++] = environ[i];
+ }
+
+ environ[j] = NULL;
+}
+
+bool tty_is_vc(const char *tty) {
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ return vtnr_from_tty(tty) >= 0;
+}
+
+int vtnr_from_tty(const char *tty) {
+ int i, r;
+
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ if (!startswith(tty, "tty") )
+ return -EINVAL;
+
+ if (tty[3] < '0' || tty[3] > '9')
+ return -EINVAL;
+
+ r = safe_atoi(tty+3, &i);
+ if (r < 0)
+ return r;
+
+ if (i < 0 || i > 63)
+ return -EINVAL;
+
+ return i;
+}
+
+bool tty_is_vc_resolve(const char *tty) {
+ char *active = NULL;
+ bool b;
+
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ /* Resolve where /dev/console is pointing to */
+ if (streq(tty, "console"))
+ if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
+ /* If multiple log outputs are configured the
+ * last one is what /dev/console points to */
+ tty = strrchr(active, ' ');
+ if (tty)
+ tty++;
+ else
+ tty = active;
+ }
+
+ b = tty_is_vc(tty);
+ free(active);
+
+ return b;
+}
+
+const char *default_term_for_tty(const char *tty) {
+ assert(tty);
+
+ return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt100";
+}
+
+bool dirent_is_file(const struct dirent *de) {
+ assert(de);
+
+ if (ignore_file(de->d_name))
+ return false;
+
+ if (de->d_type != DT_REG &&
+ de->d_type != DT_LNK &&
+ de->d_type != DT_UNKNOWN)
+ return false;
+
+ return true;
+}
+
+bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
+ assert(de);
+
+ if (!dirent_is_file(de))
+ return false;
+
+ return endswith(de->d_name, suffix);
+}
+
+void execute_directory(const char *directory, DIR *d, char *argv[]) {
+ DIR *_d = NULL;
+ struct dirent *de;
+ Hashmap *pids = NULL;
+
+ assert(directory);
+
+ /* Executes all binaries in a directory in parallel and waits
+ * until all they all finished. */
+
+ if (!d) {
+ if (!(_d = opendir(directory))) {
+
+ if (errno == ENOENT)
+ return;
+
+ log_error("Failed to enumerate directory %s: %m", directory);
+ return;
+ }
+
+ d = _d;
+ }
+
+ if (!(pids = hashmap_new(trivial_hash_func, trivial_compare_func))) {
+ log_error("Failed to allocate set.");
+ goto finish;
+ }
+
+ while ((de = readdir(d))) {
+ char *path;
+ pid_t pid;
+ int k;
+
+ if (!dirent_is_file(de))
+ continue;
+
+ if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) {
+ log_error("Out of memory");
+ continue;
+ }
+
+ if ((pid = fork()) < 0) {
+ log_error("Failed to fork: %m");
+ free(path);
+ continue;
+ }
+
+ if (pid == 0) {
+ char *_argv[2];
+ /* Child */
+
+ if (!argv) {
+ _argv[0] = path;
+ _argv[1] = NULL;
+ argv = _argv;
+ } else
+ if (!argv[0])
+ argv[0] = path;
+
+ execv(path, argv);
+
+ log_error("Failed to execute %s: %m", path);
+ _exit(EXIT_FAILURE);
+ }
+
+ log_debug("Spawned %s as %lu", path, (unsigned long) pid);
+
+ if ((k = hashmap_put(pids, UINT_TO_PTR(pid), path)) < 0) {
+ log_error("Failed to add PID to set: %s", strerror(-k));
+ free(path);
+ }
+ }
+
+ while (!hashmap_isempty(pids)) {
+ pid_t pid = PTR_TO_UINT(hashmap_first_key(pids));
+ siginfo_t si;
+ char *path;
+
+ zero(si);
+ if (waitid(P_PID, pid, &si, WEXITED) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ log_error("waitid() failed: %m");
+ goto finish;
+ }
+
+ if ((path = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) {
+ if (!is_clean_exit(si.si_code, si.si_status)) {
+ if (si.si_code == CLD_EXITED)
+ log_error("%s exited with exit status %i.", path, si.si_status);
+ else
+ log_error("%s terminated by signal %s.", path, signal_to_string(si.si_status));
+ } else
+ log_debug("%s exited successfully.", path);
+
+ free(path);
+ }
+ }
+
+finish:
+ if (_d)
+ closedir(_d);
+
+ if (pids)
+ hashmap_free_free(pids);
+}
+
+int kill_and_sigcont(pid_t pid, int sig) {
+ int r;
+
+ r = kill(pid, sig) < 0 ? -errno : 0;
+
+ if (r >= 0)
+ kill(pid, SIGCONT);
+
+ return r;
+}
+
+bool nulstr_contains(const char*nulstr, const char *needle) {
+ const char *i;
+
+ if (!nulstr)
+ return false;
+
+ NULSTR_FOREACH(i, nulstr)
+ if (streq(i, needle))
+ return true;
+
+ return false;
+}
+
+bool plymouth_running(void) {
+ return access("/run/plymouth/pid", F_OK) >= 0;
+}
+
+void parse_syslog_priority(char **p, int *priority) {
+ int a = 0, b = 0, c = 0;
+ int k;
+
+ assert(p);
+ assert(*p);
+ assert(priority);
+
+ if ((*p)[0] != '<')
+ return;
+
+ if (!strchr(*p, '>'))
+ return;
+
+ if ((*p)[2] == '>') {
+ c = undecchar((*p)[1]);
+ k = 3;
+ } else if ((*p)[3] == '>') {
+ b = undecchar((*p)[1]);
+ c = undecchar((*p)[2]);
+ k = 4;
+ } else if ((*p)[4] == '>') {
+ a = undecchar((*p)[1]);
+ b = undecchar((*p)[2]);
+ c = undecchar((*p)[3]);
+ k = 5;
+ } else
+ return;
+
+ if (a < 0 || b < 0 || c < 0)
+ return;
+
+ *priority = a*100+b*10+c;
+ *p += k;
+}
+
+void skip_syslog_pid(char **buf) {
+ char *p;
+
+ assert(buf);
+ assert(*buf);
+
+ p = *buf;
+
+ if (*p != '[')
+ return;
+
+ p++;
+ p += strspn(p, "0123456789");
+
+ if (*p != ']')
+ return;
+
+ p++;
+
+ *buf = p;
+}
+
+void skip_syslog_date(char **buf) {
+ enum {
+ LETTER,
+ SPACE,
+ NUMBER,
+ SPACE_OR_NUMBER,
+ COLON
+ } sequence[] = {
+ LETTER, LETTER, LETTER,
+ SPACE,
+ SPACE_OR_NUMBER, NUMBER,
+ SPACE,
+ SPACE_OR_NUMBER, NUMBER,
+ COLON,
+ SPACE_OR_NUMBER, NUMBER,
+ COLON,
+ SPACE_OR_NUMBER, NUMBER,
+ SPACE
+ };
+
+ char *p;
+ unsigned i;
+
+ assert(buf);
+ assert(*buf);
+
+ p = *buf;
+
+ for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
+
+ if (!*p)
+ return;
+
+ switch (sequence[i]) {
+
+ case SPACE:
+ if (*p != ' ')
+ return;
+ break;
+
+ case SPACE_OR_NUMBER:
+ if (*p == ' ')
+ break;
+
+ /* fall through */
+
+ case NUMBER:
+ if (*p < '0' || *p > '9')
+ return;
+
+ break;
+
+ case LETTER:
+ if (!(*p >= 'A' && *p <= 'Z') &&
+ !(*p >= 'a' && *p <= 'z'))
+ return;
+
+ break;
+
+ case COLON:
+ if (*p != ':')
+ return;
+ break;
+
+ }
+ }
+
+ *buf = p;
+}
+
+char* strshorten(char *s, size_t l) {
+ assert(s);
+
+ if (l < strlen(s))
+ s[l] = 0;
+
+ return s;
+}
+
+static bool hostname_valid_char(char c) {
+ return
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ c == '-' ||
+ c == '_' ||
+ c == '.';
+}
+
+bool hostname_is_valid(const char *s) {
+ const char *p;
+
+ if (isempty(s))
+ return false;
+
+ for (p = s; *p; p++)
+ if (!hostname_valid_char(*p))
+ return false;
+
+ if (p-s > HOST_NAME_MAX)
+ return false;
+
+ return true;
+}
+
+char* hostname_cleanup(char *s) {
+ char *p, *d;
+
+ for (p = s, d = s; *p; p++)
+ if ((*p >= 'a' && *p <= 'z') ||
+ (*p >= 'A' && *p <= 'Z') ||
+ (*p >= '0' && *p <= '9') ||
+ *p == '-' ||
+ *p == '_' ||
+ *p == '.')
+ *(d++) = *p;
+
+ *d = 0;
+
+ strshorten(s, HOST_NAME_MAX);
+ return s;
+}
+
+int pipe_eof(int fd) {
+ struct pollfd pollfd;
+ int r;
+
+ zero(pollfd);
+ pollfd.fd = fd;
+ pollfd.events = POLLIN|POLLHUP;
+
+ r = poll(&pollfd, 1, 0);
+ if (r < 0)
+ return -errno;
+
+ if (r == 0)
+ return 0;
+
+ return pollfd.revents & POLLHUP;
+}
+
+int fd_wait_for_event(int fd, int event, usec_t t) {
+ struct pollfd pollfd;
+ int r;
+
+ zero(pollfd);
+ pollfd.fd = fd;
+ pollfd.events = event;
+
+ r = poll(&pollfd, 1, t == (usec_t) -1 ? -1 : (int) (t / USEC_PER_MSEC));
+ if (r < 0)
+ return -errno;
+
+ if (r == 0)
+ return 0;
+
+ return pollfd.revents;
+}
+
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
+ FILE *f;
+ char *t;
+ const char *fn;
+ size_t k;
+ int fd;
+
+ assert(path);
+ assert(_f);
+ assert(_temp_path);
+
+ t = new(char, strlen(path) + 1 + 6 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ fn = file_name_from_path(path);
+ k = fn-path;
+ memcpy(t, path, k);
+ t[k] = '.';
+ stpcpy(stpcpy(t+k+1, fn), "XXXXXX");
+
+ fd = mkostemp(t, O_WRONLY|O_CLOEXEC);
+ if (fd < 0) {
+ free(t);
+ return -errno;
+ }
+
+ f = fdopen(fd, "we");
+ if (!f) {
+ unlink(t);
+ free(t);
+ return -errno;
+ }
+
+ *_f = f;
+ *_temp_path = t;
+
+ return 0;
+}
+
+int terminal_vhangup_fd(int fd) {
+ assert(fd >= 0);
+
+ if (ioctl(fd, TIOCVHANGUP) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int terminal_vhangup(const char *name) {
+ int fd, r;
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ r = terminal_vhangup_fd(fd);
+ close_nointr_nofail(fd);
+
+ return r;
+}
+
+int vt_disallocate(const char *name) {
+ int fd, r;
+ unsigned u;
+
+ /* Deallocate the VT if possible. If not possible
+ * (i.e. because it is the active one), at least clear it
+ * entirely (including the scrollback buffer) */
+
+ if (!startswith(name, "/dev/"))
+ return -EINVAL;
+
+ if (!tty_is_vc(name)) {
+ /* So this is not a VT. I guess we cannot deallocate
+ * it then. But let's at least clear the screen */
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ loop_write(fd,
+ "\033[r" /* clear scrolling region */
+ "\033[H" /* move home */
+ "\033[2J", /* clear screen */
+ 10, false);
+ close_nointr_nofail(fd);
+
+ return 0;
+ }
+
+ if (!startswith(name, "/dev/tty"))
+ return -EINVAL;
+
+ r = safe_atou(name+8, &u);
+ if (r < 0)
+ return r;
+
+ if (u <= 0)
+ return -EINVAL;
+
+ /* Try to deallocate */
+ fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ r = ioctl(fd, VT_DISALLOCATE, u);
+ close_nointr_nofail(fd);
+
+ if (r >= 0)
+ return 0;
+
+ if (errno != EBUSY)
+ return -errno;
+
+ /* Couldn't deallocate, so let's clear it fully with
+ * scrollback */
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ loop_write(fd,
+ "\033[r" /* clear scrolling region */
+ "\033[H" /* move home */
+ "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
+ 10, false);
+ close_nointr_nofail(fd);
+
+ return 0;
+}
+
+static int files_add(Hashmap *h, const char *path, const char *suffix) {
+ DIR *dir;
+ struct dirent buffer, *de;
+ int r = 0;
+
+ dir = opendir(path);
+ if (!dir) {
+ if (errno == ENOENT)
+ return 0;
+ return -errno;
+ }
+
+ for (;;) {
+ int k;
+ char *p, *f;
+
+ k = readdir_r(dir, &buffer, &de);
+ if (k != 0) {
+ r = -k;
+ goto finish;
+ }
+
+ if (!de)
+ break;
+
+ if (!dirent_is_file_with_suffix(de, suffix))
+ continue;
+
+ if (asprintf(&p, "%s/%s", path, de->d_name) < 0) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ f = canonicalize_file_name(p);
+ if (!f) {
+ log_error("Failed to canonicalize file name '%s': %m", p);
+ free(p);
+ continue;
+ }
+ free(p);
+
+ log_debug("found: %s\n", f);
+ if (hashmap_put(h, file_name_from_path(f), f) <= 0)
+ free(f);
+ }
+
+finish:
+ closedir(dir);
+ return r;
+}
+
+static int base_cmp(const void *a, const void *b) {
+ const char *s1, *s2;
+
+ s1 = *(char * const *)a;
+ s2 = *(char * const *)b;
+ return strcmp(file_name_from_path(s1), file_name_from_path(s2));
+}
+
+int conf_files_list(char ***strv, const char *suffix, const char *dir, ...) {
+ Hashmap *fh = NULL;
+ char **dirs = NULL;
+ char **files = NULL;
+ char **p;
+ va_list ap;
+ int r = 0;
+
+ va_start(ap, dir);
+ dirs = strv_new_ap(dir, ap);
+ va_end(ap);
+ if (!dirs) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ if (!strv_path_canonicalize(dirs)) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ if (!strv_uniq(dirs)) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ fh = hashmap_new(string_hash_func, string_compare_func);
+ if (!fh) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ STRV_FOREACH(p, dirs) {
+ if (files_add(fh, *p, suffix) < 0) {
+ log_error("Failed to search for files.");
+ r = -EINVAL;
+ goto finish;
+ }
+ }
+
+ files = hashmap_get_strv(fh);
+ if (files == NULL) {
+ log_error("Failed to compose list of files.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ qsort(files, hashmap_size(fh), sizeof(char *), base_cmp);
+
+finish:
+ strv_free(dirs);
+ hashmap_free(fh);
+ *strv = files;
+ return r;
+}
+
+int hwclock_is_localtime(void) {
+ FILE *f;
+ bool local = false;
+
+ /*
+ * The third line of adjtime is "UTC" or "LOCAL" or nothing.
+ * # /etc/adjtime
+ * 0.0 0 0
+ * 0
+ * UTC
+ */
+ f = fopen("/etc/adjtime", "re");
+ if (f) {
+ char line[LINE_MAX];
+ bool b;
+
+ b = fgets(line, sizeof(line), f) &&
+ fgets(line, sizeof(line), f) &&
+ fgets(line, sizeof(line), f);
+
+ fclose(f);
+
+ if (!b)
+ return -EIO;
+
+
+ truncate_nl(line);
+ local = streq(line, "LOCAL");
+
+ } else if (errno != -ENOENT)
+ return -errno;
+
+ return local;
+}
+
+int hwclock_apply_localtime_delta(int *min) {
+ const struct timeval *tv_null = NULL;
+ struct timespec ts;
+ struct tm *tm;
+ int minuteswest;
+ struct timezone tz;
+
+ assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+ assert_se(tm = localtime(&ts.tv_sec));
+ minuteswest = tm->tm_gmtoff / 60;
+
+ tz.tz_minuteswest = -minuteswest;
+ tz.tz_dsttime = 0; /* DST_NONE*/
+
+ /*
+ * If the hardware clock does not run in UTC, but in local time:
+ * The very first time we set the kernel's timezone, it will warp
+ * the clock so that it runs in UTC instead of local time.
+ */
+ if (settimeofday(tv_null, &tz) < 0)
+ return -errno;
+ if (min)
+ *min = minuteswest;
+ return 0;
+}
+
+int hwclock_reset_localtime_delta(void) {
+ const struct timeval *tv_null = NULL;
+ struct timezone tz;
+
+ tz.tz_minuteswest = 0;
+ tz.tz_dsttime = 0; /* DST_NONE*/
+
+ if (settimeofday(tv_null, &tz) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int rtc_open(int flags) {
+ int fd;
+ DIR *d;
+
+ /* First, we try to make use of the /dev/rtc symlink. If that
+ * doesn't exist, we open the first RTC which has hctosys=1
+ * set. If we don't find any we just take the first RTC that
+ * exists at all. */
+
+ fd = open("/dev/rtc", flags);
+ if (fd >= 0)
+ return fd;
+
+ d = opendir("/sys/class/rtc");
+ if (!d)
+ goto fallback;
+
+ for (;;) {
+ char *p, *v;
+ struct dirent buf, *de;
+ int r;
+
+ r = readdir_r(d, &buf, &de);
+ if (r != 0)
+ goto fallback;
+
+ if (!de)
+ goto fallback;
+
+ if (ignore_file(de->d_name))
+ continue;
+
+ p = join("/sys/class/rtc/", de->d_name, "/hctosys", NULL);
+ if (!p) {
+ closedir(d);
+ return -ENOMEM;
+ }
+
+ r = read_one_line_file(p, &v);
+ free(p);
+
+ if (r < 0)
+ continue;
+
+ r = parse_boolean(v);
+ free(v);
+
+ if (r <= 0)
+ continue;
+
+ p = strappend("/dev/", de->d_name);
+ fd = open(p, flags);
+ free(p);
+
+ if (fd >= 0) {
+ closedir(d);
+ return fd;
+ }
+ }
+
+fallback:
+ if (d)
+ closedir(d);
+
+ fd = open("/dev/rtc0", flags);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
+int hwclock_get_time(struct tm *tm) {
+ int fd;
+ int err = 0;
+
+ assert(tm);
+
+ fd = rtc_open(O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ /* This leaves the timezone fields of struct tm
+ * uninitialized! */
+ if (ioctl(fd, RTC_RD_TIME, tm) < 0)
+ err = -errno;
+
+ /* We don't now daylight saving, so we reset this in order not
+ * to confused mktime(). */
+ tm->tm_isdst = -1;
+
+ close_nointr_nofail(fd);
+
+ return err;
+}
+
+int hwclock_set_time(const struct tm *tm) {
+ int fd;
+ int err = 0;
+
+ assert(tm);
+
+ fd = rtc_open(O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (ioctl(fd, RTC_SET_TIME, tm) < 0)
+ err = -errno;
+
+ close_nointr_nofail(fd);
+
+ return err;
+}
+
+int copy_file(const char *from, const char *to) {
+ int r, fdf, fdt;
+
+ assert(from);
+ assert(to);
+
+ fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fdf < 0)
+ return -errno;
+
+ fdt = open(to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY, 0644);
+ if (fdt < 0) {
+ close_nointr_nofail(fdf);
+ return -errno;
+ }
+
+ for (;;) {
+ char buf[PIPE_BUF];
+ ssize_t n, k;
+
+ n = read(fdf, buf, sizeof(buf));
+ if (n < 0) {
+ r = -errno;
+
+ close_nointr_nofail(fdf);
+ close_nointr(fdt);
+ unlink(to);
+
+ return r;
+ }
+
+ if (n == 0)
+ break;
+
+ errno = 0;
+ k = loop_write(fdt, buf, n, false);
+ if (n != k) {
+ r = k < 0 ? k : (errno ? -errno : -EIO);
+
+ close_nointr_nofail(fdf);
+ close_nointr(fdt);
+
+ unlink(to);
+ return r;
+ }
+ }
+
+ close_nointr_nofail(fdf);
+ r = close_nointr(fdt);
+
+ if (r < 0) {
+ unlink(to);
+ return r;
+ }
+
+ return 0;
+}
+
+int symlink_or_copy(const char *from, const char *to) {
+ char *pf = NULL, *pt = NULL;
+ struct stat a, b;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ if (parent_of_path(from, &pf) < 0 ||
+ parent_of_path(to, &pt) < 0) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (stat(pf, &a) < 0 ||
+ stat(pt, &b) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (a.st_dev != b.st_dev) {
+ free(pf);
+ free(pt);
+
+ return copy_file(from, to);
+ }
+
+ if (symlink(from, to) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ free(pf);
+ free(pt);
+
+ return r;
+}
+
+int symlink_or_copy_atomic(const char *from, const char *to) {
+ char *t, *x;
+ const char *fn;
+ size_t k;
+ unsigned long long ull;
+ unsigned i;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ t = new(char, strlen(to) + 1 + 16 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ fn = file_name_from_path(to);
+ k = fn-to;
+ memcpy(t, to, k);
+ t[k] = '.';
+ x = stpcpy(t+k+1, fn);
+
+ ull = random_ull();
+ for (i = 0; i < 16; i++) {
+ *(x++) = hexchar(ull & 0xF);
+ ull >>= 4;
+ }
+
+ *x = 0;
+
+ r = symlink_or_copy(from, t);
+ if (r < 0) {
+ unlink(t);
+ free(t);
+ return r;
+ }
+
+ if (rename(t, to) < 0) {
+ r = -errno;
+ unlink(t);
+ free(t);
+ return r;
+ }
+
+ free(t);
+ return r;
+}
+
+bool display_is_local(const char *display) {
+ assert(display);
+
+ return
+ display[0] == ':' &&
+ display[1] >= '0' &&
+ display[1] <= '9';
+}
+
+int socket_from_display(const char *display, char **path) {
+ size_t k;
+ char *f, *c;
+
+ assert(display);
+ assert(path);
+
+ if (!display_is_local(display))
+ return -EINVAL;
+
+ k = strspn(display+1, "0123456789");
+
+ f = new(char, sizeof("/tmp/.X11-unix/X") + k);
+ if (!f)
+ return -ENOMEM;
+
+ c = stpcpy(f, "/tmp/.X11-unix/X");
+ memcpy(c, display+1, k);
+ c[k] = 0;
+
+ *path = f;
+
+ return 0;
+}
+
+int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
+ struct passwd *p;
+ uid_t u;
+
+ assert(username);
+ assert(*username);
+
+ /* We enforce some special rules for uid=0: in order to avoid
+ * NSS lookups for root we hardcode its data. */
+
+ if (streq(*username, "root") || streq(*username, "0")) {
+ *username = "root";
+
+ if (uid)
+ *uid = 0;
+
+ if (gid)
+ *gid = 0;
+
+ if (home)
+ *home = "/root";
+ return 0;
+ }
+
+ if (parse_uid(*username, &u) >= 0) {
+ errno = 0;
+ p = getpwuid(u);
+
+ /* If there are multiple users with the same id, make
+ * sure to leave $USER to the configured value instead
+ * of the first occurrence in the database. However if
+ * the uid was configured by a numeric uid, then let's
+ * pick the real username from /etc/passwd. */
+ if (p)
+ *username = p->pw_name;
+ } else {
+ errno = 0;
+ p = getpwnam(*username);
+ }
+
+ if (!p)
+ return errno != 0 ? -errno : -ESRCH;
+
+ if (uid)
+ *uid = p->pw_uid;
+
+ if (gid)
+ *gid = p->pw_gid;
+
+ if (home)
+ *home = p->pw_dir;
+
+ return 0;
+}
+
+int get_group_creds(const char **groupname, gid_t *gid) {
+ struct group *g;
+ gid_t id;
+
+ assert(groupname);
+
+ /* We enforce some special rules for gid=0: in order to avoid
+ * NSS lookups for root we hardcode its data. */
+
+ if (streq(*groupname, "root") || streq(*groupname, "0")) {
+ *groupname = "root";
+
+ if (gid)
+ *gid = 0;
+
+ return 0;
+ }
+
+ if (parse_gid(*groupname, &id) >= 0) {
+ errno = 0;
+ g = getgrgid(id);
+
+ if (g)
+ *groupname = g->gr_name;
+ } else {
+ errno = 0;
+ g = getgrnam(*groupname);
+ }
+
+ if (!g)
+ return errno != 0 ? -errno : -ESRCH;
+
+ if (gid)
+ *gid = g->gr_gid;
+
+ return 0;
+}
+
+int in_group(const char *name) {
+ gid_t gid, *gids;
+ int ngroups_max, r, i;
+
+ r = get_group_creds(&name, &gid);
+ if (r < 0)
+ return r;
+
+ if (getgid() == gid)
+ return 1;
+
+ if (getegid() == gid)
+ return 1;
+
+ ngroups_max = sysconf(_SC_NGROUPS_MAX);
+ assert(ngroups_max > 0);
+
+ gids = alloca(sizeof(gid_t) * ngroups_max);
+
+ r = getgroups(ngroups_max, gids);
+ if (r < 0)
+ return -errno;
+
+ for (i = 0; i < r; i++)
+ if (gids[i] == gid)
+ return 1;
+
+ return 0;
+}
+
+int glob_exists(const char *path) {
+ glob_t g;
+ int r, k;
+
+ assert(path);
+
+ zero(g);
+ errno = 0;
+ k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+
+ if (k == GLOB_NOMATCH)
+ r = 0;
+ else if (k == GLOB_NOSPACE)
+ r = -ENOMEM;
+ else if (k == 0)
+ r = !strv_isempty(g.gl_pathv);
+ else
+ r = errno ? -errno : -EIO;
+
+ globfree(&g);
+
+ return r;
+}
+
+int dirent_ensure_type(DIR *d, struct dirent *de) {
+ struct stat st;
+
+ assert(d);
+ assert(de);
+
+ if (de->d_type != DT_UNKNOWN)
+ return 0;
+
+ if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
+ return -errno;
+
+ de->d_type =
+ S_ISREG(st.st_mode) ? DT_REG :
+ S_ISDIR(st.st_mode) ? DT_DIR :
+ S_ISLNK(st.st_mode) ? DT_LNK :
+ S_ISFIFO(st.st_mode) ? DT_FIFO :
+ S_ISSOCK(st.st_mode) ? DT_SOCK :
+ S_ISCHR(st.st_mode) ? DT_CHR :
+ S_ISBLK(st.st_mode) ? DT_BLK :
+ DT_UNKNOWN;
+
+ return 0;
+}
+
+int in_search_path(const char *path, char **search) {
+ char **i, *parent;
+ int r;
+
+ r = parent_of_path(path, &parent);
+ if (r < 0)
+ return r;
+
+ r = 0;
+
+ STRV_FOREACH(i, search) {
+ if (path_equal(parent, *i)) {
+ r = 1;
+ break;
+ }
+ }
+
+ free(parent);
+
+ return r;
+}
+
+int get_files_in_directory(const char *path, char ***list) {
+ DIR *d;
+ int r = 0;
+ unsigned n = 0;
+ char **l = NULL;
+
+ assert(path);
+
+ /* Returns all files in a directory in *list, and the number
+ * of files as return value. If list is NULL returns only the
+ * number */
+
+ d = opendir(path);
+ if (!d)
+ return -errno;
+
+ for (;;) {
+ struct dirent buffer, *de;
+ int k;
+
+ k = readdir_r(d, &buffer, &de);
+ if (k != 0) {
+ r = -k;
+ goto finish;
+ }
+
+ if (!de)
+ break;
+
+ dirent_ensure_type(d, de);
+
+ if (!dirent_is_file(de))
+ continue;
+
+ if (list) {
+ if ((unsigned) r >= n) {
+ char **t;
+
+ n = MAX(16, 2*r);
+ t = realloc(l, sizeof(char*) * n);
+ if (!t) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ l = t;
+ }
+
+ assert((unsigned) r < n);
+
+ l[r] = strdup(de->d_name);
+ if (!l[r]) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ l[++r] = NULL;
+ } else
+ r++;
+ }
+
+finish:
+ if (d)
+ closedir(d);
+
+ if (r >= 0) {
+ if (list)
+ *list = l;
+ } else
+ strv_free(l);
+
+ return r;
+}
+
+char *join(const char *x, ...) {
+ va_list ap;
+ size_t l;
+ char *r, *p;
+
+ va_start(ap, x);
+
+ if (x) {
+ l = strlen(x);
+
+ for (;;) {
+ const char *t;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ l += strlen(t);
+ }
+ } else
+ l = 0;
+
+ va_end(ap);
+
+ r = new(char, l+1);
+ if (!r)
+ return NULL;
+
+ if (x) {
+ p = stpcpy(r, x);
+
+ va_start(ap, x);
+
+ for (;;) {
+ const char *t;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ p = stpcpy(p, t);
+ }
+
+ va_end(ap);
+ } else
+ r[0] = 0;
+
+ return r;
+}
+
+bool is_main_thread(void) {
+ static __thread int cached = 0;
+
+ if (_unlikely_(cached == 0))
+ cached = getpid() == gettid() ? 1 : -1;
+
+ return cached > 0;
+}
+
+int block_get_whole_disk(dev_t d, dev_t *ret) {
+ char *p, *s;
+ int r;
+ unsigned n, m;
+
+ assert(ret);
+
+ /* If it has a queue this is good enough for us */
+ if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
+ return -ENOMEM;
+
+ r = access(p, F_OK);
+ free(p);
+
+ if (r >= 0) {
+ *ret = d;
+ return 0;
+ }
+
+ /* If it is a partition find the originating device */
+ if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
+ return -ENOMEM;
+
+ r = access(p, F_OK);
+ free(p);
+
+ if (r < 0)
+ return -ENOENT;
+
+ /* Get parent dev_t */
+ if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
+ return -ENOMEM;
+
+ r = read_one_line_file(p, &s);
+ free(p);
+
+ if (r < 0)
+ return r;
+
+ r = sscanf(s, "%u:%u", &m, &n);
+ free(s);
+
+ if (r != 2)
+ return -EINVAL;
+
+ /* Only return this if it is really good enough for us. */
+ if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
+ return -ENOMEM;
+
+ r = access(p, F_OK);
+ free(p);
+
+ if (r >= 0) {
+ *ret = makedev(m, n);
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+int file_is_priv_sticky(const char *p) {
+ struct stat st;
+
+ assert(p);
+
+ if (lstat(p, &st) < 0)
+ return -errno;
+
+ return
+ (st.st_uid == 0 || st.st_uid == getuid()) &&
+ (st.st_mode & S_ISVTX);
+}
+
+static const char *const ioprio_class_table[] = {
+ [IOPRIO_CLASS_NONE] = "none",
+ [IOPRIO_CLASS_RT] = "realtime",
+ [IOPRIO_CLASS_BE] = "best-effort",
+ [IOPRIO_CLASS_IDLE] = "idle"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ioprio_class, int);
+
+static const char *const sigchld_code_table[] = {
+ [CLD_EXITED] = "exited",
+ [CLD_KILLED] = "killed",
+ [CLD_DUMPED] = "dumped",
+ [CLD_TRAPPED] = "trapped",
+ [CLD_STOPPED] = "stopped",
+ [CLD_CONTINUED] = "continued",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
+
+static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
+ [LOG_FAC(LOG_KERN)] = "kern",
+ [LOG_FAC(LOG_USER)] = "user",
+ [LOG_FAC(LOG_MAIL)] = "mail",
+ [LOG_FAC(LOG_DAEMON)] = "daemon",
+ [LOG_FAC(LOG_AUTH)] = "auth",
+ [LOG_FAC(LOG_SYSLOG)] = "syslog",
+ [LOG_FAC(LOG_LPR)] = "lpr",
+ [LOG_FAC(LOG_NEWS)] = "news",
+ [LOG_FAC(LOG_UUCP)] = "uucp",
+ [LOG_FAC(LOG_CRON)] = "cron",
+ [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
+ [LOG_FAC(LOG_FTP)] = "ftp",
+ [LOG_FAC(LOG_LOCAL0)] = "local0",
+ [LOG_FAC(LOG_LOCAL1)] = "local1",
+ [LOG_FAC(LOG_LOCAL2)] = "local2",
+ [LOG_FAC(LOG_LOCAL3)] = "local3",
+ [LOG_FAC(LOG_LOCAL4)] = "local4",
+ [LOG_FAC(LOG_LOCAL5)] = "local5",
+ [LOG_FAC(LOG_LOCAL6)] = "local6",
+ [LOG_FAC(LOG_LOCAL7)] = "local7"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(log_facility_unshifted, int);
+
+static const char *const log_level_table[] = {
+ [LOG_EMERG] = "emerg",
+ [LOG_ALERT] = "alert",
+ [LOG_CRIT] = "crit",
+ [LOG_ERR] = "err",
+ [LOG_WARNING] = "warning",
+ [LOG_NOTICE] = "notice",
+ [LOG_INFO] = "info",
+ [LOG_DEBUG] = "debug"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(log_level, int);
+
+static const char* const sched_policy_table[] = {
+ [SCHED_OTHER] = "other",
+ [SCHED_BATCH] = "batch",
+ [SCHED_IDLE] = "idle",
+ [SCHED_FIFO] = "fifo",
+ [SCHED_RR] = "rr"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(sched_policy, int);
+
+static const char* const rlimit_table[] = {
+ [RLIMIT_CPU] = "LimitCPU",
+ [RLIMIT_FSIZE] = "LimitFSIZE",
+ [RLIMIT_DATA] = "LimitDATA",
+ [RLIMIT_STACK] = "LimitSTACK",
+ [RLIMIT_CORE] = "LimitCORE",
+ [RLIMIT_RSS] = "LimitRSS",
+ [RLIMIT_NOFILE] = "LimitNOFILE",
+ [RLIMIT_AS] = "LimitAS",
+ [RLIMIT_NPROC] = "LimitNPROC",
+ [RLIMIT_MEMLOCK] = "LimitMEMLOCK",
+ [RLIMIT_LOCKS] = "LimitLOCKS",
+ [RLIMIT_SIGPENDING] = "LimitSIGPENDING",
+ [RLIMIT_MSGQUEUE] = "LimitMSGQUEUE",
+ [RLIMIT_NICE] = "LimitNICE",
+ [RLIMIT_RTPRIO] = "LimitRTPRIO",
+ [RLIMIT_RTTIME] = "LimitRTTIME"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
+
+static const char* const ip_tos_table[] = {
+ [IPTOS_LOWDELAY] = "low-delay",
+ [IPTOS_THROUGHPUT] = "throughput",
+ [IPTOS_RELIABILITY] = "reliability",
+ [IPTOS_LOWCOST] = "low-cost",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ip_tos, int);
+
+static const char *const __signal_table[] = {
+ [SIGHUP] = "HUP",
+ [SIGINT] = "INT",
+ [SIGQUIT] = "QUIT",
+ [SIGILL] = "ILL",
+ [SIGTRAP] = "TRAP",
+ [SIGABRT] = "ABRT",
+ [SIGBUS] = "BUS",
+ [SIGFPE] = "FPE",
+ [SIGKILL] = "KILL",
+ [SIGUSR1] = "USR1",
+ [SIGSEGV] = "SEGV",
+ [SIGUSR2] = "USR2",
+ [SIGPIPE] = "PIPE",
+ [SIGALRM] = "ALRM",
+ [SIGTERM] = "TERM",
+#ifdef SIGSTKFLT
+ [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
+#endif
+ [SIGCHLD] = "CHLD",
+ [SIGCONT] = "CONT",
+ [SIGSTOP] = "STOP",
+ [SIGTSTP] = "TSTP",
+ [SIGTTIN] = "TTIN",
+ [SIGTTOU] = "TTOU",
+ [SIGURG] = "URG",
+ [SIGXCPU] = "XCPU",
+ [SIGXFSZ] = "XFSZ",
+ [SIGVTALRM] = "VTALRM",
+ [SIGPROF] = "PROF",
+ [SIGWINCH] = "WINCH",
+ [SIGIO] = "IO",
+ [SIGPWR] = "PWR",
+ [SIGSYS] = "SYS"
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
+
+const char *signal_to_string(int signo) {
+ static __thread char buf[12];
+ const char *name;
+
+ name = __signal_to_string(signo);
+ if (name)
+ return name;
+
+ if (signo >= SIGRTMIN && signo <= SIGRTMAX)
+ snprintf(buf, sizeof(buf) - 1, "RTMIN+%d", signo - SIGRTMIN);
+ else
+ snprintf(buf, sizeof(buf) - 1, "%d", signo);
+ char_array_0(buf);
+ return buf;
+}
+
+int signal_from_string(const char *s) {
+ int signo;
+ int offset = 0;
+ unsigned u;
+
+ signo =__signal_from_string(s);
+ if (signo > 0)
+ return signo;
+
+ if (startswith(s, "RTMIN+")) {
+ s += 6;
+ offset = SIGRTMIN;
+ }
+ if (safe_atou(s, &u) >= 0) {
+ signo = (int) u + offset;
+ if (signo > 0 && signo < _NSIG)
+ return signo;
+ }
+ return -1;
+}
+
+bool kexec_loaded(void) {
+ bool loaded = false;
+ char *s;
+
+ if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
+ if (s[0] == '1')
+ loaded = true;
+ free(s);
+ }
+ return loaded;
+}
+
+int strdup_or_null(const char *a, char **b) {
+ char *c;
+
+ assert(b);
+
+ if (!a) {
+ *b = NULL;
+ return 0;
+ }
+
+ c = strdup(a);
+ if (!c)
+ return -ENOMEM;
+
+ *b = c;
+ return 0;
+}
+
+int prot_from_flags(int flags) {
+
+ switch (flags & O_ACCMODE) {
+
+ case O_RDONLY:
+ return PROT_READ;
+
+ case O_WRONLY:
+ return PROT_WRITE;
+
+ case O_RDWR:
+ return PROT_READ|PROT_WRITE;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+char *format_bytes(char *buf, size_t l, off_t t) {
+ unsigned i;
+
+ static const struct {
+ const char *suffix;
+ off_t factor;
+ } table[] = {
+ { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+ { "G", 1024ULL*1024ULL*1024ULL },
+ { "M", 1024ULL*1024ULL },
+ { "K", 1024ULL },
+ };
+
+ for (i = 0; i < ELEMENTSOF(table); i++) {
+
+ if (t >= table[i].factor) {
+ snprintf(buf, l,
+ "%llu.%llu%s",
+ (unsigned long long) (t / table[i].factor),
+ (unsigned long long) (((t*10ULL) / table[i].factor) % 10ULL),
+ table[i].suffix);
+
+ goto finish;
+ }
+ }
+
+ snprintf(buf, l, "%lluB", (unsigned long long) t);
+
+finish:
+ buf[l-1] = 0;
+ return buf;
+
+}
+
+void* memdup(const void *p, size_t l) {
+ void *r;
+
+ assert(p);
+
+ r = malloc(l);
+ if (!r)
+ return NULL;
+
+ memcpy(r, p, l);
+ return r;
+}
+
+int fd_inc_sndbuf(int fd, size_t n) {
+ int r, value;
+ socklen_t l = sizeof(value);
+
+ r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
+ if (r >= 0 &&
+ l == sizeof(value) &&
+ (size_t) value >= n*2)
+ return 0;
+
+ value = (int) n;
+ r = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value));
+ if (r < 0)
+ return -errno;
+
+ return 1;
+}
+
+int fd_inc_rcvbuf(int fd, size_t n) {
+ int r, value;
+ socklen_t l = sizeof(value);
+
+ r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
+ if (r >= 0 &&
+ l == sizeof(value) &&
+ (size_t) value >= n*2)
+ return 0;
+
+ value = (int) n;
+ r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value));
+ if (r < 0)
+ return -errno;
+
+ return 1;
+}
diff --git a/src/shared/util.h b/src/shared/util.h
new file mode 100644
index 0000000..e96d56d
--- /dev/null
+++ b/src/shared/util.h
@@ -0,0 +1,536 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <time.h>
+#include <sys/time.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sched.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include "macro.h"
+
+typedef uint64_t usec_t;
+
+typedef struct dual_timestamp {
+ usec_t realtime;
+ usec_t monotonic;
+} dual_timestamp;
+
+#define MSEC_PER_SEC 1000ULL
+#define USEC_PER_SEC 1000000ULL
+#define USEC_PER_MSEC 1000ULL
+#define NSEC_PER_SEC 1000000000ULL
+#define NSEC_PER_MSEC 1000000ULL
+#define NSEC_PER_USEC 1000ULL
+
+#define USEC_PER_MINUTE (60ULL*USEC_PER_SEC)
+#define USEC_PER_HOUR (60ULL*USEC_PER_MINUTE)
+#define USEC_PER_DAY (24ULL*USEC_PER_HOUR)
+#define USEC_PER_WEEK (7ULL*USEC_PER_DAY)
+#define USEC_PER_MONTH (2629800ULL*USEC_PER_SEC)
+#define USEC_PER_YEAR (31557600ULL*USEC_PER_SEC)
+
+/* What is interpreted as whitespace? */
+#define WHITESPACE " \t\n\r"
+#define NEWLINE "\n\r"
+#define QUOTES "\"\'"
+#define COMMENTS "#;\n"
+
+#define FORMAT_TIMESTAMP_MAX 64
+#define FORMAT_TIMESTAMP_PRETTY_MAX 256
+#define FORMAT_TIMESPAN_MAX 64
+#define FORMAT_BYTES_MAX 8
+
+#define ANSI_HIGHLIGHT_ON "\x1B[1;39m"
+#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m"
+#define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m"
+#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
+
+usec_t now(clockid_t clock);
+
+dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
+dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
+
+#define dual_timestamp_is_set(ts) ((ts)->realtime > 0)
+
+usec_t timespec_load(const struct timespec *ts);
+struct timespec *timespec_store(struct timespec *ts, usec_t u);
+
+usec_t timeval_load(const struct timeval *tv);
+struct timeval *timeval_store(struct timeval *tv, usec_t u);
+
+size_t page_size(void);
+#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
+
+bool streq_ptr(const char *a, const char *b);
+
+#define new(t, n) ((t*) malloc(sizeof(t)*(n)))
+
+#define new0(t, n) ((t*) calloc((n), sizeof(t)))
+
+#define malloc0(n) (calloc((n), 1))
+
+static inline const char* yes_no(bool b) {
+ return b ? "yes" : "no";
+}
+
+static inline const char* strempty(const char *s) {
+ return s ? s : "";
+}
+
+static inline const char* strnull(const char *s) {
+ return s ? s : "(null)";
+}
+
+static inline const char *strna(const char *s) {
+ return s ? s : "n/a";
+}
+
+static inline bool is_path_absolute(const char *p) {
+ return *p == '/';
+}
+
+static inline bool isempty(const char *p) {
+ return !p || !p[0];
+}
+
+bool endswith(const char *s, const char *postfix);
+bool startswith(const char *s, const char *prefix);
+bool startswith_no_case(const char *s, const char *prefix);
+
+bool first_word(const char *s, const char *word);
+
+int close_nointr(int fd);
+void close_nointr_nofail(int fd);
+void close_many(const int fds[], unsigned n_fd);
+
+int parse_boolean(const char *v);
+int parse_usec(const char *t, usec_t *usec);
+int parse_bytes(const char *t, off_t *bytes);
+int parse_pid(const char *s, pid_t* ret_pid);
+int parse_uid(const char *s, uid_t* ret_uid);
+#define parse_gid(s, ret_uid) parse_uid(s, ret_uid)
+
+int safe_atou(const char *s, unsigned *ret_u);
+int safe_atoi(const char *s, int *ret_i);
+
+int safe_atollu(const char *s, unsigned long long *ret_u);
+int safe_atolli(const char *s, long long int *ret_i);
+
+#if __WORDSIZE == 32
+static inline int safe_atolu(const char *s, unsigned long *ret_u) {
+ assert_cc(sizeof(unsigned long) == sizeof(unsigned));
+ return safe_atou(s, (unsigned*) ret_u);
+}
+static inline int safe_atoli(const char *s, long int *ret_u) {
+ assert_cc(sizeof(long int) == sizeof(int));
+ return safe_atoi(s, (int*) ret_u);
+}
+#else
+static inline int safe_atolu(const char *s, unsigned long *ret_u) {
+ assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
+ return safe_atollu(s, (unsigned long long*) ret_u);
+}
+static inline int safe_atoli(const char *s, long int *ret_u) {
+ assert_cc(sizeof(long int) == sizeof(long long int));
+ return safe_atolli(s, (long long int*) ret_u);
+}
+#endif
+
+static inline int safe_atou32(const char *s, uint32_t *ret_u) {
+ assert_cc(sizeof(uint32_t) == sizeof(unsigned));
+ return safe_atou(s, (unsigned*) ret_u);
+}
+
+static inline int safe_atoi32(const char *s, int32_t *ret_i) {
+ assert_cc(sizeof(int32_t) == sizeof(int));
+ return safe_atoi(s, (int*) ret_i);
+}
+
+static inline int safe_atou64(const char *s, uint64_t *ret_u) {
+ assert_cc(sizeof(uint64_t) == sizeof(unsigned long long));
+ return safe_atollu(s, (unsigned long long*) ret_u);
+}
+
+static inline int safe_atoi64(const char *s, int64_t *ret_i) {
+ assert_cc(sizeof(int64_t) == sizeof(long long int));
+ return safe_atolli(s, (long long int*) ret_i);
+}
+
+char *split(const char *c, size_t *l, const char *separator, char **state);
+char *split_quoted(const char *c, size_t *l, char **state);
+
+#define FOREACH_WORD(word, length, s, state) \
+ for ((state) = NULL, (word) = split((s), &(length), WHITESPACE, &(state)); (word); (word) = split((s), &(length), WHITESPACE, &(state)))
+
+#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
+ for ((state) = NULL, (word) = split((s), &(length), (separator), &(state)); (word); (word) = split((s), &(length), (separator), &(state)))
+
+#define FOREACH_WORD_QUOTED(word, length, s, state) \
+ for ((state) = NULL, (word) = split_quoted((s), &(length), &(state)); (word); (word) = split_quoted((s), &(length), &(state)))
+
+char **split_path_and_make_absolute(const char *p);
+
+pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
+int get_starttime_of_pid(pid_t pid, unsigned long long *st);
+
+int write_one_line_file(const char *fn, const char *line);
+int write_one_line_file_atomic(const char *fn, const char *line);
+int read_one_line_file(const char *fn, char **line);
+int read_full_file(const char *fn, char **contents, size_t *size);
+
+int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
+int load_env_file(const char *fname, char ***l);
+int write_env_file(const char *fname, char **l);
+
+char *strappend(const char *s, const char *suffix);
+char *strnappend(const char *s, const char *suffix, size_t length);
+
+char *replace_env(const char *format, char **env);
+char **replace_env_argv(char **argv, char **env);
+
+int readlink_malloc(const char *p, char **r);
+int readlink_and_make_absolute(const char *p, char **r);
+int readlink_and_canonicalize(const char *p, char **r);
+
+char *file_name_from_path(const char *p);
+bool is_path(const char *p);
+
+bool path_is_absolute(const char *p);
+char *path_make_absolute(const char *p, const char *prefix);
+char *path_make_absolute_cwd(const char *p);
+
+char **strv_path_make_absolute_cwd(char **l);
+char **strv_path_canonicalize(char **l);
+char **strv_path_remove_empty(char **l);
+
+int reset_all_signal_handlers(void);
+
+char *strstrip(char *s);
+char *delete_chars(char *s, const char *bad);
+char *truncate_nl(char *s);
+
+char *file_in_same_dir(const char *path, const char *filename);
+int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid);
+int mkdir_parents(const char *path, mode_t mode);
+int mkdir_p(const char *path, mode_t mode);
+
+int parent_of_path(const char *path, char **parent);
+
+int rmdir_parents(const char *path, const char *stop);
+
+int get_process_comm(pid_t pid, char **name);
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
+int get_process_exe(pid_t pid, char **name);
+int get_process_uid(pid_t pid, uid_t *uid);
+
+char hexchar(int x);
+int unhexchar(char c);
+char octchar(int x);
+int unoctchar(char c);
+char decchar(int x);
+int undecchar(char c);
+
+char *cescape(const char *s);
+char *cunescape(const char *s);
+char *cunescape_length(const char *s, size_t length);
+
+char *xescape(const char *s, const char *bad);
+
+char *bus_path_escape(const char *s);
+char *bus_path_unescape(const char *s);
+
+char *path_kill_slashes(char *path);
+
+bool path_startswith(const char *path, const char *prefix);
+bool path_equal(const char *a, const char *b);
+
+char *ascii_strlower(char *path);
+
+bool dirent_is_file(const struct dirent *de);
+bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix);
+
+bool ignore_file(const char *filename);
+
+bool chars_intersect(const char *a, const char *b);
+
+char *format_timestamp(char *buf, size_t l, usec_t t);
+char *format_timestamp_pretty(char *buf, size_t l, usec_t t);
+char *format_timespan(char *buf, size_t l, usec_t t);
+
+int make_stdio(int fd);
+int make_null_stdio(void);
+
+unsigned long long random_ull(void);
+
+#define __DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
+ scope const char *name##_to_string(type i) { \
+ if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \
+ return NULL; \
+ return name##_table[i]; \
+ } \
+ scope type name##_from_string(const char *s) { \
+ type i; \
+ unsigned u = 0; \
+ assert(s); \
+ for (i = 0; i < (type)ELEMENTSOF(name##_table); i++) \
+ if (name##_table[i] && \
+ streq(name##_table[i], s)) \
+ return i; \
+ if (safe_atou(s, &u) >= 0 && \
+ u < ELEMENTSOF(name##_table)) \
+ return (type) u; \
+ return (type) -1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define DEFINE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,static)
+
+int fd_nonblock(int fd, bool nonblock);
+int fd_cloexec(int fd, bool cloexec);
+
+int close_all_fds(const int except[], unsigned n_except);
+
+bool fstype_is_network(const char *fstype);
+
+int chvt(int vt);
+
+int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
+int ask(char *ret, const char *replies, const char *text, ...);
+
+int reset_terminal_fd(int fd, bool switch_to_text);
+int reset_terminal(const char *name);
+
+int open_terminal(const char *name, int mode);
+int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm);
+int release_terminal(void);
+
+int flush_fd(int fd);
+
+int ignore_signals(int sig, ...);
+int default_signals(int sig, ...);
+int sigaction_many(const struct sigaction *sa, ...);
+
+int close_pipe(int p[]);
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
+
+ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
+ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
+
+int path_is_mount_point(const char *path, bool allow_symlink);
+
+bool is_device_path(const char *path);
+
+int dir_is_empty(const char *path);
+
+void rename_process(const char name[8]);
+
+void sigset_add_many(sigset_t *ss, ...);
+
+char* gethostname_malloc(void);
+char* getlogname_malloc(void);
+
+int getttyname_malloc(int fd, char **r);
+int getttyname_harder(int fd, char **r);
+
+int get_ctty_devnr(pid_t pid, dev_t *d);
+int get_ctty(pid_t, dev_t *_devnr, char **r);
+
+int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
+int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
+
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
+
+int pipe_eof(int fd);
+
+cpu_set_t* cpu_set_malloc(unsigned *ncpus);
+
+void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap);
+void status_printf(const char *status, bool ellipse, const char *format, ...);
+void status_welcome(void);
+
+int fd_columns(int fd);
+unsigned columns(void);
+
+int fd_lines(int fd);
+unsigned lines(void);
+
+int running_in_chroot(void);
+
+char *ellipsize(const char *s, size_t length, unsigned percent);
+char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent);
+
+int touch(const char *path);
+
+char *unquote(const char *s, const char *quotes);
+char *normalize_env_assignment(const char *s);
+
+int wait_for_terminate(pid_t pid, siginfo_t *status);
+int wait_for_terminate_and_warn(const char *name, pid_t pid);
+
+_noreturn_ void freeze(void);
+
+bool null_or_empty(struct stat *st);
+int null_or_empty_path(const char *fn);
+
+DIR *xopendirat(int dirfd, const char *name, int flags);
+
+void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t);
+void dual_timestamp_deserialize(const char *value, dual_timestamp *t);
+
+char *fstab_node_to_udev_node(const char *p);
+
+void filter_environ(const char *prefix);
+
+bool tty_is_vc(const char *tty);
+bool tty_is_vc_resolve(const char *tty);
+int vtnr_from_tty(const char *tty);
+const char *default_term_for_tty(const char *tty);
+
+void execute_directory(const char *directory, DIR *_d, char *argv[]);
+
+int kill_and_sigcont(pid_t pid, int sig);
+
+bool nulstr_contains(const char*nulstr, const char *needle);
+
+bool plymouth_running(void);
+
+void parse_syslog_priority(char **p, int *priority);
+void skip_syslog_pid(char **buf);
+void skip_syslog_date(char **buf);
+
+bool hostname_is_valid(const char *s);
+char* hostname_cleanup(char *s);
+
+char* strshorten(char *s, size_t l);
+
+int terminal_vhangup_fd(int fd);
+int terminal_vhangup(const char *name);
+
+int vt_disallocate(const char *name);
+
+int copy_file(const char *from, const char *to);
+int symlink_or_copy(const char *from, const char *to);
+int symlink_or_copy_atomic(const char *from, const char *to);
+
+int fchmod_umask(int fd, mode_t mode);
+
+int conf_files_list(char ***strv, const char *suffix, const char *dir, ...);
+
+int hwclock_is_localtime(void);
+int hwclock_apply_localtime_delta(int *min);
+int hwclock_reset_localtime_delta(void);
+int hwclock_get_time(struct tm *tm);
+int hwclock_set_time(const struct tm *tm);
+
+bool display_is_local(const char *display);
+int socket_from_display(const char *display, char **path);
+
+int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home);
+int get_group_creds(const char **groupname, gid_t *gid);
+
+int in_group(const char *name);
+
+int glob_exists(const char *path);
+
+int dirent_ensure_type(DIR *d, struct dirent *de);
+
+int in_search_path(const char *path, char **search);
+int get_files_in_directory(const char *path, char ***list);
+
+char *join(const char *x, ...) _sentinel_;
+
+bool is_main_thread(void);
+
+bool in_charset(const char *s, const char* charset);
+
+int block_get_whole_disk(dev_t d, dev_t *ret);
+
+int file_is_priv_sticky(const char *p);
+
+int strdup_or_null(const char *a, char **b);
+
+#define NULSTR_FOREACH(i, l) \
+ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
+
+#define NULSTR_FOREACH_PAIR(i, j, l) \
+ for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i))
+
+const char *ioprio_class_to_string(int i);
+int ioprio_class_from_string(const char *s);
+
+const char *sigchld_code_to_string(int i);
+int sigchld_code_from_string(const char *s);
+
+const char *log_facility_unshifted_to_string(int i);
+int log_facility_unshifted_from_string(const char *s);
+
+const char *log_level_to_string(int i);
+int log_level_from_string(const char *s);
+
+const char *sched_policy_to_string(int i);
+int sched_policy_from_string(const char *s);
+
+const char *rlimit_to_string(int i);
+int rlimit_from_string(const char *s);
+
+const char *ip_tos_to_string(int i);
+int ip_tos_from_string(const char *s);
+
+const char *signal_to_string(int i);
+int signal_from_string(const char *s);
+
+int signal_from_string_try_harder(const char *s);
+
+extern int saved_argc;
+extern char **saved_argv;
+
+bool kexec_loaded(void);
+
+int prot_from_flags(int flags);
+
+char *format_bytes(char *buf, size_t l, off_t t);
+
+int fd_wait_for_event(int fd, int event, usec_t timeout);
+
+void* memdup(const void *p, size_t l);
+
+int rtc_open(int flags);
+
+int is_kernel_thread(pid_t pid);
+
+int fd_inc_sndbuf(int fd, size_t n);
+int fd_inc_rcvbuf(int fd, size_t n);
+#endif
diff --git a/src/shared/virt.c b/src/shared/virt.c
new file mode 100644
index 0000000..4c526ff
--- /dev/null
+++ b/src/shared/virt.c
@@ -0,0 +1,292 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "virt.h"
+
+/* Returns a short identifier for the various VM implementations */
+int detect_vm(const char **id) {
+
+#if defined(__i386__) || defined(__x86_64__)
+
+ /* Both CPUID and DMI are x86 specific interfaces... */
+
+ static const char *const dmi_vendors[] = {
+ "/sys/class/dmi/id/sys_vendor",
+ "/sys/class/dmi/id/board_vendor",
+ "/sys/class/dmi/id/bios_vendor"
+ };
+
+ static const char dmi_vendor_table[] =
+ "QEMU\0" "qemu\0"
+ /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+ "VMware\0" "vmware\0"
+ "VMW\0" "vmware\0"
+ "Microsoft Corporation\0" "microsoft\0"
+ "innotek GmbH\0" "oracle\0"
+ "Xen\0" "xen\0"
+ "Bochs\0" "bochs\0";
+
+ static const char cpuid_vendor_table[] =
+ "XenVMMXenVMM\0" "xen\0"
+ "KVMKVMKVM\0" "kvm\0"
+ /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+ "VMwareVMware\0" "vmware\0"
+ /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
+ "Microsoft Hv\0" "microsoft\0";
+
+ uint32_t eax, ecx;
+ union {
+ uint32_t sig32[3];
+ char text[13];
+ } sig;
+ unsigned i;
+ const char *j, *k;
+ bool hypervisor;
+
+ /* http://lwn.net/Articles/301888/ */
+ zero(sig);
+
+#if defined (__i386__)
+#define REG_a "eax"
+#define REG_b "ebx"
+#elif defined (__amd64__)
+#define REG_a "rax"
+#define REG_b "rbx"
+#endif
+
+ /* First detect whether there is a hypervisor */
+ eax = 1;
+ __asm__ __volatile__ (
+ /* ebx/rbx is being used for PIC! */
+ " push %%"REG_b" \n\t"
+ " cpuid \n\t"
+ " pop %%"REG_b" \n\t"
+
+ : "=a" (eax), "=c" (ecx)
+ : "0" (eax)
+ );
+
+ hypervisor = !!(ecx & 0x80000000U);
+
+ if (hypervisor) {
+
+ /* There is a hypervisor, see what it is */
+ eax = 0x40000000U;
+ __asm__ __volatile__ (
+ /* ebx/rbx is being used for PIC! */
+ " push %%"REG_b" \n\t"
+ " cpuid \n\t"
+ " mov %%ebx, %1 \n\t"
+ " pop %%"REG_b" \n\t"
+
+ : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
+ : "0" (eax)
+ );
+
+ NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
+ if (streq(sig.text, j)) {
+
+ if (id)
+ *id = k;
+
+ return 1;
+ }
+ }
+
+ for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
+ char *s;
+ int r;
+ const char *found = NULL;
+
+ if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) {
+ if (r != -ENOENT)
+ return r;
+
+ continue;
+ }
+
+ NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
+ if (startswith(s, j))
+ found = k;
+ free(s);
+
+ if (found) {
+ if (id)
+ *id = found;
+
+ return 1;
+ }
+ }
+
+ if (hypervisor) {
+ if (id)
+ *id = "other";
+
+ return 1;
+ }
+
+#endif
+ return 0;
+}
+
+int detect_container(const char **id) {
+ FILE *f;
+
+ /* Unfortunately many of these operations require root access
+ * in one way or another */
+
+ if (geteuid() != 0)
+ return -EPERM;
+
+ if (running_in_chroot() > 0) {
+
+ if (id)
+ *id = "chroot";
+
+ return 1;
+ }
+
+ /* /proc/vz exists in container and outside of the container,
+ * /proc/bc only outside of the container. */
+ if (access("/proc/vz", F_OK) >= 0 &&
+ access("/proc/bc", F_OK) < 0) {
+
+ if (id)
+ *id = "openvz";
+
+ return 1;
+ }
+
+ f = fopen("/proc/1/environ", "re");
+ if (f) {
+ bool done = false;
+
+ do {
+ char line[LINE_MAX];
+ unsigned i;
+
+ for (i = 0; i < sizeof(line)-1; i++) {
+ int c;
+
+ c = getc(f);
+ if (_unlikely_(c == EOF)) {
+ done = true;
+ break;
+ } else if (c == 0)
+ break;
+
+ line[i] = c;
+ }
+ line[i] = 0;
+
+ if (streq(line, "container=lxc")) {
+ fclose(f);
+
+ if (id)
+ *id = "lxc";
+ return 1;
+
+ } else if (streq(line, "container=lxc-libvirt")) {
+ fclose(f);
+
+ if (id)
+ *id = "lxc-libvirt";
+ return 1;
+
+ } else if (streq(line, "container=systemd-nspawn")) {
+ fclose(f);
+
+ if (id)
+ *id = "systemd-nspawn";
+ return 1;
+
+ } else if (startswith(line, "container=")) {
+ fclose(f);
+
+ if (id)
+ *id = "other";
+ return 1;
+ }
+
+ } while (!done);
+
+ fclose(f);
+ }
+
+ return 0;
+}
+
+/* Returns a short identifier for the various VM/container implementations */
+Virtualization detect_virtualization(const char **id) {
+
+ static __thread Virtualization cached_virt = _VIRTUALIZATION_INVALID;
+ static __thread const char *cached_id = NULL;
+
+ const char *_id;
+ int r;
+ Virtualization v;
+
+ if (_likely_(cached_virt >= 0)) {
+
+ if (id && cached_virt > 0)
+ *id = cached_id;
+
+ return cached_virt;
+ }
+
+ r = detect_container(&_id);
+ if (r < 0) {
+ v = r;
+ goto finish;
+ } else if (r > 0) {
+ v = VIRTUALIZATION_CONTAINER;
+ goto finish;
+ }
+
+ r = detect_vm(&_id);
+ if (r < 0) {
+ v = r;
+ goto finish;
+ } else if (r > 0) {
+ v = VIRTUALIZATION_VM;
+ goto finish;
+ }
+
+ v = VIRTUALIZATION_NONE;
+
+finish:
+ if (v > 0) {
+ cached_id = _id;
+
+ if (id)
+ *id = _id;
+ }
+
+ if (v >= 0)
+ cached_virt = v;
+
+ return v;
+}
diff --git a/src/shared/virt.h b/src/shared/virt.h
new file mode 100644
index 0000000..f55c9a6
--- /dev/null
+++ b/src/shared/virt.h
@@ -0,0 +1,38 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foovirthfoo
+#define foovirthfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+int detect_vm(const char **id);
+int detect_container(const char **id);
+
+typedef enum Virtualization {
+ VIRTUALIZATION_NONE = 0,
+ VIRTUALIZATION_VM,
+ VIRTUALIZATION_CONTAINER,
+ _VIRTUALIZATION_MAX,
+ _VIRTUALIZATION_INVALID = -1
+} Virtualization;
+
+Virtualization detect_virtualization(const char **id);
+
+#endif
diff --git a/src/socket-util.c b/src/socket-util.c
deleted file mode 100644
index acc4d33..0000000
--- a/src/socket-util.c
+++ /dev/null
@@ -1,652 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <arpa/inet.h>
-#include <stdio.h>
-#include <net/if.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stddef.h>
-#include <sys/ioctl.h>
-
-#include "macro.h"
-#include "util.h"
-#include "socket-util.h"
-#include "missing.h"
-#include "label.h"
-
-int socket_address_parse(SocketAddress *a, const char *s) {
- int r;
- char *e, *n;
- unsigned u;
-
- assert(a);
- assert(s);
-
- zero(*a);
- a->type = SOCK_STREAM;
-
- if (*s == '[') {
- /* IPv6 in [x:.....:z]:p notation */
-
- if (!socket_ipv6_is_supported()) {
- log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
- return -EAFNOSUPPORT;
- }
-
- if (!(e = strchr(s+1, ']')))
- return -EINVAL;
-
- if (!(n = strndup(s+1, e-s-1)))
- return -ENOMEM;
-
- errno = 0;
- if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) {
- free(n);
- return errno != 0 ? -errno : -EINVAL;
- }
-
- free(n);
-
- e++;
- if (*e != ':')
- return -EINVAL;
-
- e++;
- if ((r = safe_atou(e, &u)) < 0)
- return r;
-
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
- a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htons((uint16_t) u);
- a->size = sizeof(struct sockaddr_in6);
-
- } else if (*s == '/') {
- /* AF_UNIX socket */
-
- size_t l;
-
- l = strlen(s);
- if (l >= sizeof(a->sockaddr.un.sun_path))
- return -EINVAL;
-
- a->sockaddr.un.sun_family = AF_UNIX;
- memcpy(a->sockaddr.un.sun_path, s, l);
- a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
-
- } else if (*s == '@') {
- /* Abstract AF_UNIX socket */
- size_t l;
-
- l = strlen(s+1);
- if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
- return -EINVAL;
-
- a->sockaddr.un.sun_family = AF_UNIX;
- memcpy(a->sockaddr.un.sun_path+1, s+1, l);
- a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
-
- } else {
-
- if ((e = strchr(s, ':'))) {
-
- if ((r = safe_atou(e+1, &u)) < 0)
- return r;
-
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
- if (!(n = strndup(s, e-s)))
- return -ENOMEM;
-
- /* IPv4 in w.x.y.z:p notation? */
- if ((r = inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr)) < 0) {
- free(n);
- return -errno;
- }
-
- if (r > 0) {
- /* Gotcha, it's a traditional IPv4 address */
- free(n);
-
- a->sockaddr.in4.sin_family = AF_INET;
- a->sockaddr.in4.sin_port = htons((uint16_t) u);
- a->size = sizeof(struct sockaddr_in);
- } else {
- unsigned idx;
-
- if (strlen(n) > IF_NAMESIZE-1) {
- free(n);
- return -EINVAL;
- }
-
- /* Uh, our last resort, an interface name */
- idx = if_nametoindex(n);
- free(n);
-
- if (idx == 0)
- return -EINVAL;
-
- if (!socket_ipv6_is_supported()) {
- log_warning("Binding to interface is not available since kernel does not support IPv6.");
- return -EAFNOSUPPORT;
- }
-
- a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htons((uint16_t) u);
- a->sockaddr.in6.sin6_scope_id = idx;
- a->sockaddr.in6.sin6_addr = in6addr_any;
- a->size = sizeof(struct sockaddr_in6);
- }
- } else {
-
- /* Just a port */
- if ((r = safe_atou(s, &u)) < 0)
- return r;
-
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
- if (socket_ipv6_is_supported()) {
- a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htons((uint16_t) u);
- a->sockaddr.in6.sin6_addr = in6addr_any;
- a->size = sizeof(struct sockaddr_in6);
- } else {
- a->sockaddr.in4.sin_family = AF_INET;
- a->sockaddr.in4.sin_port = htons((uint16_t) u);
- a->sockaddr.in4.sin_addr.s_addr = INADDR_ANY;
- a->size = sizeof(struct sockaddr_in);
- }
- }
- }
-
- return 0;
-}
-
-int socket_address_parse_netlink(SocketAddress *a, const char *s) {
- int family;
- unsigned group = 0;
- char* sfamily = NULL;
- assert(a);
- assert(s);
-
- zero(*a);
- a->type = SOCK_RAW;
-
- errno = 0;
- if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
- return errno ? -errno : -EINVAL;
-
- if ((family = netlink_family_from_string(sfamily)) < 0)
- if (safe_atoi(sfamily, &family) < 0) {
- free(sfamily);
- return -EINVAL;
- }
-
- free(sfamily);
-
- a->sockaddr.nl.nl_family = AF_NETLINK;
- a->sockaddr.nl.nl_groups = group;
-
- a->type = SOCK_RAW;
- a->size = sizeof(struct sockaddr_nl);
- a->protocol = family;
-
- return 0;
-}
-
-int socket_address_verify(const SocketAddress *a) {
- assert(a);
-
- switch (socket_address_family(a)) {
-
- case AF_INET:
- if (a->size != sizeof(struct sockaddr_in))
- return -EINVAL;
-
- if (a->sockaddr.in4.sin_port == 0)
- return -EINVAL;
-
- if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
- return -EINVAL;
-
- return 0;
-
- case AF_INET6:
- if (a->size != sizeof(struct sockaddr_in6))
- return -EINVAL;
-
- if (a->sockaddr.in6.sin6_port == 0)
- return -EINVAL;
-
- if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
- return -EINVAL;
-
- return 0;
-
- case AF_UNIX:
- if (a->size < offsetof(struct sockaddr_un, sun_path))
- return -EINVAL;
-
- if (a->size > offsetof(struct sockaddr_un, sun_path)) {
-
- if (a->sockaddr.un.sun_path[0] != 0) {
- char *e;
-
- /* path */
- if (!(e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path))))
- return -EINVAL;
-
- if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
- return -EINVAL;
- }
- }
-
- if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
- return -EINVAL;
-
- return 0;
-
- case AF_NETLINK:
-
- if (a->size != sizeof(struct sockaddr_nl))
- return -EINVAL;
-
- if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
- return -EINVAL;
-
- return 0;
-
- default:
- return -EAFNOSUPPORT;
- }
-}
-
-int socket_address_print(const SocketAddress *a, char **p) {
- int r;
- assert(a);
- assert(p);
-
- if ((r = socket_address_verify(a)) < 0)
- return r;
-
- switch (socket_address_family(a)) {
-
- case AF_INET: {
- char *ret;
-
- if (!(ret = new(char, INET_ADDRSTRLEN+1+5+1)))
- return -ENOMEM;
-
- if (!inet_ntop(AF_INET, &a->sockaddr.in4.sin_addr, ret, INET_ADDRSTRLEN)) {
- free(ret);
- return -errno;
- }
-
- sprintf(strchr(ret, 0), ":%u", ntohs(a->sockaddr.in4.sin_port));
- *p = ret;
- return 0;
- }
-
- case AF_INET6: {
- char *ret;
-
- if (!(ret = new(char, 1+INET6_ADDRSTRLEN+2+5+1)))
- return -ENOMEM;
-
- ret[0] = '[';
- if (!inet_ntop(AF_INET6, &a->sockaddr.in6.sin6_addr, ret+1, INET6_ADDRSTRLEN)) {
- free(ret);
- return -errno;
- }
-
- sprintf(strchr(ret, 0), "]:%u", ntohs(a->sockaddr.in6.sin6_port));
- *p = ret;
- return 0;
- }
-
- case AF_UNIX: {
- char *ret;
-
- if (a->size <= offsetof(struct sockaddr_un, sun_path)) {
-
- if (!(ret = strdup("<unnamed>")))
- return -ENOMEM;
-
- } else if (a->sockaddr.un.sun_path[0] == 0) {
- /* abstract */
-
- /* FIXME: We assume we can print the
- * socket path here and that it hasn't
- * more than one NUL byte. That is
- * actually an invalid assumption */
-
- if (!(ret = new(char, sizeof(a->sockaddr.un.sun_path)+1)))
- return -ENOMEM;
-
- ret[0] = '@';
- memcpy(ret+1, a->sockaddr.un.sun_path+1, sizeof(a->sockaddr.un.sun_path)-1);
- ret[sizeof(a->sockaddr.un.sun_path)] = 0;
-
- } else {
-
- if (!(ret = strdup(a->sockaddr.un.sun_path)))
- return -ENOMEM;
- }
-
- *p = ret;
- return 0;
- }
-
- case AF_NETLINK: {
- const char *sfamily;
-
- if ((sfamily = netlink_family_to_string(a->protocol)))
- r = asprintf(p, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
- else
- r = asprintf(p, "%i %u", a->protocol, a->sockaddr.nl.nl_groups);
-
- if (r < 0)
- return -ENOMEM;
-
- return 0;
- }
-
- default:
- return -EINVAL;
- }
-}
-
-int socket_address_listen(
- const SocketAddress *a,
- int backlog,
- SocketAddressBindIPv6Only only,
- const char *bind_to_device,
- bool free_bind,
- bool transparent,
- mode_t directory_mode,
- mode_t socket_mode,
- const char *label,
- int *ret) {
-
- int r, fd, one;
- assert(a);
- assert(ret);
-
- if ((r = socket_address_verify(a)) < 0)
- return r;
-
- if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
- return -EAFNOSUPPORT;
-
- r = label_socket_set(label);
- if (r < 0)
- return r;
-
- fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, a->protocol);
- r = fd < 0 ? -errno : 0;
-
- label_socket_clear();
-
- if (r < 0)
- return r;
-
- if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
- int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
-
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
- goto fail;
- }
-
- if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) {
- if (bind_to_device)
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
- goto fail;
-
- if (free_bind) {
- one = 1;
- if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
- log_warning("IP_FREEBIND failed: %m");
- }
-
- if (transparent) {
- one = 1;
- if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
- log_warning("IP_TRANSPARENT failed: %m");
- }
- }
-
- one = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
- goto fail;
-
- if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
- mode_t old_mask;
-
- /* Create parents */
- mkdir_parents(a->sockaddr.un.sun_path, directory_mode);
-
- /* Enforce the right access mode for the socket*/
- old_mask = umask(~ socket_mode);
-
- /* Include the original umask in our mask */
- umask(~socket_mode | old_mask);
-
- r = label_bind(fd, &a->sockaddr.sa, a->size);
-
- if (r < 0 && errno == EADDRINUSE) {
- /* Unlink and try again */
- unlink(a->sockaddr.un.sun_path);
- r = bind(fd, &a->sockaddr.sa, a->size);
- }
-
- umask(old_mask);
- } else
- r = bind(fd, &a->sockaddr.sa, a->size);
-
- if (r < 0)
- goto fail;
-
- if (socket_address_can_accept(a))
- if (listen(fd, backlog) < 0)
- goto fail;
-
- *ret = fd;
- return 0;
-
-fail:
- r = -errno;
- close_nointr_nofail(fd);
- return r;
-}
-
-bool socket_address_can_accept(const SocketAddress *a) {
- assert(a);
-
- return
- a->type == SOCK_STREAM ||
- a->type == SOCK_SEQPACKET;
-}
-
-bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
- assert(a);
- assert(b);
-
- /* Invalid addresses are unequal to all */
- if (socket_address_verify(a) < 0 ||
- socket_address_verify(b) < 0)
- return false;
-
- if (a->type != b->type)
- return false;
-
- if (a->size != b->size)
- return false;
-
- if (socket_address_family(a) != socket_address_family(b))
- return false;
-
- switch (socket_address_family(a)) {
-
- case AF_INET:
- if (a->sockaddr.in4.sin_addr.s_addr != b->sockaddr.in4.sin_addr.s_addr)
- return false;
-
- if (a->sockaddr.in4.sin_port != b->sockaddr.in4.sin_port)
- return false;
-
- break;
-
- case AF_INET6:
- if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
- return false;
-
- if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
- return false;
-
- break;
-
- case AF_UNIX:
-
- if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
- return false;
-
- if (a->sockaddr.un.sun_path[0]) {
- if (strncmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
- return false;
- } else {
- if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
- return false;
- }
-
- break;
-
- case AF_NETLINK:
-
- if (a->protocol != b->protocol)
- return false;
-
- if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
- return false;
-
- break;
-
- default:
- /* Cannot compare, so we assume the addresses are different */
- return false;
- }
-
- return true;
-}
-
-bool socket_address_is(const SocketAddress *a, const char *s, int type) {
- struct SocketAddress b;
-
- assert(a);
- assert(s);
-
- if (socket_address_parse(&b, s) < 0)
- return false;
-
- b.type = type;
-
- return socket_address_equal(a, &b);
-}
-
-bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
- struct SocketAddress b;
-
- assert(a);
- assert(s);
-
- if (socket_address_parse_netlink(&b, s) < 0)
- return false;
-
- return socket_address_equal(a, &b);
-}
-
-bool socket_address_needs_mount(const SocketAddress *a, const char *prefix) {
- assert(a);
-
- if (socket_address_family(a) != AF_UNIX)
- return false;
-
- if (a->sockaddr.un.sun_path[0] == 0)
- return false;
-
- return path_startswith(a->sockaddr.un.sun_path, prefix);
-}
-
-bool socket_ipv6_is_supported(void) {
- char *l = 0;
- bool enabled;
-
- if (access("/sys/module/ipv6", F_OK) != 0)
- return 0;
-
- /* If we can't check "disable" parameter, assume enabled */
- if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
- return 1;
-
- /* If module was loaded with disable=1 no IPv6 available */
- enabled = l[0] == '0';
- free(l);
-
- return enabled;
-}
-
-static const char* const netlink_family_table[] = {
- [NETLINK_ROUTE] = "route",
- [NETLINK_FIREWALL] = "firewall",
- [NETLINK_INET_DIAG] = "inet-diag",
- [NETLINK_NFLOG] = "nflog",
- [NETLINK_XFRM] = "xfrm",
- [NETLINK_SELINUX] = "selinux",
- [NETLINK_ISCSI] = "iscsi",
- [NETLINK_AUDIT] = "audit",
- [NETLINK_FIB_LOOKUP] = "fib-lookup",
- [NETLINK_CONNECTOR] = "connector",
- [NETLINK_NETFILTER] = "netfilter",
- [NETLINK_IP6_FW] = "ip6-fw",
- [NETLINK_DNRTMSG] = "dnrtmsg",
- [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
- [NETLINK_GENERIC] = "generic",
- [NETLINK_SCSITRANSPORT] = "scsitransport",
- [NETLINK_ECRYPTFS] = "ecryptfs"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(netlink_family, int);
-
-static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
- [SOCKET_ADDRESS_DEFAULT] = "default",
- [SOCKET_ADDRESS_BOTH] = "both",
- [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
diff --git a/src/socket-util.h b/src/socket-util.h
deleted file mode 100644
index 8ccbd37..0000000
--- a/src/socket-util.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foosocketutilhfoo
-#define foosocketutilhfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <sys/un.h>
-#include <net/if.h>
-#include <asm/types.h>
-#include <linux/netlink.h>
-
-#include "macro.h"
-#include "util.h"
-
-union sockaddr_union {
- struct sockaddr sa;
- struct sockaddr_in in4;
- struct sockaddr_in6 in6;
- struct sockaddr_un un;
- struct sockaddr_nl nl;
- struct sockaddr_storage storage;
-};
-
-typedef struct SocketAddress {
- union sockaddr_union sockaddr;
-
- /* We store the size here explicitly due to the weird
- * sockaddr_un semantics for abstract sockets */
- socklen_t size;
-
- /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */
- int type;
-
- /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */
- int protocol;
-} SocketAddress;
-
-typedef enum SocketAddressBindIPv6Only {
- SOCKET_ADDRESS_DEFAULT,
- SOCKET_ADDRESS_BOTH,
- SOCKET_ADDRESS_IPV6_ONLY,
- _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX,
- _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1
-} SocketAddressBindIPv6Only;
-
-#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
-
-int socket_address_parse(SocketAddress *a, const char *s);
-int socket_address_parse_netlink(SocketAddress *a, const char *s);
-int socket_address_print(const SocketAddress *a, char **p);
-int socket_address_verify(const SocketAddress *a);
-
-bool socket_address_can_accept(const SocketAddress *a);
-
-int socket_address_listen(
- const SocketAddress *a,
- int backlog,
- SocketAddressBindIPv6Only only,
- const char *bind_to_device,
- bool free_bind,
- bool transparent,
- mode_t directory_mode,
- mode_t socket_mode,
- const char *label,
- int *ret);
-
-bool socket_address_is(const SocketAddress *a, const char *s, int type);
-bool socket_address_is_netlink(const SocketAddress *a, const char *s);
-
-bool socket_address_equal(const SocketAddress *a, const SocketAddress *b);
-
-bool socket_address_needs_mount(const SocketAddress *a, const char *prefix);
-
-const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b);
-SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s);
-
-const char* netlink_family_to_string(int b);
-int netlink_family_from_string(const char *s);
-
-bool socket_ipv6_is_supported(void);
-
-#endif
diff --git a/src/strv.c b/src/strv.c
deleted file mode 100644
index bb309d9..0000000
--- a/src/strv.c
+++ /dev/null
@@ -1,690 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-
-#include "util.h"
-#include "strv.h"
-
-char *strv_find(char **l, const char *name) {
- char **i;
-
- assert(name);
-
- STRV_FOREACH(i, l)
- if (streq(*i, name))
- return *i;
-
- return NULL;
-}
-
-char *strv_find_prefix(char **l, const char *name) {
- char **i;
-
- assert(name);
-
- STRV_FOREACH(i, l)
- if (startswith(*i, name))
- return *i;
-
- return NULL;
-}
-
-void strv_free(char **l) {
- char **k;
-
- if (!l)
- return;
-
- for (k = l; *k; k++)
- free(*k);
-
- free(l);
-}
-
-char **strv_copy(char **l) {
- char **r, **k;
-
- k = r = new(char*, strv_length(l)+1);
- if (!k)
- return NULL;
-
- if (l)
- for (; *l; k++, l++)
- if (!(*k = strdup(*l)))
- goto fail;
-
- *k = NULL;
- return r;
-
-fail:
- for (k--; k >= r; k--)
- free(*k);
-
- free(r);
-
- return NULL;
-}
-
-unsigned strv_length(char **l) {
- unsigned n = 0;
-
- if (!l)
- return 0;
-
- for (; *l; l++)
- n++;
-
- return n;
-}
-
-char **strv_new_ap(const char *x, va_list ap) {
- const char *s;
- char **a;
- unsigned n = 0, i = 0;
- va_list aq;
-
- if (x) {
- n = 1;
-
- va_copy(aq, ap);
- while (va_arg(aq, const char*))
- n++;
- va_end(aq);
- }
-
- if (!(a = new(char*, n+1)))
- return NULL;
-
- if (x) {
- if (!(a[i] = strdup(x))) {
- free(a);
- return NULL;
- }
-
- i++;
-
- while ((s = va_arg(ap, const char*))) {
- if (!(a[i] = strdup(s)))
- goto fail;
-
- i++;
- }
- }
-
- a[i] = NULL;
-
- return a;
-
-fail:
-
- for (; i > 0; i--)
- if (a[i-1])
- free(a[i-1]);
-
- free(a);
-
- return NULL;
-}
-
-char **strv_new(const char *x, ...) {
- char **r;
- va_list ap;
-
- va_start(ap, x);
- r = strv_new_ap(x, ap);
- va_end(ap);
-
- return r;
-}
-
-char **strv_merge(char **a, char **b) {
- char **r, **k;
-
- if (!a)
- return strv_copy(b);
-
- if (!b)
- return strv_copy(a);
-
- if (!(r = new(char*, strv_length(a)+strv_length(b)+1)))
- return NULL;
-
- for (k = r; *a; k++, a++)
- if (!(*k = strdup(*a)))
- goto fail;
- for (; *b; k++, b++)
- if (!(*k = strdup(*b)))
- goto fail;
-
- *k = NULL;
- return r;
-
-fail:
- for (k--; k >= r; k--)
- free(*k);
-
- free(r);
-
- return NULL;
-}
-
-char **strv_merge_concat(char **a, char **b, const char *suffix) {
- char **r, **k;
-
- /* Like strv_merge(), but appends suffix to all strings in b, before adding */
-
- if (!b)
- return strv_copy(a);
-
- r = new(char*, strv_length(a) + strv_length(b) + 1);
- if (!r)
- return NULL;
-
- k = r;
- if (a)
- for (; *a; k++, a++) {
- *k = strdup(*a);
- if (!*k)
- goto fail;
- }
-
- for (; *b; k++, b++) {
- *k = strappend(*b, suffix);
- if (!*k)
- goto fail;
- }
-
- *k = NULL;
- return r;
-
-fail:
- for (k--; k >= r; k--)
- free(*k);
-
- free(r);
-
- return NULL;
-
-}
-
-char **strv_split(const char *s, const char *separator) {
- char *state;
- char *w;
- size_t l;
- unsigned n, i;
- char **r;
-
- assert(s);
-
- n = 0;
- FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
- n++;
-
- if (!(r = new(char*, n+1)))
- return NULL;
-
- i = 0;
- FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
- if (!(r[i++] = strndup(w, l))) {
- strv_free(r);
- return NULL;
- }
-
- r[i] = NULL;
- return r;
-}
-
-char **strv_split_quoted(const char *s) {
- char *state;
- char *w;
- size_t l;
- unsigned n, i;
- char **r;
-
- assert(s);
-
- n = 0;
- FOREACH_WORD_QUOTED(w, l, s, state)
- n++;
-
- if (!(r = new(char*, n+1)))
- return NULL;
-
- i = 0;
- FOREACH_WORD_QUOTED(w, l, s, state)
- if (!(r[i++] = cunescape_length(w, l))) {
- strv_free(r);
- return NULL;
- }
-
- r[i] = NULL;
- return r;
-}
-
-char *strv_join(char **l, const char *separator) {
- char *r, *e;
- char **s;
- size_t n, k;
-
- if (!separator)
- separator = " ";
-
- k = strlen(separator);
-
- n = 0;
- STRV_FOREACH(s, l) {
- if (n != 0)
- n += k;
- n += strlen(*s);
- }
-
- if (!(r = new(char, n+1)))
- return NULL;
-
- e = r;
- STRV_FOREACH(s, l) {
- if (e != r)
- e = stpcpy(e, separator);
-
- e = stpcpy(e, *s);
- }
-
- *e = 0;
-
- return r;
-}
-
-char **strv_append(char **l, const char *s) {
- char **r, **k;
-
- if (!l)
- return strv_new(s, NULL);
-
- if (!s)
- return strv_copy(l);
-
- r = new(char*, strv_length(l)+2);
- if (!r)
- return NULL;
-
- for (k = r; *l; k++, l++)
- if (!(*k = strdup(*l)))
- goto fail;
-
- if (!(*(k++) = strdup(s)))
- goto fail;
-
- *k = NULL;
- return r;
-
-fail:
- for (k--; k >= r; k--)
- free(*k);
-
- free(r);
-
- return NULL;
-}
-
-char **strv_uniq(char **l) {
- char **i;
-
- /* Drops duplicate entries. The first identical string will be
- * kept, the others dropped */
-
- STRV_FOREACH(i, l)
- strv_remove(i+1, *i);
-
- return l;
-}
-
-char **strv_remove(char **l, const char *s) {
- char **f, **t;
-
- if (!l)
- return NULL;
-
- assert(s);
-
- /* Drops every occurrence of s in the string list, edits
- * in-place. */
-
- for (f = t = l; *f; f++) {
-
- if (streq(*f, s)) {
- free(*f);
- continue;
- }
-
- *(t++) = *f;
- }
-
- *t = NULL;
- return l;
-}
-
-static int env_append(char **r, char ***k, char **a) {
- assert(r);
- assert(k);
-
- if (!a)
- return 0;
-
- /* Add the entries of a to *k unless they already exist in *r
- * in which case they are overridden instead. This assumes
- * there is enough space in the r array. */
-
- for (; *a; a++) {
- char **j;
- size_t n;
-
- n = strcspn(*a, "=");
-
- if ((*a)[n] == '=')
- n++;
-
- for (j = r; j < *k; j++)
- if (strncmp(*j, *a, n) == 0)
- break;
-
- if (j >= *k)
- (*k)++;
- else
- free(*j);
-
- if (!(*j = strdup(*a)))
- return -ENOMEM;
- }
-
- return 0;
-}
-
-char **strv_env_merge(unsigned n_lists, ...) {
- size_t n = 0;
- char **l, **k, **r;
- va_list ap;
- unsigned i;
-
- /* Merges an arbitrary number of environment sets */
-
- va_start(ap, n_lists);
- for (i = 0; i < n_lists; i++) {
- l = va_arg(ap, char**);
- n += strv_length(l);
- }
- va_end(ap);
-
- if (!(r = new(char*, n+1)))
- return NULL;
-
- k = r;
-
- va_start(ap, n_lists);
- for (i = 0; i < n_lists; i++) {
- l = va_arg(ap, char**);
- if (env_append(r, &k, l) < 0)
- goto fail;
- }
- va_end(ap);
-
- *k = NULL;
-
- return r;
-
-fail:
- va_end(ap);
-
- for (k--; k >= r; k--)
- free(*k);
-
- free(r);
-
- return NULL;
-}
-
-static bool env_match(const char *t, const char *pattern) {
- assert(t);
- assert(pattern);
-
- /* pattern a matches string a
- * a matches a=
- * a matches a=b
- * a= matches a=
- * a=b matches a=b
- * a= does not match a
- * a=b does not match a=
- * a=b does not match a
- * a=b does not match a=c */
-
- if (streq(t, pattern))
- return true;
-
- if (!strchr(pattern, '=')) {
- size_t l = strlen(pattern);
-
- return strncmp(t, pattern, l) == 0 && t[l] == '=';
- }
-
- return false;
-}
-
-char **strv_env_delete(char **x, unsigned n_lists, ...) {
- size_t n, i = 0;
- char **k, **r;
- va_list ap;
-
- /* Deletes every entry from x that is mentioned in the other
- * string lists */
-
- n = strv_length(x);
-
- r = new(char*, n+1);
- if (!r)
- return NULL;
-
- STRV_FOREACH(k, x) {
- unsigned v;
-
- va_start(ap, n_lists);
- for (v = 0; v < n_lists; v++) {
- char **l, **j;
-
- l = va_arg(ap, char**);
- STRV_FOREACH(j, l)
- if (env_match(*k, *j))
- goto skip;
- }
- va_end(ap);
-
- r[i] = strdup(*k);
- if (!r[i]) {
- strv_free(r);
- return NULL;
- }
-
- i++;
- continue;
-
- skip:
- va_end(ap);
- }
-
- r[i] = NULL;
-
- assert(i <= n);
-
- return r;
-}
-
-char **strv_env_unset(char **l, const char *p) {
-
- char **f, **t;
-
- if (!l)
- return NULL;
-
- assert(p);
-
- /* Drops every occurrence of the env var setting p in the
- * string list. edits in-place. */
-
- for (f = t = l; *f; f++) {
-
- if (env_match(*f, p)) {
- free(*f);
- continue;
- }
-
- *(t++) = *f;
- }
-
- *t = NULL;
- return l;
-}
-
-char **strv_env_set(char **x, const char *p) {
-
- char **k, **r;
- char* m[2] = { (char*) p, NULL };
-
- /* Overrides the env var setting of p, returns a new copy */
-
- if (!(r = new(char*, strv_length(x)+2)))
- return NULL;
-
- k = r;
- if (env_append(r, &k, x) < 0)
- goto fail;
-
- if (env_append(r, &k, m) < 0)
- goto fail;
-
- *k = NULL;
-
- return r;
-
-fail:
- for (k--; k >= r; k--)
- free(*k);
-
- free(r);
-
- return NULL;
-
-}
-
-char *strv_env_get_with_length(char **l, const char *name, size_t k) {
- char **i;
-
- assert(name);
-
- STRV_FOREACH(i, l)
- if (strncmp(*i, name, k) == 0 &&
- (*i)[k] == '=')
- return *i + k + 1;
-
- return NULL;
-}
-
-char *strv_env_get(char **l, const char *name) {
- return strv_env_get_with_length(l, name, strlen(name));
-}
-
-char **strv_env_clean(char **l) {
- char **r, **ret;
-
- for (r = ret = l; *l; l++) {
- const char *equal;
-
- equal = strchr(*l, '=');
-
- if (equal && equal[1] == 0) {
- free(*l);
- continue;
- }
-
- *(r++) = *l;
- }
-
- *r = NULL;
-
- return ret;
-}
-
-char **strv_parse_nulstr(const char *s, size_t l) {
- const char *p;
- unsigned c = 0, i = 0;
- char **v;
-
- assert(s || l <= 0);
-
- if (l <= 0)
- return strv_new(NULL, NULL);
-
- for (p = s; p < s + l; p++)
- if (*p == 0)
- c++;
-
- if (s[l-1] != 0)
- c++;
-
- if (!(v = new0(char*, c+1)))
- return NULL;
-
- p = s;
- while (p < s + l) {
- const char *e;
-
- e = memchr(p, 0, s + l - p);
-
- if (!(v[i++] = strndup(p, e ? e - p : s + l - p))) {
- strv_free(v);
- return NULL;
- }
-
- if (!e)
- break;
-
- p = e + 1;
- }
-
- assert(i == c);
-
- return v;
-}
-
-bool strv_overlap(char **a, char **b) {
- char **i, **j;
-
- STRV_FOREACH(i, a) {
- STRV_FOREACH(j, b) {
- if (streq(*i, *j))
- return true;
- }
- }
-
- return false;
-}
diff --git a/src/strv.h b/src/strv.h
deleted file mode 100644
index d038c9f..0000000
--- a/src/strv.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foostrvhfoo
-#define foostrvhfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdarg.h>
-#include <stdbool.h>
-
-#include "macro.h"
-
-char *strv_find(char **l, const char *name);
-char *strv_find_prefix(char **l, const char *name);
-
-void strv_free(char **l);
-char **strv_copy(char **l) _malloc_;
-unsigned strv_length(char **l);
-
-char **strv_merge(char **a, char **b);
-char **strv_merge_concat(char **a, char **b, const char *suffix);
-char **strv_append(char **l, const char *s);
-
-char **strv_remove(char **l, const char *s);
-char **strv_uniq(char **l);
-
-#define strv_contains(l, s) (!!strv_find((l), (s)))
-
-char **strv_new(const char *x, ...) _sentinel_ _malloc_;
-char **strv_new_ap(const char *x, va_list ap) _malloc_;
-
-static inline bool strv_isempty(char **l) {
- return !l || !*l;
-}
-
-char **strv_split(const char *s, const char *separator) _malloc_;
-char **strv_split_quoted(const char *s) _malloc_;
-
-char *strv_join(char **l, const char *separator) _malloc_;
-
-char **strv_env_merge(unsigned n_lists, ...);
-char **strv_env_delete(char **x, unsigned n_lists, ...);
-
-char **strv_env_set(char **x, const char *p);
-char **strv_env_unset(char **l, const char *p);
-
-char *strv_env_get_with_length(char **l, const char *name, size_t k);
-char *strv_env_get(char **x, const char *n);
-
-char **strv_env_clean(char **l);
-
-char **strv_parse_nulstr(const char *s, size_t l);
-
-bool strv_overlap(char **a, char **b);
-
-#define STRV_FOREACH(s, l) \
- for ((s) = (l); (s) && *(s); (s)++)
-
-#define STRV_FOREACH_BACKWARDS(s, l) \
- for (; (l) && ((s) >= (l)); (s)--)
-
-#endif
diff --git a/src/utf8.c b/src/utf8.c
deleted file mode 100644
index 11619dc..0000000
--- a/src/utf8.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2012 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/* This file is based on the GLIB utf8 validation functions. The
- * original license text follows. */
-
-/* gutf8.c - Operations on UTF-8 strings.
- *
- * Copyright (C) 1999 Tom Tromey
- * Copyright (C) 2000 Red Hat, Inc.
- *
- * 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 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., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <errno.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include "utf8.h"
-
-#define FILTER_CHAR '_'
-
-static inline bool is_unicode_valid(uint32_t ch) {
-
- if (ch >= 0x110000) /* End of unicode space */
- return false;
- if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
- return false;
- if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
- return false;
- if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
- return false;
-
- return true;
-}
-
-static inline bool is_continuation_char(uint8_t ch) {
- if ((ch & 0xc0) != 0x80) /* 10xxxxxx */
- return false;
- return true;
-}
-
-static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) {
- *u_ch <<= 6;
- *u_ch |= ch & 0x3f;
-}
-
-static char* utf8_validate(const char *str, char *output) {
- uint32_t val = 0;
- uint32_t min = 0;
- const uint8_t *p, *last;
- int size;
- uint8_t *o;
-
- assert(str);
-
- o = (uint8_t*) output;
- for (p = (const uint8_t*) str; *p; p++) {
- if (*p < 128) {
- if (o)
- *o = *p;
- } else {
- last = p;
-
- if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
- size = 2;
- min = 128;
- val = (uint32_t) (*p & 0x1e);
- goto ONE_REMAINING;
- } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
- size = 3;
- min = (1 << 11);
- val = (uint32_t) (*p & 0x0f);
- goto TWO_REMAINING;
- } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
- size = 4;
- min = (1 << 16);
- val = (uint32_t) (*p & 0x07);
- } else
- goto error;
-
- p++;
- if (!is_continuation_char(*p))
- goto error;
- merge_continuation_char(&val, *p);
-
- TWO_REMAINING:
- p++;
- if (!is_continuation_char(*p))
- goto error;
- merge_continuation_char(&val, *p);
-
- ONE_REMAINING:
- p++;
- if (!is_continuation_char(*p))
- goto error;
- merge_continuation_char(&val, *p);
-
- if (val < min)
- goto error;
-
- if (!is_unicode_valid(val))
- goto error;
-
- if (o) {
- memcpy(o, last, (size_t) size);
- o += size;
- }
-
- continue;
-
- error:
- if (o) {
- *o = FILTER_CHAR;
- p = last; /* We retry at the next character */
- } else
- goto failure;
- }
-
- if (o)
- o++;
- }
-
- if (o) {
- *o = '\0';
- return output;
- }
-
- return (char*) str;
-
-failure:
- return NULL;
-}
-
-char* utf8_is_valid (const char *str) {
- return utf8_validate(str, NULL);
-}
-
-char* utf8_filter (const char *str) {
- char *new_str;
-
- assert(str);
-
- new_str = malloc(strlen(str) + 1);
- if (!new_str)
- return NULL;
-
- return utf8_validate(str, new_str);
-}
-
-char *ascii_is_valid(const char *str) {
- const char *p;
-
- assert(str);
-
- for (p = str; *p; p++)
- if ((unsigned char) *p >= 128)
- return NULL;
-
- return (char*) str;
-}
-
-char *ascii_filter(const char *str) {
- char *r, *s, *d;
- size_t l;
-
- assert(str);
-
- l = strlen(str);
- r = malloc(l + 1);
- if (!r)
- return NULL;
-
- for (s = r, d = r; *s; s++)
- if ((unsigned char) *s < 128)
- *(d++) = *s;
-
- *d = 0;
-
- return r;
-}
diff --git a/src/utf8.h b/src/utf8.h
deleted file mode 100644
index 9a72bec..0000000
--- a/src/utf8.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooutf8hfoo
-#define fooutf8hfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2012 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "macro.h"
-
-char *utf8_is_valid(const char *s) _pure_;
-char *ascii_is_valid(const char *s) _pure_;
-
-char *utf8_filter(const char *s);
-char *ascii_filter(const char *s);
-
-#endif
diff --git a/src/util.c b/src/util.c
deleted file mode 100644
index dfc1dc6..0000000
--- a/src/util.c
+++ /dev/null
@@ -1,6256 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <stdio.h>
-#include <syslog.h>
-#include <sched.h>
-#include <sys/resource.h>
-#include <linux/sched.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <sys/ioctl.h>
-#include <linux/vt.h>
-#include <linux/tiocl.h>
-#include <termios.h>
-#include <stdarg.h>
-#include <sys/inotify.h>
-#include <sys/poll.h>
-#include <libgen.h>
-#include <ctype.h>
-#include <sys/prctl.h>
-#include <sys/utsname.h>
-#include <pwd.h>
-#include <netinet/ip.h>
-#include <linux/kd.h>
-#include <dlfcn.h>
-#include <sys/wait.h>
-#include <sys/capability.h>
-#include <sys/time.h>
-#include <linux/rtc.h>
-#include <glob.h>
-#include <grp.h>
-#include <sys/mman.h>
-
-#include "macro.h"
-#include "util.h"
-#include "ioprio.h"
-#include "missing.h"
-#include "log.h"
-#include "strv.h"
-#include "label.h"
-#include "exit-status.h"
-#include "hashmap.h"
-
-int saved_argc = 0;
-char **saved_argv = NULL;
-
-size_t page_size(void) {
- static __thread size_t pgsz = 0;
- long r;
-
- if (_likely_(pgsz > 0))
- return pgsz;
-
- assert_se((r = sysconf(_SC_PAGESIZE)) > 0);
-
- pgsz = (size_t) r;
-
- return pgsz;
-}
-
-bool streq_ptr(const char *a, const char *b) {
-
- /* Like streq(), but tries to make sense of NULL pointers */
-
- if (a && b)
- return streq(a, b);
-
- if (!a && !b)
- return true;
-
- return false;
-}
-
-usec_t now(clockid_t clock_id) {
- struct timespec ts;
-
- assert_se(clock_gettime(clock_id, &ts) == 0);
-
- return timespec_load(&ts);
-}
-
-dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
- assert(ts);
-
- ts->realtime = now(CLOCK_REALTIME);
- ts->monotonic = now(CLOCK_MONOTONIC);
-
- return ts;
-}
-
-dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
- int64_t delta;
- assert(ts);
-
- ts->realtime = u;
-
- if (u == 0)
- ts->monotonic = 0;
- else {
- delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
-
- ts->monotonic = now(CLOCK_MONOTONIC);
-
- if ((int64_t) ts->monotonic > delta)
- ts->monotonic -= delta;
- else
- ts->monotonic = 0;
- }
-
- return ts;
-}
-
-usec_t timespec_load(const struct timespec *ts) {
- assert(ts);
-
- return
- (usec_t) ts->tv_sec * USEC_PER_SEC +
- (usec_t) ts->tv_nsec / NSEC_PER_USEC;
-}
-
-struct timespec *timespec_store(struct timespec *ts, usec_t u) {
- assert(ts);
-
- ts->tv_sec = (time_t) (u / USEC_PER_SEC);
- ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
-
- return ts;
-}
-
-usec_t timeval_load(const struct timeval *tv) {
- assert(tv);
-
- return
- (usec_t) tv->tv_sec * USEC_PER_SEC +
- (usec_t) tv->tv_usec;
-}
-
-struct timeval *timeval_store(struct timeval *tv, usec_t u) {
- assert(tv);
-
- tv->tv_sec = (time_t) (u / USEC_PER_SEC);
- tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
-
- return tv;
-}
-
-bool endswith(const char *s, const char *postfix) {
- size_t sl, pl;
-
- assert(s);
- assert(postfix);
-
- sl = strlen(s);
- pl = strlen(postfix);
-
- if (pl == 0)
- return true;
-
- if (sl < pl)
- return false;
-
- return memcmp(s + sl - pl, postfix, pl) == 0;
-}
-
-bool startswith(const char *s, const char *prefix) {
- size_t sl, pl;
-
- assert(s);
- assert(prefix);
-
- sl = strlen(s);
- pl = strlen(prefix);
-
- if (pl == 0)
- return true;
-
- if (sl < pl)
- return false;
-
- return memcmp(s, prefix, pl) == 0;
-}
-
-bool startswith_no_case(const char *s, const char *prefix) {
- size_t sl, pl;
- unsigned i;
-
- assert(s);
- assert(prefix);
-
- sl = strlen(s);
- pl = strlen(prefix);
-
- if (pl == 0)
- return true;
-
- if (sl < pl)
- return false;
-
- for(i = 0; i < pl; ++i) {
- if (tolower(s[i]) != tolower(prefix[i]))
- return false;
- }
-
- return true;
-}
-
-bool first_word(const char *s, const char *word) {
- size_t sl, wl;
-
- assert(s);
- assert(word);
-
- sl = strlen(s);
- wl = strlen(word);
-
- if (sl < wl)
- return false;
-
- if (wl == 0)
- return true;
-
- if (memcmp(s, word, wl) != 0)
- return false;
-
- return s[wl] == 0 ||
- strchr(WHITESPACE, s[wl]);
-}
-
-int close_nointr(int fd) {
- assert(fd >= 0);
-
- for (;;) {
- int r;
-
- r = close(fd);
- if (r >= 0)
- return r;
-
- if (errno != EINTR)
- return -errno;
- }
-}
-
-void close_nointr_nofail(int fd) {
- int saved_errno = errno;
-
- /* like close_nointr() but cannot fail, and guarantees errno
- * is unchanged */
-
- assert_se(close_nointr(fd) == 0);
-
- errno = saved_errno;
-}
-
-void close_many(const int fds[], unsigned n_fd) {
- unsigned i;
-
- for (i = 0; i < n_fd; i++)
- close_nointr_nofail(fds[i]);
-}
-
-int parse_boolean(const char *v) {
- assert(v);
-
- if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
- return 1;
- else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
- return 0;
-
- return -EINVAL;
-}
-
-int parse_pid(const char *s, pid_t* ret_pid) {
- unsigned long ul = 0;
- pid_t pid;
- int r;
-
- assert(s);
- assert(ret_pid);
-
- if ((r = safe_atolu(s, &ul)) < 0)
- return r;
-
- pid = (pid_t) ul;
-
- if ((unsigned long) pid != ul)
- return -ERANGE;
-
- if (pid <= 0)
- return -ERANGE;
-
- *ret_pid = pid;
- return 0;
-}
-
-int parse_uid(const char *s, uid_t* ret_uid) {
- unsigned long ul = 0;
- uid_t uid;
- int r;
-
- assert(s);
- assert(ret_uid);
-
- if ((r = safe_atolu(s, &ul)) < 0)
- return r;
-
- uid = (uid_t) ul;
-
- if ((unsigned long) uid != ul)
- return -ERANGE;
-
- *ret_uid = uid;
- return 0;
-}
-
-int safe_atou(const char *s, unsigned *ret_u) {
- char *x = NULL;
- unsigned long l;
-
- assert(s);
- assert(ret_u);
-
- errno = 0;
- l = strtoul(s, &x, 0);
-
- if (!x || *x || errno)
- return errno ? -errno : -EINVAL;
-
- if ((unsigned long) (unsigned) l != l)
- return -ERANGE;
-
- *ret_u = (unsigned) l;
- return 0;
-}
-
-int safe_atoi(const char *s, int *ret_i) {
- char *x = NULL;
- long l;
-
- assert(s);
- assert(ret_i);
-
- errno = 0;
- l = strtol(s, &x, 0);
-
- if (!x || *x || errno)
- return errno ? -errno : -EINVAL;
-
- if ((long) (int) l != l)
- return -ERANGE;
-
- *ret_i = (int) l;
- return 0;
-}
-
-int safe_atollu(const char *s, long long unsigned *ret_llu) {
- char *x = NULL;
- unsigned long long l;
-
- assert(s);
- assert(ret_llu);
-
- errno = 0;
- l = strtoull(s, &x, 0);
-
- if (!x || *x || errno)
- return errno ? -errno : -EINVAL;
-
- *ret_llu = l;
- return 0;
-}
-
-int safe_atolli(const char *s, long long int *ret_lli) {
- char *x = NULL;
- long long l;
-
- assert(s);
- assert(ret_lli);
-
- errno = 0;
- l = strtoll(s, &x, 0);
-
- if (!x || *x || errno)
- return errno ? -errno : -EINVAL;
-
- *ret_lli = l;
- return 0;
-}
-
-/* Split a string into words. */
-char *split(const char *c, size_t *l, const char *separator, char **state) {
- char *current;
-
- current = *state ? *state : (char*) c;
-
- if (!*current || *c == 0)
- return NULL;
-
- current += strspn(current, separator);
- *l = strcspn(current, separator);
- *state = current+*l;
-
- return (char*) current;
-}
-
-/* Split a string into words, but consider strings enclosed in '' and
- * "" as words even if they include spaces. */
-char *split_quoted(const char *c, size_t *l, char **state) {
- char *current, *e;
- bool escaped = false;
-
- current = *state ? *state : (char*) c;
-
- if (!*current || *c == 0)
- return NULL;
-
- current += strspn(current, WHITESPACE);
-
- if (*current == '\'') {
- current ++;
-
- for (e = current; *e; e++) {
- if (escaped)
- escaped = false;
- else if (*e == '\\')
- escaped = true;
- else if (*e == '\'')
- break;
- }
-
- *l = e-current;
- *state = *e == 0 ? e : e+1;
- } else if (*current == '\"') {
- current ++;
-
- for (e = current; *e; e++) {
- if (escaped)
- escaped = false;
- else if (*e == '\\')
- escaped = true;
- else if (*e == '\"')
- break;
- }
-
- *l = e-current;
- *state = *e == 0 ? e : e+1;
- } else {
- for (e = current; *e; e++) {
- if (escaped)
- escaped = false;
- else if (*e == '\\')
- escaped = true;
- else if (strchr(WHITESPACE, *e))
- break;
- }
- *l = e-current;
- *state = e;
- }
-
- return (char*) current;
-}
-
-char **split_path_and_make_absolute(const char *p) {
- char **l;
- assert(p);
-
- if (!(l = strv_split(p, ":")))
- return NULL;
-
- if (!strv_path_make_absolute_cwd(l)) {
- strv_free(l);
- return NULL;
- }
-
- return l;
-}
-
-int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
- int r;
- FILE *f;
- char fn[PATH_MAX], line[LINE_MAX], *p;
- long unsigned ppid;
-
- assert(pid > 0);
- assert(_ppid);
-
- assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
- char_array_0(fn);
-
- if (!(f = fopen(fn, "re")))
- return -errno;
-
- if (!(fgets(line, sizeof(line), f))) {
- r = feof(f) ? -EIO : -errno;
- fclose(f);
- return r;
- }
-
- fclose(f);
-
- /* Let's skip the pid and comm fields. The latter is enclosed
- * in () but does not escape any () in its value, so let's
- * skip over it manually */
-
- if (!(p = strrchr(line, ')')))
- return -EIO;
-
- p++;
-
- if (sscanf(p, " "
- "%*c " /* state */
- "%lu ", /* ppid */
- &ppid) != 1)
- return -EIO;
-
- if ((long unsigned) (pid_t) ppid != ppid)
- return -ERANGE;
-
- *_ppid = (pid_t) ppid;
-
- return 0;
-}
-
-int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
- int r;
- FILE *f;
- char fn[PATH_MAX], line[LINE_MAX], *p;
-
- assert(pid > 0);
- assert(st);
-
- assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
- char_array_0(fn);
-
- if (!(f = fopen(fn, "re")))
- return -errno;
-
- if (!(fgets(line, sizeof(line), f))) {
- r = feof(f) ? -EIO : -errno;
- fclose(f);
- return r;
- }
-
- fclose(f);
-
- /* Let's skip the pid and comm fields. The latter is enclosed
- * in () but does not escape any () in its value, so let's
- * skip over it manually */
-
- if (!(p = strrchr(line, ')')))
- return -EIO;
-
- p++;
-
- if (sscanf(p, " "
- "%*c " /* state */
- "%*d " /* ppid */
- "%*d " /* pgrp */
- "%*d " /* session */
- "%*d " /* tty_nr */
- "%*d " /* tpgid */
- "%*u " /* flags */
- "%*u " /* minflt */
- "%*u " /* cminflt */
- "%*u " /* majflt */
- "%*u " /* cmajflt */
- "%*u " /* utime */
- "%*u " /* stime */
- "%*d " /* cutime */
- "%*d " /* cstime */
- "%*d " /* priority */
- "%*d " /* nice */
- "%*d " /* num_threads */
- "%*d " /* itrealvalue */
- "%llu " /* starttime */,
- st) != 1)
- return -EIO;
-
- return 0;
-}
-
-int write_one_line_file(const char *fn, const char *line) {
- FILE *f;
- int r;
-
- assert(fn);
- assert(line);
-
- if (!(f = fopen(fn, "we")))
- return -errno;
-
- errno = 0;
- if (fputs(line, f) < 0) {
- r = -errno;
- goto finish;
- }
-
- if (!endswith(line, "\n"))
- fputc('\n', f);
-
- fflush(f);
-
- if (ferror(f)) {
- if (errno != 0)
- r = -errno;
- else
- r = -EIO;
- } else
- r = 0;
-
-finish:
- fclose(f);
- return r;
-}
-
-int fchmod_umask(int fd, mode_t m) {
- mode_t u;
- int r;
-
- u = umask(0777);
- r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
- umask(u);
-
- return r;
-}
-
-int write_one_line_file_atomic(const char *fn, const char *line) {
- FILE *f;
- int r;
- char *p;
-
- assert(fn);
- assert(line);
-
- r = fopen_temporary(fn, &f, &p);
- if (r < 0)
- return r;
-
- fchmod_umask(fileno(f), 0644);
-
- errno = 0;
- if (fputs(line, f) < 0) {
- r = -errno;
- goto finish;
- }
-
- if (!endswith(line, "\n"))
- fputc('\n', f);
-
- fflush(f);
-
- if (ferror(f)) {
- if (errno != 0)
- r = -errno;
- else
- r = -EIO;
- } else {
- if (rename(p, fn) < 0)
- r = -errno;
- else
- r = 0;
- }
-
-finish:
- if (r < 0)
- unlink(p);
-
- fclose(f);
- free(p);
-
- return r;
-}
-
-int read_one_line_file(const char *fn, char **line) {
- FILE *f;
- int r;
- char t[LINE_MAX], *c;
-
- assert(fn);
- assert(line);
-
- f = fopen(fn, "re");
- if (!f)
- return -errno;
-
- if (!fgets(t, sizeof(t), f)) {
-
- if (ferror(f)) {
- r = -errno;
- goto finish;
- }
-
- t[0] = 0;
- }
-
- c = strdup(t);
- if (!c) {
- r = -ENOMEM;
- goto finish;
- }
-
- truncate_nl(c);
-
- *line = c;
- r = 0;
-
-finish:
- fclose(f);
- return r;
-}
-
-int read_full_file(const char *fn, char **contents, size_t *size) {
- FILE *f;
- int r;
- size_t n, l;
- char *buf = NULL;
- struct stat st;
-
- if (!(f = fopen(fn, "re")))
- return -errno;
-
- if (fstat(fileno(f), &st) < 0) {
- r = -errno;
- goto finish;
- }
-
- /* Safety check */
- if (st.st_size > 4*1024*1024) {
- r = -E2BIG;
- goto finish;
- }
-
- n = st.st_size > 0 ? st.st_size : LINE_MAX;
- l = 0;
-
- for (;;) {
- char *t;
- size_t k;
-
- if (!(t = realloc(buf, n+1))) {
- r = -ENOMEM;
- goto finish;
- }
-
- buf = t;
- k = fread(buf + l, 1, n - l, f);
-
- if (k <= 0) {
- if (ferror(f)) {
- r = -errno;
- goto finish;
- }
-
- break;
- }
-
- l += k;
- n *= 2;
-
- /* Safety check */
- if (n > 4*1024*1024) {
- r = -E2BIG;
- goto finish;
- }
- }
-
- buf[l] = 0;
- *contents = buf;
- buf = NULL;
-
- if (size)
- *size = l;
-
- r = 0;
-
-finish:
- fclose(f);
- free(buf);
-
- return r;
-}
-
-int parse_env_file(
- const char *fname,
- const char *separator, ...) {
-
- int r = 0;
- char *contents = NULL, *p;
-
- assert(fname);
- assert(separator);
-
- if ((r = read_full_file(fname, &contents, NULL)) < 0)
- return r;
-
- p = contents;
- for (;;) {
- const char *key = NULL;
-
- p += strspn(p, separator);
- p += strspn(p, WHITESPACE);
-
- if (!*p)
- break;
-
- if (!strchr(COMMENTS, *p)) {
- va_list ap;
- char **value;
-
- va_start(ap, separator);
- while ((key = va_arg(ap, char *))) {
- size_t n;
- char *v;
-
- value = va_arg(ap, char **);
-
- n = strlen(key);
- if (strncmp(p, key, n) != 0 ||
- p[n] != '=')
- continue;
-
- p += n + 1;
- n = strcspn(p, separator);
-
- if (n >= 2 &&
- strchr(QUOTES, p[0]) &&
- p[n-1] == p[0])
- v = strndup(p+1, n-2);
- else
- v = strndup(p, n);
-
- if (!v) {
- r = -ENOMEM;
- va_end(ap);
- goto fail;
- }
-
- if (v[0] == '\0') {
- /* return empty value strings as NULL */
- free(v);
- v = NULL;
- }
-
- free(*value);
- *value = v;
-
- p += n;
-
- r ++;
- break;
- }
- va_end(ap);
- }
-
- if (!key)
- p += strcspn(p, separator);
- }
-
-fail:
- free(contents);
- return r;
-}
-
-int load_env_file(
- const char *fname,
- char ***rl) {
-
- FILE *f;
- char **m = NULL;
- int r;
-
- assert(fname);
- assert(rl);
-
- if (!(f = fopen(fname, "re")))
- return -errno;
-
- while (!feof(f)) {
- char l[LINE_MAX], *p, *u;
- char **t;
-
- if (!fgets(l, sizeof(l), f)) {
- if (feof(f))
- break;
-
- r = -errno;
- goto finish;
- }
-
- p = strstrip(l);
-
- if (!*p)
- continue;
-
- if (strchr(COMMENTS, *p))
- continue;
-
- if (!(u = normalize_env_assignment(p))) {
- log_error("Out of memory");
- r = -ENOMEM;
- goto finish;
- }
-
- t = strv_append(m, u);
- free(u);
-
- if (!t) {
- log_error("Out of memory");
- r = -ENOMEM;
- goto finish;
- }
-
- strv_free(m);
- m = t;
- }
-
- r = 0;
-
- *rl = m;
- m = NULL;
-
-finish:
- if (f)
- fclose(f);
-
- strv_free(m);
-
- return r;
-}
-
-int write_env_file(const char *fname, char **l) {
- char **i, *p;
- FILE *f;
- int r;
-
- r = fopen_temporary(fname, &f, &p);
- if (r < 0)
- return r;
-
- fchmod_umask(fileno(f), 0644);
-
- errno = 0;
- STRV_FOREACH(i, l) {
- fputs(*i, f);
- fputc('\n', f);
- }
-
- fflush(f);
-
- if (ferror(f)) {
- if (errno != 0)
- r = -errno;
- else
- r = -EIO;
- } else {
- if (rename(p, fname) < 0)
- r = -errno;
- else
- r = 0;
- }
-
- if (r < 0)
- unlink(p);
-
- fclose(f);
- free(p);
-
- return r;
-}
-
-char *truncate_nl(char *s) {
- assert(s);
-
- s[strcspn(s, NEWLINE)] = 0;
- return s;
-}
-
-int get_process_comm(pid_t pid, char **name) {
- int r;
-
- assert(name);
-
- if (pid == 0)
- r = read_one_line_file("/proc/self/comm", name);
- else {
- char *p;
- if (asprintf(&p, "/proc/%lu/comm", (unsigned long) pid) < 0)
- return -ENOMEM;
-
- r = read_one_line_file(p, name);
- free(p);
- }
-
- return r;
-}
-
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
- char *r, *k;
- int c;
- bool space = false;
- size_t left;
- FILE *f;
-
- assert(max_length > 0);
- assert(line);
-
- if (pid == 0)
- f = fopen("/proc/self/cmdline", "re");
- else {
- char *p;
- if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
- return -ENOMEM;
-
- f = fopen(p, "re");
- free(p);
- }
-
- if (!f)
- return -errno;
-
- r = new(char, max_length);
- if (!r) {
- fclose(f);
- return -ENOMEM;
- }
-
- k = r;
- left = max_length;
- while ((c = getc(f)) != EOF) {
-
- if (isprint(c)) {
- if (space) {
- if (left <= 4)
- break;
-
- *(k++) = ' ';
- left--;
- space = false;
- }
-
- if (left <= 4)
- break;
-
- *(k++) = (char) c;
- left--;
- } else
- space = true;
- }
-
- if (left <= 4) {
- size_t n = MIN(left-1, 3U);
- memcpy(k, "...", n);
- k[n] = 0;
- } else
- *k = 0;
-
- fclose(f);
-
- /* Kernel threads have no argv[] */
- if (r[0] == 0) {
- char *t;
- int h;
-
- free(r);
-
- if (!comm_fallback)
- return -ENOENT;
-
- h = get_process_comm(pid, &t);
- if (h < 0)
- return h;
-
- r = join("[", t, "]", NULL);
- free(t);
-
- if (!r)
- return -ENOMEM;
- }
-
- *line = r;
- return 0;
-}
-
-int is_kernel_thread(pid_t pid) {
- char *p;
- size_t count;
- char c;
- bool eof;
- FILE *f;
-
- if (pid == 0)
- return 0;
-
- if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
- return -ENOMEM;
-
- f = fopen(p, "re");
- free(p);
-
- if (!f)
- return -errno;
-
- count = fread(&c, 1, 1, f);
- eof = feof(f);
- fclose(f);
-
- /* Kernel threads have an empty cmdline */
-
- if (count <= 0)
- return eof ? 1 : -errno;
-
- return 0;
-}
-
-int get_process_exe(pid_t pid, char **name) {
- int r;
-
- assert(name);
-
- if (pid == 0)
- r = readlink_malloc("/proc/self/exe", name);
- else {
- char *p;
- if (asprintf(&p, "/proc/%lu/exe", (unsigned long) pid) < 0)
- return -ENOMEM;
-
- r = readlink_malloc(p, name);
- free(p);
- }
-
- return r;
-}
-
-int get_process_uid(pid_t pid, uid_t *uid) {
- char *p;
- FILE *f;
- int r;
-
- assert(uid);
-
- if (pid == 0)
- return getuid();
-
- if (asprintf(&p, "/proc/%lu/status", (unsigned long) pid) < 0)
- return -ENOMEM;
-
- f = fopen(p, "re");
- free(p);
-
- if (!f)
- return -errno;
-
- while (!feof(f)) {
- char line[LINE_MAX], *l;
-
- if (!fgets(line, sizeof(line), f)) {
- if (feof(f))
- break;
-
- r = -errno;
- goto finish;
- }
-
- l = strstrip(line);
-
- if (startswith(l, "Uid:")) {
- l += 4;
- l += strspn(l, WHITESPACE);
-
- l[strcspn(l, WHITESPACE)] = 0;
-
- r = parse_uid(l, uid);
- goto finish;
- }
- }
-
- r = -EIO;
-
-finish:
- fclose(f);
-
- return r;
-}
-
-char *strnappend(const char *s, const char *suffix, size_t b) {
- size_t a;
- char *r;
-
- if (!s && !suffix)
- return strdup("");
-
- if (!s)
- return strndup(suffix, b);
-
- if (!suffix)
- return strdup(s);
-
- assert(s);
- assert(suffix);
-
- a = strlen(s);
-
- if (!(r = new(char, a+b+1)))
- return NULL;
-
- memcpy(r, s, a);
- memcpy(r+a, suffix, b);
- r[a+b] = 0;
-
- return r;
-}
-
-char *strappend(const char *s, const char *suffix) {
- return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
-}
-
-int readlink_malloc(const char *p, char **r) {
- size_t l = 100;
-
- assert(p);
- assert(r);
-
- for (;;) {
- char *c;
- ssize_t n;
-
- if (!(c = new(char, l)))
- return -ENOMEM;
-
- if ((n = readlink(p, c, l-1)) < 0) {
- int ret = -errno;
- free(c);
- return ret;
- }
-
- if ((size_t) n < l-1) {
- c[n] = 0;
- *r = c;
- return 0;
- }
-
- free(c);
- l *= 2;
- }
-}
-
-int readlink_and_make_absolute(const char *p, char **r) {
- char *target, *k;
- int j;
-
- assert(p);
- assert(r);
-
- if ((j = readlink_malloc(p, &target)) < 0)
- return j;
-
- k = file_in_same_dir(p, target);
- free(target);
-
- if (!k)
- return -ENOMEM;
-
- *r = k;
- return 0;
-}
-
-int readlink_and_canonicalize(const char *p, char **r) {
- char *t, *s;
- int j;
-
- assert(p);
- assert(r);
-
- j = readlink_and_make_absolute(p, &t);
- if (j < 0)
- return j;
-
- s = canonicalize_file_name(t);
- if (s) {
- free(t);
- *r = s;
- } else
- *r = t;
-
- path_kill_slashes(*r);
-
- return 0;
-}
-
-int parent_of_path(const char *path, char **_r) {
- const char *e, *a = NULL, *b = NULL, *p;
- char *r;
- bool slash = false;
-
- assert(path);
- assert(_r);
-
- if (!*path)
- return -EINVAL;
-
- for (e = path; *e; e++) {
-
- if (!slash && *e == '/') {
- a = b;
- b = e;
- slash = true;
- } else if (slash && *e != '/')
- slash = false;
- }
-
- if (*(e-1) == '/')
- p = a;
- else
- p = b;
-
- if (!p)
- return -EINVAL;
-
- if (p == path)
- r = strdup("/");
- else
- r = strndup(path, p-path);
-
- if (!r)
- return -ENOMEM;
-
- *_r = r;
- return 0;
-}
-
-
-char *file_name_from_path(const char *p) {
- char *r;
-
- assert(p);
-
- if ((r = strrchr(p, '/')))
- return r + 1;
-
- return (char*) p;
-}
-
-bool path_is_absolute(const char *p) {
- assert(p);
-
- return p[0] == '/';
-}
-
-bool is_path(const char *p) {
-
- return !!strchr(p, '/');
-}
-
-char *path_make_absolute(const char *p, const char *prefix) {
- assert(p);
-
- /* Makes every item in the list an absolute path by prepending
- * the prefix, if specified and necessary */
-
- if (path_is_absolute(p) || !prefix)
- return strdup(p);
-
- return join(prefix, "/", p, NULL);
-}
-
-char *path_make_absolute_cwd(const char *p) {
- char *cwd, *r;
-
- assert(p);
-
- /* Similar to path_make_absolute(), but prefixes with the
- * current working directory. */
-
- if (path_is_absolute(p))
- return strdup(p);
-
- if (!(cwd = get_current_dir_name()))
- return NULL;
-
- r = path_make_absolute(p, cwd);
- free(cwd);
-
- return r;
-}
-
-char **strv_path_make_absolute_cwd(char **l) {
- char **s;
-
- /* Goes through every item in the string list and makes it
- * absolute. This works in place and won't rollback any
- * changes on failure. */
-
- STRV_FOREACH(s, l) {
- char *t;
-
- if (!(t = path_make_absolute_cwd(*s)))
- return NULL;
-
- free(*s);
- *s = t;
- }
-
- return l;
-}
-
-char **strv_path_canonicalize(char **l) {
- char **s;
- unsigned k = 0;
- bool enomem = false;
-
- if (strv_isempty(l))
- return l;
-
- /* Goes through every item in the string list and canonicalize
- * the path. This works in place and won't rollback any
- * changes on failure. */
-
- STRV_FOREACH(s, l) {
- char *t, *u;
-
- t = path_make_absolute_cwd(*s);
- free(*s);
-
- if (!t) {
- enomem = true;
- continue;
- }
-
- errno = 0;
- u = canonicalize_file_name(t);
- free(t);
-
- if (!u) {
- if (errno == ENOMEM || !errno)
- enomem = true;
-
- continue;
- }
-
- l[k++] = u;
- }
-
- l[k] = NULL;
-
- if (enomem)
- return NULL;
-
- return l;
-}
-
-char **strv_path_remove_empty(char **l) {
- char **f, **t;
-
- if (!l)
- return NULL;
-
- for (f = t = l; *f; f++) {
-
- if (dir_is_empty(*f) > 0) {
- free(*f);
- continue;
- }
-
- *(t++) = *f;
- }
-
- *t = NULL;
- return l;
-}
-
-int reset_all_signal_handlers(void) {
- int sig;
-
- for (sig = 1; sig < _NSIG; sig++) {
- struct sigaction sa;
-
- if (sig == SIGKILL || sig == SIGSTOP)
- continue;
-
- zero(sa);
- sa.sa_handler = SIG_DFL;
- sa.sa_flags = SA_RESTART;
-
- /* On Linux the first two RT signals are reserved by
- * glibc, and sigaction() will return EINVAL for them. */
- if ((sigaction(sig, &sa, NULL) < 0))
- if (errno != EINVAL)
- return -errno;
- }
-
- return 0;
-}
-
-char *strstrip(char *s) {
- char *e;
-
- /* Drops trailing whitespace. Modifies the string in
- * place. Returns pointer to first non-space character */
-
- s += strspn(s, WHITESPACE);
-
- for (e = strchr(s, 0); e > s; e --)
- if (!strchr(WHITESPACE, e[-1]))
- break;
-
- *e = 0;
-
- return s;
-}
-
-char *delete_chars(char *s, const char *bad) {
- char *f, *t;
-
- /* Drops all whitespace, regardless where in the string */
-
- for (f = s, t = s; *f; f++) {
- if (strchr(bad, *f))
- continue;
-
- *(t++) = *f;
- }
-
- *t = 0;
-
- return s;
-}
-
-bool in_charset(const char *s, const char* charset) {
- const char *i;
-
- assert(s);
- assert(charset);
-
- for (i = s; *i; i++)
- if (!strchr(charset, *i))
- return false;
-
- return true;
-}
-
-char *file_in_same_dir(const char *path, const char *filename) {
- char *e, *r;
- size_t k;
-
- assert(path);
- assert(filename);
-
- /* This removes the last component of path and appends
- * filename, unless the latter is absolute anyway or the
- * former isn't */
-
- if (path_is_absolute(filename))
- return strdup(filename);
-
- if (!(e = strrchr(path, '/')))
- return strdup(filename);
-
- k = strlen(filename);
- if (!(r = new(char, e-path+1+k+1)))
- return NULL;
-
- memcpy(r, path, e-path+1);
- memcpy(r+(e-path)+1, filename, k+1);
-
- return r;
-}
-
-int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) {
- struct stat st;
-
- if (label_mkdir(path, mode) >= 0)
- if (chmod_and_chown(path, mode, uid, gid) < 0)
- return -errno;
-
- if (lstat(path, &st) < 0)
- return -errno;
-
- if ((st.st_mode & 0777) != mode ||
- st.st_uid != uid ||
- st.st_gid != gid ||
- !S_ISDIR(st.st_mode)) {
- errno = EEXIST;
- return -errno;
- }
-
- return 0;
-}
-
-
-int mkdir_parents(const char *path, mode_t mode) {
- const char *p, *e;
-
- assert(path);
-
- /* Creates every parent directory in the path except the last
- * component. */
-
- p = path + strspn(path, "/");
- for (;;) {
- int r;
- char *t;
-
- e = p + strcspn(p, "/");
- p = e + strspn(e, "/");
-
- /* Is this the last component? If so, then we're
- * done */
- if (*p == 0)
- return 0;
-
- if (!(t = strndup(path, e - path)))
- return -ENOMEM;
-
- r = label_mkdir(t, mode);
- free(t);
-
- if (r < 0 && errno != EEXIST)
- return -errno;
- }
-}
-
-int mkdir_p(const char *path, mode_t mode) {
- int r;
-
- /* Like mkdir -p */
-
- if ((r = mkdir_parents(path, mode)) < 0)
- return r;
-
- if (label_mkdir(path, mode) < 0 && errno != EEXIST)
- return -errno;
-
- return 0;
-}
-
-int rmdir_parents(const char *path, const char *stop) {
- size_t l;
- int r = 0;
-
- assert(path);
- assert(stop);
-
- l = strlen(path);
-
- /* Skip trailing slashes */
- while (l > 0 && path[l-1] == '/')
- l--;
-
- while (l > 0) {
- char *t;
-
- /* Skip last component */
- while (l > 0 && path[l-1] != '/')
- l--;
-
- /* Skip trailing slashes */
- while (l > 0 && path[l-1] == '/')
- l--;
-
- if (l <= 0)
- break;
-
- if (!(t = strndup(path, l)))
- return -ENOMEM;
-
- if (path_startswith(stop, t)) {
- free(t);
- return 0;
- }
-
- r = rmdir(t);
- free(t);
-
- if (r < 0)
- if (errno != ENOENT)
- return -errno;
- }
-
- return 0;
-}
-
-
-char hexchar(int x) {
- static const char table[16] = "0123456789abcdef";
-
- return table[x & 15];
-}
-
-int unhexchar(char c) {
-
- if (c >= '0' && c <= '9')
- return c - '0';
-
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 10;
-
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
-
- return -1;
-}
-
-char octchar(int x) {
- return '0' + (x & 7);
-}
-
-int unoctchar(char c) {
-
- if (c >= '0' && c <= '7')
- return c - '0';
-
- return -1;
-}
-
-char decchar(int x) {
- return '0' + (x % 10);
-}
-
-int undecchar(char c) {
-
- if (c >= '0' && c <= '9')
- return c - '0';
-
- return -1;
-}
-
-char *cescape(const char *s) {
- char *r, *t;
- const char *f;
-
- assert(s);
-
- /* Does C style string escaping. */
-
- if (!(r = new(char, strlen(s)*4 + 1)))
- return NULL;
-
- for (f = s, t = r; *f; f++)
-
- switch (*f) {
-
- case '\a':
- *(t++) = '\\';
- *(t++) = 'a';
- break;
- case '\b':
- *(t++) = '\\';
- *(t++) = 'b';
- break;
- case '\f':
- *(t++) = '\\';
- *(t++) = 'f';
- break;
- case '\n':
- *(t++) = '\\';
- *(t++) = 'n';
- break;
- case '\r':
- *(t++) = '\\';
- *(t++) = 'r';
- break;
- case '\t':
- *(t++) = '\\';
- *(t++) = 't';
- break;
- case '\v':
- *(t++) = '\\';
- *(t++) = 'v';
- break;
- case '\\':
- *(t++) = '\\';
- *(t++) = '\\';
- break;
- case '"':
- *(t++) = '\\';
- *(t++) = '"';
- break;
- case '\'':
- *(t++) = '\\';
- *(t++) = '\'';
- break;
-
- default:
- /* For special chars we prefer octal over
- * hexadecimal encoding, simply because glib's
- * g_strescape() does the same */
- if ((*f < ' ') || (*f >= 127)) {
- *(t++) = '\\';
- *(t++) = octchar((unsigned char) *f >> 6);
- *(t++) = octchar((unsigned char) *f >> 3);
- *(t++) = octchar((unsigned char) *f);
- } else
- *(t++) = *f;
- break;
- }
-
- *t = 0;
-
- return r;
-}
-
-char *cunescape_length(const char *s, size_t length) {
- char *r, *t;
- const char *f;
-
- assert(s);
-
- /* Undoes C style string escaping */
-
- r = new(char, length+1);
- if (!r)
- return r;
-
- for (f = s, t = r; f < s + length; f++) {
-
- if (*f != '\\') {
- *(t++) = *f;
- continue;
- }
-
- f++;
-
- switch (*f) {
-
- case 'a':
- *(t++) = '\a';
- break;
- case 'b':
- *(t++) = '\b';
- break;
- case 'f':
- *(t++) = '\f';
- break;
- case 'n':
- *(t++) = '\n';
- break;
- case 'r':
- *(t++) = '\r';
- break;
- case 't':
- *(t++) = '\t';
- break;
- case 'v':
- *(t++) = '\v';
- break;
- case '\\':
- *(t++) = '\\';
- break;
- case '"':
- *(t++) = '"';
- break;
- case '\'':
- *(t++) = '\'';
- break;
-
- case 's':
- /* This is an extension of the XDG syntax files */
- *(t++) = ' ';
- break;
-
- case 'x': {
- /* hexadecimal encoding */
- int a, b;
-
- a = unhexchar(f[1]);
- b = unhexchar(f[2]);
-
- if (a < 0 || b < 0) {
- /* Invalid escape code, let's take it literal then */
- *(t++) = '\\';
- *(t++) = 'x';
- } else {
- *(t++) = (char) ((a << 4) | b);
- f += 2;
- }
-
- break;
- }
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7': {
- /* octal encoding */
- int a, b, c;
-
- a = unoctchar(f[0]);
- b = unoctchar(f[1]);
- c = unoctchar(f[2]);
-
- if (a < 0 || b < 0 || c < 0) {
- /* Invalid escape code, let's take it literal then */
- *(t++) = '\\';
- *(t++) = f[0];
- } else {
- *(t++) = (char) ((a << 6) | (b << 3) | c);
- f += 2;
- }
-
- break;
- }
-
- case 0:
- /* premature end of string.*/
- *(t++) = '\\';
- goto finish;
-
- default:
- /* Invalid escape code, let's take it literal then */
- *(t++) = '\\';
- *(t++) = *f;
- break;
- }
- }
-
-finish:
- *t = 0;
- return r;
-}
-
-char *cunescape(const char *s) {
- return cunescape_length(s, strlen(s));
-}
-
-char *xescape(const char *s, const char *bad) {
- char *r, *t;
- const char *f;
-
- /* Escapes all chars in bad, in addition to \ and all special
- * chars, in \xFF style escaping. May be reversed with
- * cunescape. */
-
- if (!(r = new(char, strlen(s)*4+1)))
- return NULL;
-
- for (f = s, t = r; *f; f++) {
-
- if ((*f < ' ') || (*f >= 127) ||
- (*f == '\\') || strchr(bad, *f)) {
- *(t++) = '\\';
- *(t++) = 'x';
- *(t++) = hexchar(*f >> 4);
- *(t++) = hexchar(*f);
- } else
- *(t++) = *f;
- }
-
- *t = 0;
-
- return r;
-}
-
-char *bus_path_escape(const char *s) {
- char *r, *t;
- const char *f;
-
- assert(s);
-
- /* Escapes all chars that D-Bus' object path cannot deal
- * with. Can be reverse with bus_path_unescape() */
-
- if (!(r = new(char, strlen(s)*3+1)))
- return NULL;
-
- for (f = s, t = r; *f; f++) {
-
- if (!(*f >= 'A' && *f <= 'Z') &&
- !(*f >= 'a' && *f <= 'z') &&
- !(*f >= '0' && *f <= '9')) {
- *(t++) = '_';
- *(t++) = hexchar(*f >> 4);
- *(t++) = hexchar(*f);
- } else
- *(t++) = *f;
- }
-
- *t = 0;
-
- return r;
-}
-
-char *bus_path_unescape(const char *f) {
- char *r, *t;
-
- assert(f);
-
- if (!(r = strdup(f)))
- return NULL;
-
- for (t = r; *f; f++) {
-
- if (*f == '_') {
- int a, b;
-
- if ((a = unhexchar(f[1])) < 0 ||
- (b = unhexchar(f[2])) < 0) {
- /* Invalid escape code, let's take it literal then */
- *(t++) = '_';
- } else {
- *(t++) = (char) ((a << 4) | b);
- f += 2;
- }
- } else
- *(t++) = *f;
- }
-
- *t = 0;
-
- return r;
-}
-
-char *path_kill_slashes(char *path) {
- char *f, *t;
- bool slash = false;
-
- /* Removes redundant inner and trailing slashes. Modifies the
- * passed string in-place.
- *
- * ///foo///bar/ becomes /foo/bar
- */
-
- for (f = path, t = path; *f; f++) {
-
- if (*f == '/') {
- slash = true;
- continue;
- }
-
- if (slash) {
- slash = false;
- *(t++) = '/';
- }
-
- *(t++) = *f;
- }
-
- /* Special rule, if we are talking of the root directory, a
- trailing slash is good */
-
- if (t == path && slash)
- *(t++) = '/';
-
- *t = 0;
- return path;
-}
-
-bool path_startswith(const char *path, const char *prefix) {
- assert(path);
- assert(prefix);
-
- if ((path[0] == '/') != (prefix[0] == '/'))
- return false;
-
- for (;;) {
- size_t a, b;
-
- path += strspn(path, "/");
- prefix += strspn(prefix, "/");
-
- if (*prefix == 0)
- return true;
-
- if (*path == 0)
- return false;
-
- a = strcspn(path, "/");
- b = strcspn(prefix, "/");
-
- if (a != b)
- return false;
-
- if (memcmp(path, prefix, a) != 0)
- return false;
-
- path += a;
- prefix += b;
- }
-}
-
-bool path_equal(const char *a, const char *b) {
- assert(a);
- assert(b);
-
- if ((a[0] == '/') != (b[0] == '/'))
- return false;
-
- for (;;) {
- size_t j, k;
-
- a += strspn(a, "/");
- b += strspn(b, "/");
-
- if (*a == 0 && *b == 0)
- return true;
-
- if (*a == 0 || *b == 0)
- return false;
-
- j = strcspn(a, "/");
- k = strcspn(b, "/");
-
- if (j != k)
- return false;
-
- if (memcmp(a, b, j) != 0)
- return false;
-
- a += j;
- b += k;
- }
-}
-
-char *ascii_strlower(char *t) {
- char *p;
-
- assert(t);
-
- for (p = t; *p; p++)
- if (*p >= 'A' && *p <= 'Z')
- *p = *p - 'A' + 'a';
-
- return t;
-}
-
-bool ignore_file(const char *filename) {
- assert(filename);
-
- return
- filename[0] == '.' ||
- streq(filename, "lost+found") ||
- streq(filename, "aquota.user") ||
- streq(filename, "aquota.group") ||
- endswith(filename, "~") ||
- endswith(filename, ".rpmnew") ||
- endswith(filename, ".rpmsave") ||
- endswith(filename, ".rpmorig") ||
- endswith(filename, ".dpkg-old") ||
- endswith(filename, ".dpkg-new") ||
- endswith(filename, ".swp");
-}
-
-int fd_nonblock(int fd, bool nonblock) {
- int flags;
-
- assert(fd >= 0);
-
- if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
- return -errno;
-
- if (nonblock)
- flags |= O_NONBLOCK;
- else
- flags &= ~O_NONBLOCK;
-
- if (fcntl(fd, F_SETFL, flags) < 0)
- return -errno;
-
- return 0;
-}
-
-int fd_cloexec(int fd, bool cloexec) {
- int flags;
-
- assert(fd >= 0);
-
- if ((flags = fcntl(fd, F_GETFD, 0)) < 0)
- return -errno;
-
- if (cloexec)
- flags |= FD_CLOEXEC;
- else
- flags &= ~FD_CLOEXEC;
-
- if (fcntl(fd, F_SETFD, flags) < 0)
- return -errno;
-
- return 0;
-}
-
-static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
- unsigned i;
-
- assert(n_fdset == 0 || fdset);
-
- for (i = 0; i < n_fdset; i++)
- if (fdset[i] == fd)
- return true;
-
- return false;
-}
-
-int close_all_fds(const int except[], unsigned n_except) {
- DIR *d;
- struct dirent *de;
- int r = 0;
-
- assert(n_except == 0 || except);
-
- d = opendir("/proc/self/fd");
- if (!d) {
- int fd;
- struct rlimit rl;
-
- /* When /proc isn't available (for example in chroots)
- * the fallback is brute forcing through the fd
- * table */
-
- assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
- for (fd = 3; fd < (int) rl.rlim_max; fd ++) {
-
- if (fd_in_set(fd, except, n_except))
- continue;
-
- if (close_nointr(fd) < 0)
- if (errno != EBADF && r == 0)
- r = -errno;
- }
-
- return r;
- }
-
- while ((de = readdir(d))) {
- int fd = -1;
-
- if (ignore_file(de->d_name))
- continue;
-
- if (safe_atoi(de->d_name, &fd) < 0)
- /* Let's better ignore this, just in case */
- continue;
-
- if (fd < 3)
- continue;
-
- if (fd == dirfd(d))
- continue;
-
- if (fd_in_set(fd, except, n_except))
- continue;
-
- if (close_nointr(fd) < 0) {
- /* Valgrind has its own FD and doesn't want to have it closed */
- if (errno != EBADF && r == 0)
- r = -errno;
- }
- }
-
- closedir(d);
- return r;
-}
-
-bool chars_intersect(const char *a, const char *b) {
- const char *p;
-
- /* Returns true if any of the chars in a are in b. */
- for (p = a; *p; p++)
- if (strchr(b, *p))
- return true;
-
- return false;
-}
-
-char *format_timestamp(char *buf, size_t l, usec_t t) {
- struct tm tm;
- time_t sec;
-
- assert(buf);
- assert(l > 0);
-
- if (t <= 0)
- return NULL;
-
- sec = (time_t) (t / USEC_PER_SEC);
-
- if (strftime(buf, l, "%a, %d %b %Y %H:%M:%S %z", localtime_r(&sec, &tm)) <= 0)
- return NULL;
-
- return buf;
-}
-
-char *format_timestamp_pretty(char *buf, size_t l, usec_t t) {
- usec_t n, d;
-
- n = now(CLOCK_REALTIME);
-
- if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
- return NULL;
-
- d = n - t;
-
- if (d >= USEC_PER_YEAR)
- snprintf(buf, l, "%llu years and %llu months ago",
- (unsigned long long) (d / USEC_PER_YEAR),
- (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
- else if (d >= USEC_PER_MONTH)
- snprintf(buf, l, "%llu months and %llu days ago",
- (unsigned long long) (d / USEC_PER_MONTH),
- (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
- else if (d >= USEC_PER_WEEK)
- snprintf(buf, l, "%llu weeks and %llu days ago",
- (unsigned long long) (d / USEC_PER_WEEK),
- (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
- else if (d >= 2*USEC_PER_DAY)
- snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
- else if (d >= 25*USEC_PER_HOUR)
- snprintf(buf, l, "1 day and %lluh ago",
- (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
- else if (d >= 6*USEC_PER_HOUR)
- snprintf(buf, l, "%lluh ago",
- (unsigned long long) (d / USEC_PER_HOUR));
- else if (d >= USEC_PER_HOUR)
- snprintf(buf, l, "%lluh %llumin ago",
- (unsigned long long) (d / USEC_PER_HOUR),
- (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
- else if (d >= 5*USEC_PER_MINUTE)
- snprintf(buf, l, "%llumin ago",
- (unsigned long long) (d / USEC_PER_MINUTE));
- else if (d >= USEC_PER_MINUTE)
- snprintf(buf, l, "%llumin %llus ago",
- (unsigned long long) (d / USEC_PER_MINUTE),
- (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
- else if (d >= USEC_PER_SEC)
- snprintf(buf, l, "%llus ago",
- (unsigned long long) (d / USEC_PER_SEC));
- else if (d >= USEC_PER_MSEC)
- snprintf(buf, l, "%llums ago",
- (unsigned long long) (d / USEC_PER_MSEC));
- else if (d > 0)
- snprintf(buf, l, "%lluus ago",
- (unsigned long long) d);
- else
- snprintf(buf, l, "now");
-
- buf[l-1] = 0;
- return buf;
-}
-
-char *format_timespan(char *buf, size_t l, usec_t t) {
- static const struct {
- const char *suffix;
- usec_t usec;
- } table[] = {
- { "w", USEC_PER_WEEK },
- { "d", USEC_PER_DAY },
- { "h", USEC_PER_HOUR },
- { "min", USEC_PER_MINUTE },
- { "s", USEC_PER_SEC },
- { "ms", USEC_PER_MSEC },
- { "us", 1 },
- };
-
- unsigned i;
- char *p = buf;
-
- assert(buf);
- assert(l > 0);
-
- if (t == (usec_t) -1)
- return NULL;
-
- if (t == 0) {
- snprintf(p, l, "0");
- p[l-1] = 0;
- return p;
- }
-
- /* The result of this function can be parsed with parse_usec */
-
- for (i = 0; i < ELEMENTSOF(table); i++) {
- int k;
- size_t n;
-
- if (t < table[i].usec)
- continue;
-
- if (l <= 1)
- break;
-
- k = snprintf(p, l, "%s%llu%s", p > buf ? " " : "", (unsigned long long) (t / table[i].usec), table[i].suffix);
- n = MIN((size_t) k, l);
-
- l -= n;
- p += n;
-
- t %= table[i].usec;
- }
-
- *p = 0;
-
- return buf;
-}
-
-bool fstype_is_network(const char *fstype) {
- static const char * const table[] = {
- "cifs",
- "smbfs",
- "ncpfs",
- "nfs",
- "nfs4",
- "gfs",
- "gfs2"
- };
-
- unsigned i;
-
- for (i = 0; i < ELEMENTSOF(table); i++)
- if (streq(table[i], fstype))
- return true;
-
- return false;
-}
-
-int chvt(int vt) {
- int fd, r = 0;
-
- if ((fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
- return -errno;
-
- if (vt < 0) {
- int tiocl[2] = {
- TIOCL_GETKMSGREDIRECT,
- 0
- };
-
- if (ioctl(fd, TIOCLINUX, tiocl) < 0) {
- r = -errno;
- goto fail;
- }
-
- vt = tiocl[0] <= 0 ? 1 : tiocl[0];
- }
-
- if (ioctl(fd, VT_ACTIVATE, vt) < 0)
- r = -errno;
-
-fail:
- close_nointr_nofail(fd);
- return r;
-}
-
-int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
- struct termios old_termios, new_termios;
- char c;
- char line[LINE_MAX];
-
- assert(f);
- assert(ret);
-
- if (tcgetattr(fileno(f), &old_termios) >= 0) {
- new_termios = old_termios;
-
- new_termios.c_lflag &= ~ICANON;
- new_termios.c_cc[VMIN] = 1;
- new_termios.c_cc[VTIME] = 0;
-
- if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
- size_t k;
-
- if (t != (usec_t) -1) {
- if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
- tcsetattr(fileno(f), TCSADRAIN, &old_termios);
- return -ETIMEDOUT;
- }
- }
-
- k = fread(&c, 1, 1, f);
-
- tcsetattr(fileno(f), TCSADRAIN, &old_termios);
-
- if (k <= 0)
- return -EIO;
-
- if (need_nl)
- *need_nl = c != '\n';
-
- *ret = c;
- return 0;
- }
- }
-
- if (t != (usec_t) -1)
- if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
- return -ETIMEDOUT;
-
- if (!fgets(line, sizeof(line), f))
- return -EIO;
-
- truncate_nl(line);
-
- if (strlen(line) != 1)
- return -EBADMSG;
-
- if (need_nl)
- *need_nl = false;
-
- *ret = line[0];
- return 0;
-}
-
-int ask(char *ret, const char *replies, const char *text, ...) {
- bool on_tty;
-
- assert(ret);
- assert(replies);
- assert(text);
-
- on_tty = isatty(STDOUT_FILENO);
-
- for (;;) {
- va_list ap;
- char c;
- int r;
- bool need_nl = true;
-
- if (on_tty)
- fputs(ANSI_HIGHLIGHT_ON, stdout);
-
- va_start(ap, text);
- vprintf(text, ap);
- va_end(ap);
-
- if (on_tty)
- fputs(ANSI_HIGHLIGHT_OFF, stdout);
-
- fflush(stdout);
-
- r = read_one_char(stdin, &c, (usec_t) -1, &need_nl);
- if (r < 0) {
-
- if (r == -EBADMSG) {
- puts("Bad input, please try again.");
- continue;
- }
-
- putchar('\n');
- return r;
- }
-
- if (need_nl)
- putchar('\n');
-
- if (strchr(replies, c)) {
- *ret = c;
- return 0;
- }
-
- puts("Read unexpected character, please try again.");
- }
-}
-
-int reset_terminal_fd(int fd, bool switch_to_text) {
- struct termios termios;
- int r = 0;
-
- /* Set terminal to some sane defaults */
-
- assert(fd >= 0);
-
- /* We leave locked terminal attributes untouched, so that
- * Plymouth may set whatever it wants to set, and we don't
- * interfere with that. */
-
- /* Disable exclusive mode, just in case */
- ioctl(fd, TIOCNXCL);
-
- /* Switch to text mode */
- if (switch_to_text)
- ioctl(fd, KDSETMODE, KD_TEXT);
-
- /* Enable console unicode mode */
- ioctl(fd, KDSKBMODE, K_UNICODE);
-
- if (tcgetattr(fd, &termios) < 0) {
- r = -errno;
- goto finish;
- }
-
- /* We only reset the stuff that matters to the software. How
- * hardware is set up we don't touch assuming that somebody
- * else will do that for us */
-
- termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
- termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
- termios.c_oflag |= ONLCR;
- termios.c_cflag |= CREAD;
- termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
-
- termios.c_cc[VINTR] = 03; /* ^C */
- termios.c_cc[VQUIT] = 034; /* ^\ */
- termios.c_cc[VERASE] = 0177;
- termios.c_cc[VKILL] = 025; /* ^X */
- termios.c_cc[VEOF] = 04; /* ^D */
- termios.c_cc[VSTART] = 021; /* ^Q */
- termios.c_cc[VSTOP] = 023; /* ^S */
- termios.c_cc[VSUSP] = 032; /* ^Z */
- termios.c_cc[VLNEXT] = 026; /* ^V */
- termios.c_cc[VWERASE] = 027; /* ^W */
- termios.c_cc[VREPRINT] = 022; /* ^R */
- termios.c_cc[VEOL] = 0;
- termios.c_cc[VEOL2] = 0;
-
- termios.c_cc[VTIME] = 0;
- termios.c_cc[VMIN] = 1;
-
- if (tcsetattr(fd, TCSANOW, &termios) < 0)
- r = -errno;
-
-finish:
- /* Just in case, flush all crap out */
- tcflush(fd, TCIOFLUSH);
-
- return r;
-}
-
-int reset_terminal(const char *name) {
- int fd, r;
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- r = reset_terminal_fd(fd, true);
- close_nointr_nofail(fd);
-
- return r;
-}
-
-int open_terminal(const char *name, int mode) {
- int fd, r;
- unsigned c = 0;
-
- /*
- * If a TTY is in the process of being closed opening it might
- * cause EIO. This is horribly awful, but unlikely to be
- * changed in the kernel. Hence we work around this problem by
- * retrying a couple of times.
- *
- * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
- */
-
- for (;;) {
- if ((fd = open(name, mode)) >= 0)
- break;
-
- if (errno != EIO)
- return -errno;
-
- if (c >= 20)
- return -errno;
-
- usleep(50 * USEC_PER_MSEC);
- c++;
- }
-
- if (fd < 0)
- return -errno;
-
- if ((r = isatty(fd)) < 0) {
- close_nointr_nofail(fd);
- return -errno;
- }
-
- if (!r) {
- close_nointr_nofail(fd);
- return -ENOTTY;
- }
-
- return fd;
-}
-
-int flush_fd(int fd) {
- struct pollfd pollfd;
-
- zero(pollfd);
- pollfd.fd = fd;
- pollfd.events = POLLIN;
-
- for (;;) {
- char buf[LINE_MAX];
- ssize_t l;
- int r;
-
- if ((r = poll(&pollfd, 1, 0)) < 0) {
-
- if (errno == EINTR)
- continue;
-
- return -errno;
- }
-
- if (r == 0)
- return 0;
-
- if ((l = read(fd, buf, sizeof(buf))) < 0) {
-
- if (errno == EINTR)
- continue;
-
- if (errno == EAGAIN)
- return 0;
-
- return -errno;
- }
-
- if (l <= 0)
- return 0;
- }
-}
-
-int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm) {
- int fd = -1, notify = -1, r, wd = -1;
-
- assert(name);
-
- /* We use inotify to be notified when the tty is closed. We
- * create the watch before checking if we can actually acquire
- * it, so that we don't lose any event.
- *
- * Note: strictly speaking this actually watches for the
- * device being closed, it does *not* really watch whether a
- * tty loses its controlling process. However, unless some
- * rogue process uses TIOCNOTTY on /dev/tty *after* closing
- * its tty otherwise this will not become a problem. As long
- * as the administrator makes sure not configure any service
- * on the same tty as an untrusted user this should not be a
- * problem. (Which he probably should not do anyway.) */
-
- if (!fail && !force) {
- if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
- r = -errno;
- goto fail;
- }
-
- if ((wd = inotify_add_watch(notify, name, IN_CLOSE)) < 0) {
- r = -errno;
- goto fail;
- }
- }
-
- for (;;) {
- if (notify >= 0)
- if ((r = flush_fd(notify)) < 0)
- goto fail;
-
- /* We pass here O_NOCTTY only so that we can check the return
- * value TIOCSCTTY and have a reliable way to figure out if we
- * successfully became the controlling process of the tty */
- if ((fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
- return fd;
-
- /* First, try to get the tty */
- r = ioctl(fd, TIOCSCTTY, force);
-
- /* Sometimes it makes sense to ignore TIOCSCTTY
- * returning EPERM, i.e. when very likely we already
- * are have this controlling terminal. */
- if (r < 0 && errno == EPERM && ignore_tiocstty_eperm)
- r = 0;
-
- if (r < 0 && (force || fail || errno != EPERM)) {
- r = -errno;
- goto fail;
- }
-
- if (r >= 0)
- break;
-
- assert(!fail);
- assert(!force);
- assert(notify >= 0);
-
- for (;;) {
- uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
- ssize_t l;
- struct inotify_event *e;
-
- if ((l = read(notify, inotify_buffer, sizeof(inotify_buffer))) < 0) {
-
- if (errno == EINTR)
- continue;
-
- r = -errno;
- goto fail;
- }
-
- e = (struct inotify_event*) inotify_buffer;
-
- while (l > 0) {
- size_t step;
-
- if (e->wd != wd || !(e->mask & IN_CLOSE)) {
- r = -EIO;
- goto fail;
- }
-
- step = sizeof(struct inotify_event) + e->len;
- assert(step <= (size_t) l);
-
- e = (struct inotify_event*) ((uint8_t*) e + step);
- l -= step;
- }
-
- break;
- }
-
- /* We close the tty fd here since if the old session
- * ended our handle will be dead. It's important that
- * we do this after sleeping, so that we don't enter
- * an endless loop. */
- close_nointr_nofail(fd);
- }
-
- if (notify >= 0)
- close_nointr_nofail(notify);
-
- r = reset_terminal_fd(fd, true);
- if (r < 0)
- log_warning("Failed to reset terminal: %s", strerror(-r));
-
- return fd;
-
-fail:
- if (fd >= 0)
- close_nointr_nofail(fd);
-
- if (notify >= 0)
- close_nointr_nofail(notify);
-
- return r;
-}
-
-int release_terminal(void) {
- int r = 0, fd;
- struct sigaction sa_old, sa_new;
-
- if ((fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC)) < 0)
- return -errno;
-
- /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
- * by our own TIOCNOTTY */
-
- zero(sa_new);
- sa_new.sa_handler = SIG_IGN;
- sa_new.sa_flags = SA_RESTART;
- assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
-
- if (ioctl(fd, TIOCNOTTY) < 0)
- r = -errno;
-
- assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
-
- close_nointr_nofail(fd);
- return r;
-}
-
-int sigaction_many(const struct sigaction *sa, ...) {
- va_list ap;
- int r = 0, sig;
-
- va_start(ap, sa);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
-int ignore_signals(int sig, ...) {
- struct sigaction sa;
- va_list ap;
- int r = 0;
-
- zero(sa);
- sa.sa_handler = SIG_IGN;
- sa.sa_flags = SA_RESTART;
-
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
-
- va_start(ap, sig);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
-int default_signals(int sig, ...) {
- struct sigaction sa;
- va_list ap;
- int r = 0;
-
- zero(sa);
- sa.sa_handler = SIG_DFL;
- sa.sa_flags = SA_RESTART;
-
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
-
- va_start(ap, sig);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
-int close_pipe(int p[]) {
- int a = 0, b = 0;
-
- assert(p);
-
- if (p[0] >= 0) {
- a = close_nointr(p[0]);
- p[0] = -1;
- }
-
- if (p[1] >= 0) {
- b = close_nointr(p[1]);
- p[1] = -1;
- }
-
- return a < 0 ? a : b;
-}
-
-ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
- uint8_t *p;
- ssize_t n = 0;
-
- assert(fd >= 0);
- assert(buf);
-
- p = buf;
-
- while (nbytes > 0) {
- ssize_t k;
-
- if ((k = read(fd, p, nbytes)) <= 0) {
-
- if (k < 0 && errno == EINTR)
- continue;
-
- if (k < 0 && errno == EAGAIN && do_poll) {
- struct pollfd pollfd;
-
- zero(pollfd);
- pollfd.fd = fd;
- pollfd.events = POLLIN;
-
- if (poll(&pollfd, 1, -1) < 0) {
- if (errno == EINTR)
- continue;
-
- return n > 0 ? n : -errno;
- }
-
- if (pollfd.revents != POLLIN)
- return n > 0 ? n : -EIO;
-
- continue;
- }
-
- return n > 0 ? n : (k < 0 ? -errno : 0);
- }
-
- p += k;
- nbytes -= k;
- n += k;
- }
-
- return n;
-}
-
-ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
- const uint8_t *p;
- ssize_t n = 0;
-
- assert(fd >= 0);
- assert(buf);
-
- p = buf;
-
- while (nbytes > 0) {
- ssize_t k;
-
- k = write(fd, p, nbytes);
- if (k <= 0) {
-
- if (k < 0 && errno == EINTR)
- continue;
-
- if (k < 0 && errno == EAGAIN && do_poll) {
- struct pollfd pollfd;
-
- zero(pollfd);
- pollfd.fd = fd;
- pollfd.events = POLLOUT;
-
- if (poll(&pollfd, 1, -1) < 0) {
- if (errno == EINTR)
- continue;
-
- return n > 0 ? n : -errno;
- }
-
- if (pollfd.revents != POLLOUT)
- return n > 0 ? n : -EIO;
-
- continue;
- }
-
- return n > 0 ? n : (k < 0 ? -errno : 0);
- }
-
- p += k;
- nbytes -= k;
- n += k;
- }
-
- return n;
-}
-
-int path_is_mount_point(const char *t, bool allow_symlink) {
- struct stat a, b;
- char *parent;
- int r;
-
- if (allow_symlink)
- r = stat(t, &a);
- else
- r = lstat(t, &a);
-
- if (r < 0) {
- if (errno == ENOENT)
- return 0;
-
- return -errno;
- }
-
- r = parent_of_path(t, &parent);
- if (r < 0)
- return r;
-
- r = lstat(parent, &b);
- free(parent);
-
- if (r < 0)
- return -errno;
-
- return a.st_dev != b.st_dev;
-}
-
-int parse_usec(const char *t, usec_t *usec) {
- static const struct {
- const char *suffix;
- usec_t usec;
- } table[] = {
- { "sec", USEC_PER_SEC },
- { "s", USEC_PER_SEC },
- { "min", USEC_PER_MINUTE },
- { "hr", USEC_PER_HOUR },
- { "h", USEC_PER_HOUR },
- { "d", USEC_PER_DAY },
- { "w", USEC_PER_WEEK },
- { "msec", USEC_PER_MSEC },
- { "ms", USEC_PER_MSEC },
- { "m", USEC_PER_MINUTE },
- { "usec", 1ULL },
- { "us", 1ULL },
- { "", USEC_PER_SEC },
- };
-
- const char *p;
- usec_t r = 0;
-
- assert(t);
- assert(usec);
-
- p = t;
- do {
- long long l;
- char *e;
- unsigned i;
-
- errno = 0;
- l = strtoll(p, &e, 10);
-
- if (errno != 0)
- return -errno;
-
- if (l < 0)
- return -ERANGE;
-
- if (e == p)
- return -EINVAL;
-
- e += strspn(e, WHITESPACE);
-
- for (i = 0; i < ELEMENTSOF(table); i++)
- if (startswith(e, table[i].suffix)) {
- r += (usec_t) l * table[i].usec;
- p = e + strlen(table[i].suffix);
- break;
- }
-
- if (i >= ELEMENTSOF(table))
- return -EINVAL;
-
- } while (*p != 0);
-
- *usec = r;
-
- return 0;
-}
-
-int parse_bytes(const char *t, off_t *bytes) {
- static const struct {
- const char *suffix;
- off_t factor;
- } table[] = {
- { "B", 1 },
- { "K", 1024ULL },
- { "M", 1024ULL*1024ULL },
- { "G", 1024ULL*1024ULL*1024ULL },
- { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
- { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
- { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
- { "", 1 },
- };
-
- const char *p;
- off_t r = 0;
-
- assert(t);
- assert(bytes);
-
- p = t;
- do {
- long long l;
- char *e;
- unsigned i;
-
- errno = 0;
- l = strtoll(p, &e, 10);
-
- if (errno != 0)
- return -errno;
-
- if (l < 0)
- return -ERANGE;
-
- if (e == p)
- return -EINVAL;
-
- e += strspn(e, WHITESPACE);
-
- for (i = 0; i < ELEMENTSOF(table); i++)
- if (startswith(e, table[i].suffix)) {
- r += (off_t) l * table[i].factor;
- p = e + strlen(table[i].suffix);
- break;
- }
-
- if (i >= ELEMENTSOF(table))
- return -EINVAL;
-
- } while (*p != 0);
-
- *bytes = r;
-
- return 0;
-}
-
-int make_stdio(int fd) {
- int r, s, t;
-
- assert(fd >= 0);
-
- r = dup2(fd, STDIN_FILENO);
- s = dup2(fd, STDOUT_FILENO);
- t = dup2(fd, STDERR_FILENO);
-
- if (fd >= 3)
- close_nointr_nofail(fd);
-
- if (r < 0 || s < 0 || t < 0)
- return -errno;
-
- fd_cloexec(STDIN_FILENO, false);
- fd_cloexec(STDOUT_FILENO, false);
- fd_cloexec(STDERR_FILENO, false);
-
- return 0;
-}
-
-int make_null_stdio(void) {
- int null_fd;
-
- if ((null_fd = open("/dev/null", O_RDWR|O_NOCTTY)) < 0)
- return -errno;
-
- return make_stdio(null_fd);
-}
-
-bool is_device_path(const char *path) {
-
- /* Returns true on paths that refer to a device, either in
- * sysfs or in /dev */
-
- return
- path_startswith(path, "/dev/") ||
- path_startswith(path, "/sys/");
-}
-
-int dir_is_empty(const char *path) {
- DIR *d;
- int r;
- struct dirent buf, *de;
-
- if (!(d = opendir(path)))
- return -errno;
-
- for (;;) {
- if ((r = readdir_r(d, &buf, &de)) > 0) {
- r = -r;
- break;
- }
-
- if (!de) {
- r = 1;
- break;
- }
-
- if (!ignore_file(de->d_name)) {
- r = 0;
- break;
- }
- }
-
- closedir(d);
- return r;
-}
-
-unsigned long long random_ull(void) {
- int fd;
- uint64_t ull;
- ssize_t r;
-
- if ((fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0)
- goto fallback;
-
- r = loop_read(fd, &ull, sizeof(ull), true);
- close_nointr_nofail(fd);
-
- if (r != sizeof(ull))
- goto fallback;
-
- return ull;
-
-fallback:
- return random() * RAND_MAX + random();
-}
-
-void rename_process(const char name[8]) {
- assert(name);
-
- /* This is a like a poor man's setproctitle(). It changes the
- * comm field, argv[0], and also the glibc's internally used
- * name of the process. For the first one a limit of 16 chars
- * applies, to the second one usually one of 10 (i.e. length
- * of "/sbin/init"), to the third one one of 7 (i.e. length of
- * "systemd"). If you pass a longer string it will be
- * truncated */
-
- prctl(PR_SET_NAME, name);
-
- if (program_invocation_name)
- strncpy(program_invocation_name, name, strlen(program_invocation_name));
-
- if (saved_argc > 0) {
- int i;
-
- if (saved_argv[0])
- strncpy(saved_argv[0], name, strlen(saved_argv[0]));
-
- for (i = 1; i < saved_argc; i++) {
- if (!saved_argv[i])
- break;
-
- memset(saved_argv[i], 0, strlen(saved_argv[i]));
- }
- }
-}
-
-void sigset_add_many(sigset_t *ss, ...) {
- va_list ap;
- int sig;
-
- assert(ss);
-
- va_start(ap, ss);
- while ((sig = va_arg(ap, int)) > 0)
- assert_se(sigaddset(ss, sig) == 0);
- va_end(ap);
-}
-
-char* gethostname_malloc(void) {
- struct utsname u;
-
- assert_se(uname(&u) >= 0);
-
- if (u.nodename[0])
- return strdup(u.nodename);
-
- return strdup(u.sysname);
-}
-
-char* getlogname_malloc(void) {
- uid_t uid;
- long bufsize;
- char *buf, *name;
- struct passwd pwbuf, *pw = NULL;
- struct stat st;
-
- if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
- uid = st.st_uid;
- else
- uid = getuid();
-
- /* Shortcut things to avoid NSS lookups */
- if (uid == 0)
- return strdup("root");
-
- if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) <= 0)
- bufsize = 4096;
-
- if (!(buf = malloc(bufsize)))
- return NULL;
-
- if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw) {
- name = strdup(pw->pw_name);
- free(buf);
- return name;
- }
-
- free(buf);
-
- if (asprintf(&name, "%lu", (unsigned long) uid) < 0)
- return NULL;
-
- return name;
-}
-
-int getttyname_malloc(int fd, char **r) {
- char path[PATH_MAX], *c;
- int k;
-
- assert(r);
-
- if ((k = ttyname_r(fd, path, sizeof(path))) != 0)
- return -k;
-
- char_array_0(path);
-
- if (!(c = strdup(startswith(path, "/dev/") ? path + 5 : path)))
- return -ENOMEM;
-
- *r = c;
- return 0;
-}
-
-int getttyname_harder(int fd, char **r) {
- int k;
- char *s;
-
- if ((k = getttyname_malloc(fd, &s)) < 0)
- return k;
-
- if (streq(s, "tty")) {
- free(s);
- return get_ctty(0, NULL, r);
- }
-
- *r = s;
- return 0;
-}
-
-int get_ctty_devnr(pid_t pid, dev_t *d) {
- int k;
- char line[LINE_MAX], *p, *fn;
- unsigned long ttynr;
- FILE *f;
-
- if (asprintf(&fn, "/proc/%lu/stat", (unsigned long) (pid <= 0 ? getpid() : pid)) < 0)
- return -ENOMEM;
-
- f = fopen(fn, "re");
- free(fn);
- if (!f)
- return -errno;
-
- if (!fgets(line, sizeof(line), f)) {
- k = feof(f) ? -EIO : -errno;
- fclose(f);
- return k;
- }
-
- fclose(f);
-
- p = strrchr(line, ')');
- if (!p)
- return -EIO;
-
- p++;
-
- if (sscanf(p, " "
- "%*c " /* state */
- "%*d " /* ppid */
- "%*d " /* pgrp */
- "%*d " /* session */
- "%lu ", /* ttynr */
- &ttynr) != 1)
- return -EIO;
-
- *d = (dev_t) ttynr;
- return 0;
-}
-
-int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
- int k;
- char fn[PATH_MAX], *s, *b, *p;
- dev_t devnr;
-
- assert(r);
-
- k = get_ctty_devnr(pid, &devnr);
- if (k < 0)
- return k;
-
- snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr));
- char_array_0(fn);
-
- if ((k = readlink_malloc(fn, &s)) < 0) {
-
- if (k != -ENOENT)
- return k;
-
- /* This is an ugly hack */
- if (major(devnr) == 136) {
- if (asprintf(&b, "pts/%lu", (unsigned long) minor(devnr)) < 0)
- return -ENOMEM;
-
- *r = b;
- if (_devnr)
- *_devnr = devnr;
-
- return 0;
- }
-
- /* Probably something like the ptys which have no
- * symlink in /dev/char. Let's return something
- * vaguely useful. */
-
- if (!(b = strdup(fn + 5)))
- return -ENOMEM;
-
- *r = b;
- if (_devnr)
- *_devnr = devnr;
-
- return 0;
- }
-
- if (startswith(s, "/dev/"))
- p = s + 5;
- else if (startswith(s, "../"))
- p = s + 3;
- else
- p = s;
-
- b = strdup(p);
- free(s);
-
- if (!b)
- return -ENOMEM;
-
- *r = b;
- if (_devnr)
- *_devnr = devnr;
-
- return 0;
-}
-
-static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
- DIR *d;
- int ret = 0;
-
- assert(fd >= 0);
-
- /* This returns the first error we run into, but nevertheless
- * tries to go on */
-
- if (!(d = fdopendir(fd))) {
- close_nointr_nofail(fd);
-
- return errno == ENOENT ? 0 : -errno;
- }
-
- for (;;) {
- struct dirent buf, *de;
- bool is_dir, keep_around = false;
- int r;
-
- if ((r = readdir_r(d, &buf, &de)) != 0) {
- if (ret == 0)
- ret = -r;
- break;
- }
-
- if (!de)
- break;
-
- if (streq(de->d_name, ".") || streq(de->d_name, ".."))
- continue;
-
- if (de->d_type == DT_UNKNOWN) {
- struct stat st;
-
- if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- if (honour_sticky)
- keep_around =
- (st.st_uid == 0 || st.st_uid == getuid()) &&
- (st.st_mode & S_ISVTX);
-
- is_dir = S_ISDIR(st.st_mode);
-
- } else {
- if (honour_sticky) {
- struct stat st;
-
- if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- keep_around =
- (st.st_uid == 0 || st.st_uid == getuid()) &&
- (st.st_mode & S_ISVTX);
- }
-
- is_dir = de->d_type == DT_DIR;
- }
-
- if (is_dir) {
- int subdir_fd;
-
- subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
- if (subdir_fd < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- if ((r = rm_rf_children(subdir_fd, only_dirs, honour_sticky)) < 0) {
- if (ret == 0)
- ret = r;
- }
-
- if (!keep_around)
- if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- }
-
- } else if (!only_dirs && !keep_around) {
-
- if (unlinkat(fd, de->d_name, 0) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- }
- }
- }
-
- closedir(d);
-
- return ret;
-}
-
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
- int fd;
- int r;
-
- assert(path);
-
- if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) {
-
- if (errno != ENOTDIR)
- return -errno;
-
- if (delete_root && !only_dirs)
- if (unlink(path) < 0)
- return -errno;
-
- return 0;
- }
-
- r = rm_rf_children(fd, only_dirs, honour_sticky);
-
- if (delete_root) {
-
- if (honour_sticky && file_is_priv_sticky(path) > 0)
- return r;
-
- if (rmdir(path) < 0 && errno != ENOENT) {
- if (r == 0)
- r = -errno;
- }
- }
-
- return r;
-}
-
-int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
- assert(path);
-
- /* Under the assumption that we are running privileged we
- * first change the access mode and only then hand out
- * ownership to avoid a window where access is too open. */
-
- if (mode != (mode_t) -1)
- if (chmod(path, mode) < 0)
- return -errno;
-
- if (uid != (uid_t) -1 || gid != (gid_t) -1)
- if (chown(path, uid, gid) < 0)
- return -errno;
-
- return 0;
-}
-
-int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
- assert(fd >= 0);
-
- /* Under the assumption that we are running privileged we
- * first change the access mode and only then hand out
- * ownership to avoid a window where access is too open. */
-
- if (fchmod(fd, mode) < 0)
- return -errno;
-
- if (fchown(fd, uid, gid) < 0)
- return -errno;
-
- return 0;
-}
-
-cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
- cpu_set_t *r;
- unsigned n = 1024;
-
- /* Allocates the cpuset in the right size */
-
- for (;;) {
- if (!(r = CPU_ALLOC(n)))
- return NULL;
-
- if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), r) >= 0) {
- CPU_ZERO_S(CPU_ALLOC_SIZE(n), r);
-
- if (ncpus)
- *ncpus = n;
-
- return r;
- }
-
- CPU_FREE(r);
-
- if (errno != EINVAL)
- return NULL;
-
- n *= 2;
- }
-}
-
-void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap) {
- char *s = NULL, *spaces = NULL, *e;
- int fd = -1, c;
- size_t emax, sl, left;
- struct iovec iovec[5];
- int n = 0;
-
- assert(format);
-
- /* This independent of logging, as status messages are
- * optional and go exclusively to the console. */
-
- if (vasprintf(&s, format, ap) < 0)
- goto finish;
-
- fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- goto finish;
-
- if (ellipse) {
- c = fd_columns(fd);
- if (c <= 0)
- c = 80;
-
- if (status) {
- sl = 2 + 6 + 1; /* " [" status "]" */
- emax = (size_t) c > sl ? c - sl - 1 : 0;
- } else
- emax = c - 1;
-
- e = ellipsize(s, emax, 75);
- if (e) {
- free(s);
- s = e;
- }
- }
-
- zero(iovec);
- IOVEC_SET_STRING(iovec[n++], s);
-
- if (ellipse) {
- sl = strlen(s);
- left = emax > sl ? emax - sl : 0;
- if (left > 0) {
- spaces = malloc(left);
- if (spaces) {
- memset(spaces, ' ', left);
- iovec[n].iov_base = spaces;
- iovec[n].iov_len = left;
- n++;
- }
- }
- }
-
- if (status) {
- IOVEC_SET_STRING(iovec[n++], " [");
- IOVEC_SET_STRING(iovec[n++], status);
- IOVEC_SET_STRING(iovec[n++], "]\n");
- } else
- IOVEC_SET_STRING(iovec[n++], "\n");
-
- writev(fd, iovec, n);
-
-finish:
- free(s);
- free(spaces);
-
- if (fd >= 0)
- close_nointr_nofail(fd);
-}
-
-void status_printf(const char *status, bool ellipse, const char *format, ...) {
- va_list ap;
-
- assert(format);
-
- va_start(ap, format);
- status_vprintf(status, ellipse, format, ap);
- va_end(ap);
-}
-
-void status_welcome(void) {
- char *pretty_name = NULL, *ansi_color = NULL;
- const char *const_pretty = NULL, *const_color = NULL;
- int r;
-
- if ((r = parse_env_file("/etc/os-release", NEWLINE,
- "PRETTY_NAME", &pretty_name,
- "ANSI_COLOR", &ansi_color,
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/os-release: %s", strerror(-r));
- }
-
- if (!pretty_name && !const_pretty)
- const_pretty = "Linux";
-
- if (!ansi_color && !const_color)
- const_color = "1";
-
- status_printf(NULL,
- false,
- "\nWelcome to \x1B[%sm%s\x1B[0m!\n",
- const_color ? const_color : ansi_color,
- const_pretty ? const_pretty : pretty_name);
-
- free(ansi_color);
- free(pretty_name);
-}
-
-char *replace_env(const char *format, char **env) {
- enum {
- WORD,
- CURLY,
- VARIABLE
- } state = WORD;
-
- const char *e, *word = format;
- char *r = NULL, *k;
-
- assert(format);
-
- for (e = format; *e; e ++) {
-
- switch (state) {
-
- case WORD:
- if (*e == '$')
- state = CURLY;
- break;
-
- case CURLY:
- if (*e == '{') {
- if (!(k = strnappend(r, word, e-word-1)))
- goto fail;
-
- free(r);
- r = k;
-
- word = e-1;
- state = VARIABLE;
-
- } else if (*e == '$') {
- if (!(k = strnappend(r, word, e-word)))
- goto fail;
-
- free(r);
- r = k;
-
- word = e+1;
- state = WORD;
- } else
- state = WORD;
- break;
-
- case VARIABLE:
- if (*e == '}') {
- const char *t;
-
- if (!(t = strv_env_get_with_length(env, word+2, e-word-2)))
- t = "";
-
- if (!(k = strappend(r, t)))
- goto fail;
-
- free(r);
- r = k;
-
- word = e+1;
- state = WORD;
- }
- break;
- }
- }
-
- if (!(k = strnappend(r, word, e-word)))
- goto fail;
-
- free(r);
- return k;
-
-fail:
- free(r);
- return NULL;
-}
-
-char **replace_env_argv(char **argv, char **env) {
- char **r, **i;
- unsigned k = 0, l = 0;
-
- l = strv_length(argv);
-
- if (!(r = new(char*, l+1)))
- return NULL;
-
- STRV_FOREACH(i, argv) {
-
- /* If $FOO appears as single word, replace it by the split up variable */
- if ((*i)[0] == '$' && (*i)[1] != '{') {
- char *e;
- char **w, **m;
- unsigned q;
-
- if ((e = strv_env_get(env, *i+1))) {
-
- if (!(m = strv_split_quoted(e))) {
- r[k] = NULL;
- strv_free(r);
- return NULL;
- }
- } else
- m = NULL;
-
- q = strv_length(m);
- l = l + q - 1;
-
- if (!(w = realloc(r, sizeof(char*) * (l+1)))) {
- r[k] = NULL;
- strv_free(r);
- strv_free(m);
- return NULL;
- }
-
- r = w;
- if (m) {
- memcpy(r + k, m, q * sizeof(char*));
- free(m);
- }
-
- k += q;
- continue;
- }
-
- /* If ${FOO} appears as part of a word, replace it by the variable as-is */
- if (!(r[k++] = replace_env(*i, env))) {
- strv_free(r);
- return NULL;
- }
- }
-
- r[k] = NULL;
- return r;
-}
-
-int fd_columns(int fd) {
- struct winsize ws;
- zero(ws);
-
- if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
- return -errno;
-
- if (ws.ws_col <= 0)
- return -EIO;
-
- return ws.ws_col;
-}
-
-unsigned columns(void) {
- static __thread int parsed_columns = 0;
- const char *e;
-
- if (_likely_(parsed_columns > 0))
- return parsed_columns;
-
- e = getenv("COLUMNS");
- if (e)
- parsed_columns = atoi(e);
-
- if (parsed_columns <= 0)
- parsed_columns = fd_columns(STDOUT_FILENO);
-
- if (parsed_columns <= 0)
- parsed_columns = 80;
-
- return parsed_columns;
-}
-
-int fd_lines(int fd) {
- struct winsize ws;
- zero(ws);
-
- if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
- return -errno;
-
- if (ws.ws_row <= 0)
- return -EIO;
-
- return ws.ws_row;
-}
-
-unsigned lines(void) {
- static __thread int parsed_lines = 0;
- const char *e;
-
- if (_likely_(parsed_lines > 0))
- return parsed_lines;
-
- e = getenv("LINES");
- if (e)
- parsed_lines = atoi(e);
-
- if (parsed_lines <= 0)
- parsed_lines = fd_lines(STDOUT_FILENO);
-
- if (parsed_lines <= 0)
- parsed_lines = 25;
-
- return parsed_lines;
-}
-
-int running_in_chroot(void) {
- struct stat a, b;
-
- zero(a);
- zero(b);
-
- /* Only works as root */
-
- if (stat("/proc/1/root", &a) < 0)
- return -errno;
-
- if (stat("/", &b) < 0)
- return -errno;
-
- return
- a.st_dev != b.st_dev ||
- a.st_ino != b.st_ino;
-}
-
-char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
- size_t x;
- char *r;
-
- assert(s);
- assert(percent <= 100);
- assert(new_length >= 3);
-
- if (old_length <= 3 || old_length <= new_length)
- return strndup(s, old_length);
-
- r = new0(char, new_length+1);
- if (!r)
- return r;
-
- x = (new_length * percent) / 100;
-
- if (x > new_length - 3)
- x = new_length - 3;
-
- memcpy(r, s, x);
- r[x] = '.';
- r[x+1] = '.';
- r[x+2] = '.';
- memcpy(r + x + 3,
- s + old_length - (new_length - x - 3),
- new_length - x - 3);
-
- return r;
-}
-
-char *ellipsize(const char *s, size_t length, unsigned percent) {
- return ellipsize_mem(s, strlen(s), length, percent);
-}
-
-int touch(const char *path) {
- int fd;
-
- assert(path);
-
- if ((fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644)) < 0)
- return -errno;
-
- close_nointr_nofail(fd);
- return 0;
-}
-
-char *unquote(const char *s, const char* quotes) {
- size_t l;
- assert(s);
-
- l = strlen(s);
- if (l < 2)
- return strdup(s);
-
- if (strchr(quotes, s[0]) && s[l-1] == s[0])
- return strndup(s+1, l-2);
-
- return strdup(s);
-}
-
-char *normalize_env_assignment(const char *s) {
- char *name, *value, *p, *r;
-
- p = strchr(s, '=');
-
- if (!p) {
- if (!(r = strdup(s)))
- return NULL;
-
- return strstrip(r);
- }
-
- if (!(name = strndup(s, p - s)))
- return NULL;
-
- if (!(p = strdup(p+1))) {
- free(name);
- return NULL;
- }
-
- value = unquote(strstrip(p), QUOTES);
- free(p);
-
- if (!value) {
- free(name);
- return NULL;
- }
-
- if (asprintf(&r, "%s=%s", name, value) < 0)
- r = NULL;
-
- free(value);
- free(name);
-
- return r;
-}
-
-int wait_for_terminate(pid_t pid, siginfo_t *status) {
- siginfo_t dummy;
-
- assert(pid >= 1);
-
- if (!status)
- status = &dummy;
-
- for (;;) {
- zero(*status);
-
- if (waitid(P_PID, pid, status, WEXITED) < 0) {
-
- if (errno == EINTR)
- continue;
-
- return -errno;
- }
-
- return 0;
- }
-}
-
-int wait_for_terminate_and_warn(const char *name, pid_t pid) {
- int r;
- siginfo_t status;
-
- assert(name);
- assert(pid > 1);
-
- if ((r = wait_for_terminate(pid, &status)) < 0) {
- log_warning("Failed to wait for %s: %s", name, strerror(-r));
- return r;
- }
-
- if (status.si_code == CLD_EXITED) {
- if (status.si_status != 0) {
- log_warning("%s failed with error code %i.", name, status.si_status);
- return status.si_status;
- }
-
- log_debug("%s succeeded.", name);
- return 0;
-
- } else if (status.si_code == CLD_KILLED ||
- status.si_code == CLD_DUMPED) {
-
- log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
- return -EPROTO;
- }
-
- log_warning("%s failed due to unknown reason.", name);
- return -EPROTO;
-
-}
-
-_noreturn_ void freeze(void) {
-
- /* Make sure nobody waits for us on a socket anymore */
- close_all_fds(NULL, 0);
-
- sync();
-
- for (;;)
- pause();
-}
-
-bool null_or_empty(struct stat *st) {
- assert(st);
-
- if (S_ISREG(st->st_mode) && st->st_size <= 0)
- return true;
-
- if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
- return true;
-
- return false;
-}
-
-int null_or_empty_path(const char *fn) {
- struct stat st;
-
- assert(fn);
-
- if (stat(fn, &st) < 0)
- return -errno;
-
- return null_or_empty(&st);
-}
-
-DIR *xopendirat(int fd, const char *name, int flags) {
- int nfd;
- DIR *d;
-
- if ((nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags)) < 0)
- return NULL;
-
- if (!(d = fdopendir(nfd))) {
- close_nointr_nofail(nfd);
- return NULL;
- }
-
- return d;
-}
-
-int signal_from_string_try_harder(const char *s) {
- int signo;
- assert(s);
-
- if ((signo = signal_from_string(s)) <= 0)
- if (startswith(s, "SIG"))
- return signal_from_string(s+3);
-
- return signo;
-}
-
-void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
-
- assert(f);
- assert(name);
- assert(t);
-
- if (!dual_timestamp_is_set(t))
- return;
-
- fprintf(f, "%s=%llu %llu\n",
- name,
- (unsigned long long) t->realtime,
- (unsigned long long) t->monotonic);
-}
-
-void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
- unsigned long long a, b;
-
- assert(value);
- assert(t);
-
- if (sscanf(value, "%lli %llu", &a, &b) != 2)
- log_debug("Failed to parse finish timestamp value %s", value);
- else {
- t->realtime = a;
- t->monotonic = b;
- }
-}
-
-char *fstab_node_to_udev_node(const char *p) {
- char *dn, *t, *u;
- int r;
-
- /* FIXME: to follow udev's logic 100% we need to leave valid
- * UTF8 chars unescaped */
-
- if (startswith(p, "LABEL=")) {
-
- if (!(u = unquote(p+6, "\"\'")))
- return NULL;
-
- t = xescape(u, "/ ");
- free(u);
-
- if (!t)
- return NULL;
-
- r = asprintf(&dn, "/dev/disk/by-label/%s", t);
- free(t);
-
- if (r < 0)
- return NULL;
-
- return dn;
- }
-
- if (startswith(p, "UUID=")) {
-
- if (!(u = unquote(p+5, "\"\'")))
- return NULL;
-
- t = xescape(u, "/ ");
- free(u);
-
- if (!t)
- return NULL;
-
- r = asprintf(&dn, "/dev/disk/by-uuid/%s", t);
- free(t);
-
- if (r < 0)
- return NULL;
-
- return dn;
- }
-
- return strdup(p);
-}
-
-void filter_environ(const char *prefix) {
- int i, j;
- assert(prefix);
-
- if (!environ)
- return;
-
- for (i = 0, j = 0; environ[i]; i++) {
-
- if (startswith(environ[i], prefix))
- continue;
-
- environ[j++] = environ[i];
- }
-
- environ[j] = NULL;
-}
-
-bool tty_is_vc(const char *tty) {
- assert(tty);
-
- if (startswith(tty, "/dev/"))
- tty += 5;
-
- return vtnr_from_tty(tty) >= 0;
-}
-
-int vtnr_from_tty(const char *tty) {
- int i, r;
-
- assert(tty);
-
- if (startswith(tty, "/dev/"))
- tty += 5;
-
- if (!startswith(tty, "tty") )
- return -EINVAL;
-
- if (tty[3] < '0' || tty[3] > '9')
- return -EINVAL;
-
- r = safe_atoi(tty+3, &i);
- if (r < 0)
- return r;
-
- if (i < 0 || i > 63)
- return -EINVAL;
-
- return i;
-}
-
-bool tty_is_vc_resolve(const char *tty) {
- char *active = NULL;
- bool b;
-
- assert(tty);
-
- if (startswith(tty, "/dev/"))
- tty += 5;
-
- /* Resolve where /dev/console is pointing to */
- if (streq(tty, "console"))
- if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
- /* If multiple log outputs are configured the
- * last one is what /dev/console points to */
- tty = strrchr(active, ' ');
- if (tty)
- tty++;
- else
- tty = active;
- }
-
- b = tty_is_vc(tty);
- free(active);
-
- return b;
-}
-
-const char *default_term_for_tty(const char *tty) {
- assert(tty);
-
- return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt100";
-}
-
-bool dirent_is_file(const struct dirent *de) {
- assert(de);
-
- if (ignore_file(de->d_name))
- return false;
-
- if (de->d_type != DT_REG &&
- de->d_type != DT_LNK &&
- de->d_type != DT_UNKNOWN)
- return false;
-
- return true;
-}
-
-bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
- assert(de);
-
- if (!dirent_is_file(de))
- return false;
-
- return endswith(de->d_name, suffix);
-}
-
-void execute_directory(const char *directory, DIR *d, char *argv[]) {
- DIR *_d = NULL;
- struct dirent *de;
- Hashmap *pids = NULL;
-
- assert(directory);
-
- /* Executes all binaries in a directory in parallel and waits
- * until all they all finished. */
-
- if (!d) {
- if (!(_d = opendir(directory))) {
-
- if (errno == ENOENT)
- return;
-
- log_error("Failed to enumerate directory %s: %m", directory);
- return;
- }
-
- d = _d;
- }
-
- if (!(pids = hashmap_new(trivial_hash_func, trivial_compare_func))) {
- log_error("Failed to allocate set.");
- goto finish;
- }
-
- while ((de = readdir(d))) {
- char *path;
- pid_t pid;
- int k;
-
- if (!dirent_is_file(de))
- continue;
-
- if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) {
- log_error("Out of memory");
- continue;
- }
-
- if ((pid = fork()) < 0) {
- log_error("Failed to fork: %m");
- free(path);
- continue;
- }
-
- if (pid == 0) {
- char *_argv[2];
- /* Child */
-
- if (!argv) {
- _argv[0] = path;
- _argv[1] = NULL;
- argv = _argv;
- } else
- if (!argv[0])
- argv[0] = path;
-
- execv(path, argv);
-
- log_error("Failed to execute %s: %m", path);
- _exit(EXIT_FAILURE);
- }
-
- log_debug("Spawned %s as %lu", path, (unsigned long) pid);
-
- if ((k = hashmap_put(pids, UINT_TO_PTR(pid), path)) < 0) {
- log_error("Failed to add PID to set: %s", strerror(-k));
- free(path);
- }
- }
-
- while (!hashmap_isempty(pids)) {
- pid_t pid = PTR_TO_UINT(hashmap_first_key(pids));
- siginfo_t si;
- char *path;
-
- zero(si);
- if (waitid(P_PID, pid, &si, WEXITED) < 0) {
-
- if (errno == EINTR)
- continue;
-
- log_error("waitid() failed: %m");
- goto finish;
- }
-
- if ((path = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) {
- if (!is_clean_exit(si.si_code, si.si_status)) {
- if (si.si_code == CLD_EXITED)
- log_error("%s exited with exit status %i.", path, si.si_status);
- else
- log_error("%s terminated by signal %s.", path, signal_to_string(si.si_status));
- } else
- log_debug("%s exited successfully.", path);
-
- free(path);
- }
- }
-
-finish:
- if (_d)
- closedir(_d);
-
- if (pids)
- hashmap_free_free(pids);
-}
-
-int kill_and_sigcont(pid_t pid, int sig) {
- int r;
-
- r = kill(pid, sig) < 0 ? -errno : 0;
-
- if (r >= 0)
- kill(pid, SIGCONT);
-
- return r;
-}
-
-bool nulstr_contains(const char*nulstr, const char *needle) {
- const char *i;
-
- if (!nulstr)
- return false;
-
- NULSTR_FOREACH(i, nulstr)
- if (streq(i, needle))
- return true;
-
- return false;
-}
-
-bool plymouth_running(void) {
- return access("/run/plymouth/pid", F_OK) >= 0;
-}
-
-void parse_syslog_priority(char **p, int *priority) {
- int a = 0, b = 0, c = 0;
- int k;
-
- assert(p);
- assert(*p);
- assert(priority);
-
- if ((*p)[0] != '<')
- return;
-
- if (!strchr(*p, '>'))
- return;
-
- if ((*p)[2] == '>') {
- c = undecchar((*p)[1]);
- k = 3;
- } else if ((*p)[3] == '>') {
- b = undecchar((*p)[1]);
- c = undecchar((*p)[2]);
- k = 4;
- } else if ((*p)[4] == '>') {
- a = undecchar((*p)[1]);
- b = undecchar((*p)[2]);
- c = undecchar((*p)[3]);
- k = 5;
- } else
- return;
-
- if (a < 0 || b < 0 || c < 0)
- return;
-
- *priority = a*100+b*10+c;
- *p += k;
-}
-
-void skip_syslog_pid(char **buf) {
- char *p;
-
- assert(buf);
- assert(*buf);
-
- p = *buf;
-
- if (*p != '[')
- return;
-
- p++;
- p += strspn(p, "0123456789");
-
- if (*p != ']')
- return;
-
- p++;
-
- *buf = p;
-}
-
-void skip_syslog_date(char **buf) {
- enum {
- LETTER,
- SPACE,
- NUMBER,
- SPACE_OR_NUMBER,
- COLON
- } sequence[] = {
- LETTER, LETTER, LETTER,
- SPACE,
- SPACE_OR_NUMBER, NUMBER,
- SPACE,
- SPACE_OR_NUMBER, NUMBER,
- COLON,
- SPACE_OR_NUMBER, NUMBER,
- COLON,
- SPACE_OR_NUMBER, NUMBER,
- SPACE
- };
-
- char *p;
- unsigned i;
-
- assert(buf);
- assert(*buf);
-
- p = *buf;
-
- for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
-
- if (!*p)
- return;
-
- switch (sequence[i]) {
-
- case SPACE:
- if (*p != ' ')
- return;
- break;
-
- case SPACE_OR_NUMBER:
- if (*p == ' ')
- break;
-
- /* fall through */
-
- case NUMBER:
- if (*p < '0' || *p > '9')
- return;
-
- break;
-
- case LETTER:
- if (!(*p >= 'A' && *p <= 'Z') &&
- !(*p >= 'a' && *p <= 'z'))
- return;
-
- break;
-
- case COLON:
- if (*p != ':')
- return;
- break;
-
- }
- }
-
- *buf = p;
-}
-
-int have_effective_cap(int value) {
- cap_t cap;
- cap_flag_value_t fv;
- int r;
-
- if (!(cap = cap_get_proc()))
- return -errno;
-
- if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
- r = -errno;
- else
- r = fv == CAP_SET;
-
- cap_free(cap);
- return r;
-}
-
-char* strshorten(char *s, size_t l) {
- assert(s);
-
- if (l < strlen(s))
- s[l] = 0;
-
- return s;
-}
-
-static bool hostname_valid_char(char c) {
- return
- (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9') ||
- c == '-' ||
- c == '_' ||
- c == '.';
-}
-
-bool hostname_is_valid(const char *s) {
- const char *p;
-
- if (isempty(s))
- return false;
-
- for (p = s; *p; p++)
- if (!hostname_valid_char(*p))
- return false;
-
- if (p-s > HOST_NAME_MAX)
- return false;
-
- return true;
-}
-
-char* hostname_cleanup(char *s) {
- char *p, *d;
-
- for (p = s, d = s; *p; p++)
- if ((*p >= 'a' && *p <= 'z') ||
- (*p >= 'A' && *p <= 'Z') ||
- (*p >= '0' && *p <= '9') ||
- *p == '-' ||
- *p == '_' ||
- *p == '.')
- *(d++) = *p;
-
- *d = 0;
-
- strshorten(s, HOST_NAME_MAX);
- return s;
-}
-
-int pipe_eof(int fd) {
- struct pollfd pollfd;
- int r;
-
- zero(pollfd);
- pollfd.fd = fd;
- pollfd.events = POLLIN|POLLHUP;
-
- r = poll(&pollfd, 1, 0);
- if (r < 0)
- return -errno;
-
- if (r == 0)
- return 0;
-
- return pollfd.revents & POLLHUP;
-}
-
-int fd_wait_for_event(int fd, int event, usec_t t) {
- struct pollfd pollfd;
- int r;
-
- zero(pollfd);
- pollfd.fd = fd;
- pollfd.events = event;
-
- r = poll(&pollfd, 1, t == (usec_t) -1 ? -1 : (int) (t / USEC_PER_MSEC));
- if (r < 0)
- return -errno;
-
- if (r == 0)
- return 0;
-
- return pollfd.revents;
-}
-
-int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
- FILE *f;
- char *t;
- const char *fn;
- size_t k;
- int fd;
-
- assert(path);
- assert(_f);
- assert(_temp_path);
-
- t = new(char, strlen(path) + 1 + 6 + 1);
- if (!t)
- return -ENOMEM;
-
- fn = file_name_from_path(path);
- k = fn-path;
- memcpy(t, path, k);
- t[k] = '.';
- stpcpy(stpcpy(t+k+1, fn), "XXXXXX");
-
- fd = mkostemp(t, O_WRONLY|O_CLOEXEC);
- if (fd < 0) {
- free(t);
- return -errno;
- }
-
- f = fdopen(fd, "we");
- if (!f) {
- unlink(t);
- free(t);
- return -errno;
- }
-
- *_f = f;
- *_temp_path = t;
-
- return 0;
-}
-
-int terminal_vhangup_fd(int fd) {
- assert(fd >= 0);
-
- if (ioctl(fd, TIOCVHANGUP) < 0)
- return -errno;
-
- return 0;
-}
-
-int terminal_vhangup(const char *name) {
- int fd, r;
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- r = terminal_vhangup_fd(fd);
- close_nointr_nofail(fd);
-
- return r;
-}
-
-int vt_disallocate(const char *name) {
- int fd, r;
- unsigned u;
-
- /* Deallocate the VT if possible. If not possible
- * (i.e. because it is the active one), at least clear it
- * entirely (including the scrollback buffer) */
-
- if (!startswith(name, "/dev/"))
- return -EINVAL;
-
- if (!tty_is_vc(name)) {
- /* So this is not a VT. I guess we cannot deallocate
- * it then. But let's at least clear the screen */
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- loop_write(fd,
- "\033[r" /* clear scrolling region */
- "\033[H" /* move home */
- "\033[2J", /* clear screen */
- 10, false);
- close_nointr_nofail(fd);
-
- return 0;
- }
-
- if (!startswith(name, "/dev/tty"))
- return -EINVAL;
-
- r = safe_atou(name+8, &u);
- if (r < 0)
- return r;
-
- if (u <= 0)
- return -EINVAL;
-
- /* Try to deallocate */
- fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- r = ioctl(fd, VT_DISALLOCATE, u);
- close_nointr_nofail(fd);
-
- if (r >= 0)
- return 0;
-
- if (errno != EBUSY)
- return -errno;
-
- /* Couldn't deallocate, so let's clear it fully with
- * scrollback */
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- loop_write(fd,
- "\033[r" /* clear scrolling region */
- "\033[H" /* move home */
- "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
- 10, false);
- close_nointr_nofail(fd);
-
- return 0;
-}
-
-static int files_add(Hashmap *h, const char *path, const char *suffix) {
- DIR *dir;
- struct dirent buffer, *de;
- int r = 0;
-
- dir = opendir(path);
- if (!dir) {
- if (errno == ENOENT)
- return 0;
- return -errno;
- }
-
- for (;;) {
- int k;
- char *p, *f;
-
- k = readdir_r(dir, &buffer, &de);
- if (k != 0) {
- r = -k;
- goto finish;
- }
-
- if (!de)
- break;
-
- if (!dirent_is_file_with_suffix(de, suffix))
- continue;
-
- if (asprintf(&p, "%s/%s", path, de->d_name) < 0) {
- r = -ENOMEM;
- goto finish;
- }
-
- f = canonicalize_file_name(p);
- if (!f) {
- log_error("Failed to canonicalize file name '%s': %m", p);
- free(p);
- continue;
- }
- free(p);
-
- log_debug("found: %s\n", f);
- if (hashmap_put(h, file_name_from_path(f), f) <= 0)
- free(f);
- }
-
-finish:
- closedir(dir);
- return r;
-}
-
-static int base_cmp(const void *a, const void *b) {
- const char *s1, *s2;
-
- s1 = *(char * const *)a;
- s2 = *(char * const *)b;
- return strcmp(file_name_from_path(s1), file_name_from_path(s2));
-}
-
-int conf_files_list(char ***strv, const char *suffix, const char *dir, ...) {
- Hashmap *fh = NULL;
- char **dirs = NULL;
- char **files = NULL;
- char **p;
- va_list ap;
- int r = 0;
-
- va_start(ap, dir);
- dirs = strv_new_ap(dir, ap);
- va_end(ap);
- if (!dirs) {
- r = -ENOMEM;
- goto finish;
- }
- if (!strv_path_canonicalize(dirs)) {
- r = -ENOMEM;
- goto finish;
- }
- if (!strv_uniq(dirs)) {
- r = -ENOMEM;
- goto finish;
- }
-
- fh = hashmap_new(string_hash_func, string_compare_func);
- if (!fh) {
- r = -ENOMEM;
- goto finish;
- }
-
- STRV_FOREACH(p, dirs) {
- if (files_add(fh, *p, suffix) < 0) {
- log_error("Failed to search for files.");
- r = -EINVAL;
- goto finish;
- }
- }
-
- files = hashmap_get_strv(fh);
- if (files == NULL) {
- log_error("Failed to compose list of files.");
- r = -ENOMEM;
- goto finish;
- }
-
- qsort(files, hashmap_size(fh), sizeof(char *), base_cmp);
-
-finish:
- strv_free(dirs);
- hashmap_free(fh);
- *strv = files;
- return r;
-}
-
-int hwclock_is_localtime(void) {
- FILE *f;
- bool local = false;
-
- /*
- * The third line of adjtime is "UTC" or "LOCAL" or nothing.
- * # /etc/adjtime
- * 0.0 0 0
- * 0
- * UTC
- */
- f = fopen("/etc/adjtime", "re");
- if (f) {
- char line[LINE_MAX];
- bool b;
-
- b = fgets(line, sizeof(line), f) &&
- fgets(line, sizeof(line), f) &&
- fgets(line, sizeof(line), f);
-
- fclose(f);
-
- if (!b)
- return -EIO;
-
-
- truncate_nl(line);
- local = streq(line, "LOCAL");
-
- } else if (errno != -ENOENT)
- return -errno;
-
- return local;
-}
-
-int hwclock_apply_localtime_delta(int *min) {
- const struct timeval *tv_null = NULL;
- struct timespec ts;
- struct tm *tm;
- int minuteswest;
- struct timezone tz;
-
- assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
- assert_se(tm = localtime(&ts.tv_sec));
- minuteswest = tm->tm_gmtoff / 60;
-
- tz.tz_minuteswest = -minuteswest;
- tz.tz_dsttime = 0; /* DST_NONE*/
-
- /*
- * If the hardware clock does not run in UTC, but in local time:
- * The very first time we set the kernel's timezone, it will warp
- * the clock so that it runs in UTC instead of local time.
- */
- if (settimeofday(tv_null, &tz) < 0)
- return -errno;
- if (min)
- *min = minuteswest;
- return 0;
-}
-
-int hwclock_reset_localtime_delta(void) {
- const struct timeval *tv_null = NULL;
- struct timezone tz;
-
- tz.tz_minuteswest = 0;
- tz.tz_dsttime = 0; /* DST_NONE*/
-
- if (settimeofday(tv_null, &tz) < 0)
- return -errno;
-
- return 0;
-}
-
-int rtc_open(int flags) {
- int fd;
- DIR *d;
-
- /* First, we try to make use of the /dev/rtc symlink. If that
- * doesn't exist, we open the first RTC which has hctosys=1
- * set. If we don't find any we just take the first RTC that
- * exists at all. */
-
- fd = open("/dev/rtc", flags);
- if (fd >= 0)
- return fd;
-
- d = opendir("/sys/class/rtc");
- if (!d)
- goto fallback;
-
- for (;;) {
- char *p, *v;
- struct dirent buf, *de;
- int r;
-
- r = readdir_r(d, &buf, &de);
- if (r != 0)
- goto fallback;
-
- if (!de)
- goto fallback;
-
- if (ignore_file(de->d_name))
- continue;
-
- p = join("/sys/class/rtc/", de->d_name, "/hctosys", NULL);
- if (!p) {
- closedir(d);
- return -ENOMEM;
- }
-
- r = read_one_line_file(p, &v);
- free(p);
-
- if (r < 0)
- continue;
-
- r = parse_boolean(v);
- free(v);
-
- if (r <= 0)
- continue;
-
- p = strappend("/dev/", de->d_name);
- fd = open(p, flags);
- free(p);
-
- if (fd >= 0) {
- closedir(d);
- return fd;
- }
- }
-
-fallback:
- if (d)
- closedir(d);
-
- fd = open("/dev/rtc0", flags);
- if (fd < 0)
- return -errno;
-
- return fd;
-}
-
-int hwclock_get_time(struct tm *tm) {
- int fd;
- int err = 0;
-
- assert(tm);
-
- fd = rtc_open(O_RDONLY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- /* This leaves the timezone fields of struct tm
- * uninitialized! */
- if (ioctl(fd, RTC_RD_TIME, tm) < 0)
- err = -errno;
-
- /* We don't now daylight saving, so we reset this in order not
- * to confused mktime(). */
- tm->tm_isdst = -1;
-
- close_nointr_nofail(fd);
-
- return err;
-}
-
-int hwclock_set_time(const struct tm *tm) {
- int fd;
- int err = 0;
-
- assert(tm);
-
- fd = rtc_open(O_RDONLY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- if (ioctl(fd, RTC_SET_TIME, tm) < 0)
- err = -errno;
-
- close_nointr_nofail(fd);
-
- return err;
-}
-
-int copy_file(const char *from, const char *to) {
- int r, fdf, fdt;
-
- assert(from);
- assert(to);
-
- fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fdf < 0)
- return -errno;
-
- fdt = open(to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY, 0644);
- if (fdt < 0) {
- close_nointr_nofail(fdf);
- return -errno;
- }
-
- for (;;) {
- char buf[PIPE_BUF];
- ssize_t n, k;
-
- n = read(fdf, buf, sizeof(buf));
- if (n < 0) {
- r = -errno;
-
- close_nointr_nofail(fdf);
- close_nointr(fdt);
- unlink(to);
-
- return r;
- }
-
- if (n == 0)
- break;
-
- errno = 0;
- k = loop_write(fdt, buf, n, false);
- if (n != k) {
- r = k < 0 ? k : (errno ? -errno : -EIO);
-
- close_nointr_nofail(fdf);
- close_nointr(fdt);
-
- unlink(to);
- return r;
- }
- }
-
- close_nointr_nofail(fdf);
- r = close_nointr(fdt);
-
- if (r < 0) {
- unlink(to);
- return r;
- }
-
- return 0;
-}
-
-int symlink_or_copy(const char *from, const char *to) {
- char *pf = NULL, *pt = NULL;
- struct stat a, b;
- int r;
-
- assert(from);
- assert(to);
-
- if (parent_of_path(from, &pf) < 0 ||
- parent_of_path(to, &pt) < 0) {
- r = -ENOMEM;
- goto finish;
- }
-
- if (stat(pf, &a) < 0 ||
- stat(pt, &b) < 0) {
- r = -errno;
- goto finish;
- }
-
- if (a.st_dev != b.st_dev) {
- free(pf);
- free(pt);
-
- return copy_file(from, to);
- }
-
- if (symlink(from, to) < 0) {
- r = -errno;
- goto finish;
- }
-
- r = 0;
-
-finish:
- free(pf);
- free(pt);
-
- return r;
-}
-
-int symlink_or_copy_atomic(const char *from, const char *to) {
- char *t, *x;
- const char *fn;
- size_t k;
- unsigned long long ull;
- unsigned i;
- int r;
-
- assert(from);
- assert(to);
-
- t = new(char, strlen(to) + 1 + 16 + 1);
- if (!t)
- return -ENOMEM;
-
- fn = file_name_from_path(to);
- k = fn-to;
- memcpy(t, to, k);
- t[k] = '.';
- x = stpcpy(t+k+1, fn);
-
- ull = random_ull();
- for (i = 0; i < 16; i++) {
- *(x++) = hexchar(ull & 0xF);
- ull >>= 4;
- }
-
- *x = 0;
-
- r = symlink_or_copy(from, t);
- if (r < 0) {
- unlink(t);
- free(t);
- return r;
- }
-
- if (rename(t, to) < 0) {
- r = -errno;
- unlink(t);
- free(t);
- return r;
- }
-
- free(t);
- return r;
-}
-
-int audit_session_from_pid(pid_t pid, uint32_t *id) {
- char *s;
- uint32_t u;
- int r;
-
- assert(id);
-
- if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
- return -ENOENT;
-
- if (pid == 0)
- r = read_one_line_file("/proc/self/sessionid", &s);
- else {
- char *p;
-
- if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0)
- return -ENOMEM;
-
- r = read_one_line_file(p, &s);
- free(p);
- }
-
- if (r < 0)
- return r;
-
- r = safe_atou32(s, &u);
- free(s);
-
- if (r < 0)
- return r;
-
- if (u == (uint32_t) -1 || u <= 0)
- return -ENOENT;
-
- *id = u;
- return 0;
-}
-
-int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
- char *s;
- uid_t u;
- int r;
-
- assert(uid);
-
- /* Only use audit login uid if we are executed with sufficient
- * capabilities so that pam_loginuid could do its job. If we
- * are lacking the CAP_AUDIT_CONTROL capabality we most likely
- * are being run in a container and /proc/self/loginuid is
- * useless since it probably contains a uid of the host
- * system. */
-
- if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
- return -ENOENT;
-
- if (pid == 0)
- r = read_one_line_file("/proc/self/loginuid", &s);
- else {
- char *p;
-
- if (asprintf(&p, "/proc/%lu/loginuid", (unsigned long) pid) < 0)
- return -ENOMEM;
-
- r = read_one_line_file(p, &s);
- free(p);
- }
-
- if (r < 0)
- return r;
-
- r = parse_uid(s, &u);
- free(s);
-
- if (r < 0)
- return r;
-
- if (u == (uid_t) -1)
- return -ENOENT;
-
- *uid = (uid_t) u;
- return 0;
-}
-
-bool display_is_local(const char *display) {
- assert(display);
-
- return
- display[0] == ':' &&
- display[1] >= '0' &&
- display[1] <= '9';
-}
-
-int socket_from_display(const char *display, char **path) {
- size_t k;
- char *f, *c;
-
- assert(display);
- assert(path);
-
- if (!display_is_local(display))
- return -EINVAL;
-
- k = strspn(display+1, "0123456789");
-
- f = new(char, sizeof("/tmp/.X11-unix/X") + k);
- if (!f)
- return -ENOMEM;
-
- c = stpcpy(f, "/tmp/.X11-unix/X");
- memcpy(c, display+1, k);
- c[k] = 0;
-
- *path = f;
-
- return 0;
-}
-
-int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
- struct passwd *p;
- uid_t u;
-
- assert(username);
- assert(*username);
-
- /* We enforce some special rules for uid=0: in order to avoid
- * NSS lookups for root we hardcode its data. */
-
- if (streq(*username, "root") || streq(*username, "0")) {
- *username = "root";
-
- if (uid)
- *uid = 0;
-
- if (gid)
- *gid = 0;
-
- if (home)
- *home = "/root";
- return 0;
- }
-
- if (parse_uid(*username, &u) >= 0) {
- errno = 0;
- p = getpwuid(u);
-
- /* If there are multiple users with the same id, make
- * sure to leave $USER to the configured value instead
- * of the first occurrence in the database. However if
- * the uid was configured by a numeric uid, then let's
- * pick the real username from /etc/passwd. */
- if (p)
- *username = p->pw_name;
- } else {
- errno = 0;
- p = getpwnam(*username);
- }
-
- if (!p)
- return errno != 0 ? -errno : -ESRCH;
-
- if (uid)
- *uid = p->pw_uid;
-
- if (gid)
- *gid = p->pw_gid;
-
- if (home)
- *home = p->pw_dir;
-
- return 0;
-}
-
-int get_group_creds(const char **groupname, gid_t *gid) {
- struct group *g;
- gid_t id;
-
- assert(groupname);
-
- /* We enforce some special rules for gid=0: in order to avoid
- * NSS lookups for root we hardcode its data. */
-
- if (streq(*groupname, "root") || streq(*groupname, "0")) {
- *groupname = "root";
-
- if (gid)
- *gid = 0;
-
- return 0;
- }
-
- if (parse_gid(*groupname, &id) >= 0) {
- errno = 0;
- g = getgrgid(id);
-
- if (g)
- *groupname = g->gr_name;
- } else {
- errno = 0;
- g = getgrnam(*groupname);
- }
-
- if (!g)
- return errno != 0 ? -errno : -ESRCH;
-
- if (gid)
- *gid = g->gr_gid;
-
- return 0;
-}
-
-int in_group(const char *name) {
- gid_t gid, *gids;
- int ngroups_max, r, i;
-
- r = get_group_creds(&name, &gid);
- if (r < 0)
- return r;
-
- if (getgid() == gid)
- return 1;
-
- if (getegid() == gid)
- return 1;
-
- ngroups_max = sysconf(_SC_NGROUPS_MAX);
- assert(ngroups_max > 0);
-
- gids = alloca(sizeof(gid_t) * ngroups_max);
-
- r = getgroups(ngroups_max, gids);
- if (r < 0)
- return -errno;
-
- for (i = 0; i < r; i++)
- if (gids[i] == gid)
- return 1;
-
- return 0;
-}
-
-int glob_exists(const char *path) {
- glob_t g;
- int r, k;
-
- assert(path);
-
- zero(g);
- errno = 0;
- k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
-
- if (k == GLOB_NOMATCH)
- r = 0;
- else if (k == GLOB_NOSPACE)
- r = -ENOMEM;
- else if (k == 0)
- r = !strv_isempty(g.gl_pathv);
- else
- r = errno ? -errno : -EIO;
-
- globfree(&g);
-
- return r;
-}
-
-int dirent_ensure_type(DIR *d, struct dirent *de) {
- struct stat st;
-
- assert(d);
- assert(de);
-
- if (de->d_type != DT_UNKNOWN)
- return 0;
-
- if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
- return -errno;
-
- de->d_type =
- S_ISREG(st.st_mode) ? DT_REG :
- S_ISDIR(st.st_mode) ? DT_DIR :
- S_ISLNK(st.st_mode) ? DT_LNK :
- S_ISFIFO(st.st_mode) ? DT_FIFO :
- S_ISSOCK(st.st_mode) ? DT_SOCK :
- S_ISCHR(st.st_mode) ? DT_CHR :
- S_ISBLK(st.st_mode) ? DT_BLK :
- DT_UNKNOWN;
-
- return 0;
-}
-
-int in_search_path(const char *path, char **search) {
- char **i, *parent;
- int r;
-
- r = parent_of_path(path, &parent);
- if (r < 0)
- return r;
-
- r = 0;
-
- STRV_FOREACH(i, search) {
- if (path_equal(parent, *i)) {
- r = 1;
- break;
- }
- }
-
- free(parent);
-
- return r;
-}
-
-int get_files_in_directory(const char *path, char ***list) {
- DIR *d;
- int r = 0;
- unsigned n = 0;
- char **l = NULL;
-
- assert(path);
-
- /* Returns all files in a directory in *list, and the number
- * of files as return value. If list is NULL returns only the
- * number */
-
- d = opendir(path);
- if (!d)
- return -errno;
-
- for (;;) {
- struct dirent buffer, *de;
- int k;
-
- k = readdir_r(d, &buffer, &de);
- if (k != 0) {
- r = -k;
- goto finish;
- }
-
- if (!de)
- break;
-
- dirent_ensure_type(d, de);
-
- if (!dirent_is_file(de))
- continue;
-
- if (list) {
- if ((unsigned) r >= n) {
- char **t;
-
- n = MAX(16, 2*r);
- t = realloc(l, sizeof(char*) * n);
- if (!t) {
- r = -ENOMEM;
- goto finish;
- }
-
- l = t;
- }
-
- assert((unsigned) r < n);
-
- l[r] = strdup(de->d_name);
- if (!l[r]) {
- r = -ENOMEM;
- goto finish;
- }
-
- l[++r] = NULL;
- } else
- r++;
- }
-
-finish:
- if (d)
- closedir(d);
-
- if (r >= 0) {
- if (list)
- *list = l;
- } else
- strv_free(l);
-
- return r;
-}
-
-char *join(const char *x, ...) {
- va_list ap;
- size_t l;
- char *r, *p;
-
- va_start(ap, x);
-
- if (x) {
- l = strlen(x);
-
- for (;;) {
- const char *t;
-
- t = va_arg(ap, const char *);
- if (!t)
- break;
-
- l += strlen(t);
- }
- } else
- l = 0;
-
- va_end(ap);
-
- r = new(char, l+1);
- if (!r)
- return NULL;
-
- if (x) {
- p = stpcpy(r, x);
-
- va_start(ap, x);
-
- for (;;) {
- const char *t;
-
- t = va_arg(ap, const char *);
- if (!t)
- break;
-
- p = stpcpy(p, t);
- }
-
- va_end(ap);
- } else
- r[0] = 0;
-
- return r;
-}
-
-bool is_main_thread(void) {
- static __thread int cached = 0;
-
- if (_unlikely_(cached == 0))
- cached = getpid() == gettid() ? 1 : -1;
-
- return cached > 0;
-}
-
-int block_get_whole_disk(dev_t d, dev_t *ret) {
- char *p, *s;
- int r;
- unsigned n, m;
-
- assert(ret);
-
- /* If it has a queue this is good enough for us */
- if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
- return -ENOMEM;
-
- r = access(p, F_OK);
- free(p);
-
- if (r >= 0) {
- *ret = d;
- return 0;
- }
-
- /* If it is a partition find the originating device */
- if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
- return -ENOMEM;
-
- r = access(p, F_OK);
- free(p);
-
- if (r < 0)
- return -ENOENT;
-
- /* Get parent dev_t */
- if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
- return -ENOMEM;
-
- r = read_one_line_file(p, &s);
- free(p);
-
- if (r < 0)
- return r;
-
- r = sscanf(s, "%u:%u", &m, &n);
- free(s);
-
- if (r != 2)
- return -EINVAL;
-
- /* Only return this if it is really good enough for us. */
- if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
- return -ENOMEM;
-
- r = access(p, F_OK);
- free(p);
-
- if (r >= 0) {
- *ret = makedev(m, n);
- return 0;
- }
-
- return -ENOENT;
-}
-
-int file_is_priv_sticky(const char *p) {
- struct stat st;
-
- assert(p);
-
- if (lstat(p, &st) < 0)
- return -errno;
-
- return
- (st.st_uid == 0 || st.st_uid == getuid()) &&
- (st.st_mode & S_ISVTX);
-}
-
-static const char *const ioprio_class_table[] = {
- [IOPRIO_CLASS_NONE] = "none",
- [IOPRIO_CLASS_RT] = "realtime",
- [IOPRIO_CLASS_BE] = "best-effort",
- [IOPRIO_CLASS_IDLE] = "idle"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(ioprio_class, int);
-
-static const char *const sigchld_code_table[] = {
- [CLD_EXITED] = "exited",
- [CLD_KILLED] = "killed",
- [CLD_DUMPED] = "dumped",
- [CLD_TRAPPED] = "trapped",
- [CLD_STOPPED] = "stopped",
- [CLD_CONTINUED] = "continued",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
-
-static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
- [LOG_FAC(LOG_KERN)] = "kern",
- [LOG_FAC(LOG_USER)] = "user",
- [LOG_FAC(LOG_MAIL)] = "mail",
- [LOG_FAC(LOG_DAEMON)] = "daemon",
- [LOG_FAC(LOG_AUTH)] = "auth",
- [LOG_FAC(LOG_SYSLOG)] = "syslog",
- [LOG_FAC(LOG_LPR)] = "lpr",
- [LOG_FAC(LOG_NEWS)] = "news",
- [LOG_FAC(LOG_UUCP)] = "uucp",
- [LOG_FAC(LOG_CRON)] = "cron",
- [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
- [LOG_FAC(LOG_FTP)] = "ftp",
- [LOG_FAC(LOG_LOCAL0)] = "local0",
- [LOG_FAC(LOG_LOCAL1)] = "local1",
- [LOG_FAC(LOG_LOCAL2)] = "local2",
- [LOG_FAC(LOG_LOCAL3)] = "local3",
- [LOG_FAC(LOG_LOCAL4)] = "local4",
- [LOG_FAC(LOG_LOCAL5)] = "local5",
- [LOG_FAC(LOG_LOCAL6)] = "local6",
- [LOG_FAC(LOG_LOCAL7)] = "local7"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(log_facility_unshifted, int);
-
-static const char *const log_level_table[] = {
- [LOG_EMERG] = "emerg",
- [LOG_ALERT] = "alert",
- [LOG_CRIT] = "crit",
- [LOG_ERR] = "err",
- [LOG_WARNING] = "warning",
- [LOG_NOTICE] = "notice",
- [LOG_INFO] = "info",
- [LOG_DEBUG] = "debug"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(log_level, int);
-
-static const char* const sched_policy_table[] = {
- [SCHED_OTHER] = "other",
- [SCHED_BATCH] = "batch",
- [SCHED_IDLE] = "idle",
- [SCHED_FIFO] = "fifo",
- [SCHED_RR] = "rr"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(sched_policy, int);
-
-static const char* const rlimit_table[] = {
- [RLIMIT_CPU] = "LimitCPU",
- [RLIMIT_FSIZE] = "LimitFSIZE",
- [RLIMIT_DATA] = "LimitDATA",
- [RLIMIT_STACK] = "LimitSTACK",
- [RLIMIT_CORE] = "LimitCORE",
- [RLIMIT_RSS] = "LimitRSS",
- [RLIMIT_NOFILE] = "LimitNOFILE",
- [RLIMIT_AS] = "LimitAS",
- [RLIMIT_NPROC] = "LimitNPROC",
- [RLIMIT_MEMLOCK] = "LimitMEMLOCK",
- [RLIMIT_LOCKS] = "LimitLOCKS",
- [RLIMIT_SIGPENDING] = "LimitSIGPENDING",
- [RLIMIT_MSGQUEUE] = "LimitMSGQUEUE",
- [RLIMIT_NICE] = "LimitNICE",
- [RLIMIT_RTPRIO] = "LimitRTPRIO",
- [RLIMIT_RTTIME] = "LimitRTTIME"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
-
-static const char* const ip_tos_table[] = {
- [IPTOS_LOWDELAY] = "low-delay",
- [IPTOS_THROUGHPUT] = "throughput",
- [IPTOS_RELIABILITY] = "reliability",
- [IPTOS_LOWCOST] = "low-cost",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(ip_tos, int);
-
-static const char *const __signal_table[] = {
- [SIGHUP] = "HUP",
- [SIGINT] = "INT",
- [SIGQUIT] = "QUIT",
- [SIGILL] = "ILL",
- [SIGTRAP] = "TRAP",
- [SIGABRT] = "ABRT",
- [SIGBUS] = "BUS",
- [SIGFPE] = "FPE",
- [SIGKILL] = "KILL",
- [SIGUSR1] = "USR1",
- [SIGSEGV] = "SEGV",
- [SIGUSR2] = "USR2",
- [SIGPIPE] = "PIPE",
- [SIGALRM] = "ALRM",
- [SIGTERM] = "TERM",
-#ifdef SIGSTKFLT
- [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
-#endif
- [SIGCHLD] = "CHLD",
- [SIGCONT] = "CONT",
- [SIGSTOP] = "STOP",
- [SIGTSTP] = "TSTP",
- [SIGTTIN] = "TTIN",
- [SIGTTOU] = "TTOU",
- [SIGURG] = "URG",
- [SIGXCPU] = "XCPU",
- [SIGXFSZ] = "XFSZ",
- [SIGVTALRM] = "VTALRM",
- [SIGPROF] = "PROF",
- [SIGWINCH] = "WINCH",
- [SIGIO] = "IO",
- [SIGPWR] = "PWR",
- [SIGSYS] = "SYS"
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
-
-const char *signal_to_string(int signo) {
- static __thread char buf[12];
- const char *name;
-
- name = __signal_to_string(signo);
- if (name)
- return name;
-
- if (signo >= SIGRTMIN && signo <= SIGRTMAX)
- snprintf(buf, sizeof(buf) - 1, "RTMIN+%d", signo - SIGRTMIN);
- else
- snprintf(buf, sizeof(buf) - 1, "%d", signo);
- char_array_0(buf);
- return buf;
-}
-
-int signal_from_string(const char *s) {
- int signo;
- int offset = 0;
- unsigned u;
-
- signo =__signal_from_string(s);
- if (signo > 0)
- return signo;
-
- if (startswith(s, "RTMIN+")) {
- s += 6;
- offset = SIGRTMIN;
- }
- if (safe_atou(s, &u) >= 0) {
- signo = (int) u + offset;
- if (signo > 0 && signo < _NSIG)
- return signo;
- }
- return -1;
-}
-
-bool kexec_loaded(void) {
- bool loaded = false;
- char *s;
-
- if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
- if (s[0] == '1')
- loaded = true;
- free(s);
- }
- return loaded;
-}
-
-int strdup_or_null(const char *a, char **b) {
- char *c;
-
- assert(b);
-
- if (!a) {
- *b = NULL;
- return 0;
- }
-
- c = strdup(a);
- if (!c)
- return -ENOMEM;
-
- *b = c;
- return 0;
-}
-
-int prot_from_flags(int flags) {
-
- switch (flags & O_ACCMODE) {
-
- case O_RDONLY:
- return PROT_READ;
-
- case O_WRONLY:
- return PROT_WRITE;
-
- case O_RDWR:
- return PROT_READ|PROT_WRITE;
-
- default:
- return -EINVAL;
- }
-}
-
-unsigned long cap_last_cap(void) {
- static __thread unsigned long saved;
- static __thread bool valid = false;
- unsigned long p;
-
- if (valid)
- return saved;
-
- p = (unsigned long) CAP_LAST_CAP;
-
- if (prctl(PR_CAPBSET_READ, p) < 0) {
-
- /* Hmm, look downwards, until we find one that
- * works */
- for (p--; p > 0; p --)
- if (prctl(PR_CAPBSET_READ, p) >= 0)
- break;
-
- } else {
-
- /* Hmm, look upwards, until we find one that doesn't
- * work */
- for (;; p++)
- if (prctl(PR_CAPBSET_READ, p+1) < 0)
- break;
- }
-
- saved = p;
- valid = true;
-
- return p;
-}
-
-char *format_bytes(char *buf, size_t l, off_t t) {
- unsigned i;
-
- static const struct {
- const char *suffix;
- off_t factor;
- } table[] = {
- { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
- { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
- { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
- { "G", 1024ULL*1024ULL*1024ULL },
- { "M", 1024ULL*1024ULL },
- { "K", 1024ULL },
- };
-
- for (i = 0; i < ELEMENTSOF(table); i++) {
-
- if (t >= table[i].factor) {
- snprintf(buf, l,
- "%llu.%llu%s",
- (unsigned long long) (t / table[i].factor),
- (unsigned long long) (((t*10ULL) / table[i].factor) % 10ULL),
- table[i].suffix);
-
- goto finish;
- }
- }
-
- snprintf(buf, l, "%lluB", (unsigned long long) t);
-
-finish:
- buf[l-1] = 0;
- return buf;
-
-}
-
-void* memdup(const void *p, size_t l) {
- void *r;
-
- assert(p);
-
- r = malloc(l);
- if (!r)
- return NULL;
-
- memcpy(r, p, l);
- return r;
-}
-
-int fd_inc_sndbuf(int fd, size_t n) {
- int r, value;
- socklen_t l = sizeof(value);
-
- r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
- if (r >= 0 &&
- l == sizeof(value) &&
- (size_t) value >= n*2)
- return 0;
-
- value = (int) n;
- r = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value));
- if (r < 0)
- return -errno;
-
- return 1;
-}
-
-int fd_inc_rcvbuf(int fd, size_t n) {
- int r, value;
- socklen_t l = sizeof(value);
-
- r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
- if (r >= 0 &&
- l == sizeof(value) &&
- (size_t) value >= n*2)
- return 0;
-
- value = (int) n;
- r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value));
- if (r < 0)
- return -errno;
-
- return 1;
-}
diff --git a/src/util.h b/src/util.h
deleted file mode 100644
index b1af6db..0000000
--- a/src/util.h
+++ /dev/null
@@ -1,544 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooutilhfoo
-#define fooutilhfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <inttypes.h>
-#include <time.h>
-#include <sys/time.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <signal.h>
-#include <sched.h>
-#include <limits.h>
-#include <sys/stat.h>
-#include <dirent.h>
-
-#include "macro.h"
-
-typedef uint64_t usec_t;
-
-typedef struct dual_timestamp {
- usec_t realtime;
- usec_t monotonic;
-} dual_timestamp;
-
-#define MSEC_PER_SEC 1000ULL
-#define USEC_PER_SEC 1000000ULL
-#define USEC_PER_MSEC 1000ULL
-#define NSEC_PER_SEC 1000000000ULL
-#define NSEC_PER_MSEC 1000000ULL
-#define NSEC_PER_USEC 1000ULL
-
-#define USEC_PER_MINUTE (60ULL*USEC_PER_SEC)
-#define USEC_PER_HOUR (60ULL*USEC_PER_MINUTE)
-#define USEC_PER_DAY (24ULL*USEC_PER_HOUR)
-#define USEC_PER_WEEK (7ULL*USEC_PER_DAY)
-#define USEC_PER_MONTH (2629800ULL*USEC_PER_SEC)
-#define USEC_PER_YEAR (31557600ULL*USEC_PER_SEC)
-
-/* What is interpreted as whitespace? */
-#define WHITESPACE " \t\n\r"
-#define NEWLINE "\n\r"
-#define QUOTES "\"\'"
-#define COMMENTS "#;\n"
-
-#define FORMAT_TIMESTAMP_MAX 64
-#define FORMAT_TIMESTAMP_PRETTY_MAX 256
-#define FORMAT_TIMESPAN_MAX 64
-#define FORMAT_BYTES_MAX 8
-
-#define ANSI_HIGHLIGHT_ON "\x1B[1;39m"
-#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m"
-#define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m"
-#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
-
-usec_t now(clockid_t clock);
-
-dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
-dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
-
-#define dual_timestamp_is_set(ts) ((ts)->realtime > 0)
-
-usec_t timespec_load(const struct timespec *ts);
-struct timespec *timespec_store(struct timespec *ts, usec_t u);
-
-usec_t timeval_load(const struct timeval *tv);
-struct timeval *timeval_store(struct timeval *tv, usec_t u);
-
-size_t page_size(void);
-#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
-
-#define streq(a,b) (strcmp((a),(b)) == 0)
-#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
-
-bool streq_ptr(const char *a, const char *b);
-
-#define new(t, n) ((t*) malloc(sizeof(t)*(n)))
-
-#define new0(t, n) ((t*) calloc((n), sizeof(t)))
-
-#define malloc0(n) (calloc((n), 1))
-
-static inline const char* yes_no(bool b) {
- return b ? "yes" : "no";
-}
-
-static inline const char* strempty(const char *s) {
- return s ? s : "";
-}
-
-static inline const char* strnull(const char *s) {
- return s ? s : "(null)";
-}
-
-static inline const char *strna(const char *s) {
- return s ? s : "n/a";
-}
-
-static inline bool is_path_absolute(const char *p) {
- return *p == '/';
-}
-
-static inline bool isempty(const char *p) {
- return !p || !p[0];
-}
-
-bool endswith(const char *s, const char *postfix);
-bool startswith(const char *s, const char *prefix);
-bool startswith_no_case(const char *s, const char *prefix);
-
-bool first_word(const char *s, const char *word);
-
-int close_nointr(int fd);
-void close_nointr_nofail(int fd);
-void close_many(const int fds[], unsigned n_fd);
-
-int parse_boolean(const char *v);
-int parse_usec(const char *t, usec_t *usec);
-int parse_bytes(const char *t, off_t *bytes);
-int parse_pid(const char *s, pid_t* ret_pid);
-int parse_uid(const char *s, uid_t* ret_uid);
-#define parse_gid(s, ret_uid) parse_uid(s, ret_uid)
-
-int safe_atou(const char *s, unsigned *ret_u);
-int safe_atoi(const char *s, int *ret_i);
-
-int safe_atollu(const char *s, unsigned long long *ret_u);
-int safe_atolli(const char *s, long long int *ret_i);
-
-#if __WORDSIZE == 32
-static inline int safe_atolu(const char *s, unsigned long *ret_u) {
- assert_cc(sizeof(unsigned long) == sizeof(unsigned));
- return safe_atou(s, (unsigned*) ret_u);
-}
-static inline int safe_atoli(const char *s, long int *ret_u) {
- assert_cc(sizeof(long int) == sizeof(int));
- return safe_atoi(s, (int*) ret_u);
-}
-#else
-static inline int safe_atolu(const char *s, unsigned long *ret_u) {
- assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
- return safe_atollu(s, (unsigned long long*) ret_u);
-}
-static inline int safe_atoli(const char *s, long int *ret_u) {
- assert_cc(sizeof(long int) == sizeof(long long int));
- return safe_atolli(s, (long long int*) ret_u);
-}
-#endif
-
-static inline int safe_atou32(const char *s, uint32_t *ret_u) {
- assert_cc(sizeof(uint32_t) == sizeof(unsigned));
- return safe_atou(s, (unsigned*) ret_u);
-}
-
-static inline int safe_atoi32(const char *s, int32_t *ret_i) {
- assert_cc(sizeof(int32_t) == sizeof(int));
- return safe_atoi(s, (int*) ret_i);
-}
-
-static inline int safe_atou64(const char *s, uint64_t *ret_u) {
- assert_cc(sizeof(uint64_t) == sizeof(unsigned long long));
- return safe_atollu(s, (unsigned long long*) ret_u);
-}
-
-static inline int safe_atoi64(const char *s, int64_t *ret_i) {
- assert_cc(sizeof(int64_t) == sizeof(long long int));
- return safe_atolli(s, (long long int*) ret_i);
-}
-
-char *split(const char *c, size_t *l, const char *separator, char **state);
-char *split_quoted(const char *c, size_t *l, char **state);
-
-#define FOREACH_WORD(word, length, s, state) \
- for ((state) = NULL, (word) = split((s), &(length), WHITESPACE, &(state)); (word); (word) = split((s), &(length), WHITESPACE, &(state)))
-
-#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
- for ((state) = NULL, (word) = split((s), &(length), (separator), &(state)); (word); (word) = split((s), &(length), (separator), &(state)))
-
-#define FOREACH_WORD_QUOTED(word, length, s, state) \
- for ((state) = NULL, (word) = split_quoted((s), &(length), &(state)); (word); (word) = split_quoted((s), &(length), &(state)))
-
-char **split_path_and_make_absolute(const char *p);
-
-pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
-int get_starttime_of_pid(pid_t pid, unsigned long long *st);
-
-int write_one_line_file(const char *fn, const char *line);
-int write_one_line_file_atomic(const char *fn, const char *line);
-int read_one_line_file(const char *fn, char **line);
-int read_full_file(const char *fn, char **contents, size_t *size);
-
-int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
-int load_env_file(const char *fname, char ***l);
-int write_env_file(const char *fname, char **l);
-
-char *strappend(const char *s, const char *suffix);
-char *strnappend(const char *s, const char *suffix, size_t length);
-
-char *replace_env(const char *format, char **env);
-char **replace_env_argv(char **argv, char **env);
-
-int readlink_malloc(const char *p, char **r);
-int readlink_and_make_absolute(const char *p, char **r);
-int readlink_and_canonicalize(const char *p, char **r);
-
-char *file_name_from_path(const char *p);
-bool is_path(const char *p);
-
-bool path_is_absolute(const char *p);
-char *path_make_absolute(const char *p, const char *prefix);
-char *path_make_absolute_cwd(const char *p);
-
-char **strv_path_make_absolute_cwd(char **l);
-char **strv_path_canonicalize(char **l);
-char **strv_path_remove_empty(char **l);
-
-int reset_all_signal_handlers(void);
-
-char *strstrip(char *s);
-char *delete_chars(char *s, const char *bad);
-char *truncate_nl(char *s);
-
-char *file_in_same_dir(const char *path, const char *filename);
-int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid);
-int mkdir_parents(const char *path, mode_t mode);
-int mkdir_p(const char *path, mode_t mode);
-
-int parent_of_path(const char *path, char **parent);
-
-int rmdir_parents(const char *path, const char *stop);
-
-int get_process_comm(pid_t pid, char **name);
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
-int get_process_exe(pid_t pid, char **name);
-int get_process_uid(pid_t pid, uid_t *uid);
-
-char hexchar(int x);
-int unhexchar(char c);
-char octchar(int x);
-int unoctchar(char c);
-char decchar(int x);
-int undecchar(char c);
-
-char *cescape(const char *s);
-char *cunescape(const char *s);
-char *cunescape_length(const char *s, size_t length);
-
-char *xescape(const char *s, const char *bad);
-
-char *bus_path_escape(const char *s);
-char *bus_path_unescape(const char *s);
-
-char *path_kill_slashes(char *path);
-
-bool path_startswith(const char *path, const char *prefix);
-bool path_equal(const char *a, const char *b);
-
-char *ascii_strlower(char *path);
-
-bool dirent_is_file(const struct dirent *de);
-bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix);
-
-bool ignore_file(const char *filename);
-
-bool chars_intersect(const char *a, const char *b);
-
-char *format_timestamp(char *buf, size_t l, usec_t t);
-char *format_timestamp_pretty(char *buf, size_t l, usec_t t);
-char *format_timespan(char *buf, size_t l, usec_t t);
-
-int make_stdio(int fd);
-int make_null_stdio(void);
-
-unsigned long long random_ull(void);
-
-#define __DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
- scope const char *name##_to_string(type i) { \
- if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \
- return NULL; \
- return name##_table[i]; \
- } \
- scope type name##_from_string(const char *s) { \
- type i; \
- unsigned u = 0; \
- assert(s); \
- for (i = 0; i < (type)ELEMENTSOF(name##_table); i++) \
- if (name##_table[i] && \
- streq(name##_table[i], s)) \
- return i; \
- if (safe_atou(s, &u) >= 0 && \
- u < ELEMENTSOF(name##_table)) \
- return (type) u; \
- return (type) -1; \
- } \
- struct __useless_struct_to_allow_trailing_semicolon__
-
-#define DEFINE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,)
-#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,static)
-
-int fd_nonblock(int fd, bool nonblock);
-int fd_cloexec(int fd, bool cloexec);
-
-int close_all_fds(const int except[], unsigned n_except);
-
-bool fstype_is_network(const char *fstype);
-
-int chvt(int vt);
-
-int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
-int ask(char *ret, const char *replies, const char *text, ...);
-
-int reset_terminal_fd(int fd, bool switch_to_text);
-int reset_terminal(const char *name);
-
-int open_terminal(const char *name, int mode);
-int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm);
-int release_terminal(void);
-
-int flush_fd(int fd);
-
-int ignore_signals(int sig, ...);
-int default_signals(int sig, ...);
-int sigaction_many(const struct sigaction *sa, ...);
-
-int close_pipe(int p[]);
-int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
-
-ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
-ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
-
-int path_is_mount_point(const char *path, bool allow_symlink);
-
-bool is_device_path(const char *path);
-
-int dir_is_empty(const char *path);
-
-void rename_process(const char name[8]);
-
-void sigset_add_many(sigset_t *ss, ...);
-
-char* gethostname_malloc(void);
-char* getlogname_malloc(void);
-
-int getttyname_malloc(int fd, char **r);
-int getttyname_harder(int fd, char **r);
-
-int get_ctty_devnr(pid_t pid, dev_t *d);
-int get_ctty(pid_t, dev_t *_devnr, char **r);
-
-int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
-int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
-
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
-
-int pipe_eof(int fd);
-
-cpu_set_t* cpu_set_malloc(unsigned *ncpus);
-
-void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap);
-void status_printf(const char *status, bool ellipse, const char *format, ...);
-void status_welcome(void);
-
-int fd_columns(int fd);
-unsigned columns(void);
-
-int fd_lines(int fd);
-unsigned lines(void);
-
-int running_in_chroot(void);
-
-char *ellipsize(const char *s, size_t length, unsigned percent);
-char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent);
-
-int touch(const char *path);
-
-char *unquote(const char *s, const char *quotes);
-char *normalize_env_assignment(const char *s);
-
-int wait_for_terminate(pid_t pid, siginfo_t *status);
-int wait_for_terminate_and_warn(const char *name, pid_t pid);
-
-_noreturn_ void freeze(void);
-
-bool null_or_empty(struct stat *st);
-int null_or_empty_path(const char *fn);
-
-DIR *xopendirat(int dirfd, const char *name, int flags);
-
-void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t);
-void dual_timestamp_deserialize(const char *value, dual_timestamp *t);
-
-char *fstab_node_to_udev_node(const char *p);
-
-void filter_environ(const char *prefix);
-
-bool tty_is_vc(const char *tty);
-bool tty_is_vc_resolve(const char *tty);
-int vtnr_from_tty(const char *tty);
-const char *default_term_for_tty(const char *tty);
-
-void execute_directory(const char *directory, DIR *_d, char *argv[]);
-
-int kill_and_sigcont(pid_t pid, int sig);
-
-bool nulstr_contains(const char*nulstr, const char *needle);
-
-bool plymouth_running(void);
-
-void parse_syslog_priority(char **p, int *priority);
-void skip_syslog_pid(char **buf);
-void skip_syslog_date(char **buf);
-
-int have_effective_cap(int value);
-
-bool hostname_is_valid(const char *s);
-char* hostname_cleanup(char *s);
-
-char* strshorten(char *s, size_t l);
-
-int terminal_vhangup_fd(int fd);
-int terminal_vhangup(const char *name);
-
-int vt_disallocate(const char *name);
-
-int copy_file(const char *from, const char *to);
-int symlink_or_copy(const char *from, const char *to);
-int symlink_or_copy_atomic(const char *from, const char *to);
-
-int fchmod_umask(int fd, mode_t mode);
-
-int conf_files_list(char ***strv, const char *suffix, const char *dir, ...);
-
-int hwclock_is_localtime(void);
-int hwclock_apply_localtime_delta(int *min);
-int hwclock_reset_localtime_delta(void);
-int hwclock_get_time(struct tm *tm);
-int hwclock_set_time(const struct tm *tm);
-
-int audit_session_from_pid(pid_t pid, uint32_t *id);
-int audit_loginuid_from_pid(pid_t pid, uid_t *uid);
-
-bool display_is_local(const char *display);
-int socket_from_display(const char *display, char **path);
-
-int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home);
-int get_group_creds(const char **groupname, gid_t *gid);
-
-int in_group(const char *name);
-
-int glob_exists(const char *path);
-
-int dirent_ensure_type(DIR *d, struct dirent *de);
-
-int in_search_path(const char *path, char **search);
-int get_files_in_directory(const char *path, char ***list);
-
-char *join(const char *x, ...) _sentinel_;
-
-bool is_main_thread(void);
-
-bool in_charset(const char *s, const char* charset);
-
-int block_get_whole_disk(dev_t d, dev_t *ret);
-
-int file_is_priv_sticky(const char *p);
-
-int strdup_or_null(const char *a, char **b);
-
-#define NULSTR_FOREACH(i, l) \
- for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
-
-#define NULSTR_FOREACH_PAIR(i, j, l) \
- for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i))
-
-const char *ioprio_class_to_string(int i);
-int ioprio_class_from_string(const char *s);
-
-const char *sigchld_code_to_string(int i);
-int sigchld_code_from_string(const char *s);
-
-const char *log_facility_unshifted_to_string(int i);
-int log_facility_unshifted_from_string(const char *s);
-
-const char *log_level_to_string(int i);
-int log_level_from_string(const char *s);
-
-const char *sched_policy_to_string(int i);
-int sched_policy_from_string(const char *s);
-
-const char *rlimit_to_string(int i);
-int rlimit_from_string(const char *s);
-
-const char *ip_tos_to_string(int i);
-int ip_tos_from_string(const char *s);
-
-const char *signal_to_string(int i);
-int signal_from_string(const char *s);
-
-int signal_from_string_try_harder(const char *s);
-
-extern int saved_argc;
-extern char **saved_argv;
-
-bool kexec_loaded(void);
-
-int prot_from_flags(int flags);
-
-unsigned long cap_last_cap(void);
-
-char *format_bytes(char *buf, size_t l, off_t t);
-
-int fd_wait_for_event(int fd, int event, usec_t timeout);
-
-void* memdup(const void *p, size_t l);
-
-int rtc_open(int flags);
-
-int is_kernel_thread(pid_t pid);
-
-int fd_inc_sndbuf(int fd, size_t n);
-int fd_inc_rcvbuf(int fd, size_t n);
-
-#endif
diff --git a/src/virt.c b/src/virt.c
deleted file mode 100644
index 4c526ff..0000000
--- a/src/virt.c
+++ /dev/null
@@ -1,292 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include "util.h"
-#include "virt.h"
-
-/* Returns a short identifier for the various VM implementations */
-int detect_vm(const char **id) {
-
-#if defined(__i386__) || defined(__x86_64__)
-
- /* Both CPUID and DMI are x86 specific interfaces... */
-
- static const char *const dmi_vendors[] = {
- "/sys/class/dmi/id/sys_vendor",
- "/sys/class/dmi/id/board_vendor",
- "/sys/class/dmi/id/bios_vendor"
- };
-
- static const char dmi_vendor_table[] =
- "QEMU\0" "qemu\0"
- /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
- "VMware\0" "vmware\0"
- "VMW\0" "vmware\0"
- "Microsoft Corporation\0" "microsoft\0"
- "innotek GmbH\0" "oracle\0"
- "Xen\0" "xen\0"
- "Bochs\0" "bochs\0";
-
- static const char cpuid_vendor_table[] =
- "XenVMMXenVMM\0" "xen\0"
- "KVMKVMKVM\0" "kvm\0"
- /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
- "VMwareVMware\0" "vmware\0"
- /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
- "Microsoft Hv\0" "microsoft\0";
-
- uint32_t eax, ecx;
- union {
- uint32_t sig32[3];
- char text[13];
- } sig;
- unsigned i;
- const char *j, *k;
- bool hypervisor;
-
- /* http://lwn.net/Articles/301888/ */
- zero(sig);
-
-#if defined (__i386__)
-#define REG_a "eax"
-#define REG_b "ebx"
-#elif defined (__amd64__)
-#define REG_a "rax"
-#define REG_b "rbx"
-#endif
-
- /* First detect whether there is a hypervisor */
- eax = 1;
- __asm__ __volatile__ (
- /* ebx/rbx is being used for PIC! */
- " push %%"REG_b" \n\t"
- " cpuid \n\t"
- " pop %%"REG_b" \n\t"
-
- : "=a" (eax), "=c" (ecx)
- : "0" (eax)
- );
-
- hypervisor = !!(ecx & 0x80000000U);
-
- if (hypervisor) {
-
- /* There is a hypervisor, see what it is */
- eax = 0x40000000U;
- __asm__ __volatile__ (
- /* ebx/rbx is being used for PIC! */
- " push %%"REG_b" \n\t"
- " cpuid \n\t"
- " mov %%ebx, %1 \n\t"
- " pop %%"REG_b" \n\t"
-
- : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
- : "0" (eax)
- );
-
- NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
- if (streq(sig.text, j)) {
-
- if (id)
- *id = k;
-
- return 1;
- }
- }
-
- for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
- char *s;
- int r;
- const char *found = NULL;
-
- if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) {
- if (r != -ENOENT)
- return r;
-
- continue;
- }
-
- NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
- if (startswith(s, j))
- found = k;
- free(s);
-
- if (found) {
- if (id)
- *id = found;
-
- return 1;
- }
- }
-
- if (hypervisor) {
- if (id)
- *id = "other";
-
- return 1;
- }
-
-#endif
- return 0;
-}
-
-int detect_container(const char **id) {
- FILE *f;
-
- /* Unfortunately many of these operations require root access
- * in one way or another */
-
- if (geteuid() != 0)
- return -EPERM;
-
- if (running_in_chroot() > 0) {
-
- if (id)
- *id = "chroot";
-
- return 1;
- }
-
- /* /proc/vz exists in container and outside of the container,
- * /proc/bc only outside of the container. */
- if (access("/proc/vz", F_OK) >= 0 &&
- access("/proc/bc", F_OK) < 0) {
-
- if (id)
- *id = "openvz";
-
- return 1;
- }
-
- f = fopen("/proc/1/environ", "re");
- if (f) {
- bool done = false;
-
- do {
- char line[LINE_MAX];
- unsigned i;
-
- for (i = 0; i < sizeof(line)-1; i++) {
- int c;
-
- c = getc(f);
- if (_unlikely_(c == EOF)) {
- done = true;
- break;
- } else if (c == 0)
- break;
-
- line[i] = c;
- }
- line[i] = 0;
-
- if (streq(line, "container=lxc")) {
- fclose(f);
-
- if (id)
- *id = "lxc";
- return 1;
-
- } else if (streq(line, "container=lxc-libvirt")) {
- fclose(f);
-
- if (id)
- *id = "lxc-libvirt";
- return 1;
-
- } else if (streq(line, "container=systemd-nspawn")) {
- fclose(f);
-
- if (id)
- *id = "systemd-nspawn";
- return 1;
-
- } else if (startswith(line, "container=")) {
- fclose(f);
-
- if (id)
- *id = "other";
- return 1;
- }
-
- } while (!done);
-
- fclose(f);
- }
-
- return 0;
-}
-
-/* Returns a short identifier for the various VM/container implementations */
-Virtualization detect_virtualization(const char **id) {
-
- static __thread Virtualization cached_virt = _VIRTUALIZATION_INVALID;
- static __thread const char *cached_id = NULL;
-
- const char *_id;
- int r;
- Virtualization v;
-
- if (_likely_(cached_virt >= 0)) {
-
- if (id && cached_virt > 0)
- *id = cached_id;
-
- return cached_virt;
- }
-
- r = detect_container(&_id);
- if (r < 0) {
- v = r;
- goto finish;
- } else if (r > 0) {
- v = VIRTUALIZATION_CONTAINER;
- goto finish;
- }
-
- r = detect_vm(&_id);
- if (r < 0) {
- v = r;
- goto finish;
- } else if (r > 0) {
- v = VIRTUALIZATION_VM;
- goto finish;
- }
-
- v = VIRTUALIZATION_NONE;
-
-finish:
- if (v > 0) {
- cached_id = _id;
-
- if (id)
- *id = _id;
- }
-
- if (v >= 0)
- cached_virt = v;
-
- return v;
-}
diff --git a/src/virt.h b/src/virt.h
deleted file mode 100644
index f55c9a6..0000000
--- a/src/virt.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foovirthfoo
-#define foovirthfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- systemd 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-int detect_vm(const char **id);
-int detect_container(const char **id);
-
-typedef enum Virtualization {
- VIRTUALIZATION_NONE = 0,
- VIRTUALIZATION_VM,
- VIRTUALIZATION_CONTAINER,
- _VIRTUALIZATION_MAX,
- _VIRTUALIZATION_INVALID = -1
-} Virtualization;
-
-Virtualization detect_virtualization(const char **id);
-
-#endif
commit 304b9f30781b8625c1f84fd27205f2707d4843b1
Author: Kay Sievers <kay at vrfy.org>
Date: Tue Apr 10 13:33:43 2012 +0200
udev: set errno = ENOSYS for removed interfaces
diff --git a/src/udev/libudev-monitor.c b/src/udev/libudev-monitor.c
index 5da41e7..5ff340a 100644
--- a/src/udev/libudev-monitor.c
+++ b/src/udev/libudev-monitor.c
@@ -107,6 +107,7 @@ static struct udev_monitor *udev_monitor_new(struct udev *udev)
**/
_public_ struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path)
{
+ errno = ENOSYS;
return NULL;
}
More information about the systemd-commits
mailing list