From f33d3ec1d7521c91da8b30ad5cb345d6416bb07d Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Thu, 12 Apr 2012 01:51:49 +0200 Subject: move more common files to shared/ and add them to shared.la --- Makefile.am | 161 ++- src/cgroup-show.c | 261 ----- src/cgroup-show.h | 30 - src/dbus-common.c | 1096 ------------------ src/dbus-common.h | 198 ---- src/hostname-setup.c | 187 ---- src/hostname-setup.h | 27 - src/install.c | 1954 --------------------------------- src/install.h | 89 -- src/logs-show.c | 677 ------------ src/logs-show.h | 56 - src/loopback-setup.c | 274 ----- src/loopback-setup.h | 27 - src/machine-id-setup.c | 265 ----- src/machine-id-setup.h | 27 - src/mount-setup.c | 423 ------- src/mount-setup.h | 36 - src/path-lookup.c | 348 ------ src/path-lookup.h | 40 - src/shared/cgroup-show.c | 261 +++++ src/shared/cgroup-show.h | 30 + src/shared/dbus-common.c | 1096 ++++++++++++++++++ src/shared/dbus-common.h | 198 ++++ src/shared/hostname-setup.c | 187 ++++ src/shared/hostname-setup.h | 27 + src/shared/install.c | 1954 +++++++++++++++++++++++++++++++++ src/shared/install.h | 89 ++ src/shared/logs-show.c | 677 ++++++++++++ src/shared/logs-show.h | 56 + src/shared/loopback-setup.c | 274 +++++ src/shared/loopback-setup.h | 27 + src/shared/machine-id-setup.c | 265 +++++ src/shared/machine-id-setup.h | 27 + src/shared/mount-setup.c | 423 +++++++ src/shared/mount-setup.h | 36 + src/shared/path-lookup.c | 348 ++++++ src/shared/path-lookup.h | 40 + src/shared/spawn-ask-password-agent.c | 67 ++ src/shared/spawn-ask-password-agent.h | 28 + src/shared/spawn-polkit-agent.c | 86 ++ src/shared/spawn-polkit-agent.h | 28 + src/shared/specifier.c | 108 ++ src/shared/specifier.h | 37 + src/shared/umount.c | 644 +++++++++++ src/shared/umount.h | 33 + src/shared/unit-name.c | 448 ++++++++ src/shared/unit-name.h | 57 + src/shared/utmp-wtmp.c | 430 ++++++++ src/shared/utmp-wtmp.h | 38 + src/shared/watchdog.c | 169 +++ src/shared/watchdog.h | 31 + src/spawn-ask-password-agent.c | 67 -- src/spawn-ask-password-agent.h | 28 - src/spawn-polkit-agent.c | 86 -- src/spawn-polkit-agent.h | 28 - src/specifier.c | 108 -- src/specifier.h | 37 - src/umount.c | 644 ----------- src/umount.h | 33 - src/unit-name.c | 448 -------- src/unit-name.h | 57 - src/utmp-wtmp.c | 430 -------- src/utmp-wtmp.h | 38 - src/watchdog.c | 169 --- src/watchdog.h | 31 - 65 files changed, 8281 insertions(+), 8318 deletions(-) delete mode 100644 src/cgroup-show.c delete mode 100644 src/cgroup-show.h delete mode 100644 src/dbus-common.c delete mode 100644 src/dbus-common.h delete mode 100644 src/hostname-setup.c delete mode 100644 src/hostname-setup.h delete mode 100644 src/install.c delete mode 100644 src/install.h delete mode 100644 src/logs-show.c delete mode 100644 src/logs-show.h delete mode 100644 src/loopback-setup.c delete mode 100644 src/loopback-setup.h delete mode 100644 src/machine-id-setup.c delete mode 100644 src/machine-id-setup.h delete mode 100644 src/mount-setup.c delete mode 100644 src/mount-setup.h delete mode 100644 src/path-lookup.c delete mode 100644 src/path-lookup.h create mode 100644 src/shared/cgroup-show.c create mode 100644 src/shared/cgroup-show.h create mode 100644 src/shared/dbus-common.c create mode 100644 src/shared/dbus-common.h create mode 100644 src/shared/hostname-setup.c create mode 100644 src/shared/hostname-setup.h create mode 100644 src/shared/install.c create mode 100644 src/shared/install.h create mode 100644 src/shared/logs-show.c create mode 100644 src/shared/logs-show.h create mode 100644 src/shared/loopback-setup.c create mode 100644 src/shared/loopback-setup.h create mode 100644 src/shared/machine-id-setup.c create mode 100644 src/shared/machine-id-setup.h create mode 100644 src/shared/mount-setup.c create mode 100644 src/shared/mount-setup.h create mode 100644 src/shared/path-lookup.c create mode 100644 src/shared/path-lookup.h create mode 100644 src/shared/spawn-ask-password-agent.c create mode 100644 src/shared/spawn-ask-password-agent.h create mode 100644 src/shared/spawn-polkit-agent.c create mode 100644 src/shared/spawn-polkit-agent.h create mode 100644 src/shared/specifier.c create mode 100644 src/shared/specifier.h create mode 100644 src/shared/umount.c create mode 100644 src/shared/umount.h create mode 100644 src/shared/unit-name.c create mode 100644 src/shared/unit-name.h create mode 100644 src/shared/utmp-wtmp.c create mode 100644 src/shared/utmp-wtmp.h create mode 100644 src/shared/watchdog.c create mode 100644 src/shared/watchdog.h delete mode 100644 src/spawn-ask-password-agent.c delete mode 100644 src/spawn-ask-password-agent.h delete mode 100644 src/spawn-polkit-agent.c delete mode 100644 src/spawn-polkit-agent.h delete mode 100644 src/specifier.c delete mode 100644 src/specifier.h delete mode 100644 src/umount.c delete mode 100644 src/umount.h delete mode 100644 src/unit-name.c delete mode 100644 src/unit-name.h delete mode 100644 src/utmp-wtmp.c delete mode 100644 src/utmp-wtmp.h delete mode 100644 src/watchdog.c delete mode 100644 src/watchdog.h diff --git a/Makefile.am b/Makefile.am index 0a9f2e5198..6601e18d03 100644 --- a/Makefile.am +++ b/Makefile.am @@ -120,7 +120,7 @@ AM_CPPFLAGS = \ -DSYSTEMD_KBD_MODEL_MAP=\"$(pkgdatadir)/kbd-model-map\" \ -DX_SERVER=\"$(bindir)/X\" \ -DUDEVLIBEXECDIR=\""$(libexecdir)/udev"\" \ - -DPOLKIT_AGENT_BINARY_PATH=\"$(bindir)/pkttyagent\" \ + -DPOLKIT_AGENT_BINARY_PATH=\"$(bindir)/pkttyagent\" \ -I $(top_srcdir)/src \ -I $(top_srcdir)/src/shared \ -I $(top_srcdir)/src/readahead \ @@ -458,8 +458,8 @@ dist_doc_DATA = \ README \ NEWS \ LICENSE.LGPL2.1 \ - LICENSE.GPL2 \ - LICENSE.MIT \ + LICENSE.GPL2 \ + LICENSE.MIT \ DISTRO_PORTING pkgconfigdata_DATA = \ @@ -583,14 +583,50 @@ libsystemd_shared_la_SOURCES = \ src/shared/socket-util.h \ src/shared/cgroup-util.c \ src/shared/cgroup-util.h \ + src/shared/dbus-common.c \ + src/shared/dbus-common.h \ + src/shared/hostname-setup.c \ + src/shared/hostname-setup.h \ + src/shared/logs-show.c \ + src/shared/logs-show.h \ + src/shared/cgroup-show.c \ + src/shared/cgroup-show.h \ + src/shared/unit-name.c \ + src/shared/unit-name.h \ + src/shared/utmp-wtmp.c \ + src/shared/utmp-wtmp.h \ + src/shared/watchdog.c \ + src/shared/watchdog.h \ + src/shared/umount.c \ + src/shared/umount.h \ + src/shared/spawn-ask-password-agent.c \ + src/shared/spawn-ask-password-agent.h \ + src/shared/mount-setup.c \ + src/shared/mount-setup.h \ + src/shared/machine-id-setup.c \ + src/shared/machine-id-setup.h \ + src/shared/loopback-setup.h \ + src/shared/loopback-setup.c \ + src/shared/specifier.c \ + src/shared/specifier.h \ + src/shared/spawn-polkit-agent.c \ + src/shared/spawn-polkit-agent.h \ src/shared/list.h \ src/shared/macro.h +libsystemd_shared_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + # ------------------------------------------------------------------------------ noinst_LTLIBRARIES += \ libsystemd-label.la libsystemd_label_la_SOURCES = \ + src/shared/install.c \ + src/shared/install.h \ + src/shared/path-lookup.c \ + src/shared/path-lookup.h \ src/shared/cgroup-label.c \ src/shared/socket-label.c \ src/shared/label.c \ @@ -600,6 +636,7 @@ libsystemd_label_la_SOURCES = \ libsystemd_label_la_CFLAGS = \ $(AM_CFLAGS) \ + $(DBUS_CFLAGS) \ $(SELINUX_CFLAGS) libsystemd_label_la_LIBADD = \ @@ -655,28 +692,6 @@ noinst_LTLIBRARIES += \ libsystemd_core_la_SOURCES = \ src/def.h \ src/missing.h \ - src/dbus-common.c \ - src/dbus-common.h \ - src/watchdog.c \ - src/watchdog.h \ - src/loopback-setup.h \ - src/loopback-setup.c \ - src/hostname-setup.c \ - src/hostname-setup.h \ - src/specifier.c \ - src/specifier.h \ - src/install.c \ - src/install.h \ - src/path-lookup.c \ - src/path-lookup.h \ - src/unit-name.c \ - src/unit-name.h \ - src/utmp-wtmp.c \ - src/utmp-wtmp.h \ - src/machine-id-setup.c \ - src/machine-id-setup.h \ - src/mount-setup.c \ - src/mount-setup.h \ src/linux/auto_dev-ioctl.h \ src/linux/fanotify.h \ src/core/unit.c \ @@ -840,15 +855,13 @@ test_ns_LDADD = \ libsystemd-core.la test_loopback_SOURCES = \ - src/test-loopback.c \ - src/loopback-setup.c + src/test-loopback.c test_loopback_LDADD = \ libsystemd-shared.la test_hostname_SOURCES = \ - src/test-hostname.c \ - src/hostname-setup.c + src/test-hostname.c test_hostname_LDADD = \ libsystemd-shared.la @@ -874,17 +887,13 @@ test_env_replace_LDADD = \ libsystemd-shared.la test_strv_SOURCES = \ - src/test-strv.c \ - src/specifier.c + src/test-strv.c test_strv_LDADD = \ libsystemd-shared.la test_install_SOURCES = \ - src/test-install.c \ - src/install.c \ - src/path-lookup.c \ - src/unit-name.c + src/test-install.c test_install_CFLAGS = \ $(AM_CFLAGS) \ @@ -895,17 +904,14 @@ test_install_LDADD = \ libsystemd-shared.la test_watchdog_SOURCES = \ - src/test-watchdog.c \ - src/watchdog.c \ - src/watchdog.h + src/test-watchdog.c test_watchdog_LDADD = \ libsystemd-shared.la # ------------------------------------------------------------------------------ systemd_initctl_SOURCES = \ - src/initctl.c \ - src/dbus-common.c + src/initctl.c systemd_initctl_CFLAGS = \ $(AM_CFLAGS) \ @@ -918,9 +924,7 @@ systemd_initctl_LDADD = \ # ------------------------------------------------------------------------------ systemd_update_utmp_SOURCES = \ - src/update-utmp.c \ - src/dbus-common.c \ - src/utmp-wtmp.c + src/update-utmp.c systemd_update_utmp_CFLAGS = \ $(AM_CFLAGS) \ @@ -934,7 +938,6 @@ systemd_update_utmp_LDADD = \ # ------------------------------------------------------------------------------ systemd_shutdownd_SOURCES = \ - src/utmp-wtmp.c \ src/shutdownd.c systemd_shutdownd_LDADD = \ @@ -947,12 +950,7 @@ pkginclude_HEADERS += \ # ------------------------------------------------------------------------------ systemd_shutdown_SOURCES = \ - src/mount-setup.c \ - src/umount.c \ - src/umount.h \ - src/shutdown.c \ - src/watchdog.c \ - src/watchdog.h + src/shutdown.c systemd_shutdown_LDADD = \ libsystemd-label.la \ @@ -981,7 +979,6 @@ systemd_tmpfiles_LDADD = \ # ------------------------------------------------------------------------------ systemd_machine_id_setup_SOURCES = \ - src/machine-id-setup.c \ src/machine-id-main.c systemd_machine_id_setup_LDADD = \ @@ -998,8 +995,7 @@ systemd_sysctl_LDADD = \ # ------------------------------------------------------------------------------ systemd_fsck_SOURCES = \ - src/fsck.c \ - src/dbus-common.c + src/fsck.c systemd_fsck_CFLAGS = \ $(AM_CFLAGS) \ @@ -1034,8 +1030,7 @@ systemd_detect_virt_LDADD = \ # ------------------------------------------------------------------------------ systemd_getty_generator_SOURCES = \ - src/getty-generator.c \ - src/unit-name.c + src/getty-generator.c systemd_getty_generator_LDADD = \ libsystemd-label.la \ @@ -1051,16 +1046,14 @@ systemd_rc_local_generator_LDADD = \ # ------------------------------------------------------------------------------ systemd_remount_api_vfs_SOURCES = \ - src/remount-api-vfs.c \ - src/mount-setup.c + src/remount-api-vfs.c systemd_remount_api_vfs_LDADD = \ libsystemd-shared.la # ------------------------------------------------------------------------------ systemd_cgroups_agent_SOURCES = \ - src/cgroups-agent.c \ - src/dbus-common.c + src/cgroups-agent.c systemd_cgroups_agent_CFLAGS = \ $(AM_CFLAGS) \ @@ -1072,20 +1065,7 @@ systemd_cgroups_agent_LDADD = \ # ------------------------------------------------------------------------------ systemctl_SOURCES = \ - src/systemctl.c \ - src/utmp-wtmp.c \ - src/dbus-common.c \ - src/path-lookup.c \ - src/cgroup-show.c \ - src/cgroup-show.h \ - src/unit-name.c \ - src/install.c \ - src/spawn-ask-password-agent.c \ - src/spawn-ask-password-agent.h \ - src/spawn-polkit-agent.c \ - src/spawn-polkit-agent.h \ - src/logs-show.c \ - src/logs-show.h + src/systemctl.c systemctl_CFLAGS = \ $(AM_CFLAGS) \ @@ -1126,9 +1106,7 @@ systemd_reply_password_LDADD = \ # ------------------------------------------------------------------------------ systemd_cgls_SOURCES = \ - src/cgls.c \ - src/cgroup-show.c \ - src/cgroup-show.h + src/cgls.c systemd_cgls_LDADD = \ libsystemd-shared.la @@ -1142,13 +1120,12 @@ systemd_cgtop_LDADD = \ # ------------------------------------------------------------------------------ systemd_nspawn_SOURCES = \ - src/nspawn.c \ - src/loopback-setup.c + src/nspawn.c systemd_nspawn_LDADD = \ libsystemd-label.la \ - libsystemd-shared.la \ libsystemd-capability.la \ + libsystemd-shared.la \ libsystemd-daemon.la # ------------------------------------------------------------------------------ @@ -1161,8 +1138,7 @@ systemd_stdio_bridge_LDADD = \ # ------------------------------------------------------------------------------ systemd_tty_ask_password_agent_SOURCES = \ src/tty-ask-password-agent.c \ - src/ask-password-api.c \ - src/utmp-wtmp.c + src/ask-password-api.c systemd_tty_ask_password_agent_LDADD = \ libsystemd-label.la \ @@ -1978,9 +1954,7 @@ systemd_cat_LDADD = \ libsystemd-journal.la journalctl_SOURCES = \ - src/journal/journalctl.c \ - src/logs-show.c \ - src/logs-show.h + src/journal/journalctl.c journalctl_LDADD = \ libsystemd-shared.la \ @@ -2353,8 +2327,7 @@ systemd_cryptsetup_LDADD = \ $(LIBCRYPTSETUP_LIBS) systemd_cryptsetup_generator_SOURCES = \ - src/cryptsetup/cryptsetup-generator.c \ - src/unit-name.c + src/cryptsetup/cryptsetup-generator.c systemd_cryptsetup_generator_LDADD = \ libsystemd-label.la \ @@ -2375,7 +2348,6 @@ endif if ENABLE_HOSTNAMED systemd_hostnamed_SOURCES = \ src/hostname/hostnamed.c \ - src/dbus-common.c \ src/polkit.c systemd_hostnamed_CFLAGS = \ @@ -2426,7 +2398,6 @@ endif if ENABLE_LOCALED systemd_localed_SOURCES = \ src/locale/localed.c \ - src/dbus-common.c \ src/polkit.c systemd_localed_CFLAGS = \ @@ -2488,7 +2459,6 @@ endif if ENABLE_TIMEDATED systemd_timedated_SOURCES = \ src/timedate/timedated.c \ - src/dbus-common.c \ src/polkit.c systemd_timedated_CFLAGS = \ @@ -2552,7 +2522,6 @@ systemd_logind_SOURCES = \ src/login/logind-session-dbus.c \ src/login/logind-seat-dbus.c \ src/login/logind-user-dbus.c \ - src/dbus-common.c \ src/dbus-loop.c \ src/polkit.c \ src/login/logind-acl.h @@ -2592,12 +2561,7 @@ rootlibexec_PROGRAMS += \ loginctl_SOURCES = \ src/login/loginctl.c \ - src/login/sysfs-show.c \ - src/dbus-common.c \ - src/spawn-polkit-agent.c \ - src/spawn-polkit-agent.h \ - src/cgroup-show.c \ - src/cgroup-show.h + src/login/sysfs-show.c loginctl_CFLAGS = \ $(AM_CFLAGS) \ @@ -2639,8 +2603,7 @@ libsystemd_login_la_LIBADD = \ if HAVE_PAM pam_systemd_la_SOURCES = \ - src/login/pam-module.c \ - src/dbus-common.c + src/login/pam-module.c pam_systemd_la_CFLAGS = \ $(AM_CFLAGS) \ @@ -2714,7 +2677,7 @@ polkitpolicy_in_files += \ logind-install-data-hook: $(MKDIR_P) -m 0755 \ $(DESTDIR)$(systemunitdir)/multi-user.target.wants \ - $(DESTDIR)$(localstatedir)/lib/systemd + $(DESTDIR)$(localstatedir)/lib/systemd ( cd $(DESTDIR)$(systemunitdir) && \ rm -f dbus-org.freedesktop.login1.service && \ $(LN_S) systemd-logind.service dbus-org.freedesktop.login1.service) diff --git a/src/cgroup-show.c b/src/cgroup-show.c deleted file mode 100644 index 550a2f5f31..0000000000 --- a/src/cgroup-show.c +++ /dev/null @@ -1,261 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "util.h" -#include "macro.h" -#include "cgroup-util.h" -#include "cgroup-show.h" - -static int compare(const void *a, const void *b) { - const pid_t *p = a, *q = b; - - if (*p < *q) - return -1; - if (*p > *q) - return 1; - return 0; -} - -static unsigned ilog10(unsigned long ul) { - int n = 0; - - while (ul > 0) { - n++; - ul /= 10; - } - - return n; -} - -static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads) { - char *fn; - FILE *f; - size_t n = 0, n_allocated = 0; - pid_t *pids = NULL; - char *p; - pid_t pid, biggest = 0; - int r; - - if (n_columns <= 0) - n_columns = columns(); - - if (!prefix) - prefix = ""; - - if ((r = cg_fix_path(path, &p)) < 0) - return r; - - r = asprintf(&fn, "%s/cgroup.procs", p); - free(p); - - if (r < 0) - return -ENOMEM; - - f = fopen(fn, "re"); - free(fn); - - if (!f) - return -errno; - - while ((r = cg_read_pid(f, &pid)) > 0) { - - if (!kernel_threads && is_kernel_thread(pid) > 0) - continue; - - if (n >= n_allocated) { - pid_t *npids; - - n_allocated = MAX(16U, n*2U); - - if (!(npids = realloc(pids, sizeof(pid_t) * n_allocated))) { - r = -ENOMEM; - goto finish; - } - - pids = npids; - } - - assert(n < n_allocated); - pids[n++] = pid; - - if (pid > biggest) - biggest = pid; - } - - if (r < 0) - goto finish; - - if (n > 0) { - unsigned i, m; - - /* Filter duplicates */ - m = 0; - for (i = 0; i < n; i++) { - unsigned j; - - for (j = i+1; j < n; j++) - if (pids[i] == pids[j]) - break; - - if (j >= n) - pids[m++] = pids[i]; - } - n = m; - - /* And sort */ - qsort(pids, n, sizeof(pid_t), compare); - - if (n_columns > 8) - n_columns -= 8; - else - n_columns = 20; - - for (i = 0; i < n; i++) { - char *t = NULL; - - get_process_cmdline(pids[i], n_columns, true, &t); - - printf("%s%s %*lu %s\n", - prefix, - (more || i < n-1) ? "\342\224\234" : "\342\224\224", - (int) ilog10(biggest), - (unsigned long) pids[i], - strna(t)); - - free(t); - } - } - - r = 0; - -finish: - free(pids); - - if (f) - fclose(f); - - return r; -} - -int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads) { - DIR *d; - char *last = NULL; - char *p1 = NULL, *p2 = NULL, *fn = NULL, *gn = NULL; - bool shown_pids = false; - int r; - - if (n_columns <= 0) - n_columns = columns(); - - if (!prefix) - prefix = ""; - - if ((r = cg_fix_path(path, &fn)) < 0) - return r; - - if (!(d = opendir(fn))) { - free(fn); - return -errno; - } - - while ((r = cg_read_subgroup(d, &gn)) > 0) { - - if (!shown_pids) { - show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads); - shown_pids = true; - } - - if (last) { - printf("%s\342\224\234 %s\n", prefix, file_name_from_path(last)); - - if (!p1) - if (!(p1 = strappend(prefix, "\342\224\202 "))) { - r = -ENOMEM; - goto finish; - } - - show_cgroup_by_path(last, p1, n_columns-2, kernel_threads); - - free(last); - last = NULL; - } - - r = asprintf(&last, "%s/%s", fn, gn); - free(gn); - - if (r < 0) { - r = -ENOMEM; - goto finish; - } - } - - if (r < 0) - goto finish; - - if (!shown_pids) - show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads); - - if (last) { - printf("%s\342\224\224 %s\n", prefix, file_name_from_path(last)); - - if (!p2) - if (!(p2 = strappend(prefix, " "))) { - r = -ENOMEM; - goto finish; - } - - show_cgroup_by_path(last, p2, n_columns-2, kernel_threads); - } - - r = 0; - -finish: - free(p1); - free(p2); - free(last); - free(fn); - - closedir(d); - - return r; -} - -int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads) { - char *p; - int r; - - assert(controller); - assert(path); - - r = cg_get_path(controller, path, NULL, &p); - if (r < 0) - return r; - - r = show_cgroup_by_path(p, prefix, n_columns, kernel_threads); - free(p); - - return r; -} diff --git a/src/cgroup-show.h b/src/cgroup-show.h deleted file mode 100644 index 5433f46a53..0000000000 --- a/src/cgroup-show.h +++ /dev/null @@ -1,30 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foocgroupshowhfoo -#define foocgroupshowhfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads); -int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, bool kernel_threads); - -#endif diff --git a/src/dbus-common.c b/src/dbus-common.c deleted file mode 100644 index 038fdd41a0..0000000000 --- a/src/dbus-common.c +++ /dev/null @@ -1,1096 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "dbus-common.h" -#include "util.h" -#include "def.h" -#include "strv.h" - -int bus_check_peercred(DBusConnection *c) { - int fd; - struct ucred ucred; - socklen_t l; - - assert(c); - - assert_se(dbus_connection_get_unix_fd(c, &fd)); - - l = sizeof(struct ucred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) { - log_error("SO_PEERCRED failed: %m"); - return -errno; - } - - if (l != sizeof(struct ucred)) { - log_error("SO_PEERCRED returned wrong size."); - return -E2BIG; - } - - if (ucred.uid != 0 && ucred.uid != geteuid()) - return -EPERM; - - return 1; -} - -static int sync_auth(DBusConnection *bus, DBusError *error) { - usec_t begin, tstamp; - - assert(bus); - - /* This complexity should probably move into D-Bus itself: - * - * https://bugs.freedesktop.org/show_bug.cgi?id=35189 */ - - begin = tstamp = now(CLOCK_MONOTONIC); - for (;;) { - - if (tstamp > begin + DEFAULT_TIMEOUT_USEC) - break; - - if (dbus_connection_get_is_authenticated(bus)) - break; - - if (!dbus_connection_read_write_dispatch(bus, ((begin + DEFAULT_TIMEOUT_USEC - tstamp) + USEC_PER_MSEC - 1) / USEC_PER_MSEC)) - break; - - tstamp = now(CLOCK_MONOTONIC); - } - - if (!dbus_connection_get_is_connected(bus)) { - dbus_set_error_const(error, DBUS_ERROR_NO_SERVER, "Connection terminated during authentication."); - return -ECONNREFUSED; - } - - if (!dbus_connection_get_is_authenticated(bus)) { - dbus_set_error_const(error, DBUS_ERROR_TIMEOUT, "Failed to authenticate in time."); - return -EACCES; - } - - return 0; -} - -int bus_connect(DBusBusType t, DBusConnection **_bus, bool *_private, DBusError *error) { - DBusConnection *bus = NULL; - int r; - bool private = true; - - assert(_bus); - - if (geteuid() == 0 && t == DBUS_BUS_SYSTEM) { - /* If we are root, then let's talk directly to the - * system instance, instead of going via the bus */ - - bus = dbus_connection_open_private("unix:path=/run/systemd/private", error); - if (!bus) - return -EIO; - - } else { - if (t == DBUS_BUS_SESSION) { - const char *e; - - /* If we are supposed to talk to the instance, - * try via XDG_RUNTIME_DIR first, then - * fallback to normal bus access */ - - e = getenv("XDG_RUNTIME_DIR"); - if (e) { - char *p; - - if (asprintf(&p, "unix:path=%s/systemd/private", e) < 0) - return -ENOMEM; - - bus = dbus_connection_open_private(p, NULL); - free(p); - } - } - - if (!bus) { - bus = dbus_bus_get_private(t, error); - if (!bus) - return -EIO; - - private = false; - } - } - - dbus_connection_set_exit_on_disconnect(bus, FALSE); - - if (private) { - if (bus_check_peercred(bus) < 0) { - dbus_connection_close(bus); - dbus_connection_unref(bus); - - dbus_set_error_const(error, DBUS_ERROR_ACCESS_DENIED, "Failed to verify owner of bus."); - return -EACCES; - } - } - - r = sync_auth(bus, error); - if (r < 0) { - dbus_connection_close(bus); - dbus_connection_unref(bus); - return r; - } - - if (_private) - *_private = private; - - *_bus = bus; - return 0; -} - -int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **_bus, DBusError *error) { - DBusConnection *bus; - char *p = NULL; - int r; - - assert(_bus); - assert(user || host); - - if (user && host) - asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s@%s,argv3=systemd-stdio-bridge", user, host); - else if (user) - asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s@localhost,argv3=systemd-stdio-bridge", user); - else if (host) - asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s,argv3=systemd-stdio-bridge", host); - - if (!p) { - dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); - return -ENOMEM; - } - - bus = dbus_connection_open_private(p, error); - free(p); - - if (!bus) - return -EIO; - - dbus_connection_set_exit_on_disconnect(bus, FALSE); - - if ((r = sync_auth(bus, error)) < 0) { - dbus_connection_close(bus); - dbus_connection_unref(bus); - return r; - } - - if (!dbus_bus_register(bus, error)) { - dbus_connection_close(bus); - dbus_connection_unref(bus); - return r; - } - - *_bus = bus; - return 0; -} - -int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error) { - DBusConnection *bus; - int r; - - assert(_bus); - - /* Don't bother with PolicyKit if we are root */ - if (geteuid() == 0) - return bus_connect(DBUS_BUS_SYSTEM, _bus, NULL, error); - - bus = dbus_connection_open_private("unixexec:path=pkexec,argv1=" SYSTEMD_STDIO_BRIDGE_BINARY_PATH, error); - if (!bus) - return -EIO; - - dbus_connection_set_exit_on_disconnect(bus, FALSE); - - if ((r = sync_auth(bus, error)) < 0) { - dbus_connection_close(bus); - dbus_connection_unref(bus); - return r; - } - - if (!dbus_bus_register(bus, error)) { - dbus_connection_close(bus); - dbus_connection_unref(bus); - return r; - } - - *_bus = bus; - return 0; -} - -const char *bus_error_message(const DBusError *error) { - assert(error); - - /* Sometimes the D-Bus server is a little bit too verbose with - * its error messages, so let's override them here */ - if (dbus_error_has_name(error, DBUS_ERROR_ACCESS_DENIED)) - return "Access denied"; - - return error->message; -} - -DBusHandlerResult bus_default_message_handler( - DBusConnection *c, - DBusMessage *message, - const char *introspection, - const char *interfaces, - const BusBoundProperties *bound_properties) { - - DBusError error; - DBusMessage *reply = NULL; - int r; - - assert(c); - assert(message); - - dbus_error_init(&error); - - if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") && introspection) { - - if (!(reply = dbus_message_new_method_return(message))) - goto oom; - - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) - goto oom; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Get") && bound_properties) { - const char *interface, *property; - const BusBoundProperties *bp; - const BusProperty *p; - void *data; - DBusMessageIter iter, sub; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(c, message, &error, -EINVAL); - - for (bp = bound_properties; bp->interface; bp++) { - if (!streq(bp->interface, interface)) - continue; - - for (p = bp->properties; p->property; p++) - if (streq(p->property, property)) - goto get_prop; - } - - /* no match */ - if (!nulstr_contains(interfaces, interface)) - dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); - else - dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); - - return bus_send_error_reply(c, message, &error, -EINVAL); - -get_prop: - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - - dbus_message_iter_init_append(reply, &iter); - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, p->signature, &sub)) - goto oom; - - data = (char*)bp->base + p->offset; - if (p->indirect) - data = *(void**)data; - r = p->append(&sub, property, data); - if (r < 0) { - if (r == -ENOMEM) - goto oom; - - dbus_message_unref(reply); - return bus_send_error_reply(c, message, NULL, r); - } - - if (!dbus_message_iter_close_container(&iter, &sub)) - goto oom; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll") && bound_properties) { - const char *interface; - const BusBoundProperties *bp; - const BusProperty *p; - DBusMessageIter iter, sub, sub2, sub3; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(c, message, &error, -EINVAL); - - if (interface[0] && !nulstr_contains(interfaces, interface)) { - dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); - return bus_send_error_reply(c, message, &error, -EINVAL); - } - - if (!(reply = dbus_message_new_method_return(message))) - goto oom; - - dbus_message_iter_init_append(reply, &iter); - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub)) - goto oom; - - for (bp = bound_properties; bp->interface; bp++) { - if (interface[0] && !streq(bp->interface, interface)) - continue; - - for (p = bp->properties; p->property; p++) { - void *data; - - if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, NULL, &sub2) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &p->property) || - !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, p->signature, &sub3)) - goto oom; - - data = (char*)bp->base + p->offset; - if (p->indirect) - data = *(void**)data; - r = p->append(&sub3, p->property, data); - if (r < 0) { - if (r == -ENOMEM) - goto oom; - - dbus_message_unref(reply); - return bus_send_error_reply(c, message, NULL, r); - } - - if (!dbus_message_iter_close_container(&sub2, &sub3) || - !dbus_message_iter_close_container(&sub, &sub2)) - goto oom; - } - } - - if (!dbus_message_iter_close_container(&iter, &sub)) - goto oom; - - } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Set") && bound_properties) { - const char *interface, *property; - DBusMessageIter iter; - const BusBoundProperties *bp; - const BusProperty *p; - DBusMessageIter sub; - char *sig; - void *data; - - if (!dbus_message_iter_init(message, &iter) || - dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) - return bus_send_error_reply(c, message, NULL, -EINVAL); - - dbus_message_iter_get_basic(&iter, &interface); - - if (!dbus_message_iter_next(&iter) || - dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) - return bus_send_error_reply(c, message, NULL, -EINVAL); - - dbus_message_iter_get_basic(&iter, &property); - - if (!dbus_message_iter_next(&iter) || - dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT || - dbus_message_iter_has_next(&iter)) - return bus_send_error_reply(c, message, NULL, -EINVAL); - - for (bp = bound_properties; bp->interface; bp++) { - if (!streq(bp->interface, interface)) - continue; - - for (p = bp->properties; p->property; p++) - if (streq(p->property, property)) - goto set_prop; - } - - /* no match */ - if (!nulstr_contains(interfaces, interface)) - dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); - else - dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); - - return bus_send_error_reply(c, message, &error, -EINVAL); - -set_prop: - if (!p->set) { - dbus_set_error_const(&error, DBUS_ERROR_PROPERTY_READ_ONLY, "Property read-only"); - return bus_send_error_reply(c, message, &error, -EINVAL); - } - - dbus_message_iter_recurse(&iter, &sub); - - sig = dbus_message_iter_get_signature(&sub); - if (!sig) - goto oom; - - if (!streq(sig, p->signature)) { - dbus_free(sig); - return bus_send_error_reply(c, message, NULL, -EINVAL); - } - - dbus_free(sig); - - data = (char*)bp->base + p->offset; - if (p->indirect) - data = *(void**)data; - r = p->set(&sub, property, data); - if (r < 0) { - if (r == -ENOMEM) - goto oom; - return bus_send_error_reply(c, message, NULL, r); - } - - reply = dbus_message_new_method_return(message); - if (!reply) - goto oom; - } else { - const char *interface = dbus_message_get_interface(message); - - if (!interface || !nulstr_contains(interfaces, interface)) { - dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); - return bus_send_error_reply(c, message, &error, -EINVAL); - } - } - - if (reply) { - if (!dbus_connection_send(c, reply, NULL)) - goto oom; - - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; - } - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - -oom: - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return DBUS_HANDLER_RESULT_NEED_MEMORY; -} - -int bus_property_append_string(DBusMessageIter *i, const char *property, void *data) { - const char *t = data; - - assert(i); - assert(property); - - if (!t) - t = ""; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t)) - return -ENOMEM; - - return 0; -} - -int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data) { - char **t = data; - - assert(i); - assert(property); - - return bus_append_strv_iter(i, t); -} - -int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data) { - bool *b = data; - dbus_bool_t db; - - assert(i); - assert(property); - assert(b); - - db = *b; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db)) - return -ENOMEM; - - return 0; -} - -int bus_property_append_tristate_false(DBusMessageIter *i, const char *property, void *data) { - int *b = data; - dbus_bool_t db; - - assert(i); - assert(property); - assert(b); - - db = *b > 0; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db)) - return -ENOMEM; - - return 0; -} - -int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data) { - assert(i); - assert(property); - assert(data); - - /* Let's ensure that usec_t is actually 64bit, and hence this - * function can be used for usec_t */ - assert_cc(sizeof(uint64_t) == sizeof(usec_t)); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, data)) - return -ENOMEM; - - return 0; -} - -int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *data) { - assert(i); - assert(property); - assert(data); - - /* Let's ensure that pid_t, mode_t, uid_t, gid_t are actually - * 32bit, and hence this function can be used for - * pid_t/mode_t/uid_t/gid_t */ - assert_cc(sizeof(uint32_t) == sizeof(pid_t)); - assert_cc(sizeof(uint32_t) == sizeof(mode_t)); - assert_cc(sizeof(uint32_t) == sizeof(unsigned)); - assert_cc(sizeof(uint32_t) == sizeof(uid_t)); - assert_cc(sizeof(uint32_t) == sizeof(gid_t)); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, data)) - return -ENOMEM; - - return 0; -} - -int bus_property_append_int32(DBusMessageIter *i, const char *property, void *data) { - assert(i); - assert(property); - assert(data); - - assert_cc(sizeof(int32_t) == sizeof(int)); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, data)) - return -ENOMEM; - - return 0; -} - -int bus_property_append_size(DBusMessageIter *i, const char *property, void *data) { - uint64_t u; - - assert(i); - assert(property); - assert(data); - - u = (uint64_t) *(size_t*) data; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u)) - return -ENOMEM; - - return 0; -} - -int bus_property_append_ul(DBusMessageIter *i, const char *property, void *data) { - uint64_t u; - - assert(i); - assert(property); - assert(data); - - u = (uint64_t) *(unsigned long*) data; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u)) - return -ENOMEM; - - return 0; -} - -int bus_property_append_long(DBusMessageIter *i, const char *property, void *data) { - int64_t l; - - assert(i); - assert(property); - assert(data); - - l = (int64_t) *(long*) data; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT64, &l)) - return -ENOMEM; - - return 0; -} - -const char *bus_errno_to_dbus(int error) { - - switch(error) { - - case -EINVAL: - return DBUS_ERROR_INVALID_ARGS; - - case -ENOMEM: - return DBUS_ERROR_NO_MEMORY; - - case -EPERM: - case -EACCES: - return DBUS_ERROR_ACCESS_DENIED; - - case -ESRCH: - return DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN; - - case -ENOENT: - return DBUS_ERROR_FILE_NOT_FOUND; - - case -EEXIST: - return DBUS_ERROR_FILE_EXISTS; - - case -ETIMEDOUT: - case -ETIME: - return DBUS_ERROR_TIMEOUT; - - case -EIO: - return DBUS_ERROR_IO_ERROR; - - case -ENETRESET: - case -ECONNABORTED: - case -ECONNRESET: - return DBUS_ERROR_DISCONNECTED; - } - - return DBUS_ERROR_FAILED; -} - -DBusHandlerResult bus_send_error_reply(DBusConnection *c, DBusMessage *message, DBusError *berror, int error) { - DBusMessage *reply = NULL; - const char *name, *text; - - if (berror && dbus_error_is_set(berror)) { - name = berror->name; - text = berror->message; - } else { - name = bus_errno_to_dbus(error); - text = strerror(-error); - } - - if (!(reply = dbus_message_new_error(message, name, text))) - goto oom; - - if (!dbus_connection_send(c, reply, NULL)) - goto oom; - - dbus_message_unref(reply); - - if (berror) - dbus_error_free(berror); - - return DBUS_HANDLER_RESULT_HANDLED; - -oom: - if (reply) - dbus_message_unref(reply); - - if (berror) - dbus_error_free(berror); - - return DBUS_HANDLER_RESULT_NEED_MEMORY; -} - -DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties) { - DBusMessage *m; - DBusMessageIter iter, sub; - const char *i; - - assert(interface); - assert(properties); - - if (!(m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged"))) - goto oom; - - dbus_message_iter_init_append(m, &iter); - - /* We won't send any property values, since they might be - * large and sometimes not cheap to generated */ - - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) || - !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) || - !dbus_message_iter_close_container(&iter, &sub) || - !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) - goto oom; - - NULSTR_FOREACH(i, properties) - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &i)) - goto oom; - - if (!dbus_message_iter_close_container(&iter, &sub)) - goto oom; - - return m; - -oom: - if (m) - dbus_message_unref(m); - - return NULL; -} - -uint32_t bus_flags_to_events(DBusWatch *bus_watch) { - unsigned flags; - uint32_t events = 0; - - assert(bus_watch); - - /* no watch flags for disabled watches */ - if (!dbus_watch_get_enabled(bus_watch)) - return 0; - - flags = dbus_watch_get_flags(bus_watch); - - if (flags & DBUS_WATCH_READABLE) - events |= EPOLLIN; - if (flags & DBUS_WATCH_WRITABLE) - events |= EPOLLOUT; - - return events | EPOLLHUP | EPOLLERR; -} - -unsigned bus_events_to_flags(uint32_t events) { - unsigned flags = 0; - - if (events & EPOLLIN) - flags |= DBUS_WATCH_READABLE; - if (events & EPOLLOUT) - flags |= DBUS_WATCH_WRITABLE; - if (events & EPOLLHUP) - flags |= DBUS_WATCH_HANGUP; - if (events & EPOLLERR) - flags |= DBUS_WATCH_ERROR; - - return flags; -} - -int bus_parse_strv(DBusMessage *m, char ***_l) { - DBusMessageIter iter; - - assert(m); - assert(_l); - - if (!dbus_message_iter_init(m, &iter)) - return -EINVAL; - - return bus_parse_strv_iter(&iter, _l); -} - -int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l) { - DBusMessageIter sub; - unsigned n = 0, i = 0; - char **l; - - assert(iter); - assert(_l); - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRING) - return -EINVAL; - - dbus_message_iter_recurse(iter, &sub); - - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - n++; - dbus_message_iter_next(&sub); - } - - if (!(l = new(char*, n+1))) - return -ENOMEM; - - dbus_message_iter_recurse(iter, &sub); - - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *s; - - assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING); - dbus_message_iter_get_basic(&sub, &s); - - if (!(l[i++] = strdup(s))) { - strv_free(l); - return -ENOMEM; - } - - dbus_message_iter_next(&sub); - } - - assert(i == n); - l[i] = NULL; - - if (_l) - *_l = l; - - return 0; -} - -int bus_append_strv_iter(DBusMessageIter *iter, char **l) { - DBusMessageIter sub; - - assert(iter); - - if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &sub)) - return -ENOMEM; - - STRV_FOREACH(l, l) - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, l)) - return -ENOMEM; - - if (!dbus_message_iter_close_container(iter, &sub)) - return -ENOMEM; - - return 0; -} - -int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) { - - assert(iter); - assert(data); - - if (dbus_message_iter_get_arg_type(iter) != type) - return -EIO; - - dbus_message_iter_get_basic(iter, data); - - if (!dbus_message_iter_next(iter) != !next) - return -EIO; - - return 0; -} - -int generic_print_property(const char *name, DBusMessageIter *iter, bool all) { - assert(name); - assert(iter); - - switch (dbus_message_iter_get_arg_type(iter)) { - - case DBUS_TYPE_STRING: { - const char *s; - dbus_message_iter_get_basic(iter, &s); - - if (all || !isempty(s)) - printf("%s=%s\n", name, s); - - return 1; - } - - case DBUS_TYPE_BOOLEAN: { - dbus_bool_t b; - - dbus_message_iter_get_basic(iter, &b); - printf("%s=%s\n", name, yes_no(b)); - - return 1; - } - - case DBUS_TYPE_UINT64: { - uint64_t u; - dbus_message_iter_get_basic(iter, &u); - - /* Yes, heuristics! But we can change this check - * should it turn out to not be sufficient */ - - if (endswith(name, "Timestamp")) { - char timestamp[FORMAT_TIMESTAMP_MAX], *t; - - t = format_timestamp(timestamp, sizeof(timestamp), u); - if (t || all) - printf("%s=%s\n", name, strempty(t)); - - } else if (strstr(name, "USec")) { - char timespan[FORMAT_TIMESPAN_MAX]; - - printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u)); - } else - printf("%s=%llu\n", name, (unsigned long long) u); - - return 1; - } - - case DBUS_TYPE_UINT32: { - uint32_t u; - dbus_message_iter_get_basic(iter, &u); - - if (strstr(name, "UMask") || strstr(name, "Mode")) - printf("%s=%04o\n", name, u); - else - printf("%s=%u\n", name, (unsigned) u); - - return 1; - } - - case DBUS_TYPE_INT32: { - int32_t i; - dbus_message_iter_get_basic(iter, &i); - - printf("%s=%i\n", name, (int) i); - return 1; - } - - case DBUS_TYPE_DOUBLE: { - double d; - dbus_message_iter_get_basic(iter, &d); - - printf("%s=%g\n", name, d); - return 1; - } - - case DBUS_TYPE_ARRAY: - - if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) { - DBusMessageIter sub; - bool space = false; - - dbus_message_iter_recurse(iter, &sub); - if (all || - dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - printf("%s=", name); - - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *s; - - assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING); - dbus_message_iter_get_basic(&sub, &s); - printf("%s%s", space ? " " : "", s); - - space = true; - dbus_message_iter_next(&sub); - } - - puts(""); - } - - return 1; - - } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) { - DBusMessageIter sub; - - dbus_message_iter_recurse(iter, &sub); - if (all || - dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - printf("%s=", name); - - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - uint8_t u; - - assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE); - dbus_message_iter_get_basic(&sub, &u); - printf("%02x", u); - - dbus_message_iter_next(&sub); - } - - puts(""); - } - - return 1; - } - - break; - } - - return 0; -} - -static void release_name_pending_cb(DBusPendingCall *pending, void *userdata) { - DBusMessage *reply; - DBusConnection *bus = userdata; - - assert_se(reply = dbus_pending_call_steal_reply(pending)); - dbus_message_unref(reply); - - dbus_connection_close(bus); -} - -void bus_async_unregister_and_exit(DBusConnection *bus, const char *name) { - DBusMessage *m = NULL; - DBusPendingCall *pending = NULL; - - assert(bus); - - /* We unregister the name here, but we continue to process - * requests, until we get the response for it, so that all - * requests are guaranteed to be processed. */ - - m = dbus_message_new_method_call( - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - "ReleaseName"); - if (!m) - goto oom; - - if (!dbus_message_append_args( - m, - DBUS_TYPE_STRING, - &name, - DBUS_TYPE_INVALID)) - goto oom; - - if (!dbus_connection_send_with_reply(bus, m, &pending, -1)) - goto oom; - - if (!dbus_pending_call_set_notify(pending, release_name_pending_cb, bus, NULL)) - goto oom; - - dbus_message_unref(m); - dbus_pending_call_unref(pending); - - return; - -oom: - log_error("Out of memory"); - - if (pending) { - dbus_pending_call_cancel(pending); - dbus_pending_call_unref(pending); - } - - if (m) - dbus_message_unref(m); -} - -DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata) { - usec_t *remain_until = userdata; - - assert(bus); - assert(m); - assert(remain_until); - - /* Everytime we get a new message we reset out timeout */ - *remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC; - - if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) - dbus_connection_close(bus); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} diff --git a/src/dbus-common.h b/src/dbus-common.h deleted file mode 100644 index 38d8e6538c..0000000000 --- a/src/dbus-common.h +++ /dev/null @@ -1,198 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foodbuscommonhfoo -#define foodbuscommonhfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#ifndef DBUS_ERROR_UNKNOWN_OBJECT -#define DBUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject" -#endif - -#ifndef DBUS_ERROR_UNKNOWN_INTERFACE -#define DBUS_ERROR_UNKNOWN_INTERFACE "org.freedesktop.DBus.Error.UnknownInterface" -#endif - -#ifndef DBUS_ERROR_UNKNOWN_PROPERTY -#define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty" -#endif - -#ifndef DBUS_ERROR_PROPERTY_READ_ONLY -#define DBUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly" -#endif - -#define BUS_PROPERTIES_INTERFACE \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - -#define BUS_INTROSPECTABLE_INTERFACE \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - -#define BUS_PEER_INTERFACE \ - "\n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - "\n" - -#define BUS_GENERIC_INTERFACES_LIST \ - "org.freedesktop.DBus.Properties\0" \ - "org.freedesktop.DBus.Introspectable\0" \ - "org.freedesktop.DBus.Peer\0" - -int bus_check_peercred(DBusConnection *c); - -int bus_connect(DBusBusType t, DBusConnection **_bus, bool *private_bus, DBusError *error); - -int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **_bus, DBusError *error); -int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error); - -const char *bus_error_message(const DBusError *error); - -typedef int (*BusPropertyCallback)(DBusMessageIter *iter, const char *property, void *data); -typedef int (*BusPropertySetCallback)(DBusMessageIter *iter, const char *property, void *data); - -typedef struct BusProperty { - const char *property; /* name of the property */ - BusPropertyCallback append; /* Function that is called to serialize this property */ - const char *signature; - const uint16_t offset; /* Offset from BusBoundProperties::base address to the property data. - * uint16_t is sufficient, because we have no structs too big. - * -Werror=overflow will catch it if this does not hold. */ - bool indirect; /* data is indirect, ie. not base+offset, but *(base+offset) */ - BusPropertySetCallback set; /* Optional: Function that is called to set this property */ -} BusProperty; - -typedef struct BusBoundProperties { - const char *interface; /* interface of the properties */ - const BusProperty *properties; /* array of properties, ended by a NULL-filled element */ - const void *const base; /* base pointer to which the offset must be added to reach data */ -} BusBoundProperties; - -DBusHandlerResult bus_send_error_reply( - DBusConnection *c, - DBusMessage *message, - DBusError *bus_error, - int error); - -DBusHandlerResult bus_default_message_handler( - DBusConnection *c, - DBusMessage *message, - const char *introspection, - const char *interfaces, - const BusBoundProperties *bound_properties); - -int bus_property_append_string(DBusMessageIter *i, const char *property, void *data); -int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data); -int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data); -int bus_property_append_tristate_false(DBusMessageIter *i, const char *property, void *data); -int bus_property_append_int32(DBusMessageIter *i, const char *property, void *data); -int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *data); -int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data); -int bus_property_append_size(DBusMessageIter *i, const char *property, void *data); -int bus_property_append_ul(DBusMessageIter *i, const char *property, void *data); -int bus_property_append_long(DBusMessageIter *i, const char *property, void *data); - -#define bus_property_append_int bus_property_append_int32 -#define bus_property_append_pid bus_property_append_uint32 -#define bus_property_append_uid bus_property_append_uint32 -#define bus_property_append_gid bus_property_append_uint32 -#define bus_property_append_mode bus_property_append_uint32 -#define bus_property_append_unsigned bus_property_append_uint32 -#define bus_property_append_usec bus_property_append_uint64 - -#define DEFINE_BUS_PROPERTY_APPEND_ENUM(function,name,type) \ - int function(DBusMessageIter *i, const char *property, void *data) { \ - const char *value; \ - type *field = data; \ - \ - assert(i); \ - assert(property); \ - \ - value = name##_to_string(*field); \ - \ - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &value)) \ - return -ENOMEM; \ - \ - return 0; \ - } - -#define DEFINE_BUS_PROPERTY_SET_ENUM(function,name,type) \ - int function(DBusMessageIter *i, const char *property, void *data) { \ - const char *value; \ - type *field = data; \ - \ - assert(i); \ - assert(property); \ - \ - dbus_message_iter_get_basic(i, &value); \ - \ - *field = name##_from_string(value); \ - \ - return 0; \ - } - -const char *bus_errno_to_dbus(int error); - -DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties); - -uint32_t bus_flags_to_events(DBusWatch *bus_watch); -unsigned bus_events_to_flags(uint32_t events); - -int bus_parse_strv(DBusMessage *m, char ***_l); -int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l); - -int bus_append_strv_iter(DBusMessageIter *iter, char **l); - -int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next); - -int generic_print_property(const char *name, DBusMessageIter *iter, bool all); - -void bus_async_unregister_and_exit(DBusConnection *bus, const char *name); - -DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata); - -#endif diff --git a/src/hostname-setup.c b/src/hostname-setup.c deleted file mode 100644 index 550d3c2113..0000000000 --- a/src/hostname-setup.c +++ /dev/null @@ -1,187 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "hostname-setup.h" -#include "macro.h" -#include "util.h" -#include "log.h" - -#if defined(TARGET_FEDORA) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MEEGO) || defined(TARGET_MAGEIA) -#define FILENAME "/etc/sysconfig/network" -#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE) -#define FILENAME "/etc/HOSTNAME" -#elif defined(TARGET_ARCH) -#define FILENAME "/etc/rc.conf" -#elif defined(TARGET_GENTOO) -#define FILENAME "/etc/conf.d/hostname" -#endif - -static int read_and_strip_hostname(const char *path, char **hn) { - char *s; - int r; - - assert(path); - assert(hn); - - if ((r = read_one_line_file(path, &s)) < 0) - return r; - - hostname_cleanup(s); - - if (isempty(s)) { - free(s); - return -ENOENT; - } - - *hn = s; - - return 0; -} - -static int read_distro_hostname(char **hn) { - -#if defined(TARGET_FEDORA) || defined(TARGET_ARCH) || defined(TARGET_GENTOO) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MEEGO) || defined(TARGET_MAGEIA) - int r; - FILE *f; - - assert(hn); - - if (!(f = fopen(FILENAME, "re"))) - return -errno; - - for (;;) { - char line[LINE_MAX]; - char *s, *k; - - if (!fgets(line, sizeof(line), f)) { - if (feof(f)) - break; - - r = -errno; - goto finish; - } - - s = strstrip(line); - - if (!startswith_no_case(s, "HOSTNAME=")) - continue; - - if (!(k = strdup(s+9))) { - r = -ENOMEM; - goto finish; - } - - hostname_cleanup(k); - - if (isempty(k)) { - free(k); - r = -ENOENT; - goto finish; - } - - *hn = k; - r = 0; - goto finish; - } - - r = -ENOENT; - -finish: - fclose(f); - return r; - -#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE) - return read_and_strip_hostname(FILENAME, hn); -#else - return -ENOENT; -#endif -} - -static int read_hostname(char **hn) { - int r; - - assert(hn); - - /* First, try to load the generic hostname configuration file, - * that we support on all distributions */ - - if ((r = read_and_strip_hostname("/etc/hostname", hn)) < 0) { - - if (r == -ENOENT) - return read_distro_hostname(hn); - - return r; - } - - return 0; -} - -int hostname_setup(void) { - int r; - char *b = NULL; - const char *hn = NULL; - - if ((r = read_hostname(&b)) < 0) { - if (r == -ENOENT) - log_info("No hostname configured."); - else - log_warning("Failed to read configured hostname: %s", strerror(-r)); - - hn = NULL; - } else - hn = b; - - if (!hn) { - /* Don't override the hostname if it is unset and not - * explicitly configured */ - - char *old_hostname = NULL; - - if ((old_hostname = gethostname_malloc())) { - bool already_set; - - already_set = old_hostname[0] != 0; - free(old_hostname); - - if (already_set) - goto finish; - } - - hn = "localhost"; - } - - if (sethostname(hn, strlen(hn)) < 0) { - log_warning("Failed to set hostname to <%s>: %m", hn); - r = -errno; - } else - log_info("Set hostname to <%s>.", hn); - -finish: - free(b); - - return r; -} diff --git a/src/hostname-setup.h b/src/hostname-setup.h deleted file mode 100644 index 9550b8c5ab..0000000000 --- a/src/hostname-setup.h +++ /dev/null @@ -1,27 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foohostnamesetuphfoo -#define foohostnamesetuphfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int hostname_setup(void); - -#endif diff --git a/src/install.c b/src/install.c deleted file mode 100644 index 080ae6a01d..0000000000 --- a/src/install.c +++ /dev/null @@ -1,1954 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty . -***/ - -#include -#include -#include -#include -#include - -#include "util.h" -#include "mkdir.h" -#include "hashmap.h" -#include "set.h" -#include "path-lookup.h" -#include "strv.h" -#include "unit-name.h" -#include "install.h" -#include "conf-parser.h" - -typedef struct { - char *name; - char *path; - - char **aliases; - char **wanted_by; -} InstallInfo; - -typedef struct { - Hashmap *will_install; - Hashmap *have_installed; -} InstallContext; - -static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope) { - assert(paths); - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - zero(*paths); - - return lookup_paths_init(paths, - scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER, - scope == UNIT_FILE_USER); -} - -static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) { - char *p = NULL; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(ret); - - switch (scope) { - - case UNIT_FILE_SYSTEM: - - if (root_dir && runtime) - asprintf(&p, "%s/run/systemd/system", root_dir); - else if (runtime) - p = strdup("/run/systemd/system"); - else if (root_dir) - asprintf(&p, "%s/%s", root_dir, SYSTEM_CONFIG_UNIT_PATH); - else - p = strdup(SYSTEM_CONFIG_UNIT_PATH); - - break; - - case UNIT_FILE_GLOBAL: - - if (root_dir) - return -EINVAL; - - if (runtime) - p = strdup("/run/systemd/user"); - else - p = strdup(USER_CONFIG_UNIT_PATH); - break; - - case UNIT_FILE_USER: - - if (root_dir || runtime) - return -EINVAL; - - r = user_config_home(&p); - if (r <= 0) - return r < 0 ? r : -ENOENT; - - break; - - default: - assert_not_reached("Bad scope"); - } - - if (!p) - return -ENOMEM; - - *ret = p; - return 0; -} - -static int add_file_change( - UnitFileChange **changes, - unsigned *n_changes, - UnitFileChangeType type, - const char *path, - const char *source) { - - UnitFileChange *c; - unsigned i; - - assert(path); - assert(!changes == !n_changes); - - if (!changes) - return 0; - - c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange)); - if (!c) - return -ENOMEM; - - *changes = c; - i = *n_changes; - - c[i].type = type; - c[i].path = strdup(path); - if (!c[i].path) - return -ENOMEM; - - if (source) { - c[i].source = strdup(source); - if (!c[i].source) { - free(c[i].path); - return -ENOMEM; - } - } else - c[i].source = NULL; - - *n_changes = i+1; - return 0; -} - -static int mark_symlink_for_removal( - Set **remove_symlinks_to, - const char *p) { - - char *n; - int r; - - assert(p); - - r = set_ensure_allocated(remove_symlinks_to, string_hash_func, string_compare_func); - if (r < 0) - return r; - - n = strdup(p); - if (!n) - return -ENOMEM; - - path_kill_slashes(n); - - r = set_put(*remove_symlinks_to, n); - if (r < 0) { - free(n); - return r == -EEXIST ? 0 : r; - } - - return 0; -} - -static int remove_marked_symlinks_fd( - Set *remove_symlinks_to, - int fd, - const char *path, - const char *config_path, - bool *deleted, - UnitFileChange **changes, - unsigned *n_changes) { - - int r = 0; - DIR *d; - struct dirent buffer, *de; - - assert(remove_symlinks_to); - assert(fd >= 0); - assert(path); - assert(config_path); - assert(deleted); - - d = fdopendir(fd); - if (!d) { - close_nointr_nofail(fd); - return -errno; - } - - rewinddir(d); - - for (;;) { - int k; - - k = readdir_r(d, &buffer, &de); - if (k != 0) { - r = -errno; - break; - } - - if (!de) - break; - - if (ignore_file(de->d_name)) - continue; - - dirent_ensure_type(d, de); - - if (de->d_type == DT_DIR) { - int nfd, q; - char *p; - - nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); - if (nfd < 0) { - if (errno == ENOENT) - continue; - - if (r == 0) - r = -errno; - continue; - } - - p = path_make_absolute(de->d_name, path); - if (!p) { - close_nointr_nofail(nfd); - r = -ENOMEM; - break; - } - - /* This will close nfd, regardless whether it succeeds or not */ - q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes); - free(p); - - if (r == 0) - r = q; - - } else if (de->d_type == DT_LNK) { - char *p, *dest; - int q; - bool found; - - p = path_make_absolute(de->d_name, path); - if (!p) { - r = -ENOMEM; - break; - } - - q = readlink_and_canonicalize(p, &dest); - if (q < 0) { - free(p); - - if (q == -ENOENT) - continue; - - if (r == 0) - r = q; - continue; - } - - found = - set_get(remove_symlinks_to, dest) || - set_get(remove_symlinks_to, file_name_from_path(dest)); - - if (found) { - - if (unlink(p) < 0 && errno != ENOENT) { - - if (r == 0) - r = -errno; - } else { - rmdir_parents(p, config_path); - path_kill_slashes(p); - - add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); - - if (!set_get(remove_symlinks_to, p)) { - - q = mark_symlink_for_removal(&remove_symlinks_to, p); - if (q < 0) { - if (r == 0) - r = q; - } else - *deleted = true; - } - } - } - - free(p); - free(dest); - } - } - - closedir(d); - - return r; -} - -static int remove_marked_symlinks( - Set *remove_symlinks_to, - const char *config_path, - UnitFileChange **changes, - unsigned *n_changes) { - - int fd, r = 0; - bool deleted; - - assert(config_path); - - if (set_size(remove_symlinks_to) <= 0) - return 0; - - fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); - if (fd < 0) - return -errno; - - do { - int q, cfd; - deleted = false; - - cfd = dup(fd); - if (cfd < 0) { - r = -errno; - break; - } - - /* This takes possession of cfd and closes it */ - q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes); - if (r == 0) - r = q; - } while (deleted); - - close_nointr_nofail(fd); - - return r; -} - -static int find_symlinks_fd( - const char *name, - int fd, - const char *path, - const char *config_path, - bool *same_name_link) { - - int r = 0; - DIR *d; - struct dirent buffer, *de; - - assert(name); - assert(fd >= 0); - assert(path); - assert(config_path); - assert(same_name_link); - - d = fdopendir(fd); - if (!d) { - close_nointr_nofail(fd); - return -errno; - } - - for (;;) { - int k; - - k = readdir_r(d, &buffer, &de); - if (k != 0) { - r = -errno; - break; - } - - if (!de) - break; - - if (ignore_file(de->d_name)) - continue; - - dirent_ensure_type(d, de); - - if (de->d_type == DT_DIR) { - int nfd, q; - char *p; - - nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); - if (nfd < 0) { - if (errno == ENOENT) - continue; - - if (r == 0) - r = -errno; - continue; - } - - p = path_make_absolute(de->d_name, path); - if (!p) { - close_nointr_nofail(nfd); - r = -ENOMEM; - break; - } - - /* This will close nfd, regardless whether it succeeds or not */ - q = find_symlinks_fd(name, nfd, p, config_path, same_name_link); - free(p); - - if (q > 0) { - r = 1; - break; - } - - if (r == 0) - r = q; - - } else if (de->d_type == DT_LNK) { - char *p, *dest; - bool found_path, found_dest, b = false; - int q; - - /* Acquire symlink name */ - p = path_make_absolute(de->d_name, path); - if (!p) { - r = -ENOMEM; - break; - } - - /* Acquire symlink destination */ - q = readlink_and_canonicalize(p, &dest); - if (q < 0) { - free(p); - - if (q == -ENOENT) - continue; - - if (r == 0) - r = q; - continue; - } - - /* Check if the symlink itself matches what we - * are looking for */ - if (path_is_absolute(name)) - found_path = path_equal(p, name); - else - found_path = streq(de->d_name, name); - - /* Check if what the symlink points to - * matches what we are looking for */ - if (path_is_absolute(name)) - found_dest = path_equal(dest, name); - else - found_dest = streq(file_name_from_path(dest), name); - - free(dest); - - if (found_path && found_dest) { - char *t; - - /* Filter out same name links in the main - * config path */ - t = path_make_absolute(name, config_path); - if (!t) { - free(p); - r = -ENOMEM; - break; - } - - b = path_equal(t, p); - free(t); - } - - free(p); - - if (b) - *same_name_link = true; - else if (found_path || found_dest) { - r = 1; - break; - } - } - } - - closedir(d); - - return r; -} - -static int find_symlinks( - const char *name, - const char *config_path, - bool *same_name_link) { - - int fd; - - assert(name); - assert(config_path); - assert(same_name_link); - - fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); - if (fd < 0) - return -errno; - - /* This takes possession of fd and closes it */ - return find_symlinks_fd(name, fd, config_path, config_path, same_name_link); -} - -static int find_symlinks_in_scope( - UnitFileScope scope, - const char *root_dir, - const char *name, - UnitFileState *state) { - - int r; - char *path; - bool same_name_link_runtime = false, same_name_link = false; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(name); - - if (scope == UNIT_FILE_SYSTEM || scope == UNIT_FILE_GLOBAL) { - - /* First look in runtime config path */ - r = get_config_path(scope, true, root_dir, &path); - if (r < 0) - return r; - - r = find_symlinks(name, path, &same_name_link_runtime); - free(path); - - if (r < 0) - return r; - else if (r > 0) { - *state = UNIT_FILE_ENABLED_RUNTIME; - return r; - } - } - - /* Then look in the normal config path */ - r = get_config_path(scope, false, root_dir, &path); - if (r < 0) - return r; - - r = find_symlinks(name, path, &same_name_link); - free(path); - - if (r < 0) - return r; - else if (r > 0) { - *state = UNIT_FILE_ENABLED; - return r; - } - - /* Hmm, we didn't find it, but maybe we found the same name - * link? */ - if (same_name_link_runtime) { - *state = UNIT_FILE_LINKED_RUNTIME; - return 1; - } else if (same_name_link) { - *state = UNIT_FILE_LINKED; - return 1; - } - - return 0; -} - -int unit_file_mask( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char *files[], - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - char **i, *prefix; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = get_config_path(scope, runtime, root_dir, &prefix); - if (r < 0) - return r; - - STRV_FOREACH(i, files) { - char *path; - - if (!unit_name_is_valid_no_type(*i, true)) { - if (r == 0) - r = -EINVAL; - continue; - } - - path = path_make_absolute(*i, prefix); - if (!path) { - r = -ENOMEM; - break; - } - - if (symlink("/dev/null", path) >= 0) { - add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null"); - - free(path); - continue; - } - - if (errno == EEXIST) { - - if (null_or_empty_path(path) > 0) { - free(path); - continue; - } - - if (force) { - unlink(path); - - if (symlink("/dev/null", path) >= 0) { - - add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null"); - - free(path); - continue; - } - } - - if (r == 0) - r = -EEXIST; - } else { - if (r == 0) - r = -errno; - } - - free(path); - } - - free(prefix); - - return r; -} - -int unit_file_unmask( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char *files[], - UnitFileChange **changes, - unsigned *n_changes) { - - char **i, *config_path = NULL; - int r, q; - Set *remove_symlinks_to = NULL; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - goto finish; - - STRV_FOREACH(i, files) { - char *path; - - if (!unit_name_is_valid_no_type(*i, true)) { - if (r == 0) - r = -EINVAL; - continue; - } - - path = path_make_absolute(*i, config_path); - if (!path) { - r = -ENOMEM; - break; - } - - q = null_or_empty_path(path); - if (q > 0) { - if (unlink(path) >= 0) { - mark_symlink_for_removal(&remove_symlinks_to, path); - add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - - free(path); - continue; - } - - q = -errno; - } - - if (q != -ENOENT && r == 0) - r = q; - - free(path); - } - - -finish: - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); - if (r == 0) - r = q; - - set_free_free(remove_symlinks_to); - free(config_path); - - return r; -} - -int unit_file_link( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char *files[], - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - LookupPaths paths; - char **i, *config_path = NULL; - int r, q; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - zero(paths); - - r = lookup_paths_init_from_scope(&paths, scope); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - goto finish; - - STRV_FOREACH(i, files) { - char *path, *fn; - struct stat st; - - fn = file_name_from_path(*i); - - if (!path_is_absolute(*i) || - !unit_name_is_valid_no_type(fn, true)) { - if (r == 0) - r = -EINVAL; - continue; - } - - if (lstat(*i, &st) < 0) { - if (r == 0) - r = -errno; - continue; - } - - if (!S_ISREG(st.st_mode)) { - r = -ENOENT; - continue; - } - - q = in_search_path(*i, paths.unit_path); - if (q < 0) { - r = q; - break; - } - - if (q > 0) - continue; - - path = path_make_absolute(fn, config_path); - if (!path) { - r = -ENOMEM; - break; - } - - if (symlink(*i, path) >= 0) { - add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i); - - free(path); - continue; - } - - if (errno == EEXIST) { - char *dest = NULL; - - q = readlink_and_make_absolute(path, &dest); - - if (q < 0 && errno != ENOENT) { - free(path); - - if (r == 0) - r = q; - - continue; - } - - if (q >= 0 && path_equal(dest, *i)) { - free(dest); - free(path); - continue; - } - - free(dest); - - if (force) { - unlink(path); - - if (symlink(*i, path) >= 0) { - - add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i); - - free(path); - continue; - } - } - - if (r == 0) - r = -EEXIST; - } else { - if (r == 0) - r = -errno; - } - - free(path); - } - - finish: - lookup_paths_free(&paths); - free(config_path); - - return r; -} - -void unit_file_list_free(Hashmap *h) { - UnitFileList *i; - - while ((i = hashmap_steal_first(h))) { - free(i->path); - free(i); - } - - hashmap_free(h); -} - -void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { - unsigned i; - - assert(changes || n_changes == 0); - - if (!changes) - return; - - for (i = 0; i < n_changes; i++) { - free(changes[i].path); - free(changes[i].source); - } - - free(changes); -} - -static void install_info_free(InstallInfo *i) { - assert(i); - - free(i->name); - free(i->path); - strv_free(i->aliases); - strv_free(i->wanted_by); - free(i); -} - -static void install_info_hashmap_free(Hashmap *m) { - InstallInfo *i; - - if (!m) - return; - - while ((i = hashmap_steal_first(m))) - install_info_free(i); - - hashmap_free(m); -} - -static void install_context_done(InstallContext *c) { - assert(c); - - install_info_hashmap_free(c->will_install); - install_info_hashmap_free(c->have_installed); - - c->will_install = c->have_installed = NULL; -} - -static int install_info_add( - InstallContext *c, - const char *name, - const char *path) { - InstallInfo *i = NULL; - int r; - - assert(c); - assert(name || path); - - if (!name) - name = file_name_from_path(path); - - if (!unit_name_is_valid_no_type(name, true)) - return -EINVAL; - - if (hashmap_get(c->have_installed, name) || - hashmap_get(c->will_install, name)) - return 0; - - r = hashmap_ensure_allocated(&c->will_install, string_hash_func, string_compare_func); - if (r < 0) - return r; - - i = new0(InstallInfo, 1); - if (!i) - return -ENOMEM; - - i->name = strdup(name); - if (!i->name) { - r = -ENOMEM; - goto fail; - } - - if (path) { - i->path = strdup(path); - if (!i->path) { - r = -ENOMEM; - goto fail; - } - } - - r = hashmap_put(c->will_install, i->name, i); - if (r < 0) - goto fail; - - return 0; - -fail: - if (i) - install_info_free(i); - - return r; -} - -static int install_info_add_auto( - InstallContext *c, - const char *name_or_path) { - - assert(c); - assert(name_or_path); - - if (path_is_absolute(name_or_path)) - return install_info_add(c, NULL, name_or_path); - else - return install_info_add(c, name_or_path, NULL); -} - -static int config_parse_also( - const char *filename, - unsigned line, - const char *section, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - char *w; - size_t l; - char *state; - InstallContext *c = data; - - assert(filename); - assert(lvalue); - assert(rvalue); - - FOREACH_WORD_QUOTED(w, l, rvalue, state) { - char *n; - int r; - - n = strndup(w, l); - if (!n) - return -ENOMEM; - - r = install_info_add(c, n, NULL); - if (r < 0) { - free(n); - return r; - } - - free(n); - } - - return 0; -} - -static int unit_file_load( - InstallContext *c, - InstallInfo *info, - const char *path, - bool allow_symlink) { - - const ConfigTableItem items[] = { - { "Install", "Alias", config_parse_strv, 0, &info->aliases }, - { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, - { "Install", "Also", config_parse_also, 0, c }, - { NULL, NULL, NULL, 0, NULL } - }; - - int fd; - FILE *f; - int r; - - assert(c); - assert(info); - assert(path); - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW)); - if (fd < 0) - return -errno; - - f = fdopen(fd, "re"); - if (!f) { - close_nointr_nofail(fd); - return -ENOMEM; - } - - r = config_parse(path, f, NULL, config_item_table_lookup, (void*) items, true, info); - fclose(f); - if (r < 0) - return r; - - return strv_length(info->aliases) + strv_length(info->wanted_by); -} - -static int unit_file_search( - InstallContext *c, - InstallInfo *info, - LookupPaths *paths, - const char *root_dir, - bool allow_symlink) { - - char **p; - int r; - - assert(c); - assert(info); - assert(paths); - - if (info->path) - return unit_file_load(c, info, info->path, allow_symlink); - - assert(info->name); - - STRV_FOREACH(p, paths->unit_path) { - char *path = NULL; - - if (isempty(root_dir)) - asprintf(&path, "%s/%s", *p, info->name); - else - asprintf(&path, "%s/%s/%s", root_dir, *p, info->name); - - if (!path) - return -ENOMEM; - - r = unit_file_load(c, info, path, allow_symlink); - - if (r >= 0) - info->path = path; - else - free(path); - - if (r != -ENOENT && r != -ELOOP) - return r; - } - - return -ENOENT; -} - -static int unit_file_can_install( - LookupPaths *paths, - const char *root_dir, - const char *name, - bool allow_symlink) { - - InstallContext c; - InstallInfo *i; - int r; - - assert(paths); - assert(name); - - zero(c); - - r = install_info_add_auto(&c, name); - if (r < 0) - return r; - - assert_se(i = hashmap_first(c.will_install)); - - r = unit_file_search(&c, i, paths, root_dir, allow_symlink); - - if (r >= 0) - r = strv_length(i->aliases) + strv_length(i->wanted_by); - - install_context_done(&c); - - return r; -} - -static int create_symlink( - const char *old_path, - const char *new_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - char *dest; - int r; - - assert(old_path); - assert(new_path); - - mkdir_parents(new_path, 0755); - - if (symlink(old_path, new_path) >= 0) { - add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - return 0; - } - - if (errno != EEXIST) - return -errno; - - r = readlink_and_make_absolute(new_path, &dest); - if (r < 0) - return r; - - if (path_equal(dest, old_path)) { - free(dest); - return 0; - } - - free(dest); - - if (force) - return -EEXIST; - - unlink(new_path); - - if (symlink(old_path, new_path) >= 0) { - add_file_change(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL); - add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - return 0; - } - - return -errno; -} - -static int install_info_symlink_alias( - InstallInfo *i, - const char *config_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - char **s; - int r = 0, q; - - assert(i); - assert(config_path); - - STRV_FOREACH(s, i->aliases) { - char *alias_path; - - alias_path = path_make_absolute(*s, config_path); - - if (!alias_path) - return -ENOMEM; - - q = create_symlink(i->path, alias_path, force, changes, n_changes); - free(alias_path); - - if (r == 0) - r = q; - } - - return r; -} - -static int install_info_symlink_wants( - InstallInfo *i, - const char *config_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - char **s; - int r = 0, q; - - assert(i); - assert(config_path); - - STRV_FOREACH(s, i->wanted_by) { - char *path; - - if (!unit_name_is_valid_no_type(*s, true)) { - r = -EINVAL; - continue; - } - - if (asprintf(&path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) - return -ENOMEM; - - q = create_symlink(i->path, path, force, changes, n_changes); - free(path); - - if (r == 0) - r = q; - } - - return r; -} - -static int install_info_symlink_link( - InstallInfo *i, - LookupPaths *paths, - const char *config_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - int r; - char *path; - - assert(i); - assert(paths); - assert(config_path); - assert(i->path); - - r = in_search_path(i->path, paths->unit_path); - if (r != 0) - return r; - - if (asprintf(&path, "%s/%s", config_path, i->name) < 0) - return -ENOMEM; - - r = create_symlink(i->path, path, force, changes, n_changes); - free(path); - - return r; -} - -static int install_info_apply( - InstallInfo *i, - LookupPaths *paths, - const char *config_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - int r, q; - - assert(i); - assert(paths); - assert(config_path); - - r = install_info_symlink_alias(i, config_path, force, changes, n_changes); - - q = install_info_symlink_wants(i, config_path, force, changes, n_changes); - if (r == 0) - r = q; - - q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes); - if (r == 0) - r = q; - - return r; -} - -static int install_context_apply( - InstallContext *c, - LookupPaths *paths, - const char *config_path, - const char *root_dir, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - InstallInfo *i; - int r = 0, q; - - assert(c); - assert(paths); - assert(config_path); - - while ((i = hashmap_first(c->will_install))) { - - q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func); - if (q < 0) - return q; - - assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); - - q = unit_file_search(c, i, paths, root_dir, false); - if (q < 0) { - if (r >= 0) - r = q; - - return r; - } else if (r >= 0) - r += q; - - q = install_info_apply(i, paths, config_path, force, changes, n_changes); - if (r >= 0 && q < 0) - r = q; - } - - return r; -} - -static int install_context_mark_for_removal( - InstallContext *c, - LookupPaths *paths, - Set **remove_symlinks_to, - const char *config_path, - const char *root_dir) { - - InstallInfo *i; - int r = 0, q; - - assert(c); - assert(paths); - assert(config_path); - - /* Marks all items for removal */ - - while ((i = hashmap_first(c->will_install))) { - - q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func); - if (q < 0) - return q; - - assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); - - q = unit_file_search(c, i, paths, root_dir, false); - if (q < 0) { - if (r >= 0) - r = q; - - return r; - } else if (r >= 0) - r += q; - - q = mark_symlink_for_removal(remove_symlinks_to, i->name); - if (r >= 0 && q < 0) - r = q; - } - - return r; -} - -int unit_file_enable( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char *files[], - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - LookupPaths paths; - InstallContext c; - char **i, *config_path = NULL; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - zero(paths); - zero(c); - - r = lookup_paths_init_from_scope(&paths, scope); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - goto finish; - - STRV_FOREACH(i, files) { - r = install_info_add_auto(&c, *i); - if (r < 0) - goto finish; - } - - /* This will return the number of symlink rules that were - supposed to be created, not the ones actually created. This is - useful to determine whether the passed files hat any - installation data at all. */ - r = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes); - -finish: - install_context_done(&c); - lookup_paths_free(&paths); - free(config_path); - - return r; -} - -int unit_file_disable( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char *files[], - UnitFileChange **changes, - unsigned *n_changes) { - - LookupPaths paths; - InstallContext c; - char **i, *config_path = NULL; - Set *remove_symlinks_to = NULL; - int r, q; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - zero(paths); - zero(c); - - r = lookup_paths_init_from_scope(&paths, scope); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - goto finish; - - STRV_FOREACH(i, files) { - r = install_info_add_auto(&c, *i); - if (r < 0) - goto finish; - } - - r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir); - - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); - if (r == 0) - r = q; - -finish: - install_context_done(&c); - lookup_paths_free(&paths); - set_free_free(remove_symlinks_to); - free(config_path); - - return r; -} - -int unit_file_reenable( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char *files[], - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - LookupPaths paths; - InstallContext c; - char **i, *config_path = NULL; - Set *remove_symlinks_to = NULL; - int r, q; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - zero(paths); - zero(c); - - r = lookup_paths_init_from_scope(&paths, scope); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - goto finish; - - STRV_FOREACH(i, files) { - r = mark_symlink_for_removal(&remove_symlinks_to, *i); - if (r < 0) - goto finish; - - r = install_info_add_auto(&c, *i); - if (r < 0) - goto finish; - } - - r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); - - /* Returns number of symlinks that where supposed to be installed. */ - q = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes); - if (r == 0) - r = q; - -finish: - lookup_paths_free(&paths); - install_context_done(&c); - set_free_free(remove_symlinks_to); - free(config_path); - - return r; -} - -UnitFileState unit_file_get_state( - UnitFileScope scope, - const char *root_dir, - const char *name) { - - LookupPaths paths; - UnitFileState state = _UNIT_FILE_STATE_INVALID; - char **i, *path = NULL; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(name); - - zero(paths); - - if (root_dir && scope != UNIT_FILE_SYSTEM) - return -EINVAL; - - if (!unit_name_is_valid_no_type(name, true)) - return -EINVAL; - - r = lookup_paths_init_from_scope(&paths, scope); - if (r < 0) - return r; - - STRV_FOREACH(i, paths.unit_path) { - struct stat st; - - free(path); - path = NULL; - - if (root_dir) - asprintf(&path, "%s/%s/%s", root_dir, *i, name); - else - asprintf(&path, "%s/%s", *i, name); - - if (!path) { - r = -ENOMEM; - goto finish; - } - - if (lstat(path, &st) < 0) { - r = -errno; - if (errno == ENOENT) - continue; - - goto finish; - } - - if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { - r = -ENOENT; - goto finish; - } - - r = null_or_empty_path(path); - if (r < 0 && r != -ENOENT) - goto finish; - else if (r > 0) { - state = path_startswith(*i, "/run") ? - UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; - r = 0; - goto finish; - } - - r = find_symlinks_in_scope(scope, root_dir, name, &state); - if (r < 0) { - goto finish; - } else if (r > 0) { - r = 0; - goto finish; - } - - r = unit_file_can_install(&paths, root_dir, path, true); - if (r < 0 && errno != -ENOENT) - goto finish; - else if (r > 0) { - state = UNIT_FILE_DISABLED; - r = 0; - goto finish; - } else if (r == 0) { - state = UNIT_FILE_STATIC; - r = 0; - goto finish; - } - } - -finish: - lookup_paths_free(&paths); - free(path); - - return r < 0 ? r : state; -} - -int unit_file_query_preset(UnitFileScope scope, const char *name) { - char **files, **i; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(name); - - if (scope == UNIT_FILE_SYSTEM) - r = conf_files_list(&files, ".preset", - "/etc/systemd/system.preset", - "/usr/local/lib/systemd/system.preset", - "/usr/lib/systemd/system.preset", - "/lib/systemd/system.preset", - NULL); - else if (scope == UNIT_FILE_GLOBAL) - r = conf_files_list(&files, ".preset", - "/etc/systemd/user.preset", - "/usr/local/lib/systemd/user.preset", - "/usr/lib/systemd/user.preset", - NULL); - else - return 1; - - if (r < 0) - return r; - - STRV_FOREACH(i, files) { - FILE *f; - - f = fopen(*i, "re"); - if (!f) { - if (errno == ENOENT) - continue; - - r = -errno; - goto finish; - } - - for (;;) { - char line[LINE_MAX], *l; - - if (!fgets(line, sizeof(line), f)) - break; - - l = strstrip(line); - if (!*l) - continue; - - if (strchr(COMMENTS, *l)) - continue; - - if (first_word(l, "enable")) { - l += 6; - l += strspn(l, WHITESPACE); - - if (fnmatch(l, name, FNM_NOESCAPE) == 0) { - r = 1; - fclose(f); - goto finish; - } - } else if (first_word(l, "disable")) { - l += 7; - l += strspn(l, WHITESPACE); - - if (fnmatch(l, name, FNM_NOESCAPE) == 0) { - r = 0; - fclose(f); - goto finish; - } - } else - log_debug("Couldn't parse line '%s'", l); - } - - fclose(f); - } - - /* Default is "enable" */ - r = 1; - -finish: - strv_free(files); - - return r; -} - -int unit_file_preset( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char *files[], - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - LookupPaths paths; - InstallContext plus, minus; - char **i, *config_path = NULL; - Set *remove_symlinks_to = NULL; - int r, q; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - zero(paths); - zero(plus); - zero(minus); - - r = lookup_paths_init_from_scope(&paths, scope); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - goto finish; - - STRV_FOREACH(i, files) { - - if (!unit_name_is_valid_no_type(*i, true)) { - r = -EINVAL; - goto finish; - } - - r = unit_file_query_preset(scope, *i); - if (r < 0) - goto finish; - - if (r) - r = install_info_add_auto(&plus, *i); - else - r = install_info_add_auto(&minus, *i); - - if (r < 0) - goto finish; - } - - r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir); - - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); - if (r == 0) - r = q; - - /* Returns number of symlinks that where supposed to be installed. */ - q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes); - if (r == 0) - r = q; - -finish: - lookup_paths_free(&paths); - install_context_done(&plus); - install_context_done(&minus); - set_free_free(remove_symlinks_to); - free(config_path); - - return r; -} - -int unit_file_get_list( - UnitFileScope scope, - const char *root_dir, - Hashmap *h) { - - LookupPaths paths; - char **i, *buf = NULL; - DIR *d = NULL; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(h); - - zero(paths); - - if (root_dir && scope != UNIT_FILE_SYSTEM) - return -EINVAL; - - r = lookup_paths_init_from_scope(&paths, scope); - if (r < 0) - return r; - - STRV_FOREACH(i, paths.unit_path) { - struct dirent buffer, *de; - const char *units_dir; - - free(buf); - buf = NULL; - - if (root_dir) { - if (asprintf(&buf, "%s/%s", root_dir, *i) < 0) { - r = -ENOMEM; - goto finish; - } - units_dir = buf; - } else - units_dir = *i; - - if (d) - closedir(d); - - d = opendir(units_dir); - if (!d) { - if (errno == ENOENT) - continue; - - r = -errno; - goto finish; - } - - for (;;) { - UnitFileList *f; - - r = readdir_r(d, &buffer, &de); - if (r != 0) { - r = -r; - goto finish; - } - - if (!de) - break; - - if (ignore_file(de->d_name)) - continue; - - if (!unit_name_is_valid_no_type(de->d_name, true)) - continue; - - if (hashmap_get(h, de->d_name)) - continue; - - r = dirent_ensure_type(d, de); - if (r < 0) { - if (r == -ENOENT) - continue; - - goto finish; - } - - if (de->d_type != DT_LNK && de->d_type != DT_REG) - continue; - - f = new0(UnitFileList, 1); - if (!f) { - r = -ENOMEM; - goto finish; - } - - f->path = path_make_absolute(de->d_name, units_dir); - if (!f->path) { - free(f); - r = -ENOMEM; - goto finish; - } - - r = null_or_empty_path(f->path); - if (r < 0 && r != -ENOENT) { - free(f->path); - free(f); - goto finish; - } else if (r > 0) { - f->state = - path_startswith(*i, "/run") ? - UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; - goto found; - } - - r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state); - if (r < 0) { - free(f->path); - free(f); - goto finish; - } else if (r > 0) - goto found; - - r = unit_file_can_install(&paths, root_dir, f->path, true); - if (r < 0) { - free(f->path); - free(f); - goto finish; - } else if (r > 0) { - f->state = UNIT_FILE_DISABLED; - goto found; - } else { - f->state = UNIT_FILE_STATIC; - goto found; - } - - free(f->path); - free(f); - continue; - - found: - r = hashmap_put(h, file_name_from_path(f->path), f); - if (r < 0) { - free(f->path); - free(f); - goto finish; - } - } - } - -finish: - lookup_paths_free(&paths); - free(buf); - - if (d) - closedir(d); - - return r; -} - -static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { - [UNIT_FILE_ENABLED] = "enabled", - [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtie", - [UNIT_FILE_LINKED] = "linked", - [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime", - [UNIT_FILE_MASKED] = "masked", - [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime", - [UNIT_FILE_STATIC] = "static", - [UNIT_FILE_DISABLED] = "disabled" -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); - -static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = { - [UNIT_FILE_SYMLINK] = "symlink", - [UNIT_FILE_UNLINK] = "unlink", -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType); diff --git a/src/install.h b/src/install.h deleted file mode 100644 index d365c01bc7..0000000000 --- a/src/install.h +++ /dev/null @@ -1,89 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef fooinstallhfoo -#define fooinstallhfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "hashmap.h" - -typedef enum UnitFileScope { - UNIT_FILE_SYSTEM, - UNIT_FILE_GLOBAL, - UNIT_FILE_USER, - _UNIT_FILE_SCOPE_MAX, - _UNIT_FILE_SCOPE_INVALID = -1 -} UnitFileScope; - -typedef enum UnitFileState { - UNIT_FILE_ENABLED, - UNIT_FILE_ENABLED_RUNTIME, - UNIT_FILE_LINKED, - UNIT_FILE_LINKED_RUNTIME, - UNIT_FILE_MASKED, - UNIT_FILE_MASKED_RUNTIME, - UNIT_FILE_STATIC, - UNIT_FILE_DISABLED, - _UNIT_FILE_STATE_MAX, - _UNIT_FILE_STATE_INVALID = -1 -} UnitFileState; - -typedef enum UnitFileChangeType { - UNIT_FILE_SYMLINK, - UNIT_FILE_UNLINK, - _UNIT_FILE_CHANGE_TYPE_MAX, - _UNIT_FILE_CHANGE_TYPE_INVALID = -1 -} UnitFileChangeType; - -typedef struct UnitFileChange { - UnitFileChangeType type; - char *path; - char *source; -} UnitFileChange; - -typedef struct UnitFileList { - char *path; - UnitFileState state; -} UnitFileList; - -int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes); -int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_preset(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes); - -UnitFileState unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename); - -int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h); - -void unit_file_list_free(Hashmap *h); -void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes); - -int unit_file_query_preset(UnitFileScope scope, const char *name); - -const char *unit_file_state_to_string(UnitFileState s); -UnitFileState unit_file_state_from_string(const char *s); - -const char *unit_file_change_type_to_string(UnitFileChangeType s); -UnitFileChangeType unit_file_change_type_from_string(const char *s); - -#endif diff --git a/src/logs-show.c b/src/logs-show.c deleted file mode 100644 index fedb4532db..0000000000 --- a/src/logs-show.c +++ /dev/null @@ -1,677 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "logs-show.h" -#include "log.h" -#include "util.h" - -#define PRINT_THRESHOLD 128 - -static bool contains_unprintable(const void *p, size_t l) { - const char *j; - - for (j = p; j < (const char *) p + l; j++) - if (*j < ' ' || *j >= 127) - return true; - - return false; -} - -static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) { - size_t fl, nl; - void *buf; - - assert(data); - assert(field); - assert(target); - assert(target_size); - - fl = strlen(field); - if (length < fl) - return 0; - - if (memcmp(data, field, fl)) - return 0; - - nl = length - fl; - buf = malloc(nl+1); - memcpy(buf, (const char*) data + fl, nl); - ((char*)buf)[nl] = 0; - if (!buf) { - log_error("Out of memory"); - return -ENOMEM; - } - - free(*target); - *target = buf; - *target_size = nl; - - return 1; -} - -static bool shall_print(bool show_all, char *p, size_t l) { - if (show_all) - return true; - - if (l > PRINT_THRESHOLD) - return false; - - if (contains_unprintable(p, l)) - return false; - - return true; -} - -static int output_short(sd_journal *j, unsigned line, unsigned n_columns, bool show_all, bool monotonic_mode) { - int r; - const void *data; - size_t length; - size_t n = 0; - char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL; - size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0; - - assert(j); - - SD_JOURNAL_FOREACH_DATA(j, data, length) { - - r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len); - if (r < 0) - goto finish; - else if (r > 0) - continue; - - r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len); - if (r < 0) - goto finish; - else if (r > 0) - continue; - - r = parse_field(data, length, "_COMM=", &comm, &comm_len); - if (r < 0) - goto finish; - else if (r > 0) - continue; - - r = parse_field(data, length, "_PID=", &pid, &pid_len); - if (r < 0) - goto finish; - else if (r > 0) - continue; - - r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len); - if (r < 0) - goto finish; - else if (r > 0) - continue; - - r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len); - if (r < 0) - goto finish; - else if (r > 0) - continue; - - r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len); - if (r < 0) - goto finish; - else if (r > 0) - continue; - - r = parse_field(data, length, "MESSAGE=", &message, &message_len); - if (r < 0) - goto finish; - } - - if (!message) { - r = 0; - goto finish; - } - - if (monotonic_mode) { - uint64_t t; - sd_id128_t boot_id; - - r = -ENOENT; - - if (monotonic) - r = safe_atou64(monotonic, &t); - - if (r < 0) - r = sd_journal_get_monotonic_usec(j, &t, &boot_id); - - if (r < 0) { - log_error("Failed to get monotonic: %s", strerror(-r)); - goto finish; - } - - printf("[%5llu.%06llu]", - (unsigned long long) (t / USEC_PER_SEC), - (unsigned long long) (t % USEC_PER_SEC)); - - n += 1 + 5 + 1 + 6 + 1; - - } else { - char buf[64]; - uint64_t x; - time_t t; - struct tm tm; - - r = -ENOENT; - - if (realtime) - r = safe_atou64(realtime, &x); - - if (r < 0) - r = sd_journal_get_realtime_usec(j, &x); - - if (r < 0) { - log_error("Failed to get realtime: %s", strerror(-r)); - goto finish; - } - - t = (time_t) (x / USEC_PER_SEC); - if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) { - log_error("Failed to format time."); - goto finish; - } - - fputs(buf, stdout); - n += strlen(buf); - } - - if (hostname && shall_print(show_all, hostname, hostname_len)) { - printf(" %.*s", (int) hostname_len, hostname); - n += hostname_len + 1; - } - - if (identifier && shall_print(show_all, identifier, identifier_len)) { - printf(" %.*s", (int) identifier_len, identifier); - n += identifier_len + 1; - } else if (comm && shall_print(show_all, comm, comm_len)) { - printf(" %.*s", (int) comm_len, comm); - n += comm_len + 1; - } else - putchar(' '); - - if (pid && shall_print(show_all, pid, pid_len)) { - printf("[%.*s]", (int) pid_len, pid); - n += pid_len + 2; - } else if (fake_pid && shall_print(show_all, fake_pid, fake_pid_len)) { - printf("[%.*s]", (int) fake_pid_len, fake_pid); - n += fake_pid_len + 2; - } - - if (show_all) - printf(": %.*s\n", (int) message_len, message); - else if (contains_unprintable(message, message_len)) { - char bytes[FORMAT_BYTES_MAX]; - printf(": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len)); - } else if (message_len + n < n_columns) - printf(": %.*s\n", (int) message_len, message); - else if (n < n_columns) { - char *e; - - e = ellipsize_mem(message, message_len, n_columns - n - 2, 90); - - if (!e) - printf(": %.*s\n", (int) message_len, message); - else - printf(": %s\n", e); - - free(e); - } else - fputs("\n", stdout); - - r = 0; - -finish: - free(hostname); - free(identifier); - free(comm); - free(pid); - free(fake_pid); - free(message); - free(monotonic); - free(realtime); - - return r; -} - -static int output_short_realtime(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) { - return output_short(j, line, n_columns, show_all, false); -} - -static int output_short_monotonic(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) { - return output_short(j, line, n_columns, show_all, true); -} - -static int output_verbose(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) { - const void *data; - size_t length; - char *cursor; - uint64_t realtime; - char ts[FORMAT_TIMESTAMP_MAX]; - int r; - - assert(j); - - r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - return r; - } - - printf("%s [%s]\n", - format_timestamp(ts, sizeof(ts), realtime), - cursor); - - free(cursor); - - SD_JOURNAL_FOREACH_DATA(j, data, length) { - if (!show_all && (length > PRINT_THRESHOLD || - contains_unprintable(data, length))) { - const char *c; - char bytes[FORMAT_BYTES_MAX]; - - c = memchr(data, '=', length); - if (!c) { - log_error("Invalid field."); - return -EINVAL; - } - - printf("\t%.*s=[%s blob data]\n", - (int) (c - (const char*) data), - (const char*) data, - format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1)); - } else - printf("\t%.*s\n", (int) length, (const char*) data); - } - - return 0; -} - -static int output_export(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) { - sd_id128_t boot_id; - char sid[33]; - int r; - usec_t realtime, monotonic; - char *cursor; - const void *data; - size_t length; - - assert(j); - - r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); - if (r < 0) { - log_error("Failed to get monotonic timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - return r; - } - - printf("__CURSOR=%s\n" - "__REALTIME_TIMESTAMP=%llu\n" - "__MONOTONIC_TIMESTAMP=%llu\n" - "_BOOT_ID=%s\n", - cursor, - (unsigned long long) realtime, - (unsigned long long) monotonic, - sd_id128_to_string(boot_id, sid)); - - free(cursor); - - SD_JOURNAL_FOREACH_DATA(j, data, length) { - - /* We already printed the boot id, from the data in - * the header, hence let's suppress it here */ - if (length >= 9 && - memcmp(data, "_BOOT_ID=", 9) == 0) - continue; - - if (contains_unprintable(data, length)) { - const char *c; - uint64_t le64; - - c = memchr(data, '=', length); - if (!c) { - log_error("Invalid field."); - return -EINVAL; - } - - fwrite(data, c - (const char*) data, 1, stdout); - fputc('\n', stdout); - le64 = htole64(length - (c - (const char*) data) - 1); - fwrite(&le64, sizeof(le64), 1, stdout); - fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout); - } else - fwrite(data, length, 1, stdout); - - fputc('\n', stdout); - } - - fputc('\n', stdout); - - return 0; -} - -static void json_escape(const char* p, size_t l) { - - if (contains_unprintable(p, l)) { - bool not_first = false; - - fputs("[ ", stdout); - - while (l > 0) { - if (not_first) - printf(", %u", (uint8_t) *p); - else { - not_first = true; - printf("%u", (uint8_t) *p); - } - - p++; - l--; - } - - fputs(" ]", stdout); - } else { - fputc('\"', stdout); - - while (l > 0) { - if (*p == '"' || *p == '\\') { - fputc('\\', stdout); - fputc(*p, stdout); - } else - fputc(*p, stdout); - - p++; - l--; - } - - fputc('\"', stdout); - } -} - -static int output_json(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) { - uint64_t realtime, monotonic; - char *cursor; - const void *data; - size_t length; - sd_id128_t boot_id; - char sid[33]; - int r; - - assert(j); - - r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); - if (r < 0) { - log_error("Failed to get monotonic timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - return r; - } - - if (line == 1) - fputc('\n', stdout); - else - fputs(",\n", stdout); - - printf("{\n" - "\t\"__CURSOR\" : \"%s\",\n" - "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n" - "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n" - "\t\"_BOOT_ID\" : \"%s\"", - cursor, - (unsigned long long) realtime, - (unsigned long long) monotonic, - sd_id128_to_string(boot_id, sid)); - - free(cursor); - - SD_JOURNAL_FOREACH_DATA(j, data, length) { - const char *c; - - /* We already printed the boot id, from the data in - * the header, hence let's suppress it here */ - if (length >= 9 && - memcmp(data, "_BOOT_ID=", 9) == 0) - continue; - - c = memchr(data, '=', length); - if (!c) { - log_error("Invalid field."); - return -EINVAL; - } - - fputs(",\n\t", stdout); - json_escape(data, c - (const char*) data); - fputs(" : ", stdout); - json_escape(c + 1, length - (c - (const char*) data) - 1); - } - - fputs("\n}", stdout); - fflush(stdout); - - return 0; -} - -static int output_cat(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) { - const void *data; - size_t l; - int r; - - assert(j); - - r = sd_journal_get_data(j, "MESSAGE", &data, &l); - if (r < 0) { - log_error("Failed to get data: %s", strerror(-r)); - return r; - } - - assert(l >= 8); - - fwrite((const char*) data + 8, 1, l - 8, stdout); - putchar('\n'); - - return 0; -} - -static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line, unsigned n_columns, bool show_all) = { - [OUTPUT_SHORT] = output_short_realtime, - [OUTPUT_SHORT_MONOTONIC] = output_short_monotonic, - [OUTPUT_VERBOSE] = output_verbose, - [OUTPUT_EXPORT] = output_export, - [OUTPUT_JSON] = output_json, - [OUTPUT_CAT] = output_cat -}; - -int output_journal(sd_journal *j, OutputMode mode, unsigned line, unsigned n_columns, bool show_all) { - assert(mode >= 0); - assert(mode < _OUTPUT_MODE_MAX); - - if (n_columns <= 0) - n_columns = columns(); - - return output_funcs[mode](j, line, n_columns, show_all); -} - -int show_journal_by_unit( - const char *unit, - OutputMode mode, - unsigned n_columns, - usec_t not_before, - unsigned how_many, - bool show_all, - bool follow) { - - char *m = NULL; - sd_journal *j; - int r; - int fd; - unsigned line = 0; - bool need_seek = false; - - assert(mode >= 0); - assert(mode < _OUTPUT_MODE_MAX); - assert(unit); - - if (!endswith(unit, ".service") && - !endswith(unit, ".socket") && - !endswith(unit, ".mount") && - !endswith(unit, ".swap")) - return 0; - - if (how_many <= 0) - return 0; - - if (asprintf(&m, "_SYSTEMD_UNIT=%s", unit) < 0) { - r = -ENOMEM; - goto finish; - } - - r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY); - if (r < 0) - goto finish; - - fd = sd_journal_get_fd(j); - if (fd < 0) - goto finish; - - r = sd_journal_add_match(j, m, strlen(m)); - if (r < 0) - goto finish; - - r = sd_journal_seek_tail(j); - if (r < 0) - goto finish; - - r = sd_journal_previous_skip(j, how_many); - if (r < 0) - goto finish; - - if (mode == OUTPUT_JSON) { - fputc('[', stdout); - fflush(stdout); - } - - for (;;) { - for (;;) { - usec_t usec; - - if (need_seek) { - r = sd_journal_next(j); - if (r < 0) - goto finish; - } - - if (r == 0) - break; - - need_seek = true; - - if (not_before > 0) { - r = sd_journal_get_monotonic_usec(j, &usec, NULL); - - /* -ESTALE is returned if the - timestamp is not from this boot */ - if (r == -ESTALE) - continue; - else if (r < 0) - goto finish; - - if (usec < not_before) - continue; - } - - line ++; - - r = output_journal(j, mode, line, n_columns, show_all); - if (r < 0) - goto finish; - } - - if (!follow) - break; - - r = fd_wait_for_event(fd, POLLIN, (usec_t) -1); - if (r < 0) - goto finish; - - r = sd_journal_process(j); - if (r < 0) - goto finish; - - } - - if (mode == OUTPUT_JSON) - fputs("\n]\n", stdout); - -finish: - if (m) - free(m); - - if (j) - sd_journal_close(j); - - return r; -} - -static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { - [OUTPUT_SHORT] = "short", - [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", - [OUTPUT_VERBOSE] = "verbose", - [OUTPUT_EXPORT] = "export", - [OUTPUT_JSON] = "json", - [OUTPUT_CAT] = "cat" -}; - -DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode); diff --git a/src/logs-show.h b/src/logs-show.h deleted file mode 100644 index 94caed5579..0000000000 --- a/src/logs-show.h +++ /dev/null @@ -1,56 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foologsshowhfoo -#define foologsshowhfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include - -#include "util.h" - -typedef enum OutputMode { - OUTPUT_SHORT, - OUTPUT_SHORT_MONOTONIC, - OUTPUT_VERBOSE, - OUTPUT_EXPORT, - OUTPUT_JSON, - OUTPUT_CAT, - _OUTPUT_MODE_MAX, - _OUTPUT_MODE_INVALID = -1 -} OutputMode; - -int output_journal(sd_journal *j, OutputMode mode, unsigned line, unsigned n_columns, bool show_all); - -int show_journal_by_unit( - const char *unit, - OutputMode mode, - unsigned n_columns, - usec_t not_before, - unsigned how_many, - bool show_all, - bool follow); - -const char* output_mode_to_string(OutputMode m); -OutputMode output_mode_from_string(const char *s); - -#endif diff --git a/src/loopback-setup.c b/src/loopback-setup.c deleted file mode 100644 index 46c1fc843a..0000000000 --- a/src/loopback-setup.c +++ /dev/null @@ -1,274 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" -#include "macro.h" -#include "loopback-setup.h" -#include "socket-util.h" - -#define NLMSG_TAIL(nmsg) \ - ((struct rtattr *) (((uint8_t*) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) - -static int add_rtattr(struct nlmsghdr *n, size_t max_length, int type, const void *data, size_t data_length) { - size_t length; - struct rtattr *rta; - - length = RTA_LENGTH(data_length); - - if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length) > max_length) - return -E2BIG; - - rta = NLMSG_TAIL(n); - rta->rta_type = type; - rta->rta_len = length; - memcpy(RTA_DATA(rta), data, data_length); - n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length); - - return 0; -} - -static ssize_t sendto_loop(int fd, const void *buf, size_t buf_len, int flags, const struct sockaddr *sa, socklen_t sa_len) { - - for (;;) { - ssize_t l; - - if ((l = sendto(fd, buf, buf_len, flags, sa, sa_len)) >= 0) - return l; - - if (errno != EINTR) - return -errno; - } -} - -static ssize_t recvfrom_loop(int fd, void *buf, size_t buf_len, int flags, struct sockaddr *sa, socklen_t *sa_len) { - - for (;;) { - ssize_t l; - - if ((l = recvfrom(fd, buf, buf_len, flags, sa, sa_len)) >= 0) - return l; - - if (errno != EINTR) - return -errno; - } -} - -static int add_adresses(int fd, int if_loopback, unsigned *requests) { - union { - struct sockaddr sa; - struct sockaddr_nl nl; - } sa; - union { - struct nlmsghdr header; - uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + - NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + - RTA_LENGTH(sizeof(struct in6_addr))]; - } request; - - struct ifaddrmsg *ifaddrmsg; - uint32_t ipv4_address = htonl(INADDR_LOOPBACK); - int r; - - zero(request); - - request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - request.header.nlmsg_type = RTM_NEWADDR; - request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK; - request.header.nlmsg_seq = *requests + 1; - - ifaddrmsg = NLMSG_DATA(&request.header); - ifaddrmsg->ifa_family = AF_INET; - ifaddrmsg->ifa_prefixlen = 8; - ifaddrmsg->ifa_flags = IFA_F_PERMANENT; - ifaddrmsg->ifa_scope = RT_SCOPE_HOST; - ifaddrmsg->ifa_index = if_loopback; - - if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address))) < 0) - return r; - - zero(sa); - sa.nl.nl_family = AF_NETLINK; - - if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) - return -errno; - (*requests)++; - - if (!socket_ipv6_is_supported()) - return 0; - - request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - request.header.nlmsg_seq = *requests + 1; - - ifaddrmsg->ifa_family = AF_INET6; - ifaddrmsg->ifa_prefixlen = 128; - - if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback))) < 0) - return r; - - if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) - return -errno; - (*requests)++; - - return 0; -} - -static int start_interface(int fd, int if_loopback, unsigned *requests) { - union { - struct sockaddr sa; - struct sockaddr_nl nl; - } sa; - union { - struct nlmsghdr header; - uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + - NLMSG_ALIGN(sizeof(struct ifinfomsg))]; - } request; - - struct ifinfomsg *ifinfomsg; - - zero(request); - - request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - request.header.nlmsg_type = RTM_NEWLINK; - request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; - request.header.nlmsg_seq = *requests + 1; - - ifinfomsg = NLMSG_DATA(&request.header); - ifinfomsg->ifi_family = AF_UNSPEC; - ifinfomsg->ifi_index = if_loopback; - ifinfomsg->ifi_flags = IFF_UP; - ifinfomsg->ifi_change = IFF_UP; - - zero(sa); - sa.nl.nl_family = AF_NETLINK; - - if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) - return -errno; - - (*requests)++; - - return 0; -} - -static int read_response(int fd, unsigned requests_max) { - union { - struct sockaddr sa; - struct sockaddr_nl nl; - } sa; - union { - struct nlmsghdr header; - uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + - NLMSG_ALIGN(sizeof(struct nlmsgerr))]; - } response; - - ssize_t l; - socklen_t sa_len = sizeof(sa); - struct nlmsgerr *nlmsgerr; - - if ((l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len)) < 0) - return -errno; - - if (sa_len != sizeof(sa.nl) || - sa.nl.nl_family != AF_NETLINK) - return -EIO; - - if (sa.nl.nl_pid != 0) - return 0; - - if ((size_t) l < sizeof(struct nlmsghdr)) - return -EIO; - - if (response.header.nlmsg_type != NLMSG_ERROR || - (pid_t) response.header.nlmsg_pid != getpid() || - response.header.nlmsg_seq >= requests_max) - return 0; - - if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) || - response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) - return -EIO; - - nlmsgerr = NLMSG_DATA(&response.header); - - if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST) { - log_warning("Netlink failure for request %i: %s", response.header.nlmsg_seq, strerror(-nlmsgerr->error)); - return nlmsgerr->error; - } - - return response.header.nlmsg_seq; -} - -int loopback_setup(void) { - int r, if_loopback; - union { - struct sockaddr sa; - struct sockaddr_nl nl; - struct sockaddr_storage storage; - } sa; - unsigned requests = 0, i; - int fd; - - errno = 0; - if ((if_loopback = (int) if_nametoindex("lo")) <= 0) - return errno ? -errno : -ENODEV; - - if ((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) - return -errno; - - zero(sa); - sa.nl.nl_family = AF_NETLINK; - - if (bind(fd, &sa.sa, sizeof(sa)) < 0) { - r = -errno; - goto finish; - } - - if ((r = add_adresses(fd, if_loopback, &requests)) < 0) - goto finish; - - if ((r = start_interface(fd, if_loopback, &requests)) < 0) - goto finish; - - for (i = 0; i < requests; i++) { - if ((r = read_response(fd, requests)) < 0) - goto finish; - } - - r = 0; - -finish: - if (r < 0) - log_warning("Failed to configure loopback device: %s", strerror(-r)); - - if (fd >= 0) - close_nointr_nofail(fd); - - return r; -} diff --git a/src/loopback-setup.h b/src/loopback-setup.h deleted file mode 100644 index cbb969e1e4..0000000000 --- a/src/loopback-setup.h +++ /dev/null @@ -1,27 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef fooloopbacksetuphfoo -#define fooloopbacksetuphfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int loopback_setup(void); - -#endif diff --git a/src/machine-id-setup.c b/src/machine-id-setup.c deleted file mode 100644 index 9e84ac0cb9..0000000000 --- a/src/machine-id-setup.c +++ /dev/null @@ -1,265 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "machine-id-setup.h" -#include "macro.h" -#include "util.h" -#include "mkdir.h" -#include "log.h" -#include "virt.h" - -static int shorten_uuid(char destination[36], const char *source) { - unsigned i, j; - - for (i = 0, j = 0; i < 36 && j < 32; i++) { - int t; - - t = unhexchar(source[i]); - if (t < 0) - continue; - - destination[j++] = hexchar(t); - } - - if (i == 36 && j == 32) { - destination[32] = '\n'; - destination[33] = 0; - return 0; - } - - return -EINVAL; -} - -static int generate(char id[34]) { - int fd, r; - unsigned char *p; - sd_id128_t buf; - char *q; - ssize_t k; - const char *vm_id; - - assert(id); - - /* First, try reading the D-Bus machine id, unless it is a symlink */ - fd = open("/var/lib/dbus/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd >= 0) { - - k = loop_read(fd, id, 32, false); - close_nointr_nofail(fd); - - if (k >= 32) { - id[32] = '\n'; - id[33] = 0; - - log_info("Initializing machine ID from D-Bus machine ID."); - return 0; - } - } - - /* If that didn't work, see if we are running in qemu/kvm and a - * machine ID was passed in via -uuid on the qemu/kvm command - * line */ - - r = detect_vm(&vm_id); - if (r > 0 && streq(vm_id, "kvm")) { - char uuid[37]; - - fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd >= 0) { - k = loop_read(fd, uuid, 36, false); - close_nointr_nofail(fd); - - if (k >= 36) { - r = shorten_uuid(id, uuid); - if (r >= 0) { - log_info("Initializing machine ID from KVM UUID"); - return 0; - } - } - } - } - - /* If that didn't work either, see if we are running in a - * container, and a machine ID was passed in via - * $container_uuid the way libvirt/LXC does it */ - - r = detect_container(NULL); - if (r > 0) { - FILE *f; - - 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 (startswith(line, "container_uuid=") && - strlen(line + 15) >= 36) { - r = shorten_uuid(id, line + 15); - if (r >= 0) { - log_info("Initializing machine ID from container UUID"); - return 0; - } - } - - } while (!done); - - fclose(f); - } - } - - /* If that didn't work, generate a random machine id */ - r = sd_id128_randomize(&buf); - if (r < 0) { - log_error("Failed to open /dev/urandom: %s", strerror(-r)); - return r; - } - - for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) { - q[0] = hexchar(*p >> 4); - q[1] = hexchar(*p & 15); - } - - id[32] = '\n'; - id[33] = 0; - - log_info("Initializing machine ID from random generator."); - - return 0; -} - -int machine_id_setup(void) { - int fd, r; - bool writable; - struct stat st; - char id[34]; /* 32 + \n + \0 */ - mode_t m; - - m = umask(0000); - - /* We create this 0444, to indicate that this isn't really - * something you should ever modify. Of course, since the file - * will be owned by root it doesn't matter much, but maybe - * people look. */ - - fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444); - if (fd >= 0) - writable = true; - else { - fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - umask(m); - log_error("Cannot open /etc/machine-id: %m"); - return -errno; - } - - writable = false; - } - - umask(m); - - if (fstat(fd, &st) < 0) { - log_error("fstat() failed: %m"); - r = -errno; - goto finish; - } - - if (S_ISREG(st.st_mode)) { - if (loop_read(fd, id, 32, false) >= 32) { - r = 0; - goto finish; - } - } - - /* Hmm, so, the id currently stored is not useful, then let's - * generate one */ - - r = generate(id); - if (r < 0) - goto finish; - - if (S_ISREG(st.st_mode) && writable) { - lseek(fd, 0, SEEK_SET); - - if (loop_write(fd, id, 33, false) == 33) { - r = 0; - goto finish; - } - } - - close_nointr_nofail(fd); - fd = -1; - - /* Hmm, we couldn't write it? So let's write it to - * /run/machine-id as a replacement */ - - m = umask(0022); - r = write_one_line_file("/run/machine-id", id); - umask(m); - - if (r < 0) { - log_error("Cannot write /run/machine-id: %s", strerror(-r)); - - unlink("/run/machine-id"); - goto finish; - } - - /* And now, let's mount it over */ - r = mount("/run/machine-id", "/etc/machine-id", "bind", MS_BIND|MS_RDONLY, NULL) < 0 ? -errno : 0; - if (r < 0) { - unlink("/run/machine-id"); - log_error("Failed to mount /etc/machine-id: %s", strerror(-r)); - } else - log_info("Installed transient /etc/machine-id file."); - -finish: - - if (fd >= 0) - close_nointr_nofail(fd); - - return r; -} diff --git a/src/machine-id-setup.h b/src/machine-id-setup.h deleted file mode 100644 index 16f45d86d3..0000000000 --- a/src/machine-id-setup.h +++ /dev/null @@ -1,27 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foomachineidsetuphfoo -#define foomachineidsetuphfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int machine_id_setup(void); - -#endif diff --git a/src/mount-setup.c b/src/mount-setup.c deleted file mode 100644 index 52fe523674..0000000000 --- a/src/mount-setup.c +++ /dev/null @@ -1,423 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mount-setup.h" -#include "log.h" -#include "macro.h" -#include "util.h" -#include "label.h" -#include "set.h" -#include "strv.h" -#include "mkdir.h" - -#ifndef TTY_GID -#define TTY_GID 5 -#endif - -typedef struct MountPoint { - const char *what; - const char *where; - const char *type; - const char *options; - unsigned long flags; - bool fatal; -} MountPoint; - -/* The first three entries we might need before SELinux is up. The - * fourth (securityfs) is needed by IMA to load a custom policy. The - * other ones we can delay until SELinux and IMA are loaded. */ -#define N_EARLY_MOUNT 4 - -static const MountPoint mount_table[] = { - { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true }, - { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true }, - { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true }, - { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, false }, - { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true }, - { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, false }, - { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true }, - { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, false }, - { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, false }, -}; - -/* These are API file systems that might be mounted by other software, - * we just list them here so that we know that we should ignore them */ - -static const char * const ignore_paths[] = { - "/sys/fs/selinux", - "/selinux", - "/proc/bus/usb" -}; - -bool mount_point_is_api(const char *path) { - unsigned i; - - /* Checks if this mount point is considered "API", and hence - * should be ignored */ - - for (i = 0; i < ELEMENTSOF(mount_table); i ++) - if (path_equal(path, mount_table[i].where)) - return true; - - return path_startswith(path, "/sys/fs/cgroup/"); -} - -bool mount_point_ignore(const char *path) { - unsigned i; - - for (i = 0; i < ELEMENTSOF(ignore_paths); i++) - if (path_equal(path, ignore_paths[i])) - return true; - - return false; -} - -static int mount_one(const MountPoint *p, bool relabel) { - int r; - - assert(p); - - /* Relabel first, just in case */ - if (relabel) - label_fix(p->where, true); - - if ((r = path_is_mount_point(p->where, true)) < 0) - return r; - - if (r > 0) - return 0; - - /* The access mode here doesn't really matter too much, since - * the mounted file system will take precedence anyway. */ - mkdir_p(p->where, 0755); - - log_debug("Mounting %s to %s of type %s with options %s.", - p->what, - p->where, - p->type, - strna(p->options)); - - if (mount(p->what, - p->where, - p->type, - p->flags, - p->options) < 0) { - log_error("Failed to mount %s: %s", p->where, strerror(errno)); - return p->fatal ? -errno : 0; - } - - /* Relabel again, since we now mounted something fresh here */ - if (relabel) - label_fix(p->where, false); - - return 1; -} - -int mount_setup_early(void) { - unsigned i; - int r = 0; - - assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table)); - - /* Do a minimal mount of /proc and friends to enable the most - * basic stuff, such as SELinux */ - for (i = 0; i < N_EARLY_MOUNT; i ++) { - int j; - - j = mount_one(mount_table + i, false); - if (r == 0) - r = j; - } - - return r; -} - -int mount_cgroup_controllers(char ***join_controllers) { - int r; - FILE *f; - char buf[LINE_MAX]; - Set *controllers; - - /* Mount all available cgroup controllers that are built into the kernel. */ - - f = fopen("/proc/cgroups", "re"); - if (!f) { - log_error("Failed to enumerate cgroup controllers: %m"); - return 0; - } - - controllers = set_new(string_hash_func, string_compare_func); - if (!controllers) { - r = -ENOMEM; - log_error("Failed to allocate controller set."); - goto finish; - } - - /* Ignore the header line */ - (void) fgets(buf, sizeof(buf), f); - - for (;;) { - char *controller; - int enabled = 0; - - if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) { - - if (feof(f)) - break; - - log_error("Failed to parse /proc/cgroups."); - r = -EIO; - goto finish; - } - - if (!enabled) { - free(controller); - continue; - } - - r = set_put(controllers, controller); - if (r < 0) { - log_error("Failed to add controller to set."); - free(controller); - goto finish; - } - } - - for (;;) { - MountPoint p; - char *controller, *where, *options; - char ***k = NULL; - - controller = set_steal_first(controllers); - if (!controller) - break; - - if (join_controllers) - for (k = join_controllers; *k; k++) - if (strv_find(*k, controller)) - break; - - if (k && *k) { - char **i, **j; - - for (i = *k, j = *k; *i; i++) { - - if (!streq(*i, controller)) { - char *t; - - t = set_remove(controllers, *i); - if (!t) { - free(*i); - continue; - } - free(t); - } - - *(j++) = *i; - } - - *j = NULL; - - options = strv_join(*k, ","); - if (!options) { - log_error("Failed to join options"); - free(controller); - r = -ENOMEM; - goto finish; - } - - } else { - options = controller; - controller = NULL; - } - - where = strappend("/sys/fs/cgroup/", options); - if (!where) { - log_error("Failed to build path"); - free(options); - r = -ENOMEM; - goto finish; - } - - zero(p); - p.what = "cgroup"; - p.where = where; - p.type = "cgroup"; - p.options = options; - p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV; - p.fatal = false; - - r = mount_one(&p, true); - free(controller); - free(where); - - if (r < 0) { - free(options); - goto finish; - } - - if (r > 0 && k && *k) { - char **i; - - for (i = *k; *i; i++) { - char *t; - - t = strappend("/sys/fs/cgroup/", *i); - if (!t) { - log_error("Failed to build path"); - r = -ENOMEM; - free(options); - goto finish; - } - - r = symlink(options, t); - free(t); - - if (r < 0 && errno != EEXIST) { - log_error("Failed to create symlink: %m"); - r = -errno; - free(options); - goto finish; - } - } - } - - free(options); - } - - r = 0; - -finish: - set_free_free(controllers); - - fclose(f); - - return r; -} - -static int symlink_and_label(const char *old_path, const char *new_path) { - int r; - - assert(old_path); - assert(new_path); - - if ((r = label_symlinkfile_set(new_path)) < 0) - return r; - - if (symlink(old_path, new_path) < 0) - r = -errno; - - label_file_clear(); - - return r; -} - -static int nftw_cb( - const char *fpath, - const struct stat *sb, - int tflag, - struct FTW *ftwbuf) { - - /* No need to label /dev twice in a row... */ - if (_unlikely_(ftwbuf->level == 0)) - return FTW_CONTINUE; - - label_fix(fpath, true); - - /* /run/initramfs is static data and big, no need to - * dynamically relabel its contents at boot... */ - if (_unlikely_(ftwbuf->level == 1 && - tflag == FTW_D && - streq(fpath, "/run/initramfs"))) - return FTW_SKIP_SUBTREE; - - return FTW_CONTINUE; -}; - -int mount_setup(bool loaded_policy) { - - static const char symlinks[] = - "/proc/kcore\0" "/dev/core\0" - "/proc/self/fd\0" "/dev/fd\0" - "/proc/self/fd/0\0" "/dev/stdin\0" - "/proc/self/fd/1\0" "/dev/stdout\0" - "/proc/self/fd/2\0" "/dev/stderr\0"; - - static const char relabel[] = - "/run/initramfs/root-fsck\0" - "/run/initramfs/shutdown\0"; - - int r; - unsigned i; - const char *j, *k; - - for (i = 0; i < ELEMENTSOF(mount_table); i ++) { - r = mount_one(mount_table + i, true); - - if (r < 0) - return r; - } - - /* Nodes in devtmpfs and /run need to be manually updated for - * the appropriate labels, after mounting. The other virtual - * API file systems like /sys and /proc do not need that, they - * use the same label for all their files. */ - if (loaded_policy) { - usec_t before_relabel, after_relabel; - char timespan[FORMAT_TIMESPAN_MAX]; - - before_relabel = now(CLOCK_MONOTONIC); - - nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - - /* Explicitly relabel these */ - NULSTR_FOREACH(j, relabel) - label_fix(j, true); - - after_relabel = now(CLOCK_MONOTONIC); - - log_info("Relabelled /dev and /run in %s.", - format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel)); - } - - /* Create a few default symlinks, which are normally created - * by udevd, but some scripts might need them before we start - * udevd. */ - NULSTR_FOREACH_PAIR(j, k, symlinks) - symlink_and_label(j, k); - - /* Create a few directories we always want around */ - label_mkdir("/run/systemd", 0755); - label_mkdir("/run/systemd/system", 0755); - - return 0; -} diff --git a/src/mount-setup.h b/src/mount-setup.h deleted file mode 100644 index 720b66f76c..0000000000 --- a/src/mount-setup.h +++ /dev/null @@ -1,36 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foomountsetuphfoo -#define foomountsetuphfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -int mount_setup_early(void); - -int mount_setup(bool loaded_policy); - -int mount_cgroup_controllers(char ***join_controllers); - -bool mount_point_is_api(const char *path); -bool mount_point_ignore(const char *path); - -#endif diff --git a/src/path-lookup.c b/src/path-lookup.c deleted file mode 100644 index 1d95f7d1f8..0000000000 --- a/src/path-lookup.c +++ /dev/null @@ -1,348 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "util.h" -#include "mkdir.h" -#include "strv.h" - -#include "path-lookup.h" - -int user_config_home(char **config_home) { - const char *e; - - if ((e = getenv("XDG_CONFIG_HOME"))) { - if (asprintf(config_home, "%s/systemd/user", e) < 0) - return -ENOMEM; - - return 1; - } else { - const char *home; - - if ((home = getenv("HOME"))) { - if (asprintf(config_home, "%s/.config/systemd/user", home) < 0) - return -ENOMEM; - - return 1; - } - } - - return 0; -} - -static char** user_dirs(void) { - const char * const config_unit_paths[] = { - USER_CONFIG_UNIT_PATH, - "/etc/systemd/user", - "/run/systemd/user", - NULL - }; - - const char * const data_unit_paths[] = { - "/usr/local/lib/systemd/user", - "/usr/local/share/systemd/user", - USER_DATA_UNIT_PATH, - "/usr/lib/systemd/user", - "/usr/share/systemd/user", - NULL - }; - - const char *home, *e; - char *config_home = NULL, *data_home = NULL; - char **config_dirs = NULL, **data_dirs = NULL; - char **r = NULL, **t; - - /* Implement the mechanisms defined in - * - * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html - * - * We look in both the config and the data dirs because we - * want to encourage that distributors ship their unit files - * as data, and allow overriding as configuration. - */ - - if (user_config_home(&config_home) < 0) - goto fail; - - home = getenv("HOME"); - - if ((e = getenv("XDG_CONFIG_DIRS"))) - if (!(config_dirs = strv_split(e, ":"))) - goto fail; - - /* We don't treat /etc/xdg/systemd here as the spec - * suggests because we assume that that is a link to - * /etc/systemd/ anyway. */ - - if ((e = getenv("XDG_DATA_HOME"))) { - if (asprintf(&data_home, "%s/systemd/user", e) < 0) - goto fail; - - } else if (home) { - if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0) - goto fail; - - /* There is really no need for two unit dirs in $HOME, - * except to be fully compliant with the XDG spec. We - * now try to link the two dirs, so that we can - * minimize disk seeks a little. Further down we'll - * then filter out this link, if it is actually is - * one. */ - - mkdir_parents(data_home, 0777); - (void) symlink("../../../.config/systemd/user", data_home); - } - - if ((e = getenv("XDG_DATA_DIRS"))) - data_dirs = strv_split(e, ":"); - else - data_dirs = strv_new("/usr/local/share", - "/usr/share", - NULL); - - if (!data_dirs) - goto fail; - - /* Now merge everything we found. */ - if (config_home) { - if (!(t = strv_append(r, config_home))) - goto fail; - strv_free(r); - r = t; - } - - if (!strv_isempty(config_dirs)) { - if (!(t = strv_merge_concat(r, config_dirs, "/systemd/user"))) - goto finish; - strv_free(r); - r = t; - } - - if (!(t = strv_merge(r, (char**) config_unit_paths))) - goto fail; - strv_free(r); - r = t; - - if (data_home) { - if (!(t = strv_append(r, data_home))) - goto fail; - strv_free(r); - r = t; - } - - if (!strv_isempty(data_dirs)) { - if (!(t = strv_merge_concat(r, data_dirs, "/systemd/user"))) - goto fail; - strv_free(r); - r = t; - } - - if (!(t = strv_merge(r, (char**) data_unit_paths))) - goto fail; - strv_free(r); - r = t; - - if (!strv_path_make_absolute_cwd(r)) - goto fail; - -finish: - free(config_home); - strv_free(config_dirs); - free(data_home); - strv_free(data_dirs); - - return r; - -fail: - strv_free(r); - r = NULL; - goto finish; -} - -int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal) { - const char *e; - char *t; - - assert(p); - - /* First priority is whatever has been passed to us via env - * vars */ - if ((e = getenv("SYSTEMD_UNIT_PATH"))) - if (!(p->unit_path = split_path_and_make_absolute(e))) - return -ENOMEM; - - if (strv_isempty(p->unit_path)) { - - /* Nothing is set, so let's figure something out. */ - strv_free(p->unit_path); - - if (running_as == MANAGER_USER) { - - if (personal) - p->unit_path = user_dirs(); - else - p->unit_path = strv_new( - /* If you modify this you also want to modify - * systemduserunitpath= in systemd.pc.in, and - * the arrays in user_dirs() above! */ - USER_CONFIG_UNIT_PATH, - "/etc/systemd/user", - "/run/systemd/user", - "/usr/local/lib/systemd/user", - "/usr/local/share/systemd/user", - USER_DATA_UNIT_PATH, - "/usr/lib/systemd/user", - "/usr/share/systemd/user", - NULL); - - if (!p->unit_path) - return -ENOMEM; - - } else - if (!(p->unit_path = strv_new( - /* If you modify this you also want to modify - * systemdsystemunitpath= in systemd.pc.in! */ - SYSTEM_CONFIG_UNIT_PATH, - "/etc/systemd/system", - "/run/systemd/system", - "/usr/local/lib/systemd/system", - SYSTEM_DATA_UNIT_PATH, - "/usr/lib/systemd/system", -#ifdef HAVE_SPLIT_USR - "/lib/systemd/system", -#endif - NULL))) - return -ENOMEM; - } - - if (p->unit_path) - if (!strv_path_canonicalize(p->unit_path)) - return -ENOMEM; - - strv_uniq(p->unit_path); - strv_path_remove_empty(p->unit_path); - - if (!strv_isempty(p->unit_path)) { - - if (!(t = strv_join(p->unit_path, "\n\t"))) - return -ENOMEM; - log_debug("Looking for unit files in:\n\t%s", t); - free(t); - } else { - log_debug("Ignoring unit files."); - strv_free(p->unit_path); - p->unit_path = NULL; - } - - if (running_as == MANAGER_SYSTEM) { -#ifdef HAVE_SYSV_COMPAT - /* /etc/init.d/ compatibility does not matter to users */ - - if ((e = getenv("SYSTEMD_SYSVINIT_PATH"))) - if (!(p->sysvinit_path = split_path_and_make_absolute(e))) - return -ENOMEM; - - if (strv_isempty(p->sysvinit_path)) { - strv_free(p->sysvinit_path); - - if (!(p->sysvinit_path = strv_new( - SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */ - NULL))) - return -ENOMEM; - } - - if ((e = getenv("SYSTEMD_SYSVRCND_PATH"))) - if (!(p->sysvrcnd_path = split_path_and_make_absolute(e))) - return -ENOMEM; - - if (strv_isempty(p->sysvrcnd_path)) { - strv_free(p->sysvrcnd_path); - - if (!(p->sysvrcnd_path = strv_new( - SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */ - NULL))) - return -ENOMEM; - } - - if (p->sysvinit_path) - if (!strv_path_canonicalize(p->sysvinit_path)) - return -ENOMEM; - - if (p->sysvrcnd_path) - if (!strv_path_canonicalize(p->sysvrcnd_path)) - return -ENOMEM; - - strv_uniq(p->sysvinit_path); - strv_uniq(p->sysvrcnd_path); - - strv_path_remove_empty(p->sysvinit_path); - strv_path_remove_empty(p->sysvrcnd_path); - - if (!strv_isempty(p->sysvinit_path)) { - - if (!(t = strv_join(p->sysvinit_path, "\n\t"))) - return -ENOMEM; - - log_debug("Looking for SysV init scripts in:\n\t%s", t); - free(t); - } else { - log_debug("Ignoring SysV init scripts."); - strv_free(p->sysvinit_path); - p->sysvinit_path = NULL; - } - - if (!strv_isempty(p->sysvrcnd_path)) { - - if (!(t = strv_join(p->sysvrcnd_path, "\n\t"))) - return -ENOMEM; - - log_debug("Looking for SysV rcN.d links in:\n\t%s", t); - free(t); - } else { - log_debug("Ignoring SysV rcN.d links."); - strv_free(p->sysvrcnd_path); - p->sysvrcnd_path = NULL; - } -#else - log_debug("Disabled SysV init scripts and rcN.d links support"); -#endif - } - - return 0; -} - -void lookup_paths_free(LookupPaths *p) { - assert(p); - - strv_free(p->unit_path); - p->unit_path = NULL; - -#ifdef HAVE_SYSV_COMPAT - strv_free(p->sysvinit_path); - strv_free(p->sysvrcnd_path); - p->sysvinit_path = p->sysvrcnd_path = NULL; -#endif -} diff --git a/src/path-lookup.h b/src/path-lookup.h deleted file mode 100644 index e8a5a77a7b..0000000000 --- a/src/path-lookup.h +++ /dev/null @@ -1,40 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foopathlookuphfoo -#define foopathlookuphfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct LookupPaths { - char **unit_path; -#ifdef HAVE_SYSV_COMPAT - char **sysvinit_path; - char **sysvrcnd_path; -#endif -} LookupPaths; - -#include "manager.h" - -int user_config_home(char **config_home); - -int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal); -void lookup_paths_free(LookupPaths *p); - -#endif diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c new file mode 100644 index 0000000000..550a2f5f31 --- /dev/null +++ b/src/shared/cgroup-show.c @@ -0,0 +1,261 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "util.h" +#include "macro.h" +#include "cgroup-util.h" +#include "cgroup-show.h" + +static int compare(const void *a, const void *b) { + const pid_t *p = a, *q = b; + + if (*p < *q) + return -1; + if (*p > *q) + return 1; + return 0; +} + +static unsigned ilog10(unsigned long ul) { + int n = 0; + + while (ul > 0) { + n++; + ul /= 10; + } + + return n; +} + +static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads) { + char *fn; + FILE *f; + size_t n = 0, n_allocated = 0; + pid_t *pids = NULL; + char *p; + pid_t pid, biggest = 0; + int r; + + if (n_columns <= 0) + n_columns = columns(); + + if (!prefix) + prefix = ""; + + if ((r = cg_fix_path(path, &p)) < 0) + return r; + + r = asprintf(&fn, "%s/cgroup.procs", p); + free(p); + + if (r < 0) + return -ENOMEM; + + f = fopen(fn, "re"); + free(fn); + + if (!f) + return -errno; + + while ((r = cg_read_pid(f, &pid)) > 0) { + + if (!kernel_threads && is_kernel_thread(pid) > 0) + continue; + + if (n >= n_allocated) { + pid_t *npids; + + n_allocated = MAX(16U, n*2U); + + if (!(npids = realloc(pids, sizeof(pid_t) * n_allocated))) { + r = -ENOMEM; + goto finish; + } + + pids = npids; + } + + assert(n < n_allocated); + pids[n++] = pid; + + if (pid > biggest) + biggest = pid; + } + + if (r < 0) + goto finish; + + if (n > 0) { + unsigned i, m; + + /* Filter duplicates */ + m = 0; + for (i = 0; i < n; i++) { + unsigned j; + + for (j = i+1; j < n; j++) + if (pids[i] == pids[j]) + break; + + if (j >= n) + pids[m++] = pids[i]; + } + n = m; + + /* And sort */ + qsort(pids, n, sizeof(pid_t), compare); + + if (n_columns > 8) + n_columns -= 8; + else + n_columns = 20; + + for (i = 0; i < n; i++) { + char *t = NULL; + + get_process_cmdline(pids[i], n_columns, true, &t); + + printf("%s%s %*lu %s\n", + prefix, + (more || i < n-1) ? "\342\224\234" : "\342\224\224", + (int) ilog10(biggest), + (unsigned long) pids[i], + strna(t)); + + free(t); + } + } + + r = 0; + +finish: + free(pids); + + if (f) + fclose(f); + + return r; +} + +int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads) { + DIR *d; + char *last = NULL; + char *p1 = NULL, *p2 = NULL, *fn = NULL, *gn = NULL; + bool shown_pids = false; + int r; + + if (n_columns <= 0) + n_columns = columns(); + + if (!prefix) + prefix = ""; + + if ((r = cg_fix_path(path, &fn)) < 0) + return r; + + if (!(d = opendir(fn))) { + free(fn); + return -errno; + } + + while ((r = cg_read_subgroup(d, &gn)) > 0) { + + if (!shown_pids) { + show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads); + shown_pids = true; + } + + if (last) { + printf("%s\342\224\234 %s\n", prefix, file_name_from_path(last)); + + if (!p1) + if (!(p1 = strappend(prefix, "\342\224\202 "))) { + r = -ENOMEM; + goto finish; + } + + show_cgroup_by_path(last, p1, n_columns-2, kernel_threads); + + free(last); + last = NULL; + } + + r = asprintf(&last, "%s/%s", fn, gn); + free(gn); + + if (r < 0) { + r = -ENOMEM; + goto finish; + } + } + + if (r < 0) + goto finish; + + if (!shown_pids) + show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads); + + if (last) { + printf("%s\342\224\224 %s\n", prefix, file_name_from_path(last)); + + if (!p2) + if (!(p2 = strappend(prefix, " "))) { + r = -ENOMEM; + goto finish; + } + + show_cgroup_by_path(last, p2, n_columns-2, kernel_threads); + } + + r = 0; + +finish: + free(p1); + free(p2); + free(last); + free(fn); + + closedir(d); + + return r; +} + +int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads) { + char *p; + int r; + + assert(controller); + assert(path); + + r = cg_get_path(controller, path, NULL, &p); + if (r < 0) + return r; + + r = show_cgroup_by_path(p, prefix, n_columns, kernel_threads); + free(p); + + return r; +} diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h new file mode 100644 index 0000000000..5433f46a53 --- /dev/null +++ b/src/shared/cgroup-show.h @@ -0,0 +1,30 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foocgroupshowhfoo +#define foocgroupshowhfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads); +int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, bool kernel_threads); + +#endif diff --git a/src/shared/dbus-common.c b/src/shared/dbus-common.c new file mode 100644 index 0000000000..038fdd41a0 --- /dev/null +++ b/src/shared/dbus-common.c @@ -0,0 +1,1096 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "dbus-common.h" +#include "util.h" +#include "def.h" +#include "strv.h" + +int bus_check_peercred(DBusConnection *c) { + int fd; + struct ucred ucred; + socklen_t l; + + assert(c); + + assert_se(dbus_connection_get_unix_fd(c, &fd)); + + l = sizeof(struct ucred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) { + log_error("SO_PEERCRED failed: %m"); + return -errno; + } + + if (l != sizeof(struct ucred)) { + log_error("SO_PEERCRED returned wrong size."); + return -E2BIG; + } + + if (ucred.uid != 0 && ucred.uid != geteuid()) + return -EPERM; + + return 1; +} + +static int sync_auth(DBusConnection *bus, DBusError *error) { + usec_t begin, tstamp; + + assert(bus); + + /* This complexity should probably move into D-Bus itself: + * + * https://bugs.freedesktop.org/show_bug.cgi?id=35189 */ + + begin = tstamp = now(CLOCK_MONOTONIC); + for (;;) { + + if (tstamp > begin + DEFAULT_TIMEOUT_USEC) + break; + + if (dbus_connection_get_is_authenticated(bus)) + break; + + if (!dbus_connection_read_write_dispatch(bus, ((begin + DEFAULT_TIMEOUT_USEC - tstamp) + USEC_PER_MSEC - 1) / USEC_PER_MSEC)) + break; + + tstamp = now(CLOCK_MONOTONIC); + } + + if (!dbus_connection_get_is_connected(bus)) { + dbus_set_error_const(error, DBUS_ERROR_NO_SERVER, "Connection terminated during authentication."); + return -ECONNREFUSED; + } + + if (!dbus_connection_get_is_authenticated(bus)) { + dbus_set_error_const(error, DBUS_ERROR_TIMEOUT, "Failed to authenticate in time."); + return -EACCES; + } + + return 0; +} + +int bus_connect(DBusBusType t, DBusConnection **_bus, bool *_private, DBusError *error) { + DBusConnection *bus = NULL; + int r; + bool private = true; + + assert(_bus); + + if (geteuid() == 0 && t == DBUS_BUS_SYSTEM) { + /* If we are root, then let's talk directly to the + * system instance, instead of going via the bus */ + + bus = dbus_connection_open_private("unix:path=/run/systemd/private", error); + if (!bus) + return -EIO; + + } else { + if (t == DBUS_BUS_SESSION) { + const char *e; + + /* If we are supposed to talk to the instance, + * try via XDG_RUNTIME_DIR first, then + * fallback to normal bus access */ + + e = getenv("XDG_RUNTIME_DIR"); + if (e) { + char *p; + + if (asprintf(&p, "unix:path=%s/systemd/private", e) < 0) + return -ENOMEM; + + bus = dbus_connection_open_private(p, NULL); + free(p); + } + } + + if (!bus) { + bus = dbus_bus_get_private(t, error); + if (!bus) + return -EIO; + + private = false; + } + } + + dbus_connection_set_exit_on_disconnect(bus, FALSE); + + if (private) { + if (bus_check_peercred(bus) < 0) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + + dbus_set_error_const(error, DBUS_ERROR_ACCESS_DENIED, "Failed to verify owner of bus."); + return -EACCES; + } + } + + r = sync_auth(bus, error); + if (r < 0) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + return r; + } + + if (_private) + *_private = private; + + *_bus = bus; + return 0; +} + +int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **_bus, DBusError *error) { + DBusConnection *bus; + char *p = NULL; + int r; + + assert(_bus); + assert(user || host); + + if (user && host) + asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s@%s,argv3=systemd-stdio-bridge", user, host); + else if (user) + asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s@localhost,argv3=systemd-stdio-bridge", user); + else if (host) + asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s,argv3=systemd-stdio-bridge", host); + + if (!p) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); + return -ENOMEM; + } + + bus = dbus_connection_open_private(p, error); + free(p); + + if (!bus) + return -EIO; + + dbus_connection_set_exit_on_disconnect(bus, FALSE); + + if ((r = sync_auth(bus, error)) < 0) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + return r; + } + + if (!dbus_bus_register(bus, error)) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + return r; + } + + *_bus = bus; + return 0; +} + +int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error) { + DBusConnection *bus; + int r; + + assert(_bus); + + /* Don't bother with PolicyKit if we are root */ + if (geteuid() == 0) + return bus_connect(DBUS_BUS_SYSTEM, _bus, NULL, error); + + bus = dbus_connection_open_private("unixexec:path=pkexec,argv1=" SYSTEMD_STDIO_BRIDGE_BINARY_PATH, error); + if (!bus) + return -EIO; + + dbus_connection_set_exit_on_disconnect(bus, FALSE); + + if ((r = sync_auth(bus, error)) < 0) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + return r; + } + + if (!dbus_bus_register(bus, error)) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + return r; + } + + *_bus = bus; + return 0; +} + +const char *bus_error_message(const DBusError *error) { + assert(error); + + /* Sometimes the D-Bus server is a little bit too verbose with + * its error messages, so let's override them here */ + if (dbus_error_has_name(error, DBUS_ERROR_ACCESS_DENIED)) + return "Access denied"; + + return error->message; +} + +DBusHandlerResult bus_default_message_handler( + DBusConnection *c, + DBusMessage *message, + const char *introspection, + const char *interfaces, + const BusBoundProperties *bound_properties) { + + DBusError error; + DBusMessage *reply = NULL; + int r; + + assert(c); + assert(message); + + dbus_error_init(&error); + + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") && introspection) { + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Get") && bound_properties) { + const char *interface, *property; + const BusBoundProperties *bp; + const BusProperty *p; + void *data; + DBusMessageIter iter, sub; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(c, message, &error, -EINVAL); + + for (bp = bound_properties; bp->interface; bp++) { + if (!streq(bp->interface, interface)) + continue; + + for (p = bp->properties; p->property; p++) + if (streq(p->property, property)) + goto get_prop; + } + + /* no match */ + if (!nulstr_contains(interfaces, interface)) + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); + else + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); + + return bus_send_error_reply(c, message, &error, -EINVAL); + +get_prop: + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + dbus_message_iter_init_append(reply, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, p->signature, &sub)) + goto oom; + + data = (char*)bp->base + p->offset; + if (p->indirect) + data = *(void**)data; + r = p->append(&sub, property, data); + if (r < 0) { + if (r == -ENOMEM) + goto oom; + + dbus_message_unref(reply); + return bus_send_error_reply(c, message, NULL, r); + } + + if (!dbus_message_iter_close_container(&iter, &sub)) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll") && bound_properties) { + const char *interface; + const BusBoundProperties *bp; + const BusProperty *p; + DBusMessageIter iter, sub, sub2, sub3; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(c, message, &error, -EINVAL); + + if (interface[0] && !nulstr_contains(interfaces, interface)) { + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); + return bus_send_error_reply(c, message, &error, -EINVAL); + } + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + dbus_message_iter_init_append(reply, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub)) + goto oom; + + for (bp = bound_properties; bp->interface; bp++) { + if (interface[0] && !streq(bp->interface, interface)) + continue; + + for (p = bp->properties; p->property; p++) { + void *data; + + if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, NULL, &sub2) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &p->property) || + !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, p->signature, &sub3)) + goto oom; + + data = (char*)bp->base + p->offset; + if (p->indirect) + data = *(void**)data; + r = p->append(&sub3, p->property, data); + if (r < 0) { + if (r == -ENOMEM) + goto oom; + + dbus_message_unref(reply); + return bus_send_error_reply(c, message, NULL, r); + } + + if (!dbus_message_iter_close_container(&sub2, &sub3) || + !dbus_message_iter_close_container(&sub, &sub2)) + goto oom; + } + } + + if (!dbus_message_iter_close_container(&iter, &sub)) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Set") && bound_properties) { + const char *interface, *property; + DBusMessageIter iter; + const BusBoundProperties *bp; + const BusProperty *p; + DBusMessageIter sub; + char *sig; + void *data; + + if (!dbus_message_iter_init(message, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return bus_send_error_reply(c, message, NULL, -EINVAL); + + dbus_message_iter_get_basic(&iter, &interface); + + if (!dbus_message_iter_next(&iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return bus_send_error_reply(c, message, NULL, -EINVAL); + + dbus_message_iter_get_basic(&iter, &property); + + if (!dbus_message_iter_next(&iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT || + dbus_message_iter_has_next(&iter)) + return bus_send_error_reply(c, message, NULL, -EINVAL); + + for (bp = bound_properties; bp->interface; bp++) { + if (!streq(bp->interface, interface)) + continue; + + for (p = bp->properties; p->property; p++) + if (streq(p->property, property)) + goto set_prop; + } + + /* no match */ + if (!nulstr_contains(interfaces, interface)) + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); + else + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property"); + + return bus_send_error_reply(c, message, &error, -EINVAL); + +set_prop: + if (!p->set) { + dbus_set_error_const(&error, DBUS_ERROR_PROPERTY_READ_ONLY, "Property read-only"); + return bus_send_error_reply(c, message, &error, -EINVAL); + } + + dbus_message_iter_recurse(&iter, &sub); + + sig = dbus_message_iter_get_signature(&sub); + if (!sig) + goto oom; + + if (!streq(sig, p->signature)) { + dbus_free(sig); + return bus_send_error_reply(c, message, NULL, -EINVAL); + } + + dbus_free(sig); + + data = (char*)bp->base + p->offset; + if (p->indirect) + data = *(void**)data; + r = p->set(&sub, property, data); + if (r < 0) { + if (r == -ENOMEM) + goto oom; + return bus_send_error_reply(c, message, NULL, r); + } + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else { + const char *interface = dbus_message_get_interface(message); + + if (!interface || !nulstr_contains(interfaces, interface)) { + dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface"); + return bus_send_error_reply(c, message, &error, -EINVAL); + } + } + + if (reply) { + if (!dbus_connection_send(c, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +int bus_property_append_string(DBusMessageIter *i, const char *property, void *data) { + const char *t = data; + + assert(i); + assert(property); + + if (!t) + t = ""; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t)) + return -ENOMEM; + + return 0; +} + +int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data) { + char **t = data; + + assert(i); + assert(property); + + return bus_append_strv_iter(i, t); +} + +int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data) { + bool *b = data; + dbus_bool_t db; + + assert(i); + assert(property); + assert(b); + + db = *b; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db)) + return -ENOMEM; + + return 0; +} + +int bus_property_append_tristate_false(DBusMessageIter *i, const char *property, void *data) { + int *b = data; + dbus_bool_t db; + + assert(i); + assert(property); + assert(b); + + db = *b > 0; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db)) + return -ENOMEM; + + return 0; +} + +int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data) { + assert(i); + assert(property); + assert(data); + + /* Let's ensure that usec_t is actually 64bit, and hence this + * function can be used for usec_t */ + assert_cc(sizeof(uint64_t) == sizeof(usec_t)); + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, data)) + return -ENOMEM; + + return 0; +} + +int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *data) { + assert(i); + assert(property); + assert(data); + + /* Let's ensure that pid_t, mode_t, uid_t, gid_t are actually + * 32bit, and hence this function can be used for + * pid_t/mode_t/uid_t/gid_t */ + assert_cc(sizeof(uint32_t) == sizeof(pid_t)); + assert_cc(sizeof(uint32_t) == sizeof(mode_t)); + assert_cc(sizeof(uint32_t) == sizeof(unsigned)); + assert_cc(sizeof(uint32_t) == sizeof(uid_t)); + assert_cc(sizeof(uint32_t) == sizeof(gid_t)); + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, data)) + return -ENOMEM; + + return 0; +} + +int bus_property_append_int32(DBusMessageIter *i, const char *property, void *data) { + assert(i); + assert(property); + assert(data); + + assert_cc(sizeof(int32_t) == sizeof(int)); + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, data)) + return -ENOMEM; + + return 0; +} + +int bus_property_append_size(DBusMessageIter *i, const char *property, void *data) { + uint64_t u; + + assert(i); + assert(property); + assert(data); + + u = (uint64_t) *(size_t*) data; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u)) + return -ENOMEM; + + return 0; +} + +int bus_property_append_ul(DBusMessageIter *i, const char *property, void *data) { + uint64_t u; + + assert(i); + assert(property); + assert(data); + + u = (uint64_t) *(unsigned long*) data; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u)) + return -ENOMEM; + + return 0; +} + +int bus_property_append_long(DBusMessageIter *i, const char *property, void *data) { + int64_t l; + + assert(i); + assert(property); + assert(data); + + l = (int64_t) *(long*) data; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT64, &l)) + return -ENOMEM; + + return 0; +} + +const char *bus_errno_to_dbus(int error) { + + switch(error) { + + case -EINVAL: + return DBUS_ERROR_INVALID_ARGS; + + case -ENOMEM: + return DBUS_ERROR_NO_MEMORY; + + case -EPERM: + case -EACCES: + return DBUS_ERROR_ACCESS_DENIED; + + case -ESRCH: + return DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN; + + case -ENOENT: + return DBUS_ERROR_FILE_NOT_FOUND; + + case -EEXIST: + return DBUS_ERROR_FILE_EXISTS; + + case -ETIMEDOUT: + case -ETIME: + return DBUS_ERROR_TIMEOUT; + + case -EIO: + return DBUS_ERROR_IO_ERROR; + + case -ENETRESET: + case -ECONNABORTED: + case -ECONNRESET: + return DBUS_ERROR_DISCONNECTED; + } + + return DBUS_ERROR_FAILED; +} + +DBusHandlerResult bus_send_error_reply(DBusConnection *c, DBusMessage *message, DBusError *berror, int error) { + DBusMessage *reply = NULL; + const char *name, *text; + + if (berror && dbus_error_is_set(berror)) { + name = berror->name; + text = berror->message; + } else { + name = bus_errno_to_dbus(error); + text = strerror(-error); + } + + if (!(reply = dbus_message_new_error(message, name, text))) + goto oom; + + if (!dbus_connection_send(c, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + if (berror) + dbus_error_free(berror); + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + if (berror) + dbus_error_free(berror); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties) { + DBusMessage *m; + DBusMessageIter iter, sub; + const char *i; + + assert(interface); + assert(properties); + + if (!(m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged"))) + goto oom; + + dbus_message_iter_init_append(m, &iter); + + /* We won't send any property values, since they might be + * large and sometimes not cheap to generated */ + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) || + !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) || + !dbus_message_iter_close_container(&iter, &sub) || + !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) + goto oom; + + NULSTR_FOREACH(i, properties) + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &i)) + goto oom; + + if (!dbus_message_iter_close_container(&iter, &sub)) + goto oom; + + return m; + +oom: + if (m) + dbus_message_unref(m); + + return NULL; +} + +uint32_t bus_flags_to_events(DBusWatch *bus_watch) { + unsigned flags; + uint32_t events = 0; + + assert(bus_watch); + + /* no watch flags for disabled watches */ + if (!dbus_watch_get_enabled(bus_watch)) + return 0; + + flags = dbus_watch_get_flags(bus_watch); + + if (flags & DBUS_WATCH_READABLE) + events |= EPOLLIN; + if (flags & DBUS_WATCH_WRITABLE) + events |= EPOLLOUT; + + return events | EPOLLHUP | EPOLLERR; +} + +unsigned bus_events_to_flags(uint32_t events) { + unsigned flags = 0; + + if (events & EPOLLIN) + flags |= DBUS_WATCH_READABLE; + if (events & EPOLLOUT) + flags |= DBUS_WATCH_WRITABLE; + if (events & EPOLLHUP) + flags |= DBUS_WATCH_HANGUP; + if (events & EPOLLERR) + flags |= DBUS_WATCH_ERROR; + + return flags; +} + +int bus_parse_strv(DBusMessage *m, char ***_l) { + DBusMessageIter iter; + + assert(m); + assert(_l); + + if (!dbus_message_iter_init(m, &iter)) + return -EINVAL; + + return bus_parse_strv_iter(&iter, _l); +} + +int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l) { + DBusMessageIter sub; + unsigned n = 0, i = 0; + char **l; + + assert(iter); + assert(_l); + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRING) + return -EINVAL; + + dbus_message_iter_recurse(iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + n++; + dbus_message_iter_next(&sub); + } + + if (!(l = new(char*, n+1))) + return -ENOMEM; + + dbus_message_iter_recurse(iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *s; + + assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic(&sub, &s); + + if (!(l[i++] = strdup(s))) { + strv_free(l); + return -ENOMEM; + } + + dbus_message_iter_next(&sub); + } + + assert(i == n); + l[i] = NULL; + + if (_l) + *_l = l; + + return 0; +} + +int bus_append_strv_iter(DBusMessageIter *iter, char **l) { + DBusMessageIter sub; + + assert(iter); + + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &sub)) + return -ENOMEM; + + STRV_FOREACH(l, l) + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, l)) + return -ENOMEM; + + if (!dbus_message_iter_close_container(iter, &sub)) + return -ENOMEM; + + return 0; +} + +int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) { + + assert(iter); + assert(data); + + if (dbus_message_iter_get_arg_type(iter) != type) + return -EIO; + + dbus_message_iter_get_basic(iter, data); + + if (!dbus_message_iter_next(iter) != !next) + return -EIO; + + return 0; +} + +int generic_print_property(const char *name, DBusMessageIter *iter, bool all) { + assert(name); + assert(iter); + + switch (dbus_message_iter_get_arg_type(iter)) { + + case DBUS_TYPE_STRING: { + const char *s; + dbus_message_iter_get_basic(iter, &s); + + if (all || !isempty(s)) + printf("%s=%s\n", name, s); + + return 1; + } + + case DBUS_TYPE_BOOLEAN: { + dbus_bool_t b; + + dbus_message_iter_get_basic(iter, &b); + printf("%s=%s\n", name, yes_no(b)); + + return 1; + } + + case DBUS_TYPE_UINT64: { + uint64_t u; + dbus_message_iter_get_basic(iter, &u); + + /* Yes, heuristics! But we can change this check + * should it turn out to not be sufficient */ + + if (endswith(name, "Timestamp")) { + char timestamp[FORMAT_TIMESTAMP_MAX], *t; + + t = format_timestamp(timestamp, sizeof(timestamp), u); + if (t || all) + printf("%s=%s\n", name, strempty(t)); + + } else if (strstr(name, "USec")) { + char timespan[FORMAT_TIMESPAN_MAX]; + + printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u)); + } else + printf("%s=%llu\n", name, (unsigned long long) u); + + return 1; + } + + case DBUS_TYPE_UINT32: { + uint32_t u; + dbus_message_iter_get_basic(iter, &u); + + if (strstr(name, "UMask") || strstr(name, "Mode")) + printf("%s=%04o\n", name, u); + else + printf("%s=%u\n", name, (unsigned) u); + + return 1; + } + + case DBUS_TYPE_INT32: { + int32_t i; + dbus_message_iter_get_basic(iter, &i); + + printf("%s=%i\n", name, (int) i); + return 1; + } + + case DBUS_TYPE_DOUBLE: { + double d; + dbus_message_iter_get_basic(iter, &d); + + printf("%s=%g\n", name, d); + return 1; + } + + case DBUS_TYPE_ARRAY: + + if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) { + DBusMessageIter sub; + bool space = false; + + dbus_message_iter_recurse(iter, &sub); + if (all || + dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + printf("%s=", name); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *s; + + assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic(&sub, &s); + printf("%s%s", space ? " " : "", s); + + space = true; + dbus_message_iter_next(&sub); + } + + puts(""); + } + + return 1; + + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) { + DBusMessageIter sub; + + dbus_message_iter_recurse(iter, &sub); + if (all || + dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + printf("%s=", name); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + uint8_t u; + + assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE); + dbus_message_iter_get_basic(&sub, &u); + printf("%02x", u); + + dbus_message_iter_next(&sub); + } + + puts(""); + } + + return 1; + } + + break; + } + + return 0; +} + +static void release_name_pending_cb(DBusPendingCall *pending, void *userdata) { + DBusMessage *reply; + DBusConnection *bus = userdata; + + assert_se(reply = dbus_pending_call_steal_reply(pending)); + dbus_message_unref(reply); + + dbus_connection_close(bus); +} + +void bus_async_unregister_and_exit(DBusConnection *bus, const char *name) { + DBusMessage *m = NULL; + DBusPendingCall *pending = NULL; + + assert(bus); + + /* We unregister the name here, but we continue to process + * requests, until we get the response for it, so that all + * requests are guaranteed to be processed. */ + + m = dbus_message_new_method_call( + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "ReleaseName"); + if (!m) + goto oom; + + if (!dbus_message_append_args( + m, + DBUS_TYPE_STRING, + &name, + DBUS_TYPE_INVALID)) + goto oom; + + if (!dbus_connection_send_with_reply(bus, m, &pending, -1)) + goto oom; + + if (!dbus_pending_call_set_notify(pending, release_name_pending_cb, bus, NULL)) + goto oom; + + dbus_message_unref(m); + dbus_pending_call_unref(pending); + + return; + +oom: + log_error("Out of memory"); + + if (pending) { + dbus_pending_call_cancel(pending); + dbus_pending_call_unref(pending); + } + + if (m) + dbus_message_unref(m); +} + +DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata) { + usec_t *remain_until = userdata; + + assert(bus); + assert(m); + assert(remain_until); + + /* Everytime we get a new message we reset out timeout */ + *remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC; + + if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) + dbus_connection_close(bus); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} diff --git a/src/shared/dbus-common.h b/src/shared/dbus-common.h new file mode 100644 index 0000000000..38d8e6538c --- /dev/null +++ b/src/shared/dbus-common.h @@ -0,0 +1,198 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foodbuscommonhfoo +#define foodbuscommonhfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#ifndef DBUS_ERROR_UNKNOWN_OBJECT +#define DBUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject" +#endif + +#ifndef DBUS_ERROR_UNKNOWN_INTERFACE +#define DBUS_ERROR_UNKNOWN_INTERFACE "org.freedesktop.DBus.Error.UnknownInterface" +#endif + +#ifndef DBUS_ERROR_UNKNOWN_PROPERTY +#define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty" +#endif + +#ifndef DBUS_ERROR_PROPERTY_READ_ONLY +#define DBUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly" +#endif + +#define BUS_PROPERTIES_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define BUS_INTROSPECTABLE_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define BUS_PEER_INTERFACE \ + "\n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + "\n" + +#define BUS_GENERIC_INTERFACES_LIST \ + "org.freedesktop.DBus.Properties\0" \ + "org.freedesktop.DBus.Introspectable\0" \ + "org.freedesktop.DBus.Peer\0" + +int bus_check_peercred(DBusConnection *c); + +int bus_connect(DBusBusType t, DBusConnection **_bus, bool *private_bus, DBusError *error); + +int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **_bus, DBusError *error); +int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error); + +const char *bus_error_message(const DBusError *error); + +typedef int (*BusPropertyCallback)(DBusMessageIter *iter, const char *property, void *data); +typedef int (*BusPropertySetCallback)(DBusMessageIter *iter, const char *property, void *data); + +typedef struct BusProperty { + const char *property; /* name of the property */ + BusPropertyCallback append; /* Function that is called to serialize this property */ + const char *signature; + const uint16_t offset; /* Offset from BusBoundProperties::base address to the property data. + * uint16_t is sufficient, because we have no structs too big. + * -Werror=overflow will catch it if this does not hold. */ + bool indirect; /* data is indirect, ie. not base+offset, but *(base+offset) */ + BusPropertySetCallback set; /* Optional: Function that is called to set this property */ +} BusProperty; + +typedef struct BusBoundProperties { + const char *interface; /* interface of the properties */ + const BusProperty *properties; /* array of properties, ended by a NULL-filled element */ + const void *const base; /* base pointer to which the offset must be added to reach data */ +} BusBoundProperties; + +DBusHandlerResult bus_send_error_reply( + DBusConnection *c, + DBusMessage *message, + DBusError *bus_error, + int error); + +DBusHandlerResult bus_default_message_handler( + DBusConnection *c, + DBusMessage *message, + const char *introspection, + const char *interfaces, + const BusBoundProperties *bound_properties); + +int bus_property_append_string(DBusMessageIter *i, const char *property, void *data); +int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data); +int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data); +int bus_property_append_tristate_false(DBusMessageIter *i, const char *property, void *data); +int bus_property_append_int32(DBusMessageIter *i, const char *property, void *data); +int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *data); +int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data); +int bus_property_append_size(DBusMessageIter *i, const char *property, void *data); +int bus_property_append_ul(DBusMessageIter *i, const char *property, void *data); +int bus_property_append_long(DBusMessageIter *i, const char *property, void *data); + +#define bus_property_append_int bus_property_append_int32 +#define bus_property_append_pid bus_property_append_uint32 +#define bus_property_append_uid bus_property_append_uint32 +#define bus_property_append_gid bus_property_append_uint32 +#define bus_property_append_mode bus_property_append_uint32 +#define bus_property_append_unsigned bus_property_append_uint32 +#define bus_property_append_usec bus_property_append_uint64 + +#define DEFINE_BUS_PROPERTY_APPEND_ENUM(function,name,type) \ + int function(DBusMessageIter *i, const char *property, void *data) { \ + const char *value; \ + type *field = data; \ + \ + assert(i); \ + assert(property); \ + \ + value = name##_to_string(*field); \ + \ + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &value)) \ + return -ENOMEM; \ + \ + return 0; \ + } + +#define DEFINE_BUS_PROPERTY_SET_ENUM(function,name,type) \ + int function(DBusMessageIter *i, const char *property, void *data) { \ + const char *value; \ + type *field = data; \ + \ + assert(i); \ + assert(property); \ + \ + dbus_message_iter_get_basic(i, &value); \ + \ + *field = name##_from_string(value); \ + \ + return 0; \ + } + +const char *bus_errno_to_dbus(int error); + +DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties); + +uint32_t bus_flags_to_events(DBusWatch *bus_watch); +unsigned bus_events_to_flags(uint32_t events); + +int bus_parse_strv(DBusMessage *m, char ***_l); +int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l); + +int bus_append_strv_iter(DBusMessageIter *iter, char **l); + +int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next); + +int generic_print_property(const char *name, DBusMessageIter *iter, bool all); + +void bus_async_unregister_and_exit(DBusConnection *bus, const char *name); + +DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata); + +#endif diff --git a/src/shared/hostname-setup.c b/src/shared/hostname-setup.c new file mode 100644 index 0000000000..550d3c2113 --- /dev/null +++ b/src/shared/hostname-setup.c @@ -0,0 +1,187 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "hostname-setup.h" +#include "macro.h" +#include "util.h" +#include "log.h" + +#if defined(TARGET_FEDORA) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MEEGO) || defined(TARGET_MAGEIA) +#define FILENAME "/etc/sysconfig/network" +#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE) +#define FILENAME "/etc/HOSTNAME" +#elif defined(TARGET_ARCH) +#define FILENAME "/etc/rc.conf" +#elif defined(TARGET_GENTOO) +#define FILENAME "/etc/conf.d/hostname" +#endif + +static int read_and_strip_hostname(const char *path, char **hn) { + char *s; + int r; + + assert(path); + assert(hn); + + if ((r = read_one_line_file(path, &s)) < 0) + return r; + + hostname_cleanup(s); + + if (isempty(s)) { + free(s); + return -ENOENT; + } + + *hn = s; + + return 0; +} + +static int read_distro_hostname(char **hn) { + +#if defined(TARGET_FEDORA) || defined(TARGET_ARCH) || defined(TARGET_GENTOO) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MEEGO) || defined(TARGET_MAGEIA) + int r; + FILE *f; + + assert(hn); + + if (!(f = fopen(FILENAME, "re"))) + return -errno; + + for (;;) { + char line[LINE_MAX]; + char *s, *k; + + if (!fgets(line, sizeof(line), f)) { + if (feof(f)) + break; + + r = -errno; + goto finish; + } + + s = strstrip(line); + + if (!startswith_no_case(s, "HOSTNAME=")) + continue; + + if (!(k = strdup(s+9))) { + r = -ENOMEM; + goto finish; + } + + hostname_cleanup(k); + + if (isempty(k)) { + free(k); + r = -ENOENT; + goto finish; + } + + *hn = k; + r = 0; + goto finish; + } + + r = -ENOENT; + +finish: + fclose(f); + return r; + +#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE) + return read_and_strip_hostname(FILENAME, hn); +#else + return -ENOENT; +#endif +} + +static int read_hostname(char **hn) { + int r; + + assert(hn); + + /* First, try to load the generic hostname configuration file, + * that we support on all distributions */ + + if ((r = read_and_strip_hostname("/etc/hostname", hn)) < 0) { + + if (r == -ENOENT) + return read_distro_hostname(hn); + + return r; + } + + return 0; +} + +int hostname_setup(void) { + int r; + char *b = NULL; + const char *hn = NULL; + + if ((r = read_hostname(&b)) < 0) { + if (r == -ENOENT) + log_info("No hostname configured."); + else + log_warning("Failed to read configured hostname: %s", strerror(-r)); + + hn = NULL; + } else + hn = b; + + if (!hn) { + /* Don't override the hostname if it is unset and not + * explicitly configured */ + + char *old_hostname = NULL; + + if ((old_hostname = gethostname_malloc())) { + bool already_set; + + already_set = old_hostname[0] != 0; + free(old_hostname); + + if (already_set) + goto finish; + } + + hn = "localhost"; + } + + if (sethostname(hn, strlen(hn)) < 0) { + log_warning("Failed to set hostname to <%s>: %m", hn); + r = -errno; + } else + log_info("Set hostname to <%s>.", hn); + +finish: + free(b); + + return r; +} diff --git a/src/shared/hostname-setup.h b/src/shared/hostname-setup.h new file mode 100644 index 0000000000..9550b8c5ab --- /dev/null +++ b/src/shared/hostname-setup.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foohostnamesetuphfoo +#define foohostnamesetuphfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int hostname_setup(void); + +#endif diff --git a/src/shared/install.c b/src/shared/install.c new file mode 100644 index 0000000000..080ae6a01d --- /dev/null +++ b/src/shared/install.c @@ -0,0 +1,1954 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty . +***/ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "mkdir.h" +#include "hashmap.h" +#include "set.h" +#include "path-lookup.h" +#include "strv.h" +#include "unit-name.h" +#include "install.h" +#include "conf-parser.h" + +typedef struct { + char *name; + char *path; + + char **aliases; + char **wanted_by; +} InstallInfo; + +typedef struct { + Hashmap *will_install; + Hashmap *have_installed; +} InstallContext; + +static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope) { + assert(paths); + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + zero(*paths); + + return lookup_paths_init(paths, + scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER, + scope == UNIT_FILE_USER); +} + +static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) { + char *p = NULL; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(ret); + + switch (scope) { + + case UNIT_FILE_SYSTEM: + + if (root_dir && runtime) + asprintf(&p, "%s/run/systemd/system", root_dir); + else if (runtime) + p = strdup("/run/systemd/system"); + else if (root_dir) + asprintf(&p, "%s/%s", root_dir, SYSTEM_CONFIG_UNIT_PATH); + else + p = strdup(SYSTEM_CONFIG_UNIT_PATH); + + break; + + case UNIT_FILE_GLOBAL: + + if (root_dir) + return -EINVAL; + + if (runtime) + p = strdup("/run/systemd/user"); + else + p = strdup(USER_CONFIG_UNIT_PATH); + break; + + case UNIT_FILE_USER: + + if (root_dir || runtime) + return -EINVAL; + + r = user_config_home(&p); + if (r <= 0) + return r < 0 ? r : -ENOENT; + + break; + + default: + assert_not_reached("Bad scope"); + } + + if (!p) + return -ENOMEM; + + *ret = p; + return 0; +} + +static int add_file_change( + UnitFileChange **changes, + unsigned *n_changes, + UnitFileChangeType type, + const char *path, + const char *source) { + + UnitFileChange *c; + unsigned i; + + assert(path); + assert(!changes == !n_changes); + + if (!changes) + return 0; + + c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange)); + if (!c) + return -ENOMEM; + + *changes = c; + i = *n_changes; + + c[i].type = type; + c[i].path = strdup(path); + if (!c[i].path) + return -ENOMEM; + + if (source) { + c[i].source = strdup(source); + if (!c[i].source) { + free(c[i].path); + return -ENOMEM; + } + } else + c[i].source = NULL; + + *n_changes = i+1; + return 0; +} + +static int mark_symlink_for_removal( + Set **remove_symlinks_to, + const char *p) { + + char *n; + int r; + + assert(p); + + r = set_ensure_allocated(remove_symlinks_to, string_hash_func, string_compare_func); + if (r < 0) + return r; + + n = strdup(p); + if (!n) + return -ENOMEM; + + path_kill_slashes(n); + + r = set_put(*remove_symlinks_to, n); + if (r < 0) { + free(n); + return r == -EEXIST ? 0 : r; + } + + return 0; +} + +static int remove_marked_symlinks_fd( + Set *remove_symlinks_to, + int fd, + const char *path, + const char *config_path, + bool *deleted, + UnitFileChange **changes, + unsigned *n_changes) { + + int r = 0; + DIR *d; + struct dirent buffer, *de; + + assert(remove_symlinks_to); + assert(fd >= 0); + assert(path); + assert(config_path); + assert(deleted); + + d = fdopendir(fd); + if (!d) { + close_nointr_nofail(fd); + return -errno; + } + + rewinddir(d); + + for (;;) { + int k; + + k = readdir_r(d, &buffer, &de); + if (k != 0) { + r = -errno; + break; + } + + if (!de) + break; + + if (ignore_file(de->d_name)) + continue; + + dirent_ensure_type(d, de); + + if (de->d_type == DT_DIR) { + int nfd, q; + char *p; + + nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (nfd < 0) { + if (errno == ENOENT) + continue; + + if (r == 0) + r = -errno; + continue; + } + + p = path_make_absolute(de->d_name, path); + if (!p) { + close_nointr_nofail(nfd); + r = -ENOMEM; + break; + } + + /* This will close nfd, regardless whether it succeeds or not */ + q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes); + free(p); + + if (r == 0) + r = q; + + } else if (de->d_type == DT_LNK) { + char *p, *dest; + int q; + bool found; + + p = path_make_absolute(de->d_name, path); + if (!p) { + r = -ENOMEM; + break; + } + + q = readlink_and_canonicalize(p, &dest); + if (q < 0) { + free(p); + + if (q == -ENOENT) + continue; + + if (r == 0) + r = q; + continue; + } + + found = + set_get(remove_symlinks_to, dest) || + set_get(remove_symlinks_to, file_name_from_path(dest)); + + if (found) { + + if (unlink(p) < 0 && errno != ENOENT) { + + if (r == 0) + r = -errno; + } else { + rmdir_parents(p, config_path); + path_kill_slashes(p); + + add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); + + if (!set_get(remove_symlinks_to, p)) { + + q = mark_symlink_for_removal(&remove_symlinks_to, p); + if (q < 0) { + if (r == 0) + r = q; + } else + *deleted = true; + } + } + } + + free(p); + free(dest); + } + } + + closedir(d); + + return r; +} + +static int remove_marked_symlinks( + Set *remove_symlinks_to, + const char *config_path, + UnitFileChange **changes, + unsigned *n_changes) { + + int fd, r = 0; + bool deleted; + + assert(config_path); + + if (set_size(remove_symlinks_to) <= 0) + return 0; + + fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (fd < 0) + return -errno; + + do { + int q, cfd; + deleted = false; + + cfd = dup(fd); + if (cfd < 0) { + r = -errno; + break; + } + + /* This takes possession of cfd and closes it */ + q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes); + if (r == 0) + r = q; + } while (deleted); + + close_nointr_nofail(fd); + + return r; +} + +static int find_symlinks_fd( + const char *name, + int fd, + const char *path, + const char *config_path, + bool *same_name_link) { + + int r = 0; + DIR *d; + struct dirent buffer, *de; + + assert(name); + assert(fd >= 0); + assert(path); + assert(config_path); + assert(same_name_link); + + d = fdopendir(fd); + if (!d) { + close_nointr_nofail(fd); + return -errno; + } + + for (;;) { + int k; + + k = readdir_r(d, &buffer, &de); + if (k != 0) { + r = -errno; + break; + } + + if (!de) + break; + + if (ignore_file(de->d_name)) + continue; + + dirent_ensure_type(d, de); + + if (de->d_type == DT_DIR) { + int nfd, q; + char *p; + + nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (nfd < 0) { + if (errno == ENOENT) + continue; + + if (r == 0) + r = -errno; + continue; + } + + p = path_make_absolute(de->d_name, path); + if (!p) { + close_nointr_nofail(nfd); + r = -ENOMEM; + break; + } + + /* This will close nfd, regardless whether it succeeds or not */ + q = find_symlinks_fd(name, nfd, p, config_path, same_name_link); + free(p); + + if (q > 0) { + r = 1; + break; + } + + if (r == 0) + r = q; + + } else if (de->d_type == DT_LNK) { + char *p, *dest; + bool found_path, found_dest, b = false; + int q; + + /* Acquire symlink name */ + p = path_make_absolute(de->d_name, path); + if (!p) { + r = -ENOMEM; + break; + } + + /* Acquire symlink destination */ + q = readlink_and_canonicalize(p, &dest); + if (q < 0) { + free(p); + + if (q == -ENOENT) + continue; + + if (r == 0) + r = q; + continue; + } + + /* Check if the symlink itself matches what we + * are looking for */ + if (path_is_absolute(name)) + found_path = path_equal(p, name); + else + found_path = streq(de->d_name, name); + + /* Check if what the symlink points to + * matches what we are looking for */ + if (path_is_absolute(name)) + found_dest = path_equal(dest, name); + else + found_dest = streq(file_name_from_path(dest), name); + + free(dest); + + if (found_path && found_dest) { + char *t; + + /* Filter out same name links in the main + * config path */ + t = path_make_absolute(name, config_path); + if (!t) { + free(p); + r = -ENOMEM; + break; + } + + b = path_equal(t, p); + free(t); + } + + free(p); + + if (b) + *same_name_link = true; + else if (found_path || found_dest) { + r = 1; + break; + } + } + } + + closedir(d); + + return r; +} + +static int find_symlinks( + const char *name, + const char *config_path, + bool *same_name_link) { + + int fd; + + assert(name); + assert(config_path); + assert(same_name_link); + + fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (fd < 0) + return -errno; + + /* This takes possession of fd and closes it */ + return find_symlinks_fd(name, fd, config_path, config_path, same_name_link); +} + +static int find_symlinks_in_scope( + UnitFileScope scope, + const char *root_dir, + const char *name, + UnitFileState *state) { + + int r; + char *path; + bool same_name_link_runtime = false, same_name_link = false; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(name); + + if (scope == UNIT_FILE_SYSTEM || scope == UNIT_FILE_GLOBAL) { + + /* First look in runtime config path */ + r = get_config_path(scope, true, root_dir, &path); + if (r < 0) + return r; + + r = find_symlinks(name, path, &same_name_link_runtime); + free(path); + + if (r < 0) + return r; + else if (r > 0) { + *state = UNIT_FILE_ENABLED_RUNTIME; + return r; + } + } + + /* Then look in the normal config path */ + r = get_config_path(scope, false, root_dir, &path); + if (r < 0) + return r; + + r = find_symlinks(name, path, &same_name_link); + free(path); + + if (r < 0) + return r; + else if (r > 0) { + *state = UNIT_FILE_ENABLED; + return r; + } + + /* Hmm, we didn't find it, but maybe we found the same name + * link? */ + if (same_name_link_runtime) { + *state = UNIT_FILE_LINKED_RUNTIME; + return 1; + } else if (same_name_link) { + *state = UNIT_FILE_LINKED; + return 1; + } + + return 0; +} + +int unit_file_mask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + char **i, *prefix; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + r = get_config_path(scope, runtime, root_dir, &prefix); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + char *path; + + if (!unit_name_is_valid_no_type(*i, true)) { + if (r == 0) + r = -EINVAL; + continue; + } + + path = path_make_absolute(*i, prefix); + if (!path) { + r = -ENOMEM; + break; + } + + if (symlink("/dev/null", path) >= 0) { + add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null"); + + free(path); + continue; + } + + if (errno == EEXIST) { + + if (null_or_empty_path(path) > 0) { + free(path); + continue; + } + + if (force) { + unlink(path); + + if (symlink("/dev/null", path) >= 0) { + + add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null"); + + free(path); + continue; + } + } + + if (r == 0) + r = -EEXIST; + } else { + if (r == 0) + r = -errno; + } + + free(path); + } + + free(prefix); + + return r; +} + +int unit_file_unmask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + UnitFileChange **changes, + unsigned *n_changes) { + + char **i, *config_path = NULL; + int r, q; + Set *remove_symlinks_to = NULL; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + goto finish; + + STRV_FOREACH(i, files) { + char *path; + + if (!unit_name_is_valid_no_type(*i, true)) { + if (r == 0) + r = -EINVAL; + continue; + } + + path = path_make_absolute(*i, config_path); + if (!path) { + r = -ENOMEM; + break; + } + + q = null_or_empty_path(path); + if (q > 0) { + if (unlink(path) >= 0) { + mark_symlink_for_removal(&remove_symlinks_to, path); + add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + + free(path); + continue; + } + + q = -errno; + } + + if (q != -ENOENT && r == 0) + r = q; + + free(path); + } + + +finish: + q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + if (r == 0) + r = q; + + set_free_free(remove_symlinks_to); + free(config_path); + + return r; +} + +int unit_file_link( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + LookupPaths paths; + char **i, *config_path = NULL; + int r, q; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + zero(paths); + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + goto finish; + + STRV_FOREACH(i, files) { + char *path, *fn; + struct stat st; + + fn = file_name_from_path(*i); + + if (!path_is_absolute(*i) || + !unit_name_is_valid_no_type(fn, true)) { + if (r == 0) + r = -EINVAL; + continue; + } + + if (lstat(*i, &st) < 0) { + if (r == 0) + r = -errno; + continue; + } + + if (!S_ISREG(st.st_mode)) { + r = -ENOENT; + continue; + } + + q = in_search_path(*i, paths.unit_path); + if (q < 0) { + r = q; + break; + } + + if (q > 0) + continue; + + path = path_make_absolute(fn, config_path); + if (!path) { + r = -ENOMEM; + break; + } + + if (symlink(*i, path) >= 0) { + add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i); + + free(path); + continue; + } + + if (errno == EEXIST) { + char *dest = NULL; + + q = readlink_and_make_absolute(path, &dest); + + if (q < 0 && errno != ENOENT) { + free(path); + + if (r == 0) + r = q; + + continue; + } + + if (q >= 0 && path_equal(dest, *i)) { + free(dest); + free(path); + continue; + } + + free(dest); + + if (force) { + unlink(path); + + if (symlink(*i, path) >= 0) { + + add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i); + + free(path); + continue; + } + } + + if (r == 0) + r = -EEXIST; + } else { + if (r == 0) + r = -errno; + } + + free(path); + } + + finish: + lookup_paths_free(&paths); + free(config_path); + + return r; +} + +void unit_file_list_free(Hashmap *h) { + UnitFileList *i; + + while ((i = hashmap_steal_first(h))) { + free(i->path); + free(i); + } + + hashmap_free(h); +} + +void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { + unsigned i; + + assert(changes || n_changes == 0); + + if (!changes) + return; + + for (i = 0; i < n_changes; i++) { + free(changes[i].path); + free(changes[i].source); + } + + free(changes); +} + +static void install_info_free(InstallInfo *i) { + assert(i); + + free(i->name); + free(i->path); + strv_free(i->aliases); + strv_free(i->wanted_by); + free(i); +} + +static void install_info_hashmap_free(Hashmap *m) { + InstallInfo *i; + + if (!m) + return; + + while ((i = hashmap_steal_first(m))) + install_info_free(i); + + hashmap_free(m); +} + +static void install_context_done(InstallContext *c) { + assert(c); + + install_info_hashmap_free(c->will_install); + install_info_hashmap_free(c->have_installed); + + c->will_install = c->have_installed = NULL; +} + +static int install_info_add( + InstallContext *c, + const char *name, + const char *path) { + InstallInfo *i = NULL; + int r; + + assert(c); + assert(name || path); + + if (!name) + name = file_name_from_path(path); + + if (!unit_name_is_valid_no_type(name, true)) + return -EINVAL; + + if (hashmap_get(c->have_installed, name) || + hashmap_get(c->will_install, name)) + return 0; + + r = hashmap_ensure_allocated(&c->will_install, string_hash_func, string_compare_func); + if (r < 0) + return r; + + i = new0(InstallInfo, 1); + if (!i) + return -ENOMEM; + + i->name = strdup(name); + if (!i->name) { + r = -ENOMEM; + goto fail; + } + + if (path) { + i->path = strdup(path); + if (!i->path) { + r = -ENOMEM; + goto fail; + } + } + + r = hashmap_put(c->will_install, i->name, i); + if (r < 0) + goto fail; + + return 0; + +fail: + if (i) + install_info_free(i); + + return r; +} + +static int install_info_add_auto( + InstallContext *c, + const char *name_or_path) { + + assert(c); + assert(name_or_path); + + if (path_is_absolute(name_or_path)) + return install_info_add(c, NULL, name_or_path); + else + return install_info_add(c, name_or_path, NULL); +} + +static int config_parse_also( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char *w; + size_t l; + char *state; + InstallContext *c = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + + FOREACH_WORD_QUOTED(w, l, rvalue, state) { + char *n; + int r; + + n = strndup(w, l); + if (!n) + return -ENOMEM; + + r = install_info_add(c, n, NULL); + if (r < 0) { + free(n); + return r; + } + + free(n); + } + + return 0; +} + +static int unit_file_load( + InstallContext *c, + InstallInfo *info, + const char *path, + bool allow_symlink) { + + const ConfigTableItem items[] = { + { "Install", "Alias", config_parse_strv, 0, &info->aliases }, + { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, + { "Install", "Also", config_parse_also, 0, c }, + { NULL, NULL, NULL, 0, NULL } + }; + + int fd; + FILE *f; + int r; + + assert(c); + assert(info); + assert(path); + + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW)); + if (fd < 0) + return -errno; + + f = fdopen(fd, "re"); + if (!f) { + close_nointr_nofail(fd); + return -ENOMEM; + } + + r = config_parse(path, f, NULL, config_item_table_lookup, (void*) items, true, info); + fclose(f); + if (r < 0) + return r; + + return strv_length(info->aliases) + strv_length(info->wanted_by); +} + +static int unit_file_search( + InstallContext *c, + InstallInfo *info, + LookupPaths *paths, + const char *root_dir, + bool allow_symlink) { + + char **p; + int r; + + assert(c); + assert(info); + assert(paths); + + if (info->path) + return unit_file_load(c, info, info->path, allow_symlink); + + assert(info->name); + + STRV_FOREACH(p, paths->unit_path) { + char *path = NULL; + + if (isempty(root_dir)) + asprintf(&path, "%s/%s", *p, info->name); + else + asprintf(&path, "%s/%s/%s", root_dir, *p, info->name); + + if (!path) + return -ENOMEM; + + r = unit_file_load(c, info, path, allow_symlink); + + if (r >= 0) + info->path = path; + else + free(path); + + if (r != -ENOENT && r != -ELOOP) + return r; + } + + return -ENOENT; +} + +static int unit_file_can_install( + LookupPaths *paths, + const char *root_dir, + const char *name, + bool allow_symlink) { + + InstallContext c; + InstallInfo *i; + int r; + + assert(paths); + assert(name); + + zero(c); + + r = install_info_add_auto(&c, name); + if (r < 0) + return r; + + assert_se(i = hashmap_first(c.will_install)); + + r = unit_file_search(&c, i, paths, root_dir, allow_symlink); + + if (r >= 0) + r = strv_length(i->aliases) + strv_length(i->wanted_by); + + install_context_done(&c); + + return r; +} + +static int create_symlink( + const char *old_path, + const char *new_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + char *dest; + int r; + + assert(old_path); + assert(new_path); + + mkdir_parents(new_path, 0755); + + if (symlink(old_path, new_path) >= 0) { + add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); + return 0; + } + + if (errno != EEXIST) + return -errno; + + r = readlink_and_make_absolute(new_path, &dest); + if (r < 0) + return r; + + if (path_equal(dest, old_path)) { + free(dest); + return 0; + } + + free(dest); + + if (force) + return -EEXIST; + + unlink(new_path); + + if (symlink(old_path, new_path) >= 0) { + add_file_change(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL); + add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); + return 0; + } + + return -errno; +} + +static int install_info_symlink_alias( + InstallInfo *i, + const char *config_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + char **s; + int r = 0, q; + + assert(i); + assert(config_path); + + STRV_FOREACH(s, i->aliases) { + char *alias_path; + + alias_path = path_make_absolute(*s, config_path); + + if (!alias_path) + return -ENOMEM; + + q = create_symlink(i->path, alias_path, force, changes, n_changes); + free(alias_path); + + if (r == 0) + r = q; + } + + return r; +} + +static int install_info_symlink_wants( + InstallInfo *i, + const char *config_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + char **s; + int r = 0, q; + + assert(i); + assert(config_path); + + STRV_FOREACH(s, i->wanted_by) { + char *path; + + if (!unit_name_is_valid_no_type(*s, true)) { + r = -EINVAL; + continue; + } + + if (asprintf(&path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) + return -ENOMEM; + + q = create_symlink(i->path, path, force, changes, n_changes); + free(path); + + if (r == 0) + r = q; + } + + return r; +} + +static int install_info_symlink_link( + InstallInfo *i, + LookupPaths *paths, + const char *config_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + int r; + char *path; + + assert(i); + assert(paths); + assert(config_path); + assert(i->path); + + r = in_search_path(i->path, paths->unit_path); + if (r != 0) + return r; + + if (asprintf(&path, "%s/%s", config_path, i->name) < 0) + return -ENOMEM; + + r = create_symlink(i->path, path, force, changes, n_changes); + free(path); + + return r; +} + +static int install_info_apply( + InstallInfo *i, + LookupPaths *paths, + const char *config_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + int r, q; + + assert(i); + assert(paths); + assert(config_path); + + r = install_info_symlink_alias(i, config_path, force, changes, n_changes); + + q = install_info_symlink_wants(i, config_path, force, changes, n_changes); + if (r == 0) + r = q; + + q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes); + if (r == 0) + r = q; + + return r; +} + +static int install_context_apply( + InstallContext *c, + LookupPaths *paths, + const char *config_path, + const char *root_dir, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + InstallInfo *i; + int r = 0, q; + + assert(c); + assert(paths); + assert(config_path); + + while ((i = hashmap_first(c->will_install))) { + + q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func); + if (q < 0) + return q; + + assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); + + q = unit_file_search(c, i, paths, root_dir, false); + if (q < 0) { + if (r >= 0) + r = q; + + return r; + } else if (r >= 0) + r += q; + + q = install_info_apply(i, paths, config_path, force, changes, n_changes); + if (r >= 0 && q < 0) + r = q; + } + + return r; +} + +static int install_context_mark_for_removal( + InstallContext *c, + LookupPaths *paths, + Set **remove_symlinks_to, + const char *config_path, + const char *root_dir) { + + InstallInfo *i; + int r = 0, q; + + assert(c); + assert(paths); + assert(config_path); + + /* Marks all items for removal */ + + while ((i = hashmap_first(c->will_install))) { + + q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func); + if (q < 0) + return q; + + assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); + + q = unit_file_search(c, i, paths, root_dir, false); + if (q < 0) { + if (r >= 0) + r = q; + + return r; + } else if (r >= 0) + r += q; + + q = mark_symlink_for_removal(remove_symlinks_to, i->name); + if (r >= 0 && q < 0) + r = q; + } + + return r; +} + +int unit_file_enable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + LookupPaths paths; + InstallContext c; + char **i, *config_path = NULL; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + zero(paths); + zero(c); + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + goto finish; + + STRV_FOREACH(i, files) { + r = install_info_add_auto(&c, *i); + if (r < 0) + goto finish; + } + + /* This will return the number of symlink rules that were + supposed to be created, not the ones actually created. This is + useful to determine whether the passed files hat any + installation data at all. */ + r = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes); + +finish: + install_context_done(&c); + lookup_paths_free(&paths); + free(config_path); + + return r; +} + +int unit_file_disable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + UnitFileChange **changes, + unsigned *n_changes) { + + LookupPaths paths; + InstallContext c; + char **i, *config_path = NULL; + Set *remove_symlinks_to = NULL; + int r, q; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + zero(paths); + zero(c); + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + goto finish; + + STRV_FOREACH(i, files) { + r = install_info_add_auto(&c, *i); + if (r < 0) + goto finish; + } + + r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir); + + q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + if (r == 0) + r = q; + +finish: + install_context_done(&c); + lookup_paths_free(&paths); + set_free_free(remove_symlinks_to); + free(config_path); + + return r; +} + +int unit_file_reenable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + LookupPaths paths; + InstallContext c; + char **i, *config_path = NULL; + Set *remove_symlinks_to = NULL; + int r, q; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + zero(paths); + zero(c); + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + goto finish; + + STRV_FOREACH(i, files) { + r = mark_symlink_for_removal(&remove_symlinks_to, *i); + if (r < 0) + goto finish; + + r = install_info_add_auto(&c, *i); + if (r < 0) + goto finish; + } + + r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + + /* Returns number of symlinks that where supposed to be installed. */ + q = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes); + if (r == 0) + r = q; + +finish: + lookup_paths_free(&paths); + install_context_done(&c); + set_free_free(remove_symlinks_to); + free(config_path); + + return r; +} + +UnitFileState unit_file_get_state( + UnitFileScope scope, + const char *root_dir, + const char *name) { + + LookupPaths paths; + UnitFileState state = _UNIT_FILE_STATE_INVALID; + char **i, *path = NULL; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(name); + + zero(paths); + + if (root_dir && scope != UNIT_FILE_SYSTEM) + return -EINVAL; + + if (!unit_name_is_valid_no_type(name, true)) + return -EINVAL; + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + STRV_FOREACH(i, paths.unit_path) { + struct stat st; + + free(path); + path = NULL; + + if (root_dir) + asprintf(&path, "%s/%s/%s", root_dir, *i, name); + else + asprintf(&path, "%s/%s", *i, name); + + if (!path) { + r = -ENOMEM; + goto finish; + } + + if (lstat(path, &st) < 0) { + r = -errno; + if (errno == ENOENT) + continue; + + goto finish; + } + + if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { + r = -ENOENT; + goto finish; + } + + r = null_or_empty_path(path); + if (r < 0 && r != -ENOENT) + goto finish; + else if (r > 0) { + state = path_startswith(*i, "/run") ? + UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; + r = 0; + goto finish; + } + + r = find_symlinks_in_scope(scope, root_dir, name, &state); + if (r < 0) { + goto finish; + } else if (r > 0) { + r = 0; + goto finish; + } + + r = unit_file_can_install(&paths, root_dir, path, true); + if (r < 0 && errno != -ENOENT) + goto finish; + else if (r > 0) { + state = UNIT_FILE_DISABLED; + r = 0; + goto finish; + } else if (r == 0) { + state = UNIT_FILE_STATIC; + r = 0; + goto finish; + } + } + +finish: + lookup_paths_free(&paths); + free(path); + + return r < 0 ? r : state; +} + +int unit_file_query_preset(UnitFileScope scope, const char *name) { + char **files, **i; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(name); + + if (scope == UNIT_FILE_SYSTEM) + r = conf_files_list(&files, ".preset", + "/etc/systemd/system.preset", + "/usr/local/lib/systemd/system.preset", + "/usr/lib/systemd/system.preset", + "/lib/systemd/system.preset", + NULL); + else if (scope == UNIT_FILE_GLOBAL) + r = conf_files_list(&files, ".preset", + "/etc/systemd/user.preset", + "/usr/local/lib/systemd/user.preset", + "/usr/lib/systemd/user.preset", + NULL); + else + return 1; + + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + FILE *f; + + f = fopen(*i, "re"); + if (!f) { + if (errno == ENOENT) + continue; + + r = -errno; + goto finish; + } + + for (;;) { + char line[LINE_MAX], *l; + + if (!fgets(line, sizeof(line), f)) + break; + + l = strstrip(line); + if (!*l) + continue; + + if (strchr(COMMENTS, *l)) + continue; + + if (first_word(l, "enable")) { + l += 6; + l += strspn(l, WHITESPACE); + + if (fnmatch(l, name, FNM_NOESCAPE) == 0) { + r = 1; + fclose(f); + goto finish; + } + } else if (first_word(l, "disable")) { + l += 7; + l += strspn(l, WHITESPACE); + + if (fnmatch(l, name, FNM_NOESCAPE) == 0) { + r = 0; + fclose(f); + goto finish; + } + } else + log_debug("Couldn't parse line '%s'", l); + } + + fclose(f); + } + + /* Default is "enable" */ + r = 1; + +finish: + strv_free(files); + + return r; +} + +int unit_file_preset( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + LookupPaths paths; + InstallContext plus, minus; + char **i, *config_path = NULL; + Set *remove_symlinks_to = NULL; + int r, q; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + zero(paths); + zero(plus); + zero(minus); + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + goto finish; + + STRV_FOREACH(i, files) { + + if (!unit_name_is_valid_no_type(*i, true)) { + r = -EINVAL; + goto finish; + } + + r = unit_file_query_preset(scope, *i); + if (r < 0) + goto finish; + + if (r) + r = install_info_add_auto(&plus, *i); + else + r = install_info_add_auto(&minus, *i); + + if (r < 0) + goto finish; + } + + r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir); + + q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + if (r == 0) + r = q; + + /* Returns number of symlinks that where supposed to be installed. */ + q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes); + if (r == 0) + r = q; + +finish: + lookup_paths_free(&paths); + install_context_done(&plus); + install_context_done(&minus); + set_free_free(remove_symlinks_to); + free(config_path); + + return r; +} + +int unit_file_get_list( + UnitFileScope scope, + const char *root_dir, + Hashmap *h) { + + LookupPaths paths; + char **i, *buf = NULL; + DIR *d = NULL; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(h); + + zero(paths); + + if (root_dir && scope != UNIT_FILE_SYSTEM) + return -EINVAL; + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + STRV_FOREACH(i, paths.unit_path) { + struct dirent buffer, *de; + const char *units_dir; + + free(buf); + buf = NULL; + + if (root_dir) { + if (asprintf(&buf, "%s/%s", root_dir, *i) < 0) { + r = -ENOMEM; + goto finish; + } + units_dir = buf; + } else + units_dir = *i; + + if (d) + closedir(d); + + d = opendir(units_dir); + if (!d) { + if (errno == ENOENT) + continue; + + r = -errno; + goto finish; + } + + for (;;) { + UnitFileList *f; + + r = readdir_r(d, &buffer, &de); + if (r != 0) { + r = -r; + goto finish; + } + + if (!de) + break; + + if (ignore_file(de->d_name)) + continue; + + if (!unit_name_is_valid_no_type(de->d_name, true)) + continue; + + if (hashmap_get(h, de->d_name)) + continue; + + r = dirent_ensure_type(d, de); + if (r < 0) { + if (r == -ENOENT) + continue; + + goto finish; + } + + if (de->d_type != DT_LNK && de->d_type != DT_REG) + continue; + + f = new0(UnitFileList, 1); + if (!f) { + r = -ENOMEM; + goto finish; + } + + f->path = path_make_absolute(de->d_name, units_dir); + if (!f->path) { + free(f); + r = -ENOMEM; + goto finish; + } + + r = null_or_empty_path(f->path); + if (r < 0 && r != -ENOENT) { + free(f->path); + free(f); + goto finish; + } else if (r > 0) { + f->state = + path_startswith(*i, "/run") ? + UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; + goto found; + } + + r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state); + if (r < 0) { + free(f->path); + free(f); + goto finish; + } else if (r > 0) + goto found; + + r = unit_file_can_install(&paths, root_dir, f->path, true); + if (r < 0) { + free(f->path); + free(f); + goto finish; + } else if (r > 0) { + f->state = UNIT_FILE_DISABLED; + goto found; + } else { + f->state = UNIT_FILE_STATIC; + goto found; + } + + free(f->path); + free(f); + continue; + + found: + r = hashmap_put(h, file_name_from_path(f->path), f); + if (r < 0) { + free(f->path); + free(f); + goto finish; + } + } + } + +finish: + lookup_paths_free(&paths); + free(buf); + + if (d) + closedir(d); + + return r; +} + +static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { + [UNIT_FILE_ENABLED] = "enabled", + [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtie", + [UNIT_FILE_LINKED] = "linked", + [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime", + [UNIT_FILE_MASKED] = "masked", + [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime", + [UNIT_FILE_STATIC] = "static", + [UNIT_FILE_DISABLED] = "disabled" +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); + +static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = { + [UNIT_FILE_SYMLINK] = "symlink", + [UNIT_FILE_UNLINK] = "unlink", +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType); diff --git a/src/shared/install.h b/src/shared/install.h new file mode 100644 index 0000000000..d365c01bc7 --- /dev/null +++ b/src/shared/install.h @@ -0,0 +1,89 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooinstallhfoo +#define fooinstallhfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "hashmap.h" + +typedef enum UnitFileScope { + UNIT_FILE_SYSTEM, + UNIT_FILE_GLOBAL, + UNIT_FILE_USER, + _UNIT_FILE_SCOPE_MAX, + _UNIT_FILE_SCOPE_INVALID = -1 +} UnitFileScope; + +typedef enum UnitFileState { + UNIT_FILE_ENABLED, + UNIT_FILE_ENABLED_RUNTIME, + UNIT_FILE_LINKED, + UNIT_FILE_LINKED_RUNTIME, + UNIT_FILE_MASKED, + UNIT_FILE_MASKED_RUNTIME, + UNIT_FILE_STATIC, + UNIT_FILE_DISABLED, + _UNIT_FILE_STATE_MAX, + _UNIT_FILE_STATE_INVALID = -1 +} UnitFileState; + +typedef enum UnitFileChangeType { + UNIT_FILE_SYMLINK, + UNIT_FILE_UNLINK, + _UNIT_FILE_CHANGE_TYPE_MAX, + _UNIT_FILE_CHANGE_TYPE_INVALID = -1 +} UnitFileChangeType; + +typedef struct UnitFileChange { + UnitFileChangeType type; + char *path; + char *source; +} UnitFileChange; + +typedef struct UnitFileList { + char *path; + UnitFileState state; +} UnitFileList; + +int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); +int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes); +int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); +int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); +int unit_file_preset(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); +int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); +int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes); + +UnitFileState unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename); + +int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h); + +void unit_file_list_free(Hashmap *h); +void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes); + +int unit_file_query_preset(UnitFileScope scope, const char *name); + +const char *unit_file_state_to_string(UnitFileState s); +UnitFileState unit_file_state_from_string(const char *s); + +const char *unit_file_change_type_to_string(UnitFileChangeType s); +UnitFileChangeType unit_file_change_type_from_string(const char *s); + +#endif diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c new file mode 100644 index 0000000000..fedb4532db --- /dev/null +++ b/src/shared/logs-show.c @@ -0,0 +1,677 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "logs-show.h" +#include "log.h" +#include "util.h" + +#define PRINT_THRESHOLD 128 + +static bool contains_unprintable(const void *p, size_t l) { + const char *j; + + for (j = p; j < (const char *) p + l; j++) + if (*j < ' ' || *j >= 127) + return true; + + return false; +} + +static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) { + size_t fl, nl; + void *buf; + + assert(data); + assert(field); + assert(target); + assert(target_size); + + fl = strlen(field); + if (length < fl) + return 0; + + if (memcmp(data, field, fl)) + return 0; + + nl = length - fl; + buf = malloc(nl+1); + memcpy(buf, (const char*) data + fl, nl); + ((char*)buf)[nl] = 0; + if (!buf) { + log_error("Out of memory"); + return -ENOMEM; + } + + free(*target); + *target = buf; + *target_size = nl; + + return 1; +} + +static bool shall_print(bool show_all, char *p, size_t l) { + if (show_all) + return true; + + if (l > PRINT_THRESHOLD) + return false; + + if (contains_unprintable(p, l)) + return false; + + return true; +} + +static int output_short(sd_journal *j, unsigned line, unsigned n_columns, bool show_all, bool monotonic_mode) { + int r; + const void *data; + size_t length; + size_t n = 0; + char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL; + size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0; + + assert(j); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + + r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "_COMM=", &comm, &comm_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "_PID=", &pid, &pid_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len); + if (r < 0) + goto finish; + else if (r > 0) + continue; + + r = parse_field(data, length, "MESSAGE=", &message, &message_len); + if (r < 0) + goto finish; + } + + if (!message) { + r = 0; + goto finish; + } + + if (monotonic_mode) { + uint64_t t; + sd_id128_t boot_id; + + r = -ENOENT; + + if (monotonic) + r = safe_atou64(monotonic, &t); + + if (r < 0) + r = sd_journal_get_monotonic_usec(j, &t, &boot_id); + + if (r < 0) { + log_error("Failed to get monotonic: %s", strerror(-r)); + goto finish; + } + + printf("[%5llu.%06llu]", + (unsigned long long) (t / USEC_PER_SEC), + (unsigned long long) (t % USEC_PER_SEC)); + + n += 1 + 5 + 1 + 6 + 1; + + } else { + char buf[64]; + uint64_t x; + time_t t; + struct tm tm; + + r = -ENOENT; + + if (realtime) + r = safe_atou64(realtime, &x); + + if (r < 0) + r = sd_journal_get_realtime_usec(j, &x); + + if (r < 0) { + log_error("Failed to get realtime: %s", strerror(-r)); + goto finish; + } + + t = (time_t) (x / USEC_PER_SEC); + if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) { + log_error("Failed to format time."); + goto finish; + } + + fputs(buf, stdout); + n += strlen(buf); + } + + if (hostname && shall_print(show_all, hostname, hostname_len)) { + printf(" %.*s", (int) hostname_len, hostname); + n += hostname_len + 1; + } + + if (identifier && shall_print(show_all, identifier, identifier_len)) { + printf(" %.*s", (int) identifier_len, identifier); + n += identifier_len + 1; + } else if (comm && shall_print(show_all, comm, comm_len)) { + printf(" %.*s", (int) comm_len, comm); + n += comm_len + 1; + } else + putchar(' '); + + if (pid && shall_print(show_all, pid, pid_len)) { + printf("[%.*s]", (int) pid_len, pid); + n += pid_len + 2; + } else if (fake_pid && shall_print(show_all, fake_pid, fake_pid_len)) { + printf("[%.*s]", (int) fake_pid_len, fake_pid); + n += fake_pid_len + 2; + } + + if (show_all) + printf(": %.*s\n", (int) message_len, message); + else if (contains_unprintable(message, message_len)) { + char bytes[FORMAT_BYTES_MAX]; + printf(": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len)); + } else if (message_len + n < n_columns) + printf(": %.*s\n", (int) message_len, message); + else if (n < n_columns) { + char *e; + + e = ellipsize_mem(message, message_len, n_columns - n - 2, 90); + + if (!e) + printf(": %.*s\n", (int) message_len, message); + else + printf(": %s\n", e); + + free(e); + } else + fputs("\n", stdout); + + r = 0; + +finish: + free(hostname); + free(identifier); + free(comm); + free(pid); + free(fake_pid); + free(message); + free(monotonic); + free(realtime); + + return r; +} + +static int output_short_realtime(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) { + return output_short(j, line, n_columns, show_all, false); +} + +static int output_short_monotonic(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) { + return output_short(j, line, n_columns, show_all, true); +} + +static int output_verbose(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) { + const void *data; + size_t length; + char *cursor; + uint64_t realtime; + char ts[FORMAT_TIMESTAMP_MAX]; + int r; + + assert(j); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) { + log_error("Failed to get realtime timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) { + log_error("Failed to get cursor: %s", strerror(-r)); + return r; + } + + printf("%s [%s]\n", + format_timestamp(ts, sizeof(ts), realtime), + cursor); + + free(cursor); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + if (!show_all && (length > PRINT_THRESHOLD || + contains_unprintable(data, length))) { + const char *c; + char bytes[FORMAT_BYTES_MAX]; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + printf("\t%.*s=[%s blob data]\n", + (int) (c - (const char*) data), + (const char*) data, + format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1)); + } else + printf("\t%.*s\n", (int) length, (const char*) data); + } + + return 0; +} + +static int output_export(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) { + sd_id128_t boot_id; + char sid[33]; + int r; + usec_t realtime, monotonic; + char *cursor; + const void *data; + size_t length; + + assert(j); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) { + log_error("Failed to get realtime timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); + if (r < 0) { + log_error("Failed to get monotonic timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) { + log_error("Failed to get cursor: %s", strerror(-r)); + return r; + } + + printf("__CURSOR=%s\n" + "__REALTIME_TIMESTAMP=%llu\n" + "__MONOTONIC_TIMESTAMP=%llu\n" + "_BOOT_ID=%s\n", + cursor, + (unsigned long long) realtime, + (unsigned long long) monotonic, + sd_id128_to_string(boot_id, sid)); + + free(cursor); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + + /* We already printed the boot id, from the data in + * the header, hence let's suppress it here */ + if (length >= 9 && + memcmp(data, "_BOOT_ID=", 9) == 0) + continue; + + if (contains_unprintable(data, length)) { + const char *c; + uint64_t le64; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + fwrite(data, c - (const char*) data, 1, stdout); + fputc('\n', stdout); + le64 = htole64(length - (c - (const char*) data) - 1); + fwrite(&le64, sizeof(le64), 1, stdout); + fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout); + } else + fwrite(data, length, 1, stdout); + + fputc('\n', stdout); + } + + fputc('\n', stdout); + + return 0; +} + +static void json_escape(const char* p, size_t l) { + + if (contains_unprintable(p, l)) { + bool not_first = false; + + fputs("[ ", stdout); + + while (l > 0) { + if (not_first) + printf(", %u", (uint8_t) *p); + else { + not_first = true; + printf("%u", (uint8_t) *p); + } + + p++; + l--; + } + + fputs(" ]", stdout); + } else { + fputc('\"', stdout); + + while (l > 0) { + if (*p == '"' || *p == '\\') { + fputc('\\', stdout); + fputc(*p, stdout); + } else + fputc(*p, stdout); + + p++; + l--; + } + + fputc('\"', stdout); + } +} + +static int output_json(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) { + uint64_t realtime, monotonic; + char *cursor; + const void *data; + size_t length; + sd_id128_t boot_id; + char sid[33]; + int r; + + assert(j); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) { + log_error("Failed to get realtime timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); + if (r < 0) { + log_error("Failed to get monotonic timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) { + log_error("Failed to get cursor: %s", strerror(-r)); + return r; + } + + if (line == 1) + fputc('\n', stdout); + else + fputs(",\n", stdout); + + printf("{\n" + "\t\"__CURSOR\" : \"%s\",\n" + "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n" + "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n" + "\t\"_BOOT_ID\" : \"%s\"", + cursor, + (unsigned long long) realtime, + (unsigned long long) monotonic, + sd_id128_to_string(boot_id, sid)); + + free(cursor); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + const char *c; + + /* We already printed the boot id, from the data in + * the header, hence let's suppress it here */ + if (length >= 9 && + memcmp(data, "_BOOT_ID=", 9) == 0) + continue; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + fputs(",\n\t", stdout); + json_escape(data, c - (const char*) data); + fputs(" : ", stdout); + json_escape(c + 1, length - (c - (const char*) data) - 1); + } + + fputs("\n}", stdout); + fflush(stdout); + + return 0; +} + +static int output_cat(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) { + const void *data; + size_t l; + int r; + + assert(j); + + r = sd_journal_get_data(j, "MESSAGE", &data, &l); + if (r < 0) { + log_error("Failed to get data: %s", strerror(-r)); + return r; + } + + assert(l >= 8); + + fwrite((const char*) data + 8, 1, l - 8, stdout); + putchar('\n'); + + return 0; +} + +static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line, unsigned n_columns, bool show_all) = { + [OUTPUT_SHORT] = output_short_realtime, + [OUTPUT_SHORT_MONOTONIC] = output_short_monotonic, + [OUTPUT_VERBOSE] = output_verbose, + [OUTPUT_EXPORT] = output_export, + [OUTPUT_JSON] = output_json, + [OUTPUT_CAT] = output_cat +}; + +int output_journal(sd_journal *j, OutputMode mode, unsigned line, unsigned n_columns, bool show_all) { + assert(mode >= 0); + assert(mode < _OUTPUT_MODE_MAX); + + if (n_columns <= 0) + n_columns = columns(); + + return output_funcs[mode](j, line, n_columns, show_all); +} + +int show_journal_by_unit( + const char *unit, + OutputMode mode, + unsigned n_columns, + usec_t not_before, + unsigned how_many, + bool show_all, + bool follow) { + + char *m = NULL; + sd_journal *j; + int r; + int fd; + unsigned line = 0; + bool need_seek = false; + + assert(mode >= 0); + assert(mode < _OUTPUT_MODE_MAX); + assert(unit); + + if (!endswith(unit, ".service") && + !endswith(unit, ".socket") && + !endswith(unit, ".mount") && + !endswith(unit, ".swap")) + return 0; + + if (how_many <= 0) + return 0; + + if (asprintf(&m, "_SYSTEMD_UNIT=%s", unit) < 0) { + r = -ENOMEM; + goto finish; + } + + r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY); + if (r < 0) + goto finish; + + fd = sd_journal_get_fd(j); + if (fd < 0) + goto finish; + + r = sd_journal_add_match(j, m, strlen(m)); + if (r < 0) + goto finish; + + r = sd_journal_seek_tail(j); + if (r < 0) + goto finish; + + r = sd_journal_previous_skip(j, how_many); + if (r < 0) + goto finish; + + if (mode == OUTPUT_JSON) { + fputc('[', stdout); + fflush(stdout); + } + + for (;;) { + for (;;) { + usec_t usec; + + if (need_seek) { + r = sd_journal_next(j); + if (r < 0) + goto finish; + } + + if (r == 0) + break; + + need_seek = true; + + if (not_before > 0) { + r = sd_journal_get_monotonic_usec(j, &usec, NULL); + + /* -ESTALE is returned if the + timestamp is not from this boot */ + if (r == -ESTALE) + continue; + else if (r < 0) + goto finish; + + if (usec < not_before) + continue; + } + + line ++; + + r = output_journal(j, mode, line, n_columns, show_all); + if (r < 0) + goto finish; + } + + if (!follow) + break; + + r = fd_wait_for_event(fd, POLLIN, (usec_t) -1); + if (r < 0) + goto finish; + + r = sd_journal_process(j); + if (r < 0) + goto finish; + + } + + if (mode == OUTPUT_JSON) + fputs("\n]\n", stdout); + +finish: + if (m) + free(m); + + if (j) + sd_journal_close(j); + + return r; +} + +static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { + [OUTPUT_SHORT] = "short", + [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", + [OUTPUT_VERBOSE] = "verbose", + [OUTPUT_EXPORT] = "export", + [OUTPUT_JSON] = "json", + [OUTPUT_CAT] = "cat" +}; + +DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode); diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h new file mode 100644 index 0000000000..94caed5579 --- /dev/null +++ b/src/shared/logs-show.h @@ -0,0 +1,56 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foologsshowhfoo +#define foologsshowhfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "util.h" + +typedef enum OutputMode { + OUTPUT_SHORT, + OUTPUT_SHORT_MONOTONIC, + OUTPUT_VERBOSE, + OUTPUT_EXPORT, + OUTPUT_JSON, + OUTPUT_CAT, + _OUTPUT_MODE_MAX, + _OUTPUT_MODE_INVALID = -1 +} OutputMode; + +int output_journal(sd_journal *j, OutputMode mode, unsigned line, unsigned n_columns, bool show_all); + +int show_journal_by_unit( + const char *unit, + OutputMode mode, + unsigned n_columns, + usec_t not_before, + unsigned how_many, + bool show_all, + bool follow); + +const char* output_mode_to_string(OutputMode m); +OutputMode output_mode_from_string(const char *s); + +#endif diff --git a/src/shared/loopback-setup.c b/src/shared/loopback-setup.c new file mode 100644 index 0000000000..46c1fc843a --- /dev/null +++ b/src/shared/loopback-setup.c @@ -0,0 +1,274 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "macro.h" +#include "loopback-setup.h" +#include "socket-util.h" + +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((uint8_t*) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +static int add_rtattr(struct nlmsghdr *n, size_t max_length, int type, const void *data, size_t data_length) { + size_t length; + struct rtattr *rta; + + length = RTA_LENGTH(data_length); + + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length) > max_length) + return -E2BIG; + + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = length; + memcpy(RTA_DATA(rta), data, data_length); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length); + + return 0; +} + +static ssize_t sendto_loop(int fd, const void *buf, size_t buf_len, int flags, const struct sockaddr *sa, socklen_t sa_len) { + + for (;;) { + ssize_t l; + + if ((l = sendto(fd, buf, buf_len, flags, sa, sa_len)) >= 0) + return l; + + if (errno != EINTR) + return -errno; + } +} + +static ssize_t recvfrom_loop(int fd, void *buf, size_t buf_len, int flags, struct sockaddr *sa, socklen_t *sa_len) { + + for (;;) { + ssize_t l; + + if ((l = recvfrom(fd, buf, buf_len, flags, sa, sa_len)) >= 0) + return l; + + if (errno != EINTR) + return -errno; + } +} + +static int add_adresses(int fd, int if_loopback, unsigned *requests) { + union { + struct sockaddr sa; + struct sockaddr_nl nl; + } sa; + union { + struct nlmsghdr header; + uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + + RTA_LENGTH(sizeof(struct in6_addr))]; + } request; + + struct ifaddrmsg *ifaddrmsg; + uint32_t ipv4_address = htonl(INADDR_LOOPBACK); + int r; + + zero(request); + + request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + request.header.nlmsg_type = RTM_NEWADDR; + request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK; + request.header.nlmsg_seq = *requests + 1; + + ifaddrmsg = NLMSG_DATA(&request.header); + ifaddrmsg->ifa_family = AF_INET; + ifaddrmsg->ifa_prefixlen = 8; + ifaddrmsg->ifa_flags = IFA_F_PERMANENT; + ifaddrmsg->ifa_scope = RT_SCOPE_HOST; + ifaddrmsg->ifa_index = if_loopback; + + if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address))) < 0) + return r; + + zero(sa); + sa.nl.nl_family = AF_NETLINK; + + if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) + return -errno; + (*requests)++; + + if (!socket_ipv6_is_supported()) + return 0; + + request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + request.header.nlmsg_seq = *requests + 1; + + ifaddrmsg->ifa_family = AF_INET6; + ifaddrmsg->ifa_prefixlen = 128; + + if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback))) < 0) + return r; + + if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) + return -errno; + (*requests)++; + + return 0; +} + +static int start_interface(int fd, int if_loopback, unsigned *requests) { + union { + struct sockaddr sa; + struct sockaddr_nl nl; + } sa; + union { + struct nlmsghdr header; + uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct ifinfomsg))]; + } request; + + struct ifinfomsg *ifinfomsg; + + zero(request); + + request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + request.header.nlmsg_type = RTM_NEWLINK; + request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; + request.header.nlmsg_seq = *requests + 1; + + ifinfomsg = NLMSG_DATA(&request.header); + ifinfomsg->ifi_family = AF_UNSPEC; + ifinfomsg->ifi_index = if_loopback; + ifinfomsg->ifi_flags = IFF_UP; + ifinfomsg->ifi_change = IFF_UP; + + zero(sa); + sa.nl.nl_family = AF_NETLINK; + + if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) + return -errno; + + (*requests)++; + + return 0; +} + +static int read_response(int fd, unsigned requests_max) { + union { + struct sockaddr sa; + struct sockaddr_nl nl; + } sa; + union { + struct nlmsghdr header; + uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct nlmsgerr))]; + } response; + + ssize_t l; + socklen_t sa_len = sizeof(sa); + struct nlmsgerr *nlmsgerr; + + if ((l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len)) < 0) + return -errno; + + if (sa_len != sizeof(sa.nl) || + sa.nl.nl_family != AF_NETLINK) + return -EIO; + + if (sa.nl.nl_pid != 0) + return 0; + + if ((size_t) l < sizeof(struct nlmsghdr)) + return -EIO; + + if (response.header.nlmsg_type != NLMSG_ERROR || + (pid_t) response.header.nlmsg_pid != getpid() || + response.header.nlmsg_seq >= requests_max) + return 0; + + if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) || + response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) + return -EIO; + + nlmsgerr = NLMSG_DATA(&response.header); + + if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST) { + log_warning("Netlink failure for request %i: %s", response.header.nlmsg_seq, strerror(-nlmsgerr->error)); + return nlmsgerr->error; + } + + return response.header.nlmsg_seq; +} + +int loopback_setup(void) { + int r, if_loopback; + union { + struct sockaddr sa; + struct sockaddr_nl nl; + struct sockaddr_storage storage; + } sa; + unsigned requests = 0, i; + int fd; + + errno = 0; + if ((if_loopback = (int) if_nametoindex("lo")) <= 0) + return errno ? -errno : -ENODEV; + + if ((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) + return -errno; + + zero(sa); + sa.nl.nl_family = AF_NETLINK; + + if (bind(fd, &sa.sa, sizeof(sa)) < 0) { + r = -errno; + goto finish; + } + + if ((r = add_adresses(fd, if_loopback, &requests)) < 0) + goto finish; + + if ((r = start_interface(fd, if_loopback, &requests)) < 0) + goto finish; + + for (i = 0; i < requests; i++) { + if ((r = read_response(fd, requests)) < 0) + goto finish; + } + + r = 0; + +finish: + if (r < 0) + log_warning("Failed to configure loopback device: %s", strerror(-r)); + + if (fd >= 0) + close_nointr_nofail(fd); + + return r; +} diff --git a/src/shared/loopback-setup.h b/src/shared/loopback-setup.h new file mode 100644 index 0000000000..cbb969e1e4 --- /dev/null +++ b/src/shared/loopback-setup.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooloopbacksetuphfoo +#define fooloopbacksetuphfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int loopback_setup(void); + +#endif diff --git a/src/shared/machine-id-setup.c b/src/shared/machine-id-setup.c new file mode 100644 index 0000000000..9e84ac0cb9 --- /dev/null +++ b/src/shared/machine-id-setup.c @@ -0,0 +1,265 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "machine-id-setup.h" +#include "macro.h" +#include "util.h" +#include "mkdir.h" +#include "log.h" +#include "virt.h" + +static int shorten_uuid(char destination[36], const char *source) { + unsigned i, j; + + for (i = 0, j = 0; i < 36 && j < 32; i++) { + int t; + + t = unhexchar(source[i]); + if (t < 0) + continue; + + destination[j++] = hexchar(t); + } + + if (i == 36 && j == 32) { + destination[32] = '\n'; + destination[33] = 0; + return 0; + } + + return -EINVAL; +} + +static int generate(char id[34]) { + int fd, r; + unsigned char *p; + sd_id128_t buf; + char *q; + ssize_t k; + const char *vm_id; + + assert(id); + + /* First, try reading the D-Bus machine id, unless it is a symlink */ + fd = open("/var/lib/dbus/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd >= 0) { + + k = loop_read(fd, id, 32, false); + close_nointr_nofail(fd); + + if (k >= 32) { + id[32] = '\n'; + id[33] = 0; + + log_info("Initializing machine ID from D-Bus machine ID."); + return 0; + } + } + + /* If that didn't work, see if we are running in qemu/kvm and a + * machine ID was passed in via -uuid on the qemu/kvm command + * line */ + + r = detect_vm(&vm_id); + if (r > 0 && streq(vm_id, "kvm")) { + char uuid[37]; + + fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd >= 0) { + k = loop_read(fd, uuid, 36, false); + close_nointr_nofail(fd); + + if (k >= 36) { + r = shorten_uuid(id, uuid); + if (r >= 0) { + log_info("Initializing machine ID from KVM UUID"); + return 0; + } + } + } + } + + /* If that didn't work either, see if we are running in a + * container, and a machine ID was passed in via + * $container_uuid the way libvirt/LXC does it */ + + r = detect_container(NULL); + if (r > 0) { + FILE *f; + + 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 (startswith(line, "container_uuid=") && + strlen(line + 15) >= 36) { + r = shorten_uuid(id, line + 15); + if (r >= 0) { + log_info("Initializing machine ID from container UUID"); + return 0; + } + } + + } while (!done); + + fclose(f); + } + } + + /* If that didn't work, generate a random machine id */ + r = sd_id128_randomize(&buf); + if (r < 0) { + log_error("Failed to open /dev/urandom: %s", strerror(-r)); + return r; + } + + for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) { + q[0] = hexchar(*p >> 4); + q[1] = hexchar(*p & 15); + } + + id[32] = '\n'; + id[33] = 0; + + log_info("Initializing machine ID from random generator."); + + return 0; +} + +int machine_id_setup(void) { + int fd, r; + bool writable; + struct stat st; + char id[34]; /* 32 + \n + \0 */ + mode_t m; + + m = umask(0000); + + /* We create this 0444, to indicate that this isn't really + * something you should ever modify. Of course, since the file + * will be owned by root it doesn't matter much, but maybe + * people look. */ + + fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444); + if (fd >= 0) + writable = true; + else { + fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) { + umask(m); + log_error("Cannot open /etc/machine-id: %m"); + return -errno; + } + + writable = false; + } + + umask(m); + + if (fstat(fd, &st) < 0) { + log_error("fstat() failed: %m"); + r = -errno; + goto finish; + } + + if (S_ISREG(st.st_mode)) { + if (loop_read(fd, id, 32, false) >= 32) { + r = 0; + goto finish; + } + } + + /* Hmm, so, the id currently stored is not useful, then let's + * generate one */ + + r = generate(id); + if (r < 0) + goto finish; + + if (S_ISREG(st.st_mode) && writable) { + lseek(fd, 0, SEEK_SET); + + if (loop_write(fd, id, 33, false) == 33) { + r = 0; + goto finish; + } + } + + close_nointr_nofail(fd); + fd = -1; + + /* Hmm, we couldn't write it? So let's write it to + * /run/machine-id as a replacement */ + + m = umask(0022); + r = write_one_line_file("/run/machine-id", id); + umask(m); + + if (r < 0) { + log_error("Cannot write /run/machine-id: %s", strerror(-r)); + + unlink("/run/machine-id"); + goto finish; + } + + /* And now, let's mount it over */ + r = mount("/run/machine-id", "/etc/machine-id", "bind", MS_BIND|MS_RDONLY, NULL) < 0 ? -errno : 0; + if (r < 0) { + unlink("/run/machine-id"); + log_error("Failed to mount /etc/machine-id: %s", strerror(-r)); + } else + log_info("Installed transient /etc/machine-id file."); + +finish: + + if (fd >= 0) + close_nointr_nofail(fd); + + return r; +} diff --git a/src/shared/machine-id-setup.h b/src/shared/machine-id-setup.h new file mode 100644 index 0000000000..16f45d86d3 --- /dev/null +++ b/src/shared/machine-id-setup.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foomachineidsetuphfoo +#define foomachineidsetuphfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int machine_id_setup(void); + +#endif diff --git a/src/shared/mount-setup.c b/src/shared/mount-setup.c new file mode 100644 index 0000000000..52fe523674 --- /dev/null +++ b/src/shared/mount-setup.c @@ -0,0 +1,423 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mount-setup.h" +#include "log.h" +#include "macro.h" +#include "util.h" +#include "label.h" +#include "set.h" +#include "strv.h" +#include "mkdir.h" + +#ifndef TTY_GID +#define TTY_GID 5 +#endif + +typedef struct MountPoint { + const char *what; + const char *where; + const char *type; + const char *options; + unsigned long flags; + bool fatal; +} MountPoint; + +/* The first three entries we might need before SELinux is up. The + * fourth (securityfs) is needed by IMA to load a custom policy. The + * other ones we can delay until SELinux and IMA are loaded. */ +#define N_EARLY_MOUNT 4 + +static const MountPoint mount_table[] = { + { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true }, + { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true }, + { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true }, + { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, false }, + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true }, + { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, false }, + { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true }, + { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, false }, + { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, false }, +}; + +/* These are API file systems that might be mounted by other software, + * we just list them here so that we know that we should ignore them */ + +static const char * const ignore_paths[] = { + "/sys/fs/selinux", + "/selinux", + "/proc/bus/usb" +}; + +bool mount_point_is_api(const char *path) { + unsigned i; + + /* Checks if this mount point is considered "API", and hence + * should be ignored */ + + for (i = 0; i < ELEMENTSOF(mount_table); i ++) + if (path_equal(path, mount_table[i].where)) + return true; + + return path_startswith(path, "/sys/fs/cgroup/"); +} + +bool mount_point_ignore(const char *path) { + unsigned i; + + for (i = 0; i < ELEMENTSOF(ignore_paths); i++) + if (path_equal(path, ignore_paths[i])) + return true; + + return false; +} + +static int mount_one(const MountPoint *p, bool relabel) { + int r; + + assert(p); + + /* Relabel first, just in case */ + if (relabel) + label_fix(p->where, true); + + if ((r = path_is_mount_point(p->where, true)) < 0) + return r; + + if (r > 0) + return 0; + + /* The access mode here doesn't really matter too much, since + * the mounted file system will take precedence anyway. */ + mkdir_p(p->where, 0755); + + log_debug("Mounting %s to %s of type %s with options %s.", + p->what, + p->where, + p->type, + strna(p->options)); + + if (mount(p->what, + p->where, + p->type, + p->flags, + p->options) < 0) { + log_error("Failed to mount %s: %s", p->where, strerror(errno)); + return p->fatal ? -errno : 0; + } + + /* Relabel again, since we now mounted something fresh here */ + if (relabel) + label_fix(p->where, false); + + return 1; +} + +int mount_setup_early(void) { + unsigned i; + int r = 0; + + assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table)); + + /* Do a minimal mount of /proc and friends to enable the most + * basic stuff, such as SELinux */ + for (i = 0; i < N_EARLY_MOUNT; i ++) { + int j; + + j = mount_one(mount_table + i, false); + if (r == 0) + r = j; + } + + return r; +} + +int mount_cgroup_controllers(char ***join_controllers) { + int r; + FILE *f; + char buf[LINE_MAX]; + Set *controllers; + + /* Mount all available cgroup controllers that are built into the kernel. */ + + f = fopen("/proc/cgroups", "re"); + if (!f) { + log_error("Failed to enumerate cgroup controllers: %m"); + return 0; + } + + controllers = set_new(string_hash_func, string_compare_func); + if (!controllers) { + r = -ENOMEM; + log_error("Failed to allocate controller set."); + goto finish; + } + + /* Ignore the header line */ + (void) fgets(buf, sizeof(buf), f); + + for (;;) { + char *controller; + int enabled = 0; + + if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) { + + if (feof(f)) + break; + + log_error("Failed to parse /proc/cgroups."); + r = -EIO; + goto finish; + } + + if (!enabled) { + free(controller); + continue; + } + + r = set_put(controllers, controller); + if (r < 0) { + log_error("Failed to add controller to set."); + free(controller); + goto finish; + } + } + + for (;;) { + MountPoint p; + char *controller, *where, *options; + char ***k = NULL; + + controller = set_steal_first(controllers); + if (!controller) + break; + + if (join_controllers) + for (k = join_controllers; *k; k++) + if (strv_find(*k, controller)) + break; + + if (k && *k) { + char **i, **j; + + for (i = *k, j = *k; *i; i++) { + + if (!streq(*i, controller)) { + char *t; + + t = set_remove(controllers, *i); + if (!t) { + free(*i); + continue; + } + free(t); + } + + *(j++) = *i; + } + + *j = NULL; + + options = strv_join(*k, ","); + if (!options) { + log_error("Failed to join options"); + free(controller); + r = -ENOMEM; + goto finish; + } + + } else { + options = controller; + controller = NULL; + } + + where = strappend("/sys/fs/cgroup/", options); + if (!where) { + log_error("Failed to build path"); + free(options); + r = -ENOMEM; + goto finish; + } + + zero(p); + p.what = "cgroup"; + p.where = where; + p.type = "cgroup"; + p.options = options; + p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV; + p.fatal = false; + + r = mount_one(&p, true); + free(controller); + free(where); + + if (r < 0) { + free(options); + goto finish; + } + + if (r > 0 && k && *k) { + char **i; + + for (i = *k; *i; i++) { + char *t; + + t = strappend("/sys/fs/cgroup/", *i); + if (!t) { + log_error("Failed to build path"); + r = -ENOMEM; + free(options); + goto finish; + } + + r = symlink(options, t); + free(t); + + if (r < 0 && errno != EEXIST) { + log_error("Failed to create symlink: %m"); + r = -errno; + free(options); + goto finish; + } + } + } + + free(options); + } + + r = 0; + +finish: + set_free_free(controllers); + + fclose(f); + + return r; +} + +static int symlink_and_label(const char *old_path, const char *new_path) { + int r; + + assert(old_path); + assert(new_path); + + if ((r = label_symlinkfile_set(new_path)) < 0) + return r; + + if (symlink(old_path, new_path) < 0) + r = -errno; + + label_file_clear(); + + return r; +} + +static int nftw_cb( + const char *fpath, + const struct stat *sb, + int tflag, + struct FTW *ftwbuf) { + + /* No need to label /dev twice in a row... */ + if (_unlikely_(ftwbuf->level == 0)) + return FTW_CONTINUE; + + label_fix(fpath, true); + + /* /run/initramfs is static data and big, no need to + * dynamically relabel its contents at boot... */ + if (_unlikely_(ftwbuf->level == 1 && + tflag == FTW_D && + streq(fpath, "/run/initramfs"))) + return FTW_SKIP_SUBTREE; + + return FTW_CONTINUE; +}; + +int mount_setup(bool loaded_policy) { + + static const char symlinks[] = + "/proc/kcore\0" "/dev/core\0" + "/proc/self/fd\0" "/dev/fd\0" + "/proc/self/fd/0\0" "/dev/stdin\0" + "/proc/self/fd/1\0" "/dev/stdout\0" + "/proc/self/fd/2\0" "/dev/stderr\0"; + + static const char relabel[] = + "/run/initramfs/root-fsck\0" + "/run/initramfs/shutdown\0"; + + int r; + unsigned i; + const char *j, *k; + + for (i = 0; i < ELEMENTSOF(mount_table); i ++) { + r = mount_one(mount_table + i, true); + + if (r < 0) + return r; + } + + /* Nodes in devtmpfs and /run need to be manually updated for + * the appropriate labels, after mounting. The other virtual + * API file systems like /sys and /proc do not need that, they + * use the same label for all their files. */ + if (loaded_policy) { + usec_t before_relabel, after_relabel; + char timespan[FORMAT_TIMESPAN_MAX]; + + before_relabel = now(CLOCK_MONOTONIC); + + nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + + /* Explicitly relabel these */ + NULSTR_FOREACH(j, relabel) + label_fix(j, true); + + after_relabel = now(CLOCK_MONOTONIC); + + log_info("Relabelled /dev and /run in %s.", + format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel)); + } + + /* Create a few default symlinks, which are normally created + * by udevd, but some scripts might need them before we start + * udevd. */ + NULSTR_FOREACH_PAIR(j, k, symlinks) + symlink_and_label(j, k); + + /* Create a few directories we always want around */ + label_mkdir("/run/systemd", 0755); + label_mkdir("/run/systemd/system", 0755); + + return 0; +} diff --git a/src/shared/mount-setup.h b/src/shared/mount-setup.h new file mode 100644 index 0000000000..720b66f76c --- /dev/null +++ b/src/shared/mount-setup.h @@ -0,0 +1,36 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foomountsetuphfoo +#define foomountsetuphfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +int mount_setup_early(void); + +int mount_setup(bool loaded_policy); + +int mount_cgroup_controllers(char ***join_controllers); + +bool mount_point_is_api(const char *path); +bool mount_point_ignore(const char *path); + +#endif diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c new file mode 100644 index 0000000000..1d95f7d1f8 --- /dev/null +++ b/src/shared/path-lookup.c @@ -0,0 +1,348 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "mkdir.h" +#include "strv.h" + +#include "path-lookup.h" + +int user_config_home(char **config_home) { + const char *e; + + if ((e = getenv("XDG_CONFIG_HOME"))) { + if (asprintf(config_home, "%s/systemd/user", e) < 0) + return -ENOMEM; + + return 1; + } else { + const char *home; + + if ((home = getenv("HOME"))) { + if (asprintf(config_home, "%s/.config/systemd/user", home) < 0) + return -ENOMEM; + + return 1; + } + } + + return 0; +} + +static char** user_dirs(void) { + const char * const config_unit_paths[] = { + USER_CONFIG_UNIT_PATH, + "/etc/systemd/user", + "/run/systemd/user", + NULL + }; + + const char * const data_unit_paths[] = { + "/usr/local/lib/systemd/user", + "/usr/local/share/systemd/user", + USER_DATA_UNIT_PATH, + "/usr/lib/systemd/user", + "/usr/share/systemd/user", + NULL + }; + + const char *home, *e; + char *config_home = NULL, *data_home = NULL; + char **config_dirs = NULL, **data_dirs = NULL; + char **r = NULL, **t; + + /* Implement the mechanisms defined in + * + * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html + * + * We look in both the config and the data dirs because we + * want to encourage that distributors ship their unit files + * as data, and allow overriding as configuration. + */ + + if (user_config_home(&config_home) < 0) + goto fail; + + home = getenv("HOME"); + + if ((e = getenv("XDG_CONFIG_DIRS"))) + if (!(config_dirs = strv_split(e, ":"))) + goto fail; + + /* We don't treat /etc/xdg/systemd here as the spec + * suggests because we assume that that is a link to + * /etc/systemd/ anyway. */ + + if ((e = getenv("XDG_DATA_HOME"))) { + if (asprintf(&data_home, "%s/systemd/user", e) < 0) + goto fail; + + } else if (home) { + if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0) + goto fail; + + /* There is really no need for two unit dirs in $HOME, + * except to be fully compliant with the XDG spec. We + * now try to link the two dirs, so that we can + * minimize disk seeks a little. Further down we'll + * then filter out this link, if it is actually is + * one. */ + + mkdir_parents(data_home, 0777); + (void) symlink("../../../.config/systemd/user", data_home); + } + + if ((e = getenv("XDG_DATA_DIRS"))) + data_dirs = strv_split(e, ":"); + else + data_dirs = strv_new("/usr/local/share", + "/usr/share", + NULL); + + if (!data_dirs) + goto fail; + + /* Now merge everything we found. */ + if (config_home) { + if (!(t = strv_append(r, config_home))) + goto fail; + strv_free(r); + r = t; + } + + if (!strv_isempty(config_dirs)) { + if (!(t = strv_merge_concat(r, config_dirs, "/systemd/user"))) + goto finish; + strv_free(r); + r = t; + } + + if (!(t = strv_merge(r, (char**) config_unit_paths))) + goto fail; + strv_free(r); + r = t; + + if (data_home) { + if (!(t = strv_append(r, data_home))) + goto fail; + strv_free(r); + r = t; + } + + if (!strv_isempty(data_dirs)) { + if (!(t = strv_merge_concat(r, data_dirs, "/systemd/user"))) + goto fail; + strv_free(r); + r = t; + } + + if (!(t = strv_merge(r, (char**) data_unit_paths))) + goto fail; + strv_free(r); + r = t; + + if (!strv_path_make_absolute_cwd(r)) + goto fail; + +finish: + free(config_home); + strv_free(config_dirs); + free(data_home); + strv_free(data_dirs); + + return r; + +fail: + strv_free(r); + r = NULL; + goto finish; +} + +int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal) { + const char *e; + char *t; + + assert(p); + + /* First priority is whatever has been passed to us via env + * vars */ + if ((e = getenv("SYSTEMD_UNIT_PATH"))) + if (!(p->unit_path = split_path_and_make_absolute(e))) + return -ENOMEM; + + if (strv_isempty(p->unit_path)) { + + /* Nothing is set, so let's figure something out. */ + strv_free(p->unit_path); + + if (running_as == MANAGER_USER) { + + if (personal) + p->unit_path = user_dirs(); + else + p->unit_path = strv_new( + /* If you modify this you also want to modify + * systemduserunitpath= in systemd.pc.in, and + * the arrays in user_dirs() above! */ + USER_CONFIG_UNIT_PATH, + "/etc/systemd/user", + "/run/systemd/user", + "/usr/local/lib/systemd/user", + "/usr/local/share/systemd/user", + USER_DATA_UNIT_PATH, + "/usr/lib/systemd/user", + "/usr/share/systemd/user", + NULL); + + if (!p->unit_path) + return -ENOMEM; + + } else + if (!(p->unit_path = strv_new( + /* If you modify this you also want to modify + * systemdsystemunitpath= in systemd.pc.in! */ + SYSTEM_CONFIG_UNIT_PATH, + "/etc/systemd/system", + "/run/systemd/system", + "/usr/local/lib/systemd/system", + SYSTEM_DATA_UNIT_PATH, + "/usr/lib/systemd/system", +#ifdef HAVE_SPLIT_USR + "/lib/systemd/system", +#endif + NULL))) + return -ENOMEM; + } + + if (p->unit_path) + if (!strv_path_canonicalize(p->unit_path)) + return -ENOMEM; + + strv_uniq(p->unit_path); + strv_path_remove_empty(p->unit_path); + + if (!strv_isempty(p->unit_path)) { + + if (!(t = strv_join(p->unit_path, "\n\t"))) + return -ENOMEM; + log_debug("Looking for unit files in:\n\t%s", t); + free(t); + } else { + log_debug("Ignoring unit files."); + strv_free(p->unit_path); + p->unit_path = NULL; + } + + if (running_as == MANAGER_SYSTEM) { +#ifdef HAVE_SYSV_COMPAT + /* /etc/init.d/ compatibility does not matter to users */ + + if ((e = getenv("SYSTEMD_SYSVINIT_PATH"))) + if (!(p->sysvinit_path = split_path_and_make_absolute(e))) + return -ENOMEM; + + if (strv_isempty(p->sysvinit_path)) { + strv_free(p->sysvinit_path); + + if (!(p->sysvinit_path = strv_new( + SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */ + NULL))) + return -ENOMEM; + } + + if ((e = getenv("SYSTEMD_SYSVRCND_PATH"))) + if (!(p->sysvrcnd_path = split_path_and_make_absolute(e))) + return -ENOMEM; + + if (strv_isempty(p->sysvrcnd_path)) { + strv_free(p->sysvrcnd_path); + + if (!(p->sysvrcnd_path = strv_new( + SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */ + NULL))) + return -ENOMEM; + } + + if (p->sysvinit_path) + if (!strv_path_canonicalize(p->sysvinit_path)) + return -ENOMEM; + + if (p->sysvrcnd_path) + if (!strv_path_canonicalize(p->sysvrcnd_path)) + return -ENOMEM; + + strv_uniq(p->sysvinit_path); + strv_uniq(p->sysvrcnd_path); + + strv_path_remove_empty(p->sysvinit_path); + strv_path_remove_empty(p->sysvrcnd_path); + + if (!strv_isempty(p->sysvinit_path)) { + + if (!(t = strv_join(p->sysvinit_path, "\n\t"))) + return -ENOMEM; + + log_debug("Looking for SysV init scripts in:\n\t%s", t); + free(t); + } else { + log_debug("Ignoring SysV init scripts."); + strv_free(p->sysvinit_path); + p->sysvinit_path = NULL; + } + + if (!strv_isempty(p->sysvrcnd_path)) { + + if (!(t = strv_join(p->sysvrcnd_path, "\n\t"))) + return -ENOMEM; + + log_debug("Looking for SysV rcN.d links in:\n\t%s", t); + free(t); + } else { + log_debug("Ignoring SysV rcN.d links."); + strv_free(p->sysvrcnd_path); + p->sysvrcnd_path = NULL; + } +#else + log_debug("Disabled SysV init scripts and rcN.d links support"); +#endif + } + + return 0; +} + +void lookup_paths_free(LookupPaths *p) { + assert(p); + + strv_free(p->unit_path); + p->unit_path = NULL; + +#ifdef HAVE_SYSV_COMPAT + strv_free(p->sysvinit_path); + strv_free(p->sysvrcnd_path); + p->sysvinit_path = p->sysvrcnd_path = NULL; +#endif +} diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h new file mode 100644 index 0000000000..e8a5a77a7b --- /dev/null +++ b/src/shared/path-lookup.h @@ -0,0 +1,40 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foopathlookuphfoo +#define foopathlookuphfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct LookupPaths { + char **unit_path; +#ifdef HAVE_SYSV_COMPAT + char **sysvinit_path; + char **sysvrcnd_path; +#endif +} LookupPaths; + +#include "manager.h" + +int user_config_home(char **config_home); + +int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal); +void lookup_paths_free(LookupPaths *p); + +#endif diff --git a/src/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c new file mode 100644 index 0000000000..c1a9c58681 --- /dev/null +++ b/src/shared/spawn-ask-password-agent.c @@ -0,0 +1,67 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "spawn-ask-password-agent.h" + +static pid_t agent_pid = 0; + +int ask_password_agent_open(void) { + int r; + + if (agent_pid > 0) + return 0; + + /* We check STDIN here, not STDOUT, since this is about input, + * not output */ + if (!isatty(STDIN_FILENO)) + return 0; + + r = fork_agent(&agent_pid, + NULL, 0, + SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, + SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL); + if (r < 0) + log_error("Failed to fork TTY ask password agent: %s", strerror(-r)); + + return r; +} + +void ask_password_agent_close(void) { + + if (agent_pid <= 0) + return; + + /* Inform agent that we are done */ + kill(agent_pid, SIGTERM); + kill(agent_pid, SIGCONT); + wait_for_terminate(agent_pid, NULL); + agent_pid = 0; +} diff --git a/src/shared/spawn-ask-password-agent.h b/src/shared/spawn-ask-password-agent.h new file mode 100644 index 0000000000..fa5e7b0260 --- /dev/null +++ b/src/shared/spawn-ask-password-agent.h @@ -0,0 +1,28 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foospawnaskpasswordagenthfoo +#define foospawnaskpasswordagenthfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int ask_password_agent_open(void); +void ask_password_agent_close(void); + +#endif diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c new file mode 100644 index 0000000000..fd72588417 --- /dev/null +++ b/src/shared/spawn-polkit-agent.c @@ -0,0 +1,86 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "spawn-polkit-agent.h" + +static pid_t agent_pid = 0; + +int polkit_agent_open(void) { + int r; + int pipe_fd[2]; + char notify_fd[10 + 1]; + + if (agent_pid > 0) + return 0; + + /* We check STDIN here, not STDOUT, since this is about input, + * not output */ + if (!isatty(STDIN_FILENO)) + return 0; + + if (pipe2(pipe_fd, 0) < 0) + return -errno; + + snprintf(notify_fd, sizeof(notify_fd), "%i", pipe_fd[1]); + char_array_0(notify_fd); + + r = fork_agent(&agent_pid, + &pipe_fd[1], 1, + POLKIT_AGENT_BINARY_PATH, + POLKIT_AGENT_BINARY_PATH, "--notify-fd", notify_fd, NULL); + + /* Close the writing side, because that's the one for the agent */ + close_nointr_nofail(pipe_fd[1]); + + if (r < 0) + log_error("Failed to fork TTY ask password agent: %s", strerror(-r)); + else + /* Wait until the agent closes the fd */ + fd_wait_for_event(pipe_fd[0], POLLHUP, (usec_t) -1); + + close_nointr_nofail(pipe_fd[0]); + + return r; +} + +void polkit_agent_close(void) { + + if (agent_pid <= 0) + return; + + /* Inform agent that we are done */ + kill(agent_pid, SIGTERM); + kill(agent_pid, SIGCONT); + wait_for_terminate(agent_pid, NULL); + agent_pid = 0; +} diff --git a/src/shared/spawn-polkit-agent.h b/src/shared/spawn-polkit-agent.h new file mode 100644 index 0000000000..b91d20f120 --- /dev/null +++ b/src/shared/spawn-polkit-agent.h @@ -0,0 +1,28 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foospawnpolkitagenthfoo +#define foospawnpolkitagenthfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int polkit_agent_open(void); +void polkit_agent_close(void); + +#endif diff --git a/src/shared/specifier.c b/src/shared/specifier.c new file mode 100644 index 0000000000..ae00ae10bb --- /dev/null +++ b/src/shared/specifier.c @@ -0,0 +1,108 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "macro.h" +#include "util.h" +#include "specifier.h" + +/* + * Generic infrastructure for replacing %x style specifiers in + * strings. Will call a callback for each replacement. + * + */ + +char *specifier_printf(const char *text, const Specifier table[], void *userdata) { + char *r, *t; + const char *f; + bool percent = false; + size_t l; + + assert(text); + assert(table); + + l = strlen(text); + if (!(r = new(char, l+1))) + return NULL; + + t = r; + + for (f = text; *f; f++, l--) { + + if (percent) { + if (*f == '%') + *(t++) = '%'; + else { + const Specifier *i; + + for (i = table; i->specifier; i++) + if (i->specifier == *f) + break; + + if (i->lookup) { + char *n, *w; + size_t k, j; + + if (!(w = i->lookup(i->specifier, i->data, userdata))) { + free(r); + return NULL; + } + + j = t - r; + k = strlen(w); + + if (!(n = new(char, j + k + l + 1))) { + free(r); + free(w); + return NULL; + } + + memcpy(n, r, j); + memcpy(n + j, w, k); + + free(r); + free(w); + + r = n; + t = n + j + k; + } else { + *(t++) = '%'; + *(t++) = *f; + } + } + + percent = false; + } else if (*f == '%') + percent = true; + else + *(t++) = *f; + } + + *t = 0; + return r; +} + +/* Generic handler for simple string replacements */ + +char* specifier_string(char specifier, void *data, void *userdata) { + return strdup(strempty(data)); +} diff --git a/src/shared/specifier.h b/src/shared/specifier.h new file mode 100644 index 0000000000..57d1fcb35c --- /dev/null +++ b/src/shared/specifier.h @@ -0,0 +1,37 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foospecifierhfoo +#define foospecifierhfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef char* (*SpecifierCallback)(char specifier, void *data, void *userdata); + +typedef struct Specifier { + const char specifier; + const SpecifierCallback lookup; + void *data; +} Specifier; + +char *specifier_printf(const char *text, const Specifier table[], void *userdata); + +char* specifier_string(char specifier, void *data, void *userdata); + +#endif diff --git a/src/shared/umount.c b/src/shared/umount.c new file mode 100644 index 0000000000..24c0947f21 --- /dev/null +++ b/src/shared/umount.c @@ -0,0 +1,644 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 ProFUSION embedded systems + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" +#include "mount-setup.h" +#include "umount.h" +#include "util.h" + +typedef struct MountPoint { + char *path; + dev_t devnum; + bool skip_ro; + LIST_FIELDS (struct MountPoint, mount_point); +} MountPoint; + +static void mount_point_free(MountPoint **head, MountPoint *m) { + assert(head); + assert(m); + + LIST_REMOVE(MountPoint, mount_point, *head, m); + + free(m->path); + free(m); +} + +static void mount_points_list_free(MountPoint **head) { + assert(head); + + while (*head) + mount_point_free(head, *head); +} + +static int mount_points_list_get(MountPoint **head) { + FILE *proc_self_mountinfo; + char *path, *p; + unsigned int i; + int r; + + assert(head); + + if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"))) + return -errno; + + for (i = 1;; i++) { + int k; + MountPoint *m; + char *root; + bool skip_ro; + + path = p = NULL; + + if ((k = fscanf(proc_self_mountinfo, + "%*s " /* (1) mount id */ + "%*s " /* (2) parent id */ + "%*s " /* (3) major:minor */ + "%ms " /* (4) root */ + "%ms " /* (5) mount point */ + "%*s" /* (6) mount options */ + "%*[^-]" /* (7) optional fields */ + "- " /* (8) separator */ + "%*s " /* (9) file system type */ + "%*s" /* (10) mount source */ + "%*s" /* (11) mount options 2 */ + "%*[^\n]", /* some rubbish at the end */ + &root, + &path)) != 2) { + if (k == EOF) + break; + + log_warning("Failed to parse /proc/self/mountinfo:%u.", i); + + free(path); + continue; + } + + /* If we encounter a bind mount, don't try to remount + * the source dir too early */ + skip_ro = !streq(root, "/"); + free(root); + + p = cunescape(path); + free(path); + + if (!p) { + r = -ENOMEM; + goto finish; + } + + if (mount_point_is_api(p) || mount_point_ignore(p)) { + free(p); + continue; + } + + if (!(m = new0(MountPoint, 1))) { + free(p); + r = -ENOMEM; + goto finish; + } + + m->path = p; + m->skip_ro = skip_ro; + LIST_PREPEND(MountPoint, mount_point, *head, m); + } + + r = 0; + +finish: + fclose(proc_self_mountinfo); + + return r; +} + +static int swap_list_get(MountPoint **head) { + FILE *proc_swaps; + unsigned int i; + int r; + + assert(head); + + if (!(proc_swaps = fopen("/proc/swaps", "re"))) + return (errno == ENOENT) ? 0 : -errno; + + (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n"); + + for (i = 2;; i++) { + MountPoint *swap; + char *dev = NULL, *d; + int k; + + if ((k = fscanf(proc_swaps, + "%ms " /* device/file */ + "%*s " /* type of swap */ + "%*s " /* swap size */ + "%*s " /* used */ + "%*s\n", /* priority */ + &dev)) != 1) { + + if (k == EOF) + break; + + log_warning("Failed to parse /proc/swaps:%u.", i); + + free(dev); + continue; + } + + if (endswith(dev, "(deleted)")) { + free(dev); + continue; + } + + d = cunescape(dev); + free(dev); + + if (!d) { + r = -ENOMEM; + goto finish; + } + + if (!(swap = new0(MountPoint, 1))) { + free(d); + r = -ENOMEM; + goto finish; + } + + swap->path = d; + LIST_PREPEND(MountPoint, mount_point, *head, swap); + } + + r = 0; + +finish: + fclose(proc_swaps); + + return r; +} + +static int loopback_list_get(MountPoint **head) { + int r; + struct udev *udev; + struct udev_enumerate *e = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + + assert(head); + + if (!(udev = udev_new())) { + r = -ENOMEM; + goto finish; + } + + if (!(e = udev_enumerate_new(udev))) { + r = -ENOMEM; + goto finish; + } + + if (udev_enumerate_add_match_subsystem(e, "block") < 0 || + udev_enumerate_add_match_sysname(e, "loop*") < 0) { + r = -EIO; + goto finish; + } + + if (udev_enumerate_scan_devices(e) < 0) { + r = -EIO; + goto finish; + } + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + MountPoint *lb; + struct udev_device *d; + char *loop; + const char *dn; + + if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) { + r = -ENOMEM; + goto finish; + } + + if (!(dn = udev_device_get_devnode(d))) { + udev_device_unref(d); + continue; + } + + loop = strdup(dn); + udev_device_unref(d); + + if (!loop) { + r = -ENOMEM; + goto finish; + } + + if (!(lb = new0(MountPoint, 1))) { + free(loop); + r = -ENOMEM; + goto finish; + } + + lb->path = loop; + LIST_PREPEND(MountPoint, mount_point, *head, lb); + } + + r = 0; + +finish: + if (e) + udev_enumerate_unref(e); + + if (udev) + udev_unref(udev); + + return r; +} + +static int dm_list_get(MountPoint **head) { + int r; + struct udev *udev; + struct udev_enumerate *e = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + + assert(head); + + if (!(udev = udev_new())) { + r = -ENOMEM; + goto finish; + } + + if (!(e = udev_enumerate_new(udev))) { + r = -ENOMEM; + goto finish; + } + + if (udev_enumerate_add_match_subsystem(e, "block") < 0 || + udev_enumerate_add_match_sysname(e, "dm-*") < 0) { + r = -EIO; + goto finish; + } + + if (udev_enumerate_scan_devices(e) < 0) { + r = -EIO; + goto finish; + } + + first = udev_enumerate_get_list_entry(e); + + udev_list_entry_foreach(item, first) { + MountPoint *m; + struct udev_device *d; + dev_t devnum; + char *node; + const char *dn; + + if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) { + r = -ENOMEM; + goto finish; + } + + devnum = udev_device_get_devnum(d); + dn = udev_device_get_devnode(d); + + if (major(devnum) == 0 || !dn) { + udev_device_unref(d); + continue; + } + + node = strdup(dn); + udev_device_unref(d); + + if (!node) { + r = -ENOMEM; + goto finish; + } + + if (!(m = new(MountPoint, 1))) { + free(node); + r = -ENOMEM; + goto finish; + } + + m->path = node; + m->devnum = devnum; + LIST_PREPEND(MountPoint, mount_point, *head, m); + } + + r = 0; + +finish: + if (e) + udev_enumerate_unref(e); + + if (udev) + udev_unref(udev); + + return r; +} + +static int delete_loopback(const char *device) { + int fd, r; + + if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0) + return errno == ENOENT ? 0 : -errno; + + r = ioctl(fd, LOOP_CLR_FD, 0); + close_nointr_nofail(fd); + + if (r >= 0) + return 1; + + /* ENXIO: not bound, so no error */ + if (errno == ENXIO) + return 0; + + return -errno; +} + +static int delete_dm(dev_t devnum) { + int fd, r; + struct dm_ioctl dm; + + assert(major(devnum) != 0); + + if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0) + return -errno; + + zero(dm); + dm.version[0] = DM_VERSION_MAJOR; + dm.version[1] = DM_VERSION_MINOR; + dm.version[2] = DM_VERSION_PATCHLEVEL; + + dm.data_size = sizeof(dm); + dm.dev = devnum; + + r = ioctl(fd, DM_DEV_REMOVE, &dm); + close_nointr_nofail(fd); + + return r >= 0 ? 0 : -errno; +} + +static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) { + MountPoint *m, *n; + int n_failed = 0; + + assert(head); + + LIST_FOREACH_SAFE(mount_point, m, n, *head) { + if (path_equal(m->path, "/") +#ifndef HAVE_SPLIT_USR + || path_equal(m->path, "/usr") +#endif + ) { + n_failed++; + continue; + } + + /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */ + if (umount2(m->path, MNT_FORCE) == 0) { + log_info("Unmounted %s.", m->path); + if (changed) + *changed = true; + + mount_point_free(head, m); + } else if (log_error) { + log_warning("Could not unmount %s: %m", m->path); + n_failed++; + } + } + + return n_failed; +} + +static int mount_points_list_remount_read_only(MountPoint **head, bool *changed) { + MountPoint *m, *n; + int n_failed = 0; + + assert(head); + + LIST_FOREACH_SAFE(mount_point, m, n, *head) { + + if (m->skip_ro) { + n_failed++; + continue; + } + + /* Trying to remount read-only */ + if (mount(NULL, m->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) { + if (changed) + *changed = true; + + mount_point_free(head, m); + } else { + log_warning("Could not remount as read-only %s: %m", m->path); + n_failed++; + } + } + + return n_failed; +} + +static int swap_points_list_off(MountPoint **head, bool *changed) { + MountPoint *m, *n; + int n_failed = 0; + + assert(head); + + LIST_FOREACH_SAFE(mount_point, m, n, *head) { + if (swapoff(m->path) == 0) { + if (changed) + *changed = true; + + mount_point_free(head, m); + } else { + log_warning("Could not deactivate swap %s: %m", m->path); + n_failed++; + } + } + + return n_failed; +} + +static int loopback_points_list_detach(MountPoint **head, bool *changed) { + MountPoint *m, *n; + int n_failed = 0, k; + struct stat root_st; + + assert(head); + + k = lstat("/", &root_st); + + LIST_FOREACH_SAFE(mount_point, m, n, *head) { + int r; + struct stat loopback_st; + + if (k >= 0 && + major(root_st.st_dev) != 0 && + lstat(m->path, &loopback_st) >= 0 && + root_st.st_dev == loopback_st.st_rdev) { + n_failed ++; + continue; + } + + if ((r = delete_loopback(m->path)) >= 0) { + + if (r > 0 && changed) + *changed = true; + + mount_point_free(head, m); + } else { + log_warning("Could not delete loopback %s: %m", m->path); + n_failed++; + } + } + + return n_failed; +} + +static int dm_points_list_detach(MountPoint **head, bool *changed) { + MountPoint *m, *n; + int n_failed = 0, k; + struct stat root_st; + + assert(head); + + k = lstat("/", &root_st); + + LIST_FOREACH_SAFE(mount_point, m, n, *head) { + int r; + + if (k >= 0 && + major(root_st.st_dev) != 0 && + root_st.st_dev == m->devnum) { + n_failed ++; + continue; + } + + if ((r = delete_dm(m->devnum)) >= 0) { + + if (r > 0 && changed) + *changed = true; + + mount_point_free(head, m); + } else { + log_warning("Could not delete dm %s: %m", m->path); + n_failed++; + } + } + + return n_failed; +} + +int umount_all(bool *changed) { + int r; + bool umount_changed; + + LIST_HEAD(MountPoint, mp_list_head); + + LIST_HEAD_INIT(MountPoint, mp_list_head); + + r = mount_points_list_get(&mp_list_head); + if (r < 0) + goto end; + + /* retry umount, until nothing can be umounted anymore */ + do { + umount_changed = false; + + mount_points_list_umount(&mp_list_head, &umount_changed, false); + if (umount_changed) + *changed = true; + + } while (umount_changed); + + /* umount one more time with logging enabled */ + r = mount_points_list_umount(&mp_list_head, &umount_changed, true); + if (r <= 0) + goto end; + + r = mount_points_list_remount_read_only(&mp_list_head, changed); + + end: + mount_points_list_free(&mp_list_head); + + return r; +} + +int swapoff_all(bool *changed) { + int r; + LIST_HEAD(MountPoint, swap_list_head); + + LIST_HEAD_INIT(MountPoint, swap_list_head); + + r = swap_list_get(&swap_list_head); + if (r < 0) + goto end; + + r = swap_points_list_off(&swap_list_head, changed); + + end: + mount_points_list_free(&swap_list_head); + + return r; +} + +int loopback_detach_all(bool *changed) { + int r; + LIST_HEAD(MountPoint, loopback_list_head); + + LIST_HEAD_INIT(MountPoint, loopback_list_head); + + r = loopback_list_get(&loopback_list_head); + if (r < 0) + goto end; + + r = loopback_points_list_detach(&loopback_list_head, changed); + + end: + mount_points_list_free(&loopback_list_head); + + return r; +} + +int dm_detach_all(bool *changed) { + int r; + LIST_HEAD(MountPoint, dm_list_head); + + LIST_HEAD_INIT(MountPoint, dm_list_head); + + r = dm_list_get(&dm_list_head); + if (r < 0) + goto end; + + r = dm_points_list_detach(&dm_list_head, changed); + + end: + mount_points_list_free(&dm_list_head); + + return r; +} diff --git a/src/shared/umount.h b/src/shared/umount.h new file mode 100644 index 0000000000..2e2f9c181a --- /dev/null +++ b/src/shared/umount.h @@ -0,0 +1,33 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooumounthfoo +#define fooumounthfoo + +/*** + This file is part of systemd. + + Copyright 2010 ProFUSION embedded systems + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int umount_all(bool *changed); + +int swapoff_all(bool *changed); + +int loopback_detach_all(bool *changed); + +int dm_detach_all(bool *changed); + +#endif diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c new file mode 100644 index 0000000000..566cdc51cc --- /dev/null +++ b/src/shared/unit-name.c @@ -0,0 +1,448 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "util.h" +#include "unit-name.h" + +#define VALID_CHARS \ + "0123456789" \ + "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + ":-_.\\" + +bool unit_name_is_valid_no_type(const char *n, bool template_ok) { + const char *e, *i, *at; + + /* Valid formats: + * + * string@instance.suffix + * string.suffix + */ + + assert(n); + + if (strlen(n) >= UNIT_NAME_MAX) + return false; + + e = strrchr(n, '.'); + if (!e || e == n) + return false; + + for (i = n, at = NULL; i < e; i++) { + + if (*i == '@' && !at) + at = i; + + if (!strchr("@" VALID_CHARS, *i)) + return false; + } + + if (at) { + if (at == n) + return false; + + if (!template_ok && at+1 == e) + return false; + } + + return true; +} + +bool unit_instance_is_valid(const char *i) { + assert(i); + + /* The max length depends on the length of the string, so we + * don't really check this here. */ + + if (i[0] == 0) + return false; + + /* We allow additional @ in the instance string, we do not + * allow them in the prefix! */ + + for (; *i; i++) + if (!strchr("@" VALID_CHARS, *i)) + return false; + + return true; +} + +bool unit_prefix_is_valid(const char *p) { + + /* We don't allow additional @ in the instance string */ + + if (p[0] == 0) + return false; + + for (; *p; p++) + if (!strchr(VALID_CHARS, *p)) + return false; + + return true; +} + +int unit_name_to_instance(const char *n, char **instance) { + const char *p, *d; + char *i; + + assert(n); + assert(instance); + + /* Everything past the first @ and before the last . is the instance */ + if (!(p = strchr(n, '@'))) { + *instance = NULL; + return 0; + } + + assert_se(d = strrchr(n, '.')); + assert(p < d); + + if (!(i = strndup(p+1, d-p-1))) + return -ENOMEM; + + *instance = i; + return 0; +} + +char *unit_name_to_prefix_and_instance(const char *n) { + const char *d; + + assert(n); + + assert_se(d = strrchr(n, '.')); + + return strndup(n, d - n); +} + +char *unit_name_to_prefix(const char *n) { + const char *p; + + if ((p = strchr(n, '@'))) + return strndup(n, p - n); + + return unit_name_to_prefix_and_instance(n); +} + +char *unit_name_change_suffix(const char *n, const char *suffix) { + char *e, *r; + size_t a, b; + + assert(n); + assert(unit_name_is_valid_no_type(n, true)); + assert(suffix); + + assert_se(e = strrchr(n, '.')); + a = e - n; + b = strlen(suffix); + + if (!(r = new(char, a + b + 1))) + return NULL; + + memcpy(r, n, a); + memcpy(r+a, suffix, b+1); + + return r; +} + +char *unit_name_build(const char *prefix, const char *instance, const char *suffix) { + assert(prefix); + assert(unit_prefix_is_valid(prefix)); + assert(!instance || unit_instance_is_valid(instance)); + assert(suffix); + + if (!instance) + return strappend(prefix, suffix); + + return join(prefix, "@", instance, suffix, NULL); +} + +static char* do_escape(const char *f, char *t) { + assert(f); + assert(t); + + for (; *f; f++) { + if (*f == '/') + *(t++) = '-'; + else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f)) { + *(t++) = '\\'; + *(t++) = 'x'; + *(t++) = hexchar(*f >> 4); + *(t++) = hexchar(*f); + } else + *(t++) = *f; + } + + return t; +} + +char *unit_name_build_escape(const char *prefix, const char *instance, const char *suffix) { + char *r, *t; + size_t a, b, c; + + assert(prefix); + assert(suffix); + + /* Takes a arbitrary string for prefix and instance plus a + * suffix and makes a nice string suitable as unit name of it, + * escaping all weird chars on the way. + * + * / becomes ., and all chars not allowed in a unit name get + * escaped as \xFF, including \ and ., of course. This + * escaping is hence reversible. + * + * This is primarily useful to make nice unit names from + * strings, but is actually useful for any kind of string. + */ + + a = strlen(prefix); + c = strlen(suffix); + + if (instance) { + b = strlen(instance); + + if (!(r = new(char, a*4 + 1 + b*4 + c + 1))) + return NULL; + + t = do_escape(prefix, r); + *(t++) = '@'; + t = do_escape(instance, t); + } else { + + if (!(r = new(char, a*4 + c + 1))) + return NULL; + + t = do_escape(prefix, r); + } + + strcpy(t, suffix); + return r; +} + +char *unit_name_escape(const char *f) { + char *r, *t; + + if (!(r = new(char, strlen(f)*4+1))) + return NULL; + + t = do_escape(f, r); + *t = 0; + + return r; + +} + +char *unit_name_unescape(const char *f) { + char *r, *t; + + assert(f); + + if (!(r = strdup(f))) + return NULL; + + for (t = r; *f; f++) { + if (*f == '-') + *(t++) = '/'; + else if (*f == '\\') { + int a, b; + + if (f[1] != 'x' || + (a = unhexchar(f[2])) < 0 || + (b = unhexchar(f[3])) < 0) { + /* Invalid escape code, let's take it literal then */ + *(t++) = '\\'; + } else { + *(t++) = (char) ((a << 4) | b); + f += 3; + } + } else + *(t++) = *f; + } + + *t = 0; + + return r; +} + +bool unit_name_is_template(const char *n) { + const char *p; + + assert(n); + + if (!(p = strchr(n, '@'))) + return false; + + return p[1] == '.'; +} + +char *unit_name_replace_instance(const char *f, const char *i) { + const char *p, *e; + char *r, *k; + size_t a; + + assert(f); + + p = strchr(f, '@'); + assert_se(e = strrchr(f, '.')); + + a = p - f; + + if (p) { + size_t b; + + b = strlen(i); + + if (!(r = new(char, a + 1 + b + strlen(e) + 1))) + return NULL; + + k = mempcpy(r, f, a + 1); + k = mempcpy(k, i, b); + } else { + + if (!(r = new(char, a + strlen(e) + 1))) + return NULL; + + k = mempcpy(r, f, a); + } + + strcpy(k, e); + return r; +} + +char *unit_name_template(const char *f) { + const char *p, *e; + char *r; + size_t a; + + if (!(p = strchr(f, '@'))) + return strdup(f); + + assert_se(e = strrchr(f, '.')); + a = p - f + 1; + + if (!(r = new(char, a + strlen(e) + 1))) + return NULL; + + strcpy(mempcpy(r, f, a), e); + return r; + +} + +char *unit_name_from_path(const char *path, const char *suffix) { + char *p, *r; + + assert(path); + assert(suffix); + + if (!(p = strdup(path))) + return NULL; + + path_kill_slashes(p); + + path = p[0] == '/' ? p + 1 : p; + + if (path[0] == 0) { + free(p); + return strappend("-", suffix); + } + + r = unit_name_build_escape(path, NULL, suffix); + free(p); + + return r; +} + +char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) { + char *p, *r; + + assert(path); + assert(suffix); + + if (!(p = strdup(path))) + return NULL; + + path_kill_slashes(p); + + path = p[0] == '/' ? p + 1 : p; + + if (path[0] == 0) { + free(p); + return unit_name_build_escape(prefix, "-", suffix); + } + + r = unit_name_build_escape(prefix, path, suffix); + free(p); + + return r; +} + +char *unit_name_to_path(const char *name) { + char *w, *e; + + assert(name); + + if (!(w = unit_name_to_prefix(name))) + return NULL; + + e = unit_name_unescape(w); + free(w); + + if (!e) + return NULL; + + if (e[0] != '/') { + w = strappend("/", e); + free(e); + + if (!w) + return NULL; + + e = w; + } + + return e; +} + +char *unit_name_path_unescape(const char *f) { + char *e; + + assert(f); + + if (!(e = unit_name_unescape(f))) + return NULL; + + if (e[0] != '/') { + char *w; + + w = strappend("/", e); + free(e); + + if (!w) + return NULL; + + e = w; + } + + return e; +} diff --git a/src/shared/unit-name.h b/src/shared/unit-name.h new file mode 100644 index 0000000000..4dfb9fa3ce --- /dev/null +++ b/src/shared/unit-name.h @@ -0,0 +1,57 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foounitnamehfoo +#define foounitnamehfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#define UNIT_NAME_MAX 256 + +int unit_name_to_instance(const char *n, char **instance); +char* unit_name_to_prefix(const char *n); +char* unit_name_to_prefix_and_instance(const char *n); + +bool unit_name_is_valid_no_type(const char *n, bool template_ok); +bool unit_prefix_is_valid(const char *p); +bool unit_instance_is_valid(const char *i); + +char *unit_name_change_suffix(const char *n, const char *suffix); + +char *unit_name_build(const char *prefix, const char *instance, const char *suffix); +char *unit_name_build_escape(const char *prefix, const char *instance, const char *suffix); + +char *unit_name_escape(const char *f); +char *unit_name_unescape(const char *f); + +char *unit_name_path_unescape(const char *f); + +bool unit_name_is_template(const char *n); + +char *unit_name_replace_instance(const char *f, const char *i); + +char *unit_name_template(const char *f); + +char *unit_name_from_path(const char *path, const char *suffix); +char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix); +char *unit_name_to_path(const char *name); + +#endif diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c new file mode 100644 index 0000000000..6bba325d3e --- /dev/null +++ b/src/shared/utmp-wtmp.c @@ -0,0 +1,430 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "macro.h" +#include "utmp-wtmp.h" + +int utmp_get_runlevel(int *runlevel, int *previous) { + struct utmpx lookup, *found; + int r; + const char *e; + + assert(runlevel); + + /* If these values are set in the environment this takes + * precedence. Presumably, sysvinit does this to work around a + * race condition that would otherwise exist where we'd always + * go to disk and hence might read runlevel data that might be + * very new and does not apply to the current script being + * executed. */ + + if ((e = getenv("RUNLEVEL")) && e[0] > 0) { + *runlevel = e[0]; + + if (previous) { + /* $PREVLEVEL seems to be an Upstart thing */ + + if ((e = getenv("PREVLEVEL")) && e[0] > 0) + *previous = e[0]; + else + *previous = 0; + } + + return 0; + } + + if (utmpxname(_PATH_UTMPX) < 0) + return -errno; + + setutxent(); + + zero(lookup); + lookup.ut_type = RUN_LVL; + + if (!(found = getutxid(&lookup))) + r = -errno; + else { + int a, b; + + a = found->ut_pid & 0xFF; + b = (found->ut_pid >> 8) & 0xFF; + + if (a < 0 || b < 0) + r = -EIO; + else { + *runlevel = a; + + if (previous) + *previous = b; + r = 0; + } + } + + endutxent(); + + return r; +} + +static void init_timestamp(struct utmpx *store, usec_t t) { + assert(store); + + zero(*store); + + if (t <= 0) + t = now(CLOCK_REALTIME); + + store->ut_tv.tv_sec = t / USEC_PER_SEC; + store->ut_tv.tv_usec = t % USEC_PER_SEC; +} + +static void init_entry(struct utmpx *store, usec_t t) { + struct utsname uts; + + assert(store); + + init_timestamp(store, t); + + zero(uts); + + if (uname(&uts) >= 0) + strncpy(store->ut_host, uts.release, sizeof(store->ut_host)); + + strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */ + strncpy(store->ut_id, "~~", sizeof(store->ut_id)); +} + +static int write_entry_utmp(const struct utmpx *store) { + int r; + + assert(store); + + /* utmp is similar to wtmp, but there is only one entry for + * each entry type resp. user; i.e. basically a key/value + * table. */ + + if (utmpxname(_PATH_UTMPX) < 0) + return -errno; + + setutxent(); + + if (!pututxline(store)) + r = -errno; + else + r = 0; + + endutxent(); + + return r; +} + +static int write_entry_wtmp(const struct utmpx *store) { + assert(store); + + /* wtmp is a simple append-only file where each entry is + simply appended to * the end; i.e. basically a log. */ + + errno = 0; + updwtmpx(_PATH_WTMPX, store); + return -errno; +} + +static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) { + int r, s; + + r = write_entry_utmp(store_utmp); + s = write_entry_wtmp(store_wtmp); + + if (r >= 0) + r = s; + + /* If utmp/wtmp have been disabled, that's a good thing, hence + * ignore the errors */ + if (r == -ENOENT) + r = 0; + + return r; +} + +static int write_entry_both(const struct utmpx *store) { + return write_utmp_wtmp(store, store); +} + +int utmp_put_shutdown(void) { + struct utmpx store; + + init_entry(&store, 0); + + store.ut_type = RUN_LVL; + strncpy(store.ut_user, "shutdown", sizeof(store.ut_user)); + + return write_entry_both(&store); +} + +int utmp_put_reboot(usec_t t) { + struct utmpx store; + + init_entry(&store, t); + + store.ut_type = BOOT_TIME; + strncpy(store.ut_user, "reboot", sizeof(store.ut_user)); + + return write_entry_both(&store); +} + +static const char *sanitize_id(const char *id) { + size_t l; + + assert(id); + l = strlen(id); + + if (l <= sizeof(((struct utmpx*) NULL)->ut_id)) + return id; + + return id + l - sizeof(((struct utmpx*) NULL)->ut_id); +} + +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) { + struct utmpx store; + + assert(id); + + init_timestamp(&store, 0); + + store.ut_type = INIT_PROCESS; + store.ut_pid = pid; + store.ut_session = sid; + + strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id)); + + if (line) + strncpy(store.ut_line, file_name_from_path(line), sizeof(store.ut_line)); + + return write_entry_both(&store); +} + +int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { + struct utmpx lookup, store, store_wtmp, *found; + + assert(id); + + setutxent(); + + zero(lookup); + lookup.ut_type = INIT_PROCESS; /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */ + strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id)); + + if (!(found = getutxid(&lookup))) + return 0; + + if (found->ut_pid != pid) + return 0; + + memcpy(&store, found, sizeof(store)); + store.ut_type = DEAD_PROCESS; + store.ut_exit.e_termination = code; + store.ut_exit.e_exit = status; + + zero(store.ut_user); + zero(store.ut_host); + zero(store.ut_tv); + + memcpy(&store_wtmp, &store, sizeof(store_wtmp)); + /* wtmp wants the current time */ + init_timestamp(&store_wtmp, 0); + + return write_utmp_wtmp(&store, &store_wtmp); +} + + +int utmp_put_runlevel(int runlevel, int previous) { + struct utmpx store; + int r; + + assert(runlevel > 0); + + if (previous <= 0) { + /* Find the old runlevel automatically */ + + if ((r = utmp_get_runlevel(&previous, NULL)) < 0) { + if (r != -ESRCH) + return r; + + previous = 0; + } + } + + if (previous == runlevel) + return 0; + + init_entry(&store, 0); + + store.ut_type = RUN_LVL; + store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8); + strncpy(store.ut_user, "runlevel", sizeof(store.ut_user)); + + return write_entry_both(&store); +} + +#define TIMEOUT_MSEC 50 + +static int write_to_terminal(const char *tty, const char *message) { + int fd, r; + const char *p; + size_t left; + usec_t end; + + assert(tty); + assert(message); + + if ((fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC)) < 0) + return -errno; + + if (!isatty(fd)) { + r = -errno; + goto finish; + } + + p = message; + left = strlen(message); + + end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC; + + while (left > 0) { + ssize_t n; + struct pollfd pollfd; + usec_t t; + int k; + + t = now(CLOCK_MONOTONIC); + + if (t >= end) { + r = -ETIME; + goto finish; + } + + zero(pollfd); + pollfd.fd = fd; + pollfd.events = POLLOUT; + + if ((k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC)) < 0) + return -errno; + + if (k <= 0) { + r = -ETIME; + goto finish; + } + + if ((n = write(fd, p, left)) < 0) { + + if (errno == EAGAIN) + continue; + + r = -errno; + goto finish; + } + + assert((size_t) n <= left); + + p += n; + left -= n; + } + + r = 0; + +finish: + close_nointr_nofail(fd); + + return r; +} + +int utmp_wall(const char *message, bool (*match_tty)(const char *tty)) { + struct utmpx *u; + char date[FORMAT_TIMESTAMP_MAX]; + char *text = NULL, *hn = NULL, *un = NULL, *tty = NULL; + int r; + + if (!(hn = gethostname_malloc()) || + !(un = getlogname_malloc())) { + r = -ENOMEM; + goto finish; + } + + getttyname_harder(STDIN_FILENO, &tty); + + if (asprintf(&text, + "\a\r\n" + "Broadcast message from %s@%s%s%s (%s):\r\n\r\n" + "%s\r\n\r\n", + un, hn, + tty ? " on " : "", strempty(tty), + format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)), + message) < 0) { + r = -ENOMEM; + goto finish; + } + + setutxent(); + + r = 0; + + while ((u = getutxent())) { + int q; + const char *path; + char *buf = NULL; + + if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0) + continue; + + if (path_startswith(u->ut_line, "/dev/")) + path = u->ut_line; + else { + if (asprintf(&buf, "/dev/%s", u->ut_line) < 0) { + r = -ENOMEM; + goto finish; + } + + path = buf; + } + + if (!match_tty || match_tty(path)) + if ((q = write_to_terminal(path, text)) < 0) + r = q; + + free(buf); + } + +finish: + free(hn); + free(un); + free(tty); + free(text); + + return r; +} diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h new file mode 100644 index 0000000000..ab950617a1 --- /dev/null +++ b/src/shared/utmp-wtmp.h @@ -0,0 +1,38 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooutmpwtmphfoo +#define fooutmpwtmphfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "util.h" + +int utmp_get_runlevel(int *runlevel, int *previous); + +int utmp_put_shutdown(void); +int utmp_put_reboot(usec_t timestamp); +int utmp_put_runlevel(int runlevel, int previous); + +int utmp_put_dead_process(const char *id, pid_t pid, int code, int status); +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line); + +int utmp_wall(const char *message, bool (*match_tty)(const char *tty)); + +#endif diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c new file mode 100644 index 0000000000..13265e7692 --- /dev/null +++ b/src/shared/watchdog.c @@ -0,0 +1,169 @@ +/*-*- 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "watchdog.h" +#include "log.h" + +static int watchdog_fd = -1; +static usec_t watchdog_timeout = (usec_t) -1; + +static int update_timeout(void) { + int r; + + if (watchdog_fd < 0) + return 0; + + if (watchdog_timeout == (usec_t) -1) + return 0; + else if (watchdog_timeout == 0) { + int flags; + + flags = WDIOS_DISABLECARD; + r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); + if (r < 0) { + log_warning("Failed to disable hardware watchdog: %m"); + return -errno; + } + } else { + int sec, flags; + char buf[FORMAT_TIMESPAN_MAX]; + + sec = (int) ((watchdog_timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); + r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec); + if (r < 0) { + log_warning("Failed to set timeout to %is: %m", sec); + return -errno; + } + + watchdog_timeout = (usec_t) sec * USEC_PER_SEC; + log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout)); + + flags = WDIOS_ENABLECARD; + r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); + if (r < 0) { + log_warning("Failed to enable hardware watchdog: %m"); + return -errno; + } + + r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); + if (r < 0) { + log_warning("Failed to ping hardware watchdog: %m"); + return -errno; + } + } + + return 0; +} + +static int open_watchdog(void) { + struct watchdog_info ident; + + if (watchdog_fd >= 0) + return 0; + + watchdog_fd = open("/dev/watchdog", O_WRONLY|O_CLOEXEC); + if (watchdog_fd < 0) + return -errno; + + if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0) + log_info("Hardware watchdog '%s', version %x", + ident.identity, + ident.firmware_version); + + return update_timeout(); +} + +int watchdog_set_timeout(usec_t *usec) { + int r; + + watchdog_timeout = *usec; + + /* If we didn't open the watchdog yet and didn't get any + * explicit timeout value set, don't do anything */ + if (watchdog_fd < 0 && watchdog_timeout == (usec_t) -1) + return 0; + + if (watchdog_fd < 0) + r = open_watchdog(); + else + r = update_timeout(); + + *usec = watchdog_timeout; + + return r; +} + +int watchdog_ping(void) { + int r; + + if (watchdog_fd < 0) { + r = open_watchdog(); + if (r < 0) + return r; + } + + r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); + if (r < 0) { + log_warning("Failed to ping hardware watchdog: %m"); + return -errno; + } + + return 0; +} + +void watchdog_close(bool disarm) { + int r; + + if (watchdog_fd < 0) + return; + + if (disarm) { + int flags; + + /* Explicitly disarm it */ + flags = WDIOS_DISABLECARD; + r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); + if (r < 0) + log_warning("Failed to disable hardware watchdog: %m"); + + /* To be sure, use magic close logic, too */ + for (;;) { + static const char v = 'V'; + + if (write(watchdog_fd, &v, 1) > 0) + break; + + if (errno != EINTR) { + log_error("Failed to disarm watchdog timer: %m"); + break; + } + } + } + + close_nointr_nofail(watchdog_fd); + watchdog_fd = -1; +} diff --git a/src/shared/watchdog.h b/src/shared/watchdog.h new file mode 100644 index 0000000000..2e00cb9f4b --- /dev/null +++ b/src/shared/watchdog.h @@ -0,0 +1,31 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foowatchdoghfoo +#define foowatchdoghfoo + +/*** + 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 Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "util.h" + +int watchdog_set_timeout(usec_t *usec); +int watchdog_ping(void); +void watchdog_close(bool disarm); + +#endif diff --git a/src/spawn-ask-password-agent.c b/src/spawn-ask-password-agent.c deleted file mode 100644 index c1a9c58681..0000000000 --- a/src/spawn-ask-password-agent.c +++ /dev/null @@ -1,67 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "util.h" -#include "spawn-ask-password-agent.h" - -static pid_t agent_pid = 0; - -int ask_password_agent_open(void) { - int r; - - if (agent_pid > 0) - return 0; - - /* We check STDIN here, not STDOUT, since this is about input, - * not output */ - if (!isatty(STDIN_FILENO)) - return 0; - - r = fork_agent(&agent_pid, - NULL, 0, - SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, - SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL); - if (r < 0) - log_error("Failed to fork TTY ask password agent: %s", strerror(-r)); - - return r; -} - -void ask_password_agent_close(void) { - - if (agent_pid <= 0) - return; - - /* Inform agent that we are done */ - kill(agent_pid, SIGTERM); - kill(agent_pid, SIGCONT); - wait_for_terminate(agent_pid, NULL); - agent_pid = 0; -} diff --git a/src/spawn-ask-password-agent.h b/src/spawn-ask-password-agent.h deleted file mode 100644 index fa5e7b0260..0000000000 --- a/src/spawn-ask-password-agent.h +++ /dev/null @@ -1,28 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foospawnaskpasswordagenthfoo -#define foospawnaskpasswordagenthfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int ask_password_agent_open(void); -void ask_password_agent_close(void); - -#endif diff --git a/src/spawn-polkit-agent.c b/src/spawn-polkit-agent.c deleted file mode 100644 index fd72588417..0000000000 --- a/src/spawn-polkit-agent.c +++ /dev/null @@ -1,86 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "util.h" -#include "spawn-polkit-agent.h" - -static pid_t agent_pid = 0; - -int polkit_agent_open(void) { - int r; - int pipe_fd[2]; - char notify_fd[10 + 1]; - - if (agent_pid > 0) - return 0; - - /* We check STDIN here, not STDOUT, since this is about input, - * not output */ - if (!isatty(STDIN_FILENO)) - return 0; - - if (pipe2(pipe_fd, 0) < 0) - return -errno; - - snprintf(notify_fd, sizeof(notify_fd), "%i", pipe_fd[1]); - char_array_0(notify_fd); - - r = fork_agent(&agent_pid, - &pipe_fd[1], 1, - POLKIT_AGENT_BINARY_PATH, - POLKIT_AGENT_BINARY_PATH, "--notify-fd", notify_fd, NULL); - - /* Close the writing side, because that's the one for the agent */ - close_nointr_nofail(pipe_fd[1]); - - if (r < 0) - log_error("Failed to fork TTY ask password agent: %s", strerror(-r)); - else - /* Wait until the agent closes the fd */ - fd_wait_for_event(pipe_fd[0], POLLHUP, (usec_t) -1); - - close_nointr_nofail(pipe_fd[0]); - - return r; -} - -void polkit_agent_close(void) { - - if (agent_pid <= 0) - return; - - /* Inform agent that we are done */ - kill(agent_pid, SIGTERM); - kill(agent_pid, SIGCONT); - wait_for_terminate(agent_pid, NULL); - agent_pid = 0; -} diff --git a/src/spawn-polkit-agent.h b/src/spawn-polkit-agent.h deleted file mode 100644 index b91d20f120..0000000000 --- a/src/spawn-polkit-agent.h +++ /dev/null @@ -1,28 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foospawnpolkitagenthfoo -#define foospawnpolkitagenthfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int polkit_agent_open(void); -void polkit_agent_close(void); - -#endif diff --git a/src/specifier.c b/src/specifier.c deleted file mode 100644 index ae00ae10bb..0000000000 --- a/src/specifier.c +++ /dev/null @@ -1,108 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "macro.h" -#include "util.h" -#include "specifier.h" - -/* - * Generic infrastructure for replacing %x style specifiers in - * strings. Will call a callback for each replacement. - * - */ - -char *specifier_printf(const char *text, const Specifier table[], void *userdata) { - char *r, *t; - const char *f; - bool percent = false; - size_t l; - - assert(text); - assert(table); - - l = strlen(text); - if (!(r = new(char, l+1))) - return NULL; - - t = r; - - for (f = text; *f; f++, l--) { - - if (percent) { - if (*f == '%') - *(t++) = '%'; - else { - const Specifier *i; - - for (i = table; i->specifier; i++) - if (i->specifier == *f) - break; - - if (i->lookup) { - char *n, *w; - size_t k, j; - - if (!(w = i->lookup(i->specifier, i->data, userdata))) { - free(r); - return NULL; - } - - j = t - r; - k = strlen(w); - - if (!(n = new(char, j + k + l + 1))) { - free(r); - free(w); - return NULL; - } - - memcpy(n, r, j); - memcpy(n + j, w, k); - - free(r); - free(w); - - r = n; - t = n + j + k; - } else { - *(t++) = '%'; - *(t++) = *f; - } - } - - percent = false; - } else if (*f == '%') - percent = true; - else - *(t++) = *f; - } - - *t = 0; - return r; -} - -/* Generic handler for simple string replacements */ - -char* specifier_string(char specifier, void *data, void *userdata) { - return strdup(strempty(data)); -} diff --git a/src/specifier.h b/src/specifier.h deleted file mode 100644 index 57d1fcb35c..0000000000 --- a/src/specifier.h +++ /dev/null @@ -1,37 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foospecifierhfoo -#define foospecifierhfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef char* (*SpecifierCallback)(char specifier, void *data, void *userdata); - -typedef struct Specifier { - const char specifier; - const SpecifierCallback lookup; - void *data; -} Specifier; - -char *specifier_printf(const char *text, const Specifier table[], void *userdata); - -char* specifier_string(char specifier, void *data, void *userdata); - -#endif diff --git a/src/umount.c b/src/umount.c deleted file mode 100644 index 24c0947f21..0000000000 --- a/src/umount.c +++ /dev/null @@ -1,644 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "list.h" -#include "mount-setup.h" -#include "umount.h" -#include "util.h" - -typedef struct MountPoint { - char *path; - dev_t devnum; - bool skip_ro; - LIST_FIELDS (struct MountPoint, mount_point); -} MountPoint; - -static void mount_point_free(MountPoint **head, MountPoint *m) { - assert(head); - assert(m); - - LIST_REMOVE(MountPoint, mount_point, *head, m); - - free(m->path); - free(m); -} - -static void mount_points_list_free(MountPoint **head) { - assert(head); - - while (*head) - mount_point_free(head, *head); -} - -static int mount_points_list_get(MountPoint **head) { - FILE *proc_self_mountinfo; - char *path, *p; - unsigned int i; - int r; - - assert(head); - - if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"))) - return -errno; - - for (i = 1;; i++) { - int k; - MountPoint *m; - char *root; - bool skip_ro; - - path = p = NULL; - - if ((k = fscanf(proc_self_mountinfo, - "%*s " /* (1) mount id */ - "%*s " /* (2) parent id */ - "%*s " /* (3) major:minor */ - "%ms " /* (4) root */ - "%ms " /* (5) mount point */ - "%*s" /* (6) mount options */ - "%*[^-]" /* (7) optional fields */ - "- " /* (8) separator */ - "%*s " /* (9) file system type */ - "%*s" /* (10) mount source */ - "%*s" /* (11) mount options 2 */ - "%*[^\n]", /* some rubbish at the end */ - &root, - &path)) != 2) { - if (k == EOF) - break; - - log_warning("Failed to parse /proc/self/mountinfo:%u.", i); - - free(path); - continue; - } - - /* If we encounter a bind mount, don't try to remount - * the source dir too early */ - skip_ro = !streq(root, "/"); - free(root); - - p = cunescape(path); - free(path); - - if (!p) { - r = -ENOMEM; - goto finish; - } - - if (mount_point_is_api(p) || mount_point_ignore(p)) { - free(p); - continue; - } - - if (!(m = new0(MountPoint, 1))) { - free(p); - r = -ENOMEM; - goto finish; - } - - m->path = p; - m->skip_ro = skip_ro; - LIST_PREPEND(MountPoint, mount_point, *head, m); - } - - r = 0; - -finish: - fclose(proc_self_mountinfo); - - return r; -} - -static int swap_list_get(MountPoint **head) { - FILE *proc_swaps; - unsigned int i; - int r; - - assert(head); - - if (!(proc_swaps = fopen("/proc/swaps", "re"))) - return (errno == ENOENT) ? 0 : -errno; - - (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n"); - - for (i = 2;; i++) { - MountPoint *swap; - char *dev = NULL, *d; - int k; - - if ((k = fscanf(proc_swaps, - "%ms " /* device/file */ - "%*s " /* type of swap */ - "%*s " /* swap size */ - "%*s " /* used */ - "%*s\n", /* priority */ - &dev)) != 1) { - - if (k == EOF) - break; - - log_warning("Failed to parse /proc/swaps:%u.", i); - - free(dev); - continue; - } - - if (endswith(dev, "(deleted)")) { - free(dev); - continue; - } - - d = cunescape(dev); - free(dev); - - if (!d) { - r = -ENOMEM; - goto finish; - } - - if (!(swap = new0(MountPoint, 1))) { - free(d); - r = -ENOMEM; - goto finish; - } - - swap->path = d; - LIST_PREPEND(MountPoint, mount_point, *head, swap); - } - - r = 0; - -finish: - fclose(proc_swaps); - - return r; -} - -static int loopback_list_get(MountPoint **head) { - int r; - struct udev *udev; - struct udev_enumerate *e = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - - assert(head); - - if (!(udev = udev_new())) { - r = -ENOMEM; - goto finish; - } - - if (!(e = udev_enumerate_new(udev))) { - r = -ENOMEM; - goto finish; - } - - if (udev_enumerate_add_match_subsystem(e, "block") < 0 || - udev_enumerate_add_match_sysname(e, "loop*") < 0) { - r = -EIO; - goto finish; - } - - if (udev_enumerate_scan_devices(e) < 0) { - r = -EIO; - goto finish; - } - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - MountPoint *lb; - struct udev_device *d; - char *loop; - const char *dn; - - if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) { - r = -ENOMEM; - goto finish; - } - - if (!(dn = udev_device_get_devnode(d))) { - udev_device_unref(d); - continue; - } - - loop = strdup(dn); - udev_device_unref(d); - - if (!loop) { - r = -ENOMEM; - goto finish; - } - - if (!(lb = new0(MountPoint, 1))) { - free(loop); - r = -ENOMEM; - goto finish; - } - - lb->path = loop; - LIST_PREPEND(MountPoint, mount_point, *head, lb); - } - - r = 0; - -finish: - if (e) - udev_enumerate_unref(e); - - if (udev) - udev_unref(udev); - - return r; -} - -static int dm_list_get(MountPoint **head) { - int r; - struct udev *udev; - struct udev_enumerate *e = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - - assert(head); - - if (!(udev = udev_new())) { - r = -ENOMEM; - goto finish; - } - - if (!(e = udev_enumerate_new(udev))) { - r = -ENOMEM; - goto finish; - } - - if (udev_enumerate_add_match_subsystem(e, "block") < 0 || - udev_enumerate_add_match_sysname(e, "dm-*") < 0) { - r = -EIO; - goto finish; - } - - if (udev_enumerate_scan_devices(e) < 0) { - r = -EIO; - goto finish; - } - - first = udev_enumerate_get_list_entry(e); - - udev_list_entry_foreach(item, first) { - MountPoint *m; - struct udev_device *d; - dev_t devnum; - char *node; - const char *dn; - - if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) { - r = -ENOMEM; - goto finish; - } - - devnum = udev_device_get_devnum(d); - dn = udev_device_get_devnode(d); - - if (major(devnum) == 0 || !dn) { - udev_device_unref(d); - continue; - } - - node = strdup(dn); - udev_device_unref(d); - - if (!node) { - r = -ENOMEM; - goto finish; - } - - if (!(m = new(MountPoint, 1))) { - free(node); - r = -ENOMEM; - goto finish; - } - - m->path = node; - m->devnum = devnum; - LIST_PREPEND(MountPoint, mount_point, *head, m); - } - - r = 0; - -finish: - if (e) - udev_enumerate_unref(e); - - if (udev) - udev_unref(udev); - - return r; -} - -static int delete_loopback(const char *device) { - int fd, r; - - if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0) - return errno == ENOENT ? 0 : -errno; - - r = ioctl(fd, LOOP_CLR_FD, 0); - close_nointr_nofail(fd); - - if (r >= 0) - return 1; - - /* ENXIO: not bound, so no error */ - if (errno == ENXIO) - return 0; - - return -errno; -} - -static int delete_dm(dev_t devnum) { - int fd, r; - struct dm_ioctl dm; - - assert(major(devnum) != 0); - - if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0) - return -errno; - - zero(dm); - dm.version[0] = DM_VERSION_MAJOR; - dm.version[1] = DM_VERSION_MINOR; - dm.version[2] = DM_VERSION_PATCHLEVEL; - - dm.data_size = sizeof(dm); - dm.dev = devnum; - - r = ioctl(fd, DM_DEV_REMOVE, &dm); - close_nointr_nofail(fd); - - return r >= 0 ? 0 : -errno; -} - -static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) { - MountPoint *m, *n; - int n_failed = 0; - - assert(head); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - if (path_equal(m->path, "/") -#ifndef HAVE_SPLIT_USR - || path_equal(m->path, "/usr") -#endif - ) { - n_failed++; - continue; - } - - /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */ - if (umount2(m->path, MNT_FORCE) == 0) { - log_info("Unmounted %s.", m->path); - if (changed) - *changed = true; - - mount_point_free(head, m); - } else if (log_error) { - log_warning("Could not unmount %s: %m", m->path); - n_failed++; - } - } - - return n_failed; -} - -static int mount_points_list_remount_read_only(MountPoint **head, bool *changed) { - MountPoint *m, *n; - int n_failed = 0; - - assert(head); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - - if (m->skip_ro) { - n_failed++; - continue; - } - - /* Trying to remount read-only */ - if (mount(NULL, m->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) { - if (changed) - *changed = true; - - mount_point_free(head, m); - } else { - log_warning("Could not remount as read-only %s: %m", m->path); - n_failed++; - } - } - - return n_failed; -} - -static int swap_points_list_off(MountPoint **head, bool *changed) { - MountPoint *m, *n; - int n_failed = 0; - - assert(head); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - if (swapoff(m->path) == 0) { - if (changed) - *changed = true; - - mount_point_free(head, m); - } else { - log_warning("Could not deactivate swap %s: %m", m->path); - n_failed++; - } - } - - return n_failed; -} - -static int loopback_points_list_detach(MountPoint **head, bool *changed) { - MountPoint *m, *n; - int n_failed = 0, k; - struct stat root_st; - - assert(head); - - k = lstat("/", &root_st); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - int r; - struct stat loopback_st; - - if (k >= 0 && - major(root_st.st_dev) != 0 && - lstat(m->path, &loopback_st) >= 0 && - root_st.st_dev == loopback_st.st_rdev) { - n_failed ++; - continue; - } - - if ((r = delete_loopback(m->path)) >= 0) { - - if (r > 0 && changed) - *changed = true; - - mount_point_free(head, m); - } else { - log_warning("Could not delete loopback %s: %m", m->path); - n_failed++; - } - } - - return n_failed; -} - -static int dm_points_list_detach(MountPoint **head, bool *changed) { - MountPoint *m, *n; - int n_failed = 0, k; - struct stat root_st; - - assert(head); - - k = lstat("/", &root_st); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - int r; - - if (k >= 0 && - major(root_st.st_dev) != 0 && - root_st.st_dev == m->devnum) { - n_failed ++; - continue; - } - - if ((r = delete_dm(m->devnum)) >= 0) { - - if (r > 0 && changed) - *changed = true; - - mount_point_free(head, m); - } else { - log_warning("Could not delete dm %s: %m", m->path); - n_failed++; - } - } - - return n_failed; -} - -int umount_all(bool *changed) { - int r; - bool umount_changed; - - LIST_HEAD(MountPoint, mp_list_head); - - LIST_HEAD_INIT(MountPoint, mp_list_head); - - r = mount_points_list_get(&mp_list_head); - if (r < 0) - goto end; - - /* retry umount, until nothing can be umounted anymore */ - do { - umount_changed = false; - - mount_points_list_umount(&mp_list_head, &umount_changed, false); - if (umount_changed) - *changed = true; - - } while (umount_changed); - - /* umount one more time with logging enabled */ - r = mount_points_list_umount(&mp_list_head, &umount_changed, true); - if (r <= 0) - goto end; - - r = mount_points_list_remount_read_only(&mp_list_head, changed); - - end: - mount_points_list_free(&mp_list_head); - - return r; -} - -int swapoff_all(bool *changed) { - int r; - LIST_HEAD(MountPoint, swap_list_head); - - LIST_HEAD_INIT(MountPoint, swap_list_head); - - r = swap_list_get(&swap_list_head); - if (r < 0) - goto end; - - r = swap_points_list_off(&swap_list_head, changed); - - end: - mount_points_list_free(&swap_list_head); - - return r; -} - -int loopback_detach_all(bool *changed) { - int r; - LIST_HEAD(MountPoint, loopback_list_head); - - LIST_HEAD_INIT(MountPoint, loopback_list_head); - - r = loopback_list_get(&loopback_list_head); - if (r < 0) - goto end; - - r = loopback_points_list_detach(&loopback_list_head, changed); - - end: - mount_points_list_free(&loopback_list_head); - - return r; -} - -int dm_detach_all(bool *changed) { - int r; - LIST_HEAD(MountPoint, dm_list_head); - - LIST_HEAD_INIT(MountPoint, dm_list_head); - - r = dm_list_get(&dm_list_head); - if (r < 0) - goto end; - - r = dm_points_list_detach(&dm_list_head, changed); - - end: - mount_points_list_free(&dm_list_head); - - return r; -} diff --git a/src/umount.h b/src/umount.h deleted file mode 100644 index 2e2f9c181a..0000000000 --- a/src/umount.h +++ /dev/null @@ -1,33 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef fooumounthfoo -#define fooumounthfoo - -/*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int umount_all(bool *changed); - -int swapoff_all(bool *changed); - -int loopback_detach_all(bool *changed); - -int dm_detach_all(bool *changed); - -#endif diff --git a/src/unit-name.c b/src/unit-name.c deleted file mode 100644 index 566cdc51cc..0000000000 --- a/src/unit-name.c +++ /dev/null @@ -1,448 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "util.h" -#include "unit-name.h" - -#define VALID_CHARS \ - "0123456789" \ - "abcdefghijklmnopqrstuvwxyz" \ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ - ":-_.\\" - -bool unit_name_is_valid_no_type(const char *n, bool template_ok) { - const char *e, *i, *at; - - /* Valid formats: - * - * string@instance.suffix - * string.suffix - */ - - assert(n); - - if (strlen(n) >= UNIT_NAME_MAX) - return false; - - e = strrchr(n, '.'); - if (!e || e == n) - return false; - - for (i = n, at = NULL; i < e; i++) { - - if (*i == '@' && !at) - at = i; - - if (!strchr("@" VALID_CHARS, *i)) - return false; - } - - if (at) { - if (at == n) - return false; - - if (!template_ok && at+1 == e) - return false; - } - - return true; -} - -bool unit_instance_is_valid(const char *i) { - assert(i); - - /* The max length depends on the length of the string, so we - * don't really check this here. */ - - if (i[0] == 0) - return false; - - /* We allow additional @ in the instance string, we do not - * allow them in the prefix! */ - - for (; *i; i++) - if (!strchr("@" VALID_CHARS, *i)) - return false; - - return true; -} - -bool unit_prefix_is_valid(const char *p) { - - /* We don't allow additional @ in the instance string */ - - if (p[0] == 0) - return false; - - for (; *p; p++) - if (!strchr(VALID_CHARS, *p)) - return false; - - return true; -} - -int unit_name_to_instance(const char *n, char **instance) { - const char *p, *d; - char *i; - - assert(n); - assert(instance); - - /* Everything past the first @ and before the last . is the instance */ - if (!(p = strchr(n, '@'))) { - *instance = NULL; - return 0; - } - - assert_se(d = strrchr(n, '.')); - assert(p < d); - - if (!(i = strndup(p+1, d-p-1))) - return -ENOMEM; - - *instance = i; - return 0; -} - -char *unit_name_to_prefix_and_instance(const char *n) { - const char *d; - - assert(n); - - assert_se(d = strrchr(n, '.')); - - return strndup(n, d - n); -} - -char *unit_name_to_prefix(const char *n) { - const char *p; - - if ((p = strchr(n, '@'))) - return strndup(n, p - n); - - return unit_name_to_prefix_and_instance(n); -} - -char *unit_name_change_suffix(const char *n, const char *suffix) { - char *e, *r; - size_t a, b; - - assert(n); - assert(unit_name_is_valid_no_type(n, true)); - assert(suffix); - - assert_se(e = strrchr(n, '.')); - a = e - n; - b = strlen(suffix); - - if (!(r = new(char, a + b + 1))) - return NULL; - - memcpy(r, n, a); - memcpy(r+a, suffix, b+1); - - return r; -} - -char *unit_name_build(const char *prefix, const char *instance, const char *suffix) { - assert(prefix); - assert(unit_prefix_is_valid(prefix)); - assert(!instance || unit_instance_is_valid(instance)); - assert(suffix); - - if (!instance) - return strappend(prefix, suffix); - - return join(prefix, "@", instance, suffix, NULL); -} - -static char* do_escape(const char *f, char *t) { - assert(f); - assert(t); - - for (; *f; f++) { - if (*f == '/') - *(t++) = '-'; - else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f)) { - *(t++) = '\\'; - *(t++) = 'x'; - *(t++) = hexchar(*f >> 4); - *(t++) = hexchar(*f); - } else - *(t++) = *f; - } - - return t; -} - -char *unit_name_build_escape(const char *prefix, const char *instance, const char *suffix) { - char *r, *t; - size_t a, b, c; - - assert(prefix); - assert(suffix); - - /* Takes a arbitrary string for prefix and instance plus a - * suffix and makes a nice string suitable as unit name of it, - * escaping all weird chars on the way. - * - * / becomes ., and all chars not allowed in a unit name get - * escaped as \xFF, including \ and ., of course. This - * escaping is hence reversible. - * - * This is primarily useful to make nice unit names from - * strings, but is actually useful for any kind of string. - */ - - a = strlen(prefix); - c = strlen(suffix); - - if (instance) { - b = strlen(instance); - - if (!(r = new(char, a*4 + 1 + b*4 + c + 1))) - return NULL; - - t = do_escape(prefix, r); - *(t++) = '@'; - t = do_escape(instance, t); - } else { - - if (!(r = new(char, a*4 + c + 1))) - return NULL; - - t = do_escape(prefix, r); - } - - strcpy(t, suffix); - return r; -} - -char *unit_name_escape(const char *f) { - char *r, *t; - - if (!(r = new(char, strlen(f)*4+1))) - return NULL; - - t = do_escape(f, r); - *t = 0; - - return r; - -} - -char *unit_name_unescape(const char *f) { - char *r, *t; - - assert(f); - - if (!(r = strdup(f))) - return NULL; - - for (t = r; *f; f++) { - if (*f == '-') - *(t++) = '/'; - else if (*f == '\\') { - int a, b; - - if (f[1] != 'x' || - (a = unhexchar(f[2])) < 0 || - (b = unhexchar(f[3])) < 0) { - /* Invalid escape code, let's take it literal then */ - *(t++) = '\\'; - } else { - *(t++) = (char) ((a << 4) | b); - f += 3; - } - } else - *(t++) = *f; - } - - *t = 0; - - return r; -} - -bool unit_name_is_template(const char *n) { - const char *p; - - assert(n); - - if (!(p = strchr(n, '@'))) - return false; - - return p[1] == '.'; -} - -char *unit_name_replace_instance(const char *f, const char *i) { - const char *p, *e; - char *r, *k; - size_t a; - - assert(f); - - p = strchr(f, '@'); - assert_se(e = strrchr(f, '.')); - - a = p - f; - - if (p) { - size_t b; - - b = strlen(i); - - if (!(r = new(char, a + 1 + b + strlen(e) + 1))) - return NULL; - - k = mempcpy(r, f, a + 1); - k = mempcpy(k, i, b); - } else { - - if (!(r = new(char, a + strlen(e) + 1))) - return NULL; - - k = mempcpy(r, f, a); - } - - strcpy(k, e); - return r; -} - -char *unit_name_template(const char *f) { - const char *p, *e; - char *r; - size_t a; - - if (!(p = strchr(f, '@'))) - return strdup(f); - - assert_se(e = strrchr(f, '.')); - a = p - f + 1; - - if (!(r = new(char, a + strlen(e) + 1))) - return NULL; - - strcpy(mempcpy(r, f, a), e); - return r; - -} - -char *unit_name_from_path(const char *path, const char *suffix) { - char *p, *r; - - assert(path); - assert(suffix); - - if (!(p = strdup(path))) - return NULL; - - path_kill_slashes(p); - - path = p[0] == '/' ? p + 1 : p; - - if (path[0] == 0) { - free(p); - return strappend("-", suffix); - } - - r = unit_name_build_escape(path, NULL, suffix); - free(p); - - return r; -} - -char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) { - char *p, *r; - - assert(path); - assert(suffix); - - if (!(p = strdup(path))) - return NULL; - - path_kill_slashes(p); - - path = p[0] == '/' ? p + 1 : p; - - if (path[0] == 0) { - free(p); - return unit_name_build_escape(prefix, "-", suffix); - } - - r = unit_name_build_escape(prefix, path, suffix); - free(p); - - return r; -} - -char *unit_name_to_path(const char *name) { - char *w, *e; - - assert(name); - - if (!(w = unit_name_to_prefix(name))) - return NULL; - - e = unit_name_unescape(w); - free(w); - - if (!e) - return NULL; - - if (e[0] != '/') { - w = strappend("/", e); - free(e); - - if (!w) - return NULL; - - e = w; - } - - return e; -} - -char *unit_name_path_unescape(const char *f) { - char *e; - - assert(f); - - if (!(e = unit_name_unescape(f))) - return NULL; - - if (e[0] != '/') { - char *w; - - w = strappend("/", e); - free(e); - - if (!w) - return NULL; - - e = w; - } - - return e; -} diff --git a/src/unit-name.h b/src/unit-name.h deleted file mode 100644 index 4dfb9fa3ce..0000000000 --- a/src/unit-name.h +++ /dev/null @@ -1,57 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foounitnamehfoo -#define foounitnamehfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#define UNIT_NAME_MAX 256 - -int unit_name_to_instance(const char *n, char **instance); -char* unit_name_to_prefix(const char *n); -char* unit_name_to_prefix_and_instance(const char *n); - -bool unit_name_is_valid_no_type(const char *n, bool template_ok); -bool unit_prefix_is_valid(const char *p); -bool unit_instance_is_valid(const char *i); - -char *unit_name_change_suffix(const char *n, const char *suffix); - -char *unit_name_build(const char *prefix, const char *instance, const char *suffix); -char *unit_name_build_escape(const char *prefix, const char *instance, const char *suffix); - -char *unit_name_escape(const char *f); -char *unit_name_unescape(const char *f); - -char *unit_name_path_unescape(const char *f); - -bool unit_name_is_template(const char *n); - -char *unit_name_replace_instance(const char *f, const char *i); - -char *unit_name_template(const char *f); - -char *unit_name_from_path(const char *path, const char *suffix); -char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix); -char *unit_name_to_path(const char *name); - -#endif diff --git a/src/utmp-wtmp.c b/src/utmp-wtmp.c deleted file mode 100644 index 6bba325d3e..0000000000 --- a/src/utmp-wtmp.c +++ /dev/null @@ -1,430 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "macro.h" -#include "utmp-wtmp.h" - -int utmp_get_runlevel(int *runlevel, int *previous) { - struct utmpx lookup, *found; - int r; - const char *e; - - assert(runlevel); - - /* If these values are set in the environment this takes - * precedence. Presumably, sysvinit does this to work around a - * race condition that would otherwise exist where we'd always - * go to disk and hence might read runlevel data that might be - * very new and does not apply to the current script being - * executed. */ - - if ((e = getenv("RUNLEVEL")) && e[0] > 0) { - *runlevel = e[0]; - - if (previous) { - /* $PREVLEVEL seems to be an Upstart thing */ - - if ((e = getenv("PREVLEVEL")) && e[0] > 0) - *previous = e[0]; - else - *previous = 0; - } - - return 0; - } - - if (utmpxname(_PATH_UTMPX) < 0) - return -errno; - - setutxent(); - - zero(lookup); - lookup.ut_type = RUN_LVL; - - if (!(found = getutxid(&lookup))) - r = -errno; - else { - int a, b; - - a = found->ut_pid & 0xFF; - b = (found->ut_pid >> 8) & 0xFF; - - if (a < 0 || b < 0) - r = -EIO; - else { - *runlevel = a; - - if (previous) - *previous = b; - r = 0; - } - } - - endutxent(); - - return r; -} - -static void init_timestamp(struct utmpx *store, usec_t t) { - assert(store); - - zero(*store); - - if (t <= 0) - t = now(CLOCK_REALTIME); - - store->ut_tv.tv_sec = t / USEC_PER_SEC; - store->ut_tv.tv_usec = t % USEC_PER_SEC; -} - -static void init_entry(struct utmpx *store, usec_t t) { - struct utsname uts; - - assert(store); - - init_timestamp(store, t); - - zero(uts); - - if (uname(&uts) >= 0) - strncpy(store->ut_host, uts.release, sizeof(store->ut_host)); - - strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */ - strncpy(store->ut_id, "~~", sizeof(store->ut_id)); -} - -static int write_entry_utmp(const struct utmpx *store) { - int r; - - assert(store); - - /* utmp is similar to wtmp, but there is only one entry for - * each entry type resp. user; i.e. basically a key/value - * table. */ - - if (utmpxname(_PATH_UTMPX) < 0) - return -errno; - - setutxent(); - - if (!pututxline(store)) - r = -errno; - else - r = 0; - - endutxent(); - - return r; -} - -static int write_entry_wtmp(const struct utmpx *store) { - assert(store); - - /* wtmp is a simple append-only file where each entry is - simply appended to * the end; i.e. basically a log. */ - - errno = 0; - updwtmpx(_PATH_WTMPX, store); - return -errno; -} - -static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) { - int r, s; - - r = write_entry_utmp(store_utmp); - s = write_entry_wtmp(store_wtmp); - - if (r >= 0) - r = s; - - /* If utmp/wtmp have been disabled, that's a good thing, hence - * ignore the errors */ - if (r == -ENOENT) - r = 0; - - return r; -} - -static int write_entry_both(const struct utmpx *store) { - return write_utmp_wtmp(store, store); -} - -int utmp_put_shutdown(void) { - struct utmpx store; - - init_entry(&store, 0); - - store.ut_type = RUN_LVL; - strncpy(store.ut_user, "shutdown", sizeof(store.ut_user)); - - return write_entry_both(&store); -} - -int utmp_put_reboot(usec_t t) { - struct utmpx store; - - init_entry(&store, t); - - store.ut_type = BOOT_TIME; - strncpy(store.ut_user, "reboot", sizeof(store.ut_user)); - - return write_entry_both(&store); -} - -static const char *sanitize_id(const char *id) { - size_t l; - - assert(id); - l = strlen(id); - - if (l <= sizeof(((struct utmpx*) NULL)->ut_id)) - return id; - - return id + l - sizeof(((struct utmpx*) NULL)->ut_id); -} - -int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) { - struct utmpx store; - - assert(id); - - init_timestamp(&store, 0); - - store.ut_type = INIT_PROCESS; - store.ut_pid = pid; - store.ut_session = sid; - - strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id)); - - if (line) - strncpy(store.ut_line, file_name_from_path(line), sizeof(store.ut_line)); - - return write_entry_both(&store); -} - -int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { - struct utmpx lookup, store, store_wtmp, *found; - - assert(id); - - setutxent(); - - zero(lookup); - lookup.ut_type = INIT_PROCESS; /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */ - strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id)); - - if (!(found = getutxid(&lookup))) - return 0; - - if (found->ut_pid != pid) - return 0; - - memcpy(&store, found, sizeof(store)); - store.ut_type = DEAD_PROCESS; - store.ut_exit.e_termination = code; - store.ut_exit.e_exit = status; - - zero(store.ut_user); - zero(store.ut_host); - zero(store.ut_tv); - - memcpy(&store_wtmp, &store, sizeof(store_wtmp)); - /* wtmp wants the current time */ - init_timestamp(&store_wtmp, 0); - - return write_utmp_wtmp(&store, &store_wtmp); -} - - -int utmp_put_runlevel(int runlevel, int previous) { - struct utmpx store; - int r; - - assert(runlevel > 0); - - if (previous <= 0) { - /* Find the old runlevel automatically */ - - if ((r = utmp_get_runlevel(&previous, NULL)) < 0) { - if (r != -ESRCH) - return r; - - previous = 0; - } - } - - if (previous == runlevel) - return 0; - - init_entry(&store, 0); - - store.ut_type = RUN_LVL; - store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8); - strncpy(store.ut_user, "runlevel", sizeof(store.ut_user)); - - return write_entry_both(&store); -} - -#define TIMEOUT_MSEC 50 - -static int write_to_terminal(const char *tty, const char *message) { - int fd, r; - const char *p; - size_t left; - usec_t end; - - assert(tty); - assert(message); - - if ((fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC)) < 0) - return -errno; - - if (!isatty(fd)) { - r = -errno; - goto finish; - } - - p = message; - left = strlen(message); - - end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC; - - while (left > 0) { - ssize_t n; - struct pollfd pollfd; - usec_t t; - int k; - - t = now(CLOCK_MONOTONIC); - - if (t >= end) { - r = -ETIME; - goto finish; - } - - zero(pollfd); - pollfd.fd = fd; - pollfd.events = POLLOUT; - - if ((k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC)) < 0) - return -errno; - - if (k <= 0) { - r = -ETIME; - goto finish; - } - - if ((n = write(fd, p, left)) < 0) { - - if (errno == EAGAIN) - continue; - - r = -errno; - goto finish; - } - - assert((size_t) n <= left); - - p += n; - left -= n; - } - - r = 0; - -finish: - close_nointr_nofail(fd); - - return r; -} - -int utmp_wall(const char *message, bool (*match_tty)(const char *tty)) { - struct utmpx *u; - char date[FORMAT_TIMESTAMP_MAX]; - char *text = NULL, *hn = NULL, *un = NULL, *tty = NULL; - int r; - - if (!(hn = gethostname_malloc()) || - !(un = getlogname_malloc())) { - r = -ENOMEM; - goto finish; - } - - getttyname_harder(STDIN_FILENO, &tty); - - if (asprintf(&text, - "\a\r\n" - "Broadcast message from %s@%s%s%s (%s):\r\n\r\n" - "%s\r\n\r\n", - un, hn, - tty ? " on " : "", strempty(tty), - format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)), - message) < 0) { - r = -ENOMEM; - goto finish; - } - - setutxent(); - - r = 0; - - while ((u = getutxent())) { - int q; - const char *path; - char *buf = NULL; - - if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0) - continue; - - if (path_startswith(u->ut_line, "/dev/")) - path = u->ut_line; - else { - if (asprintf(&buf, "/dev/%s", u->ut_line) < 0) { - r = -ENOMEM; - goto finish; - } - - path = buf; - } - - if (!match_tty || match_tty(path)) - if ((q = write_to_terminal(path, text)) < 0) - r = q; - - free(buf); - } - -finish: - free(hn); - free(un); - free(tty); - free(text); - - return r; -} diff --git a/src/utmp-wtmp.h b/src/utmp-wtmp.h deleted file mode 100644 index ab950617a1..0000000000 --- a/src/utmp-wtmp.h +++ /dev/null @@ -1,38 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef fooutmpwtmphfoo -#define fooutmpwtmphfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "util.h" - -int utmp_get_runlevel(int *runlevel, int *previous); - -int utmp_put_shutdown(void); -int utmp_put_reboot(usec_t timestamp); -int utmp_put_runlevel(int runlevel, int previous); - -int utmp_put_dead_process(const char *id, pid_t pid, int code, int status); -int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line); - -int utmp_wall(const char *message, bool (*match_tty)(const char *tty)); - -#endif diff --git a/src/watchdog.c b/src/watchdog.c deleted file mode 100644 index 13265e7692..0000000000 --- a/src/watchdog.c +++ /dev/null @@ -1,169 +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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "watchdog.h" -#include "log.h" - -static int watchdog_fd = -1; -static usec_t watchdog_timeout = (usec_t) -1; - -static int update_timeout(void) { - int r; - - if (watchdog_fd < 0) - return 0; - - if (watchdog_timeout == (usec_t) -1) - return 0; - else if (watchdog_timeout == 0) { - int flags; - - flags = WDIOS_DISABLECARD; - r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); - if (r < 0) { - log_warning("Failed to disable hardware watchdog: %m"); - return -errno; - } - } else { - int sec, flags; - char buf[FORMAT_TIMESPAN_MAX]; - - sec = (int) ((watchdog_timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); - r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec); - if (r < 0) { - log_warning("Failed to set timeout to %is: %m", sec); - return -errno; - } - - watchdog_timeout = (usec_t) sec * USEC_PER_SEC; - log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout)); - - flags = WDIOS_ENABLECARD; - r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); - if (r < 0) { - log_warning("Failed to enable hardware watchdog: %m"); - return -errno; - } - - r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); - if (r < 0) { - log_warning("Failed to ping hardware watchdog: %m"); - return -errno; - } - } - - return 0; -} - -static int open_watchdog(void) { - struct watchdog_info ident; - - if (watchdog_fd >= 0) - return 0; - - watchdog_fd = open("/dev/watchdog", O_WRONLY|O_CLOEXEC); - if (watchdog_fd < 0) - return -errno; - - if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0) - log_info("Hardware watchdog '%s', version %x", - ident.identity, - ident.firmware_version); - - return update_timeout(); -} - -int watchdog_set_timeout(usec_t *usec) { - int r; - - watchdog_timeout = *usec; - - /* If we didn't open the watchdog yet and didn't get any - * explicit timeout value set, don't do anything */ - if (watchdog_fd < 0 && watchdog_timeout == (usec_t) -1) - return 0; - - if (watchdog_fd < 0) - r = open_watchdog(); - else - r = update_timeout(); - - *usec = watchdog_timeout; - - return r; -} - -int watchdog_ping(void) { - int r; - - if (watchdog_fd < 0) { - r = open_watchdog(); - if (r < 0) - return r; - } - - r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); - if (r < 0) { - log_warning("Failed to ping hardware watchdog: %m"); - return -errno; - } - - return 0; -} - -void watchdog_close(bool disarm) { - int r; - - if (watchdog_fd < 0) - return; - - if (disarm) { - int flags; - - /* Explicitly disarm it */ - flags = WDIOS_DISABLECARD; - r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); - if (r < 0) - log_warning("Failed to disable hardware watchdog: %m"); - - /* To be sure, use magic close logic, too */ - for (;;) { - static const char v = 'V'; - - if (write(watchdog_fd, &v, 1) > 0) - break; - - if (errno != EINTR) { - log_error("Failed to disarm watchdog timer: %m"); - break; - } - } - } - - close_nointr_nofail(watchdog_fd); - watchdog_fd = -1; -} diff --git a/src/watchdog.h b/src/watchdog.h deleted file mode 100644 index 2e00cb9f4b..0000000000 --- a/src/watchdog.h +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foowatchdoghfoo -#define foowatchdoghfoo - -/*** - 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 Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "util.h" - -int watchdog_set_timeout(usec_t *usec); -int watchdog_ping(void); -void watchdog_close(bool disarm); - -#endif -- cgit v1.2.3-54-g00ecf