summaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/acl-util.c182
-rw-r--r--src/shared/acl-util.h6
-rw-r--r--src/shared/acpi-fpdt.c2
-rw-r--r--src/shared/apparmor-util.c1
-rw-r--r--src/shared/architecture.c10
-rw-r--r--src/shared/architecture.h2
-rw-r--r--src/shared/arphrd-list.c1
-rw-r--r--src/shared/ask-password-api.c7
-rw-r--r--src/shared/ask-password-api.h1
-rw-r--r--src/shared/audit.c10
-rw-r--r--src/shared/audit.h4
-rw-r--r--src/shared/barrier.c7
-rw-r--r--src/shared/barrier.h7
-rw-r--r--src/shared/base-filesystem.c23
-rw-r--r--src/shared/base-filesystem.h4
-rw-r--r--src/shared/boot-timestamps.c3
-rw-r--r--src/shared/btrfs-ctree.h6
-rw-r--r--src/shared/btrfs-util.c703
-rw-r--r--src/shared/btrfs-util.h31
-rw-r--r--src/shared/bus-label.c24
-rw-r--r--src/shared/bus-label.h9
-rw-r--r--src/shared/cap-list.c1
-rw-r--r--src/shared/capability.c7
-rw-r--r--src/shared/capability.h3
-rw-r--r--src/shared/cgroup-show.c3
-rw-r--r--src/shared/cgroup-show.h1
-rw-r--r--src/shared/cgroup-util.c309
-rw-r--r--src/shared/cgroup-util.h2
-rw-r--r--src/shared/clean-ipc.c2
-rw-r--r--src/shared/clock-util.c13
-rw-r--r--src/shared/clock-util.h1
-rw-r--r--src/shared/condition.c11
-rw-r--r--src/shared/conf-files.c9
-rw-r--r--src/shared/conf-files.h1
-rw-r--r--src/shared/conf-parser.c85
-rw-r--r--src/shared/conf-parser.h17
-rw-r--r--src/shared/copy.c24
-rw-r--r--src/shared/dev-setup.c34
-rw-r--r--src/shared/dev-setup.h4
-rw-r--r--src/shared/device-nodes.c24
-rw-r--r--src/shared/dropin.c15
-rw-r--r--src/shared/efivars.c406
-rw-r--r--src/shared/efivars.h94
-rw-r--r--src/shared/env-util.c145
-rw-r--r--src/shared/env-util.h4
-rw-r--r--src/shared/errno-list.c1
-rw-r--r--src/shared/exit-status.c17
-rw-r--r--src/shared/exit-status.h1
-rw-r--r--src/shared/fdset.c3
-rw-r--r--src/shared/fdset.h1
-rw-r--r--src/shared/fileio-label.c4
-rw-r--r--src/shared/formats-util.h63
-rw-r--r--src/shared/fstab-util.c30
-rw-r--r--src/shared/fstab-util.h2
-rw-r--r--src/shared/fw-util.c8
-rw-r--r--src/shared/fw-util.h4
-rw-r--r--src/shared/generator.c97
-rw-r--r--src/shared/generator.h17
-rw-r--r--src/shared/hashmap.c68
-rw-r--r--src/shared/hashmap.h26
-rw-r--r--src/shared/hostname-util.c193
-rw-r--r--src/shared/hostname-util.h39
-rw-r--r--src/shared/import-util.c21
-rw-r--r--src/shared/import-util.h2
-rw-r--r--src/shared/install-printf.c28
-rw-r--r--src/shared/install-printf.h3
-rw-r--r--src/shared/install.c279
-rw-r--r--src/shared/install.h48
-rw-r--r--src/shared/json.c442
-rw-r--r--src/shared/json.h40
-rw-r--r--src/shared/lockfile-util.c154
-rw-r--r--src/shared/lockfile-util.h39
-rw-r--r--src/shared/log.c79
-rw-r--r--src/shared/log.h37
-rw-r--r--src/shared/login-shared.c6
-rw-r--r--src/shared/logs-show.c68
-rw-r--r--src/shared/logs-show.h1
-rw-r--r--src/shared/machine-image.c57
-rw-r--r--src/shared/machine-image.h5
-rw-r--r--src/shared/machine-pool.c379
-rw-r--r--src/shared/machine-pool.h30
-rw-r--r--src/shared/macro.h9
-rw-r--r--src/shared/memfd-util.c2
-rw-r--r--src/shared/memfd-util.h5
-rw-r--r--src/shared/missing.h240
-rw-r--r--src/shared/mkdir-label.c6
-rw-r--r--src/shared/mkdir.c11
-rw-r--r--src/shared/mkdir.h1
-rw-r--r--src/shared/ordered-set.h59
-rw-r--r--src/shared/pager.c3
-rw-r--r--src/shared/path-lookup.c16
-rw-r--r--src/shared/path-lookup.h22
-rw-r--r--src/shared/path-util.c274
-rw-r--r--src/shared/path-util.h32
-rw-r--r--src/shared/prioq.c6
-rw-r--r--src/shared/prioq.h2
-rw-r--r--src/shared/process-util.c539
-rw-r--r--src/shared/process-util.h65
-rw-r--r--src/shared/pty.c9
-rw-r--r--src/shared/pty.h5
-rw-r--r--src/shared/ptyfwd.c78
-rw-r--r--src/shared/ptyfwd.h5
-rw-r--r--src/shared/random-util.c129
-rw-r--r--src/shared/random-util.h38
-rw-r--r--src/shared/ratelimit.c2
-rw-r--r--src/shared/ring.c1
-rw-r--r--src/shared/ring.h5
-rw-r--r--src/shared/rm-rf.c224
-rw-r--r--src/shared/rm-rf.h34
-rw-r--r--src/shared/selinux-util.c4
-rw-r--r--src/shared/selinux-util.h1
-rw-r--r--src/shared/set.h2
-rw-r--r--src/shared/signal-util.c228
-rw-r--r--src/shared/signal-util.h41
-rw-r--r--src/shared/smack-util.c3
-rw-r--r--src/shared/socket-label.c11
-rw-r--r--src/shared/socket-util.c159
-rw-r--r--src/shared/socket-util.h6
-rw-r--r--src/shared/spawn-ask-password-agent.c5
-rw-r--r--src/shared/spawn-polkit-agent.c5
-rw-r--r--src/shared/special.h9
-rw-r--r--src/shared/specifier.c1
-rw-r--r--src/shared/strbuf.h2
-rw-r--r--src/shared/strv.c8
-rw-r--r--src/shared/strv.h4
-rw-r--r--src/shared/strxcpyx.h2
-rw-r--r--src/shared/switch-root.c7
-rw-r--r--src/shared/sysctl-util.c80
-rw-r--r--src/shared/sysctl-util.h (renamed from src/shared/time-dst.h)9
-rw-r--r--src/shared/terminal-util.c1072
-rw-r--r--src/shared/terminal-util.h109
-rw-r--r--src/shared/time-dst.c335
-rw-r--r--src/shared/time-util.c17
-rw-r--r--src/shared/time-util.h2
-rw-r--r--src/shared/udev-util.h4
-rw-r--r--src/shared/unit-name.c678
-rw-r--r--src/shared/unit-name.h86
-rw-r--r--src/shared/utf8.c13
-rw-r--r--src/shared/utf8.h2
-rw-r--r--src/shared/util.c3234
-rw-r--r--src/shared/util.h315
-rw-r--r--src/shared/utmp-wtmp.c22
-rw-r--r--src/shared/utmp-wtmp.h15
-rw-r--r--src/shared/virt.c25
144 files changed, 8189 insertions, 4699 deletions
diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c
index a4ff1ab878..466f9aa601 100644
--- a/src/shared/acl-util.c
+++ b/src/shared/acl-util.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <errno.h>
#include <stdbool.h>
@@ -82,17 +81,18 @@ int calc_acl_mask_if_needed(acl_t *acl_p) {
if (tag == ACL_MASK)
return 0;
- if (IN_SET(tag, ACL_USER, ACL_GROUP))
- goto calc;
+
+ if (IN_SET(tag, ACL_USER, ACL_GROUP)) {
+ if (acl_calc_mask(acl_p) < 0)
+ return -errno;
+
+ return 1;
+ }
}
if (r < 0)
return -errno;
- return 0;
-calc:
- if (acl_calc_mask(acl_p) < 0)
- return -errno;
- return 1;
+ return 0;
}
int add_base_acls_if_needed(acl_t *acl_p, const char *path) {
@@ -159,59 +159,68 @@ int add_base_acls_if_needed(acl_t *acl_p, const char *path) {
return 0;
}
-int search_acl_groups(char*** dst, const char* path, bool* belong) {
- acl_t acl;
+int acl_search_groups(const char *path, char ***ret_groups) {
+ _cleanup_strv_free_ char **g = NULL;
+ _cleanup_(acl_free) acl_t acl = NULL;
+ bool ret = false;
+ acl_entry_t entry;
+ int r;
assert(path);
- assert(belong);
acl = acl_get_file(path, ACL_TYPE_DEFAULT);
- if (acl) {
- acl_entry_t entry;
- int r;
-
- r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
- while (r > 0) {
- acl_tag_t tag;
- gid_t *gid;
- char *name;
+ if (!acl)
+ return -errno;
- r = acl_get_tag_type(entry, &tag);
- if (r < 0)
- break;
+ r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
+ for (;;) {
+ _cleanup_(acl_free_gid_tpp) gid_t *gid = NULL;
+ acl_tag_t tag;
+
+ if (r < 0)
+ return -errno;
+ if (r == 0)
+ break;
- if (tag != ACL_GROUP)
- goto next;
+ if (acl_get_tag_type(entry, &tag) < 0)
+ return -errno;
- gid = acl_get_qualifier(entry);
- if (!gid)
- break;
+ if (tag != ACL_GROUP)
+ goto next;
- if (in_gid(*gid) > 0) {
- *belong = true;
- break;
- }
+ gid = acl_get_qualifier(entry);
+ if (!gid)
+ return -errno;
+
+ if (in_gid(*gid) > 0) {
+ if (!ret_groups)
+ return true;
+
+ ret = true;
+ }
+
+ if (ret_groups) {
+ char *name;
name = gid_to_name(*gid);
- if (!name) {
- acl_free(acl);
- return log_oom();
- }
-
- r = strv_consume(dst, name);
- if (r < 0) {
- acl_free(acl);
- return log_oom();
- }
-
- next:
- r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
+ if (!name)
+ return -ENOMEM;
+
+ r = strv_consume(&g, name);
+ if (r < 0)
+ return r;
}
- acl_free(acl);
+ next:
+ r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
}
- return 0;
+ if (ret_groups) {
+ *ret_groups = g;
+ g = NULL;
+ }
+
+ return ret;
}
int parse_acl(char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) {
@@ -282,6 +291,77 @@ int parse_acl(char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask)
return 0;
}
+static int acl_entry_equal(acl_entry_t a, acl_entry_t b) {
+ acl_tag_t tag_a, tag_b;
+
+ if (acl_get_tag_type(a, &tag_a) < 0)
+ return -errno;
+
+ if (acl_get_tag_type(b, &tag_b) < 0)
+ return -errno;
+
+ if (tag_a != tag_b)
+ return false;
+
+ switch (tag_a) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ /* can have only one of those */
+ return true;
+ case ACL_USER: {
+ _cleanup_(acl_free_uid_tpp) uid_t *uid_a = NULL, *uid_b = NULL;
+
+ uid_a = acl_get_qualifier(a);
+ if (!uid_a)
+ return -errno;
+
+ uid_b = acl_get_qualifier(b);
+ if (!uid_b)
+ return -errno;
+
+ return *uid_a == *uid_b;
+ }
+ case ACL_GROUP: {
+ _cleanup_(acl_free_gid_tpp) gid_t *gid_a = NULL, *gid_b = NULL;
+
+ gid_a = acl_get_qualifier(a);
+ if (!gid_a)
+ return -errno;
+
+ gid_b = acl_get_qualifier(b);
+ if (!gid_b)
+ return -errno;
+
+ return *gid_a == *gid_b;
+ }
+ default:
+ assert_not_reached("Unknown acl tag type");
+ }
+}
+
+static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *out) {
+ acl_entry_t i;
+ int r;
+
+ for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
+ r > 0;
+ r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
+
+ r = acl_entry_equal(i, entry);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ *out = i;
+ return 1;
+ }
+ }
+ if (r < 0)
+ return -errno;
+ return 0;
+}
+
int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
_cleanup_(acl_freep) acl_t old;
acl_entry_t i;
@@ -297,8 +377,12 @@ int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
acl_entry_t j;
- if (acl_create_entry(&old, &j) < 0)
- return -errno;
+ r = find_acl_entry(old, i, &j);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ if (acl_create_entry(&old, &j) < 0)
+ return -errno;
if (acl_copy_entry(j, i) < 0)
return -errno;
diff --git a/src/shared/acl-util.h b/src/shared/acl-util.h
index 90e88ffa26..c8bcc266d0 100644
--- a/src/shared/acl-util.h
+++ b/src/shared/acl-util.h
@@ -32,7 +32,7 @@
int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry);
int calc_acl_mask_if_needed(acl_t *acl_p);
int add_base_acls_if_needed(acl_t *acl_p, const char *path);
-int search_acl_groups(char*** dst, const char* path, bool* belong);
+int acl_search_groups(const char* path, char ***ret_groups);
int parse_acl(char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask);
int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl);
@@ -41,5 +41,9 @@ int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl);
DEFINE_TRIVIAL_CLEANUP_FUNC(acl_t, acl_free);
#define acl_free_charp acl_free
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, acl_free_charp);
+#define acl_free_uid_tp acl_free
+DEFINE_TRIVIAL_CLEANUP_FUNC(uid_t*, acl_free_uid_tp);
+#define acl_free_gid_tp acl_free
+DEFINE_TRIVIAL_CLEANUP_FUNC(gid_t*, acl_free_gid_tp);
#endif
diff --git a/src/shared/acpi-fpdt.c b/src/shared/acpi-fpdt.c
index 390c3236e0..64e50401b9 100644
--- a/src/shared/acpi-fpdt.c
+++ b/src/shared/acpi-fpdt.c
@@ -19,13 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
-#include <sys/types.h>
#include <util.h>
#include <fileio.h>
diff --git a/src/shared/apparmor-util.c b/src/shared/apparmor-util.c
index c14843da49..c2bbd330bd 100644
--- a/src/shared/apparmor-util.c
+++ b/src/shared/apparmor-util.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include "util.h"
#include "fileio.h"
diff --git a/src/shared/architecture.c b/src/shared/architecture.c
index 34c5a53fa9..8e72e7a36a 100644
--- a/src/shared/architecture.c
+++ b/src/shared/architecture.c
@@ -35,7 +35,7 @@ int uname_architecture(void) {
* 1:1. Instead we try to clean it up and break down the
* confusion on x86 and arm in particular.
*
- * We do not try to distuingish CPUs not CPU features, but
+ * We do not try to distinguish CPUs not CPU features, but
* actual architectures, i.e. that have genuinely different
* code. */
@@ -108,8 +108,12 @@ int uname_architecture(void) {
{ "armv8l", ARCHITECTURE_ARM },
{ "armv8b", ARCHITECTURE_ARM_BE },
#elif defined(__sh__) || defined(__sh64__)
- { "sh64", ARCHITECTURE_SH64 },
- { "sh", ARCHITECTURE_SH },
+ { "sh5", ARCHITECTURE_SH64 },
+ { "sh2", ARCHITECTURE_SH },
+ { "sh2a", ARCHITECTURE_SH },
+ { "sh3", ARCHITECTURE_SH },
+ { "sh4", ARCHITECTURE_SH },
+ { "sh4a", ARCHITECTURE_SH },
#elif defined(__m68k__)
{ "m68k", ARCHITECTURE_M68K },
#elif defined(__tilegx__)
diff --git a/src/shared/architecture.h b/src/shared/architecture.h
index cb82418a5e..f5bbf65a90 100644
--- a/src/shared/architecture.h
+++ b/src/shared/architecture.h
@@ -27,7 +27,7 @@
/* A cleaned up architecture definition. We don't want to get lost in
* processor features, models, generations or even ABIs. Hence we
- * focus on general family, and distuignish word width and
+ * focus on general family, and distinguish word width and
* endianness. */
enum {
diff --git a/src/shared/arphrd-list.c b/src/shared/arphrd-list.c
index 6e113eff7a..284043cd90 100644
--- a/src/shared/arphrd-list.c
+++ b/src/shared/arphrd-list.c
@@ -20,7 +20,6 @@
***/
#include <net/if_arp.h>
-#include <sys/socket.h>
#include <string.h>
#include "util.h"
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index 0a61dafc59..ef3788be68 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -32,9 +32,12 @@
#include <sys/signalfd.h>
#include "util.h"
+#include "formats-util.h"
#include "mkdir.h"
#include "strv.h"
-
+#include "random-util.h"
+#include "terminal-util.h"
+#include "signal-util.h"
#include "ask-password-api.h"
static void backspace_chars(int ttyfd, size_t p) {
@@ -475,6 +478,8 @@ int ask_password_agent(
goto finish;
}
+ cmsg_close_all(&msghdr);
+
if (n <= 0) {
log_error("Message too short");
continue;
diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h
index 704ee6e1b4..0954e072be 100644
--- a/src/shared/ask-password-api.h
+++ b/src/shared/ask-password-api.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
int ask_password_tty(const char *message, usec_t until, bool echo, const char *flag_file, char **_passphrase);
diff --git a/src/shared/audit.c b/src/shared/audit.c
index 4701c0a8de..54148fcf18 100644
--- a/src/shared/audit.c
+++ b/src/shared/audit.c
@@ -19,20 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
-#include <string.h>
-#include <unistd.h>
#include <errno.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <ctype.h>
#include "macro.h"
#include "audit.h"
#include "util.h"
-#include "log.h"
+#include "process-util.h"
#include "fileio.h"
-#include "virt.h"
int audit_session_from_pid(pid_t pid, uint32_t *id) {
_cleanup_free_ char *s = NULL;
@@ -52,7 +46,7 @@ int audit_session_from_pid(pid_t pid, uint32_t *id) {
if (r < 0)
return r;
- if (u == (uint32_t) -1 || u <= 0)
+ if (u == AUDIT_SESSION_INVALID || u <= 0)
return -ENXIO;
*id = u;
diff --git a/src/shared/audit.h b/src/shared/audit.h
index b4aecffb30..6de331c73e 100644
--- a/src/shared/audit.h
+++ b/src/shared/audit.h
@@ -21,9 +21,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdint.h>
+#include <stdbool.h>
#include <sys/types.h>
-#include "capability.h"
+#define AUDIT_SESSION_INVALID ((uint32_t) -1)
int audit_session_from_pid(pid_t pid, uint32_t *id);
int audit_loginuid_from_pid(pid_t pid, uid_t *uid);
diff --git a/src/shared/barrier.c b/src/shared/barrier.c
index f65363a67b..436ba95989 100644
--- a/src/shared/barrier.c
+++ b/src/shared/barrier.c
@@ -21,13 +21,10 @@
#include <errno.h>
#include <fcntl.h>
-#include <limits.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/eventfd.h>
#include <sys/types.h>
#include <unistd.h>
@@ -141,7 +138,7 @@ int barrier_create(Barrier *b) {
* barrier_create(). The object is released and reset to invalid
* state. Therefore, it is safe to call barrier_destroy() multiple
* times or even if barrier_create() failed. However, barrier must be
- * always initalized with BARRIER_NULL.
+ * always initialized with BARRIER_NULL.
*
* If @b is NULL, this is a no-op.
*/
@@ -178,7 +175,7 @@ void barrier_set_role(Barrier *b, unsigned int role) {
assert(b);
assert(role == BARRIER_PARENT || role == BARRIER_CHILD);
/* make sure this is only called once */
- assert(b->pipe[1] >= 0 && b->pipe[1] >= 0);
+ assert(b->pipe[0] >= 0 && b->pipe[1] >= 0);
if (role == BARRIER_PARENT)
b->pipe[1] = safe_close(b->pipe[1]);
diff --git a/src/shared/barrier.h b/src/shared/barrier.h
index d4ad2a419b..b8954694d3 100644
--- a/src/shared/barrier.h
+++ b/src/shared/barrier.h
@@ -21,14 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
#include <sys/types.h>
#include "macro.h"
-#include "util.h"
/* See source file for an API description. */
@@ -91,6 +86,6 @@ static inline bool barrier_is_aborted(Barrier *b) {
}
static inline bool barrier_place_and_sync(Barrier *b) {
- (void)barrier_place(b);
+ (void) barrier_place(b);
return barrier_sync(b);
}
diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c
index 73907c6354..ab6fc171b0 100644
--- a/src/shared/base-filesystem.c
+++ b/src/shared/base-filesystem.c
@@ -22,17 +22,12 @@
#include <errno.h>
#include <sys/stat.h>
#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
#include <unistd.h>
#include "base-filesystem.h"
#include "log.h"
#include "macro.h"
-#include "strv.h"
#include "util.h"
-#include "label.h"
-#include "mkdir.h"
typedef struct BaseFilesystem {
const char *dir;
@@ -46,16 +41,19 @@ static const BaseFilesystem table[] = {
{ "lib", 0, "usr/lib\0", NULL },
{ "root", 0755, NULL, NULL },
{ "sbin", 0, "usr/sbin\0", NULL },
+ { "usr", 0755, NULL, NULL },
+ { "var", 0755, NULL, NULL },
+ { "etc", 0755, NULL, NULL },
#if defined(__i386__) || defined(__x86_64__)
{ "lib64", 0, "usr/lib/x86_64-linux-gnu\0"
"usr/lib64\0", "ld-linux-x86-64.so.2" },
#endif
};
-int base_filesystem_create(const char *root) {
+int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
_cleanup_close_ int fd = -1;
unsigned i;
- int r;
+ int r = 0;
fd = open(root, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
if (fd < 0)
@@ -95,6 +93,12 @@ int base_filesystem_create(const char *root) {
r = symlinkat(target, fd, table[i].dir);
if (r < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create symlink at %s/%s: %m", root, table[i].dir);
+
+ if (uid != UID_INVALID || gid != UID_INVALID) {
+ if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
+ return log_error_errno(errno, "Failed to chown symlink at %s/%s: %m", root, table[i].dir);
+ }
+
continue;
}
@@ -102,6 +106,11 @@ int base_filesystem_create(const char *root) {
r = mkdirat(fd, table[i].dir, table[i].mode);
if (r < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create directory at %s/%s: %m", root, table[i].dir);
+
+ if (uid != UID_INVALID || gid != UID_INVALID) {
+ if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
+ return log_error_errno(errno, "Failed to chown directory at %s/%s: %m", root, table[i].dir);
+ }
}
return 0;
diff --git a/src/shared/base-filesystem.h b/src/shared/base-filesystem.h
index 03201f7083..39a496090f 100644
--- a/src/shared/base-filesystem.h
+++ b/src/shared/base-filesystem.h
@@ -21,4 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-int base_filesystem_create(const char *root);
+#include <sys/types.h>
+
+int base_filesystem_create(const char *root, uid_t uid, gid_t gid);
diff --git a/src/shared/boot-timestamps.c b/src/shared/boot-timestamps.c
index 54e0537a21..ecbe1aaa0f 100644
--- a/src/shared/boot-timestamps.c
+++ b/src/shared/boot-timestamps.c
@@ -19,7 +19,6 @@
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include "boot-timestamps.h"
#include "acpi-fpdt.h"
@@ -40,10 +39,8 @@ int boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_time
r = acpi_get_boot_usec(&x, &y);
if (r < 0) {
-#ifdef ENABLE_EFI
r = efi_loader_get_boot_usec(&x, &y);
if (r < 0)
-#endif
return r;
}
diff --git a/src/shared/btrfs-ctree.h b/src/shared/btrfs-ctree.h
index 8b6f1ab4f4..d3ae57331c 100644
--- a/src/shared/btrfs-ctree.h
+++ b/src/shared/btrfs-ctree.h
@@ -90,3 +90,9 @@ struct btrfs_qgroup_limit_item {
le64_t rsv_rfer;
le64_t rsv_excl;
} _packed_;
+
+struct btrfs_root_ref {
+ le64_t dirid;
+ le64_t sequence;
+ le16_t name_len;
+} _packed_;
diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c
index b34ac8b15a..49528dbf01 100644
--- a/src/shared/btrfs-util.c
+++ b/src/shared/btrfs-util.c
@@ -31,13 +31,21 @@
#include "util.h"
#include "path-util.h"
#include "macro.h"
-#include "strv.h"
#include "copy.h"
#include "selinux-util.h"
#include "smack-util.h"
+#include "fileio.h"
#include "btrfs-ctree.h"
#include "btrfs-util.h"
+/* WARNING: Be careful with file system ioctls! When we get an fd, we
+ * need to make sure it either refers to only a regular file or
+ * directory, or that it is located on btrfs, before invoking any
+ * btrfs ioctls. The ioctl numbers are reused by some device drivers
+ * (such as DRM), and hence might have bad effects when invoked on
+ * device nodes (that reference drivers) rather than fds to normal
+ * files or directories. */
+
static int validate_subvolume_name(const char *name) {
if (!filename_is_valid(name))
@@ -83,17 +91,10 @@ static int extract_subvolume_name(const char *path, const char **subvolume) {
return 0;
}
-int btrfs_is_snapshot(int fd) {
- struct stat st;
+int btrfs_is_filesystem(int fd) {
struct statfs sfs;
- /* On btrfs subvolumes always have the inode 256 */
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
- return 0;
+ assert(fd >= 0);
if (fstatfs(fd, &sfs) < 0)
return -errno;
@@ -101,65 +102,20 @@ int btrfs_is_snapshot(int fd) {
return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
}
-int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy) {
- struct btrfs_ioctl_vol_args_v2 args = {
- .flags = read_only ? BTRFS_SUBVOL_RDONLY : 0,
- };
- _cleanup_close_ int old_fd = -1, new_fd = -1;
- const char *subvolume;
- int r;
-
- assert(old_path);
-
- old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (old_fd < 0)
- return -errno;
-
- r = btrfs_is_snapshot(old_fd);
- if (r < 0)
- return r;
- if (r == 0) {
-
- if (fallback_copy) {
- r = btrfs_subvol_make(new_path);
- if (r < 0)
- return r;
-
- r = copy_directory_fd(old_fd, new_path, true);
- if (r < 0) {
- btrfs_subvol_remove(new_path);
- return r;
- }
-
- if (read_only) {
- r = btrfs_subvol_set_read_only(new_path, true);
- if (r < 0) {
- btrfs_subvol_remove(new_path);
- return r;
- }
- }
-
- return 0;
- }
-
- return -EISDIR;
- }
-
- r = extract_subvolume_name(new_path, &subvolume);
- if (r < 0)
- return r;
+int btrfs_is_subvol(int fd) {
+ struct stat st;
- new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (new_fd < 0)
- return new_fd;
+ assert(fd >= 0);
- strncpy(args.name, subvolume, sizeof(args.name)-1);
- args.fd = old_fd;
+ /* On btrfs subvolumes always have the inode 256 */
- if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0)
+ if (fstat(fd, &st) < 0)
return -errno;
- return 0;
+ if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
+ return 0;
+
+ return btrfs_is_filesystem(fd);
}
int btrfs_subvol_make(const char *path) {
@@ -204,30 +160,6 @@ int btrfs_subvol_make_label(const char *path) {
return mac_smack_fix(path, false, false);
}
-int btrfs_subvol_remove(const char *path) {
- struct btrfs_ioctl_vol_args args = {};
- _cleanup_close_ int fd = -1;
- const char *subvolume;
- int r;
-
- assert(path);
-
- r = extract_subvolume_name(path, &subvolume);
- if (r < 0)
- return r;
-
- fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return fd;
-
- strncpy(args.name, subvolume, sizeof(args.name)-1);
-
- if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) < 0)
- return -errno;
-
- return 0;
-}
-
int btrfs_subvol_set_read_only_fd(int fd, bool b) {
uint64_t flags, nflags;
struct stat st;
@@ -269,6 +201,15 @@ int btrfs_subvol_set_read_only(const char *path, bool b) {
int btrfs_subvol_get_read_only_fd(int fd) {
uint64_t flags;
+ struct stat st;
+
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
+ return -EINVAL;
if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
return -errno;
@@ -277,11 +218,21 @@ int btrfs_subvol_get_read_only_fd(int fd) {
}
int btrfs_reflink(int infd, int outfd) {
+ struct stat st;
int r;
assert(infd >= 0);
assert(outfd >= 0);
+ /* Make sure we invoke the ioctl on a regular file, so that no
+ * device driver accidentally gets it. */
+
+ if (fstat(outfd, &st) < 0)
+ return -errno;
+
+ if (!S_ISREG(st.st_mode))
+ return -EINVAL;
+
r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
if (r < 0)
return -errno;
@@ -296,12 +247,19 @@ int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offs
.src_length = sz,
.dest_offset = out_offset,
};
+ struct stat st;
int r;
assert(infd >= 0);
assert(outfd >= 0);
assert(sz > 0);
+ if (fstat(outfd, &st) < 0)
+ return -errno;
+
+ if (!S_ISREG(st.st_mode))
+ return -EINVAL;
+
r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
if (r < 0)
return -errno;
@@ -309,17 +267,19 @@ int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offs
return 0;
}
-int btrfs_get_block_device(const char *path, dev_t *dev) {
+int btrfs_get_block_device_fd(int fd, dev_t *dev) {
struct btrfs_ioctl_fs_info_args fsi = {};
- _cleanup_close_ int fd = -1;
uint64_t id;
+ int r;
- assert(path);
+ assert(fd >= 0);
assert(dev);
- fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return -errno;
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
return -errno;
@@ -357,14 +317,34 @@ int btrfs_get_block_device(const char *path, dev_t *dev) {
return -ENODEV;
}
+int btrfs_get_block_device(const char *path, dev_t *dev) {
+ _cleanup_close_ int fd = -1;
+
+ assert(path);
+ assert(dev);
+
+ fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_get_block_device_fd(fd, dev);
+}
+
int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
struct btrfs_ioctl_ino_lookup_args args = {
.objectid = BTRFS_FIRST_FREE_OBJECTID
};
+ int r;
assert(fd >= 0);
assert(ret);
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+
if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
return -errno;
@@ -584,7 +564,7 @@ int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
if (sh->type == BTRFS_QGROUP_INFO_KEY) {
const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
- ret->referred = le64toh(qii->rfer);
+ ret->referenced = le64toh(qii->rfer);
ret->exclusive = le64toh(qii->excl);
found_info = true;
@@ -592,11 +572,11 @@ int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
} else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
- ret->referred_max = le64toh(qli->max_rfer);
+ ret->referenced_max = le64toh(qli->max_rfer);
ret->exclusive_max = le64toh(qli->max_excl);
- if (ret->referred_max == 0)
- ret->referred_max = (uint64_t) -1;
+ if (ret->referenced_max == 0)
+ ret->referenced_max = (uint64_t) -1;
if (ret->exclusive_max == 0)
ret->exclusive_max = (uint64_t) -1;
@@ -617,12 +597,12 @@ finish:
return -ENODATA;
if (!found_info) {
- ret->referred = (uint64_t) -1;
+ ret->referenced = (uint64_t) -1;
ret->exclusive = (uint64_t) -1;
}
if (!found_limit) {
- ret->referred_max = (uint64_t) -1;
+ ret->referenced_max = (uint64_t) -1;
ret->exclusive_max = (uint64_t) -1;
}
@@ -630,8 +610,16 @@ finish:
}
int btrfs_defrag_fd(int fd) {
+ struct stat st;
+
assert(fd >= 0);
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISREG(st.st_mode))
+ return -EINVAL;
+
if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
return -errno;
@@ -647,3 +635,518 @@ int btrfs_defrag(const char *p) {
return btrfs_defrag_fd(fd);
}
+
+int btrfs_quota_enable_fd(int fd, bool b) {
+ struct btrfs_ioctl_quota_ctl_args args = {
+ .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
+ };
+ int r;
+
+ assert(fd >= 0);
+
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+
+ if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_quota_enable(const char *path, bool b) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_quota_enable_fd(fd, b);
+}
+
+int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
+ struct btrfs_ioctl_qgroup_limit_args args = {
+ .lim.max_rfer =
+ referenced_max == (uint64_t) -1 ? 0 :
+ referenced_max == 0 ? 1 : referenced_max,
+ .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
+ };
+ int r;
+
+ assert(fd >= 0);
+
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ENOTTY;
+
+ if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_quota_limit_fd(fd, referenced_max);
+}
+
+int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
+ struct btrfs_ioctl_vol_args args = {};
+ _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
+ _cleanup_close_ int loop_fd = -1, backing_fd = -1;
+ struct stat st;
+ dev_t dev = 0;
+ int r;
+
+ /* btrfs cannot handle file systems < 16M, hence use this as minimum */
+ if (new_size < 16*1024*1024)
+ new_size = 16*1024*1024;
+
+ r = btrfs_get_block_device_fd(fd, &dev);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENODEV;
+
+ if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
+ return -ENOMEM;
+ r = read_one_line_file(p, &backing);
+ if (r == -ENOENT)
+ return -ENODEV;
+ if (r < 0)
+ return r;
+ if (isempty(backing) || !path_is_absolute(backing))
+ return -ENODEV;
+
+ backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
+ if (backing_fd < 0)
+ return -errno;
+
+ if (fstat(backing_fd, &st) < 0)
+ return -errno;
+ if (!S_ISREG(st.st_mode))
+ return -ENODEV;
+
+ if (new_size == (uint64_t) st.st_size)
+ return 0;
+
+ if (grow_only && new_size < (uint64_t) st.st_size)
+ return -EINVAL;
+
+ if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
+ return -ENOMEM;
+ loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
+ if (loop_fd < 0)
+ return -errno;
+
+ if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
+ return -EINVAL;
+
+ if (new_size < (uint64_t) st.st_size) {
+ /* Decrease size: first decrease btrfs size, then shorten loopback */
+ if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
+ return -errno;
+ }
+
+ if (ftruncate(backing_fd, new_size) < 0)
+ return -errno;
+
+ if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
+ return -errno;
+
+ if (new_size > (uint64_t) st.st_size) {
+ /* Increase size: first enlarge loopback, then increase btrfs size */
+ if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
+ return -errno;
+ }
+
+ /* Make sure the free disk space is correctly updated for both file systems */
+ (void) fsync(fd);
+ (void) fsync(backing_fd);
+
+ return 1;
+}
+
+int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ return btrfs_resize_loopback_fd(fd, new_size, grow_only);
+}
+
+static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) {
+ struct btrfs_ioctl_search_args args = {
+ .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
+
+ .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
+ .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
+
+ .key.min_type = BTRFS_ROOT_BACKREF_KEY,
+ .key.max_type = BTRFS_ROOT_BACKREF_KEY,
+
+ .key.min_transid = 0,
+ .key.max_transid = (uint64_t) -1,
+ };
+
+ struct btrfs_ioctl_vol_args vol_args = {};
+ _cleanup_close_ int subvol_fd = -1;
+ struct stat st;
+ bool made_writable = false;
+ int r;
+
+ assert(fd >= 0);
+ assert(subvolume);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode))
+ return -EINVAL;
+
+ /* First, try to remove the subvolume. If it happens to be
+ * already empty, this will just work. */
+ strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
+ if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0)
+ return 0;
+ if (!recursive || errno != ENOTEMPTY)
+ return -errno;
+
+ /* OK, the subvolume is not empty, let's look for child
+ * subvolumes, and remove them, first */
+ subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (subvol_fd < 0)
+ return -errno;
+
+ if (subvol_id == 0) {
+ r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
+ if (r < 0)
+ return r;
+ }
+
+ args.key.min_offset = args.key.max_offset = subvol_id;
+
+ while (btrfs_ioctl_search_args_compare(&args) <= 0) {
+ const struct btrfs_ioctl_search_header *sh;
+ unsigned i;
+
+ args.key.nr_items = 256;
+ if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
+ return -errno;
+
+ if (args.key.nr_items <= 0)
+ break;
+
+ FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
+ _cleanup_free_ char *p = NULL;
+ const struct btrfs_root_ref *ref;
+ struct btrfs_ioctl_ino_lookup_args ino_args;
+
+ btrfs_ioctl_search_args_set(&args, sh);
+
+ if (sh->type != BTRFS_ROOT_BACKREF_KEY)
+ continue;
+ if (sh->offset != subvol_id)
+ continue;
+
+ ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
+
+ p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
+ if (!p)
+ return -ENOMEM;
+
+ zero(ino_args);
+ ino_args.treeid = subvol_id;
+ ino_args.objectid = htole64(ref->dirid);
+
+ if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
+ return -errno;
+
+ if (!made_writable) {
+ r = btrfs_subvol_set_read_only_fd(subvol_fd, false);
+ if (r < 0)
+ return r;
+
+ made_writable = true;
+ }
+
+ if (isempty(ino_args.name))
+ /* Subvolume is in the top-level
+ * directory of the subvolume. */
+ r = subvol_remove_children(subvol_fd, p, sh->objectid, recursive);
+ else {
+ _cleanup_close_ int child_fd = -1;
+
+ /* Subvolume is somewhere further down,
+ * hence we need to open the
+ * containing directory first */
+
+ child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (child_fd < 0)
+ return -errno;
+
+ r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
+ }
+ if (r < 0)
+ return r;
+ }
+
+ /* Increase search key by one, to read the next item, if we can. */
+ if (!btrfs_ioctl_search_args_inc(&args))
+ break;
+ }
+
+ /* OK, the child subvolumes should all be gone now, let's try
+ * again to remove the subvolume */
+ if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int btrfs_subvol_remove(const char *path, bool recursive) {
+ _cleanup_close_ int fd = -1;
+ const char *subvolume;
+ int r;
+
+ assert(path);
+
+ r = extract_subvolume_name(path, &subvolume);
+ if (r < 0)
+ return r;
+
+ fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return fd;
+
+ return subvol_remove_children(fd, subvolume, 0, recursive);
+}
+
+int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
+ return subvol_remove_children(fd, subvolume, 0, recursive);
+}
+
+static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t subvol_id, BtrfsSnapshotFlags flags) {
+
+ struct btrfs_ioctl_search_args args = {
+ .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
+
+ .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
+ .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
+
+ .key.min_type = BTRFS_ROOT_BACKREF_KEY,
+ .key.max_type = BTRFS_ROOT_BACKREF_KEY,
+
+ .key.min_transid = 0,
+ .key.max_transid = (uint64_t) -1,
+ };
+
+ struct btrfs_ioctl_vol_args_v2 vol_args = {
+ .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
+ .fd = old_fd,
+ };
+ int r;
+ _cleanup_close_ int subvolume_fd = -1;
+
+ assert(old_fd >= 0);
+ assert(new_fd >= 0);
+ assert(subvolume);
+
+ strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
+ vol_args.fd = old_fd;
+
+ if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
+ return -errno;
+
+ if (!(flags & BTRFS_SNAPSHOT_RECURSIVE))
+ return 0;
+
+ if (subvol_id == 0) {
+ r = btrfs_subvol_get_id_fd(old_fd, &subvol_id);
+ if (r < 0)
+ return r;
+ }
+
+ args.key.min_offset = args.key.max_offset = subvol_id;
+
+ while (btrfs_ioctl_search_args_compare(&args) <= 0) {
+ const struct btrfs_ioctl_search_header *sh;
+ unsigned i;
+
+ args.key.nr_items = 256;
+ if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
+ return -errno;
+
+ if (args.key.nr_items <= 0)
+ break;
+
+ FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
+ _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
+ struct btrfs_ioctl_ino_lookup_args ino_args;
+ const struct btrfs_root_ref *ref;
+ _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
+
+ btrfs_ioctl_search_args_set(&args, sh);
+
+ if (sh->type != BTRFS_ROOT_BACKREF_KEY)
+ continue;
+ if (sh->offset != subvol_id)
+ continue;
+
+ ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
+
+ p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
+ if (!p)
+ return -ENOMEM;
+
+ zero(ino_args);
+ ino_args.treeid = subvol_id;
+ ino_args.objectid = htole64(ref->dirid);
+
+ if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
+ return -errno;
+
+ /* The kernel returns an empty name if the
+ * subvolume is in the top-level directory,
+ * and otherwise appends a slash, so that we
+ * can just concatenate easily here, without
+ * adding a slash. */
+ c = strappend(ino_args.name, p);
+ if (!c)
+ return -ENOMEM;
+
+ old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (old_child_fd < 0)
+ return -errno;
+
+ np = strjoin(subvolume, "/", ino_args.name, NULL);
+ if (!np)
+ return -ENOMEM;
+
+ new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (new_child_fd < 0)
+ return -errno;
+
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
+ /* If the snapshot is read-only we
+ * need to mark it writable
+ * temporarily, to put the subsnapshot
+ * into place. */
+
+ if (subvolume_fd < 0) {
+ subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (subvolume_fd < 0)
+ return -errno;
+ }
+
+ r = btrfs_subvol_set_read_only_fd(subvolume_fd, false);
+ if (r < 0)
+ return r;
+ }
+
+ /* When btrfs clones the subvolumes, child
+ * subvolumes appear as directories. Remove
+ * them, so that we can create a new snapshot
+ * in their place */
+ if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
+ int k = -errno;
+
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY)
+ (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true);
+
+ return k;
+ }
+
+ r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
+
+ /* Restore the readonly flag */
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
+ int k;
+
+ k = btrfs_subvol_set_read_only_fd(subvolume_fd, true);
+ if (r >= 0 && k < 0)
+ return k;
+ }
+
+ if (r < 0)
+ return r;
+ }
+
+ /* Increase search key by one, to read the next item, if we can. */
+ if (!btrfs_ioctl_search_args_inc(&args))
+ break;
+ }
+
+ return 0;
+}
+
+int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
+ _cleanup_close_ int new_fd = -1;
+ const char *subvolume;
+ int r;
+
+ assert(old_fd >= 0);
+ assert(new_path);
+
+ r = btrfs_is_subvol(old_fd);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
+ return -EISDIR;
+
+ r = btrfs_subvol_make(new_path);
+ if (r < 0)
+ return r;
+
+ r = copy_directory_fd(old_fd, new_path, true);
+ if (r < 0) {
+ btrfs_subvol_remove(new_path, false);
+ return r;
+ }
+
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
+ r = btrfs_subvol_set_read_only(new_path, true);
+ if (r < 0) {
+ btrfs_subvol_remove(new_path, false);
+ return r;
+ }
+ }
+
+ return 0;
+ }
+
+ r = extract_subvolume_name(new_path, &subvolume);
+ if (r < 0)
+ return r;
+
+ new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (new_fd < 0)
+ return new_fd;
+
+ return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
+}
+
+int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
+ _cleanup_close_ int old_fd = -1;
+
+ assert(old_path);
+ assert(new_path);
+
+ old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (old_fd < 0)
+ return -errno;
+
+ return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
+}
diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h
index 1b9c142e5c..a7eb895c93 100644
--- a/src/shared/btrfs-util.h
+++ b/src/shared/btrfs-util.h
@@ -37,18 +37,26 @@ typedef struct BtrfsSubvolInfo {
} BtrfsSubvolInfo;
typedef struct BtrfsQuotaInfo {
- uint64_t referred;
+ uint64_t referenced;
uint64_t exclusive;
- uint64_t referred_max;
+ uint64_t referenced_max;
uint64_t exclusive_max;
} BtrfsQuotaInfo;
-int btrfs_is_snapshot(int fd);
+typedef enum BtrfsSnapshotFlags {
+ BTRFS_SNAPSHOT_FALLBACK_COPY = 1,
+ BTRFS_SNAPSHOT_READ_ONLY = 2,
+ BTRFS_SNAPSHOT_RECURSIVE = 4,
+} BtrfsSnapshotFlags;
+
+int btrfs_is_filesystem(int fd);
+int btrfs_is_subvol(int fd);
int btrfs_subvol_make(const char *path);
int btrfs_subvol_make_label(const char *path);
-int btrfs_subvol_remove(const char *path);
-int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy);
+
+int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags);
+int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags);
int btrfs_subvol_set_read_only_fd(int fd, bool b);
int btrfs_subvol_set_read_only(const char *path, bool b);
@@ -60,7 +68,20 @@ int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *quota);
int btrfs_reflink(int infd, int outfd);
int btrfs_clone_range(int infd, uint64_t in_offset, int ofd, uint64_t out_offset, uint64_t sz);
+int btrfs_get_block_device_fd(int fd, dev_t *dev);
int btrfs_get_block_device(const char *path, dev_t *dev);
int btrfs_defrag_fd(int fd);
int btrfs_defrag(const char *p);
+
+int btrfs_quota_enable_fd(int fd, bool b);
+int btrfs_quota_enable(const char *path, bool b);
+
+int btrfs_quota_limit_fd(int fd, uint64_t referenced_max);
+int btrfs_quota_limit(const char *path, uint64_t referenced_max);
+
+int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
+int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
+
+int btrfs_subvol_remove(const char *path, bool recursive);
+int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive);
diff --git a/src/shared/bus-label.c b/src/shared/bus-label.c
index 61eb75bca2..ccc9f2bf8e 100644
--- a/src/shared/bus-label.c
+++ b/src/shared/bus-label.c
@@ -19,13 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <stdlib.h>
-#include <unistd.h>
#include "util.h"
#include "macro.h"
-#include "def.h"
#include "bus-label.h"
@@ -66,34 +63,35 @@ char *bus_label_escape(const char *s) {
return r;
}
-char *bus_label_unescape(const char *f) {
+char *bus_label_unescape_n(const char *f, size_t l) {
char *r, *t;
+ size_t i;
assert_return(f, NULL);
/* Special case for the empty string */
- if (streq(f, "_"))
+ if (l == 1 && *f == '_')
return strdup("");
- r = new(char, strlen(f) + 1);
+ r = new(char, l + 1);
if (!r)
return NULL;
- for (t = r; *f; f++) {
-
- if (*f == '_') {
+ for (i = 0, t = r; i < l; ++i) {
+ if (f[i] == '_') {
int a, b;
- if ((a = unhexchar(f[1])) < 0 ||
- (b = unhexchar(f[2])) < 0) {
+ if (l - i < 3 ||
+ (a = unhexchar(f[i + 1])) < 0 ||
+ (b = unhexchar(f[i + 2])) < 0) {
/* Invalid escape code, let's take it literal then */
*(t++) = '_';
} else {
*(t++) = (char) ((a << 4) | b);
- f += 2;
+ i += 2;
}
} else
- *(t++) = *f;
+ *(t++) = f[i];
}
*t = 0;
diff --git a/src/shared/bus-label.h b/src/shared/bus-label.h
index c27c8517fd..ed1dc4e0a7 100644
--- a/src/shared/bus-label.h
+++ b/src/shared/bus-label.h
@@ -21,5 +21,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdlib.h>
+#include <string.h>
+
char *bus_label_escape(const char *s);
-char *bus_label_unescape(const char *f);
+char *bus_label_unescape_n(const char *f, size_t l);
+
+static inline char *bus_label_unescape(const char *f) {
+ return bus_label_unescape_n(f, f ? strlen(f) : 0);
+}
diff --git a/src/shared/cap-list.c b/src/shared/cap-list.c
index 8033e8c7b2..bd5bffbfa5 100644
--- a/src/shared/cap-list.c
+++ b/src/shared/cap-list.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <linux/capability.h>
#include <string.h>
#include "util.h"
diff --git a/src/shared/capability.c b/src/shared/capability.c
index 915ceb9d9b..58f00e6dae 100644
--- a/src/shared/capability.c
+++ b/src/shared/capability.c
@@ -19,14 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
-#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <stdarg.h>
-#include <ctype.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include "grp.h"
@@ -55,7 +50,7 @@ unsigned long cap_last_cap(void) {
static thread_local unsigned long saved;
static thread_local bool valid = false;
_cleanup_free_ char *content = NULL;
- unsigned long p;
+ unsigned long p = 0;
int r;
if (valid)
diff --git a/src/shared/capability.h b/src/shared/capability.h
index 6f2f6f997d..4eb5c2a835 100644
--- a/src/shared/capability.h
+++ b/src/shared/capability.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
#include <stdbool.h>
#include <sys/capability.h>
@@ -32,7 +31,7 @@ int have_effective_cap(int value);
int capability_bounding_set_drop(uint64_t drop, bool right_now);
int capability_bounding_set_drop_usermode(uint64_t drop);
-int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilites);
+int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
int drop_capability(cap_value_t cv);
diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c
index cbd94e86d9..1a2c4b28cd 100644
--- a/src/shared/cgroup-show.c
+++ b/src/shared/cgroup-show.c
@@ -25,10 +25,13 @@
#include <errno.h>
#include "util.h"
+#include "formats-util.h"
+#include "process-util.h"
#include "macro.h"
#include "path-util.h"
#include "cgroup-util.h"
#include "cgroup-show.h"
+#include "terminal-util.h"
static int compare(const void *a, const void *b) {
const pid_t *p = a, *q = b;
diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h
index 3146f56cad..aa832454b5 100644
--- a/src/shared/cgroup-show.h
+++ b/src/shared/cgroup-show.h
@@ -23,7 +23,6 @@
#include <stdbool.h>
#include <sys/types.h>
-#include "util.h"
#include "logs-show.h"
int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags);
diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c
index dfd8689b72..9988e5c574 100644
--- a/src/shared/cgroup-util.c
+++ b/src/shared/cgroup-util.c
@@ -30,16 +30,17 @@
#include <ftw.h>
#include "cgroup-util.h"
-#include "log.h"
#include "set.h"
#include "macro.h"
#include "util.h"
+#include "formats-util.h"
+#include "process-util.h"
#include "path-util.h"
-#include "strv.h"
#include "unit-name.h"
#include "fileio.h"
#include "special.h"
#include "mkdir.h"
+#include "login-shared.h"
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
_cleanup_free_ char *fs = NULL;
@@ -488,9 +489,11 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
if (_unlikely_(!good)) {
int r;
- r = path_is_mount_point("/sys/fs/cgroup", false);
- if (r <= 0)
- return r < 0 ? r : -ENOENT;
+ r = path_is_mount_point("/sys/fs/cgroup", 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
/* Cache this to save a few stat()s */
good = true;
@@ -1138,17 +1141,21 @@ int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
}
int cg_path_decode_unit(const char *cgroup, char **unit){
- char *e, *c, *s;
+ char *c, *s;
+ size_t n;
assert(cgroup);
assert(unit);
- e = strchrnul(cgroup, '/');
- c = strndupa(cgroup, e - cgroup);
+ n = strcspn(cgroup, "/");
+ if (n < 3)
+ return -ENXIO;
+
+ c = strndupa(cgroup, n);
c = cg_unescape(c);
- if (!unit_name_is_valid(c, TEMPLATE_INVALID))
- return -EINVAL;
+ if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
+ return -ENXIO;
s = strdup(c);
if (!s)
@@ -1158,7 +1165,31 @@ int cg_path_decode_unit(const char *cgroup, char **unit){
return 0;
}
+static bool valid_slice_name(const char *p, size_t n) {
+
+ if (!p)
+ return false;
+
+ if (n < strlen("x.slice"))
+ return false;
+
+ if (memcmp(p + n - 6, ".slice", 6) == 0) {
+ char buf[n+1], *c;
+
+ memcpy(buf, p, n);
+ buf[n] = 0;
+
+ c = cg_unescape(buf);
+
+ return unit_name_is_valid(c, UNIT_NAME_PLAIN);
+ }
+
+ return false;
+}
+
static const char *skip_slices(const char *p) {
+ assert(p);
+
/* Skips over all slice assignments */
for (;;) {
@@ -1167,22 +1198,35 @@ static const char *skip_slices(const char *p) {
p += strspn(p, "/");
n = strcspn(p, "/");
- if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0)
+ if (!valid_slice_name(p, n))
return p;
p += n;
}
}
-int cg_path_get_unit(const char *path, char **unit) {
+int cg_path_get_unit(const char *path, char **ret) {
const char *e;
+ char *unit;
+ int r;
assert(path);
- assert(unit);
+ assert(ret);
e = skip_slices(path);
- return cg_path_decode_unit(e, unit);
+ r = cg_path_decode_unit(e, &unit);
+ if (r < 0)
+ return r;
+
+ /* We skipped over the slices, don't accept any now */
+ if (endswith(unit, ".slice")) {
+ free(unit);
+ return -ENXIO;
+ }
+
+ *ret = unit;
+ return 0;
}
int cg_pid_get_unit(pid_t pid, char **unit) {
@@ -1204,18 +1248,35 @@ int cg_pid_get_unit(pid_t pid, char **unit) {
static const char *skip_session(const char *p) {
size_t n;
- assert(p);
+ if (isempty(p))
+ return NULL;
p += strspn(p, "/");
n = strcspn(p, "/");
- if (n < strlen("session-x.scope") || memcmp(p, "session-", 8) != 0 || memcmp(p + n - 6, ".scope", 6) != 0)
+ if (n < strlen("session-x.scope"))
return NULL;
- p += n;
- p += strspn(p, "/");
+ if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
+ char buf[n - 8 - 6 + 1];
+
+ memcpy(buf, p + 8, n - 8 - 6);
+ buf[n - 8 - 6] = 0;
+
+ /* Note that session scopes never need unescaping,
+ * since they cannot conflict with the kernel's own
+ * names, hence we don't need to call cg_unescape()
+ * here. */
+
+ if (!session_id_valid(buf))
+ return false;
- return p;
+ p += n;
+ p += strspn(p, "/");
+ return p;
+ }
+
+ return NULL;
}
/**
@@ -1224,44 +1285,69 @@ static const char *skip_session(const char *p) {
static const char *skip_user_manager(const char *p) {
size_t n;
- assert(p);
+ if (isempty(p))
+ return NULL;
p += strspn(p, "/");
n = strcspn(p, "/");
- if (n < strlen("user@x.service") || memcmp(p, "user@", 5) != 0 || memcmp(p + n - 8, ".service", 8) != 0)
+ if (n < strlen("user@x.service"))
return NULL;
- p += n;
- p += strspn(p, "/");
+ if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
+ char buf[n - 5 - 8 + 1];
- return p;
+ memcpy(buf, p + 5, n - 5 - 8);
+ buf[n - 5 - 8] = 0;
+
+ /* Note that user manager services never need unescaping,
+ * since they cannot conflict with the kernel's own
+ * names, hence we don't need to call cg_unescape()
+ * here. */
+
+ if (parse_uid(buf, NULL) < 0)
+ return NULL;
+
+ p += n;
+ p += strspn(p, "/");
+
+ return p;
+ }
+
+ return NULL;
}
-int cg_path_get_user_unit(const char *path, char **unit) {
+static const char *skip_user_prefix(const char *path) {
const char *e, *t;
assert(path);
- assert(unit);
-
- /* We always have to parse the path from the beginning as unit
- * cgroups might have arbitrary child cgroups and we shouldn't get
- * confused by those */
/* Skip slices, if there are any */
e = skip_slices(path);
- /* Skip the session scope or user manager... */
- t = skip_session(e);
- if (!t)
- t = skip_user_manager(e);
- if (!t)
- return -ENOENT;
+ /* Skip the user manager, if it's in the path now... */
+ t = skip_user_manager(e);
+ if (t)
+ return t;
+
+ /* Alternatively skip the user session if it is in the path... */
+ return skip_session(e);
+}
- /* ... and skip more slices if there are any */
- e = skip_slices(t);
+int cg_path_get_user_unit(const char *path, char **ret) {
+ const char *t;
- return cg_path_decode_unit(e, unit);
+ assert(path);
+ assert(ret);
+
+ t = skip_user_prefix(path);
+ if (!t)
+ return -ENXIO;
+
+ /* And from here on it looks pretty much the same as for a
+ * system unit, hence let's use the same parser from here
+ * on. */
+ return cg_path_get_unit(t, ret);
}
int cg_pid_get_user_unit(pid_t pid, char **unit) {
@@ -1306,36 +1392,35 @@ int cg_pid_get_machine_name(pid_t pid, char **machine) {
}
int cg_path_get_session(const char *path, char **session) {
- const char *e, *n, *x, *y;
- char *s;
+ _cleanup_free_ char *unit = NULL;
+ char *start, *end;
+ int r;
assert(path);
- /* Skip slices, if there are any */
- e = skip_slices(path);
-
- n = strchrnul(e, '/');
- if (e == n)
- return -ENOENT;
+ r = cg_path_get_unit(path, &unit);
+ if (r < 0)
+ return r;
- s = strndupa(e, n - e);
- s = cg_unescape(s);
+ start = startswith(unit, "session-");
+ if (!start)
+ return -ENXIO;
+ end = endswith(start, ".scope");
+ if (!end)
+ return -ENXIO;
- x = startswith(s, "session-");
- if (!x)
- return -ENOENT;
- y = endswith(x, ".scope");
- if (!y || x == y)
- return -ENOENT;
+ *end = 0;
+ if (!session_id_valid(start))
+ return -ENXIO;
if (session) {
- char *r;
+ char *rr;
- r = strndup(x, y - x);
- if (!r)
+ rr = strdup(start);
+ if (!rr)
return -ENOMEM;
- *session = r;
+ *session = rr;
}
return 0;
@@ -1354,9 +1439,7 @@ int cg_pid_get_session(pid_t pid, char **session) {
int cg_path_get_owner_uid(const char *path, uid_t *uid) {
_cleanup_free_ char *slice = NULL;
- const char *start, *end;
- char *s;
- uid_t u;
+ char *start, *end;
int r;
assert(path);
@@ -1367,20 +1450,14 @@ int cg_path_get_owner_uid(const char *path, uid_t *uid) {
start = startswith(slice, "user-");
if (!start)
- return -ENOENT;
- end = endswith(slice, ".slice");
+ return -ENXIO;
+ end = endswith(start, ".slice");
if (!end)
- return -ENOENT;
+ return -ENXIO;
- s = strndupa(start, end - start);
- if (!s)
- return -ENOENT;
-
- if (parse_uid(s, &u) < 0)
- return -EIO;
-
- if (uid)
- *uid = u;
+ *end = 0;
+ if (parse_uid(start, uid) < 0)
+ return -ENXIO;
return 0;
}
@@ -1398,34 +1475,36 @@ int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
int cg_path_get_slice(const char *p, char **slice) {
const char *e = NULL;
- size_t m = 0;
assert(p);
assert(slice);
+ /* Finds the right-most slice unit from the beginning, but
+ * stops before we come to the first non-slice unit. */
+
for (;;) {
size_t n;
p += strspn(p, "/");
n = strcspn(p, "/");
- if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) {
- char *s;
+ if (!valid_slice_name(p, n)) {
- if (!e)
- return -ENOENT;
+ if (!e) {
+ char *s;
- s = strndup(e, m);
- if (!s)
- return -ENOMEM;
+ s = strdup("-.slice");
+ if (!s)
+ return -ENOMEM;
- *slice = s;
- return 0;
+ *slice = s;
+ return 0;
+ }
+
+ return cg_path_decode_unit(e, slice);
}
e = p;
- m = n;
-
p += n;
}
}
@@ -1443,6 +1522,33 @@ int cg_pid_get_slice(pid_t pid, char **slice) {
return cg_path_get_slice(cgroup, slice);
}
+int cg_path_get_user_slice(const char *p, char **slice) {
+ const char *t;
+ assert(p);
+ assert(slice);
+
+ t = skip_user_prefix(p);
+ if (!t)
+ return -ENXIO;
+
+ /* And now it looks pretty much the same as for a system
+ * slice, so let's just use the same parser from here on. */
+ return cg_path_get_slice(t, slice);
+}
+
+int cg_pid_get_user_slice(pid_t pid, char **slice) {
+ _cleanup_free_ char *cgroup = NULL;
+ int r;
+
+ assert(slice);
+
+ r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ if (r < 0)
+ return r;
+
+ return cg_path_get_user_slice(cgroup, slice);
+}
+
char *cg_escape(const char *p) {
bool need_prefix = false;
@@ -1532,28 +1638,47 @@ bool cg_controller_is_valid(const char *p, bool allow_named) {
int cg_slice_to_path(const char *unit, char **ret) {
_cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
const char *dash;
+ int r;
assert(unit);
assert(ret);
- if (!unit_name_is_valid(unit, TEMPLATE_INVALID))
+ if (streq(unit, "-.slice")) {
+ char *x;
+
+ x = strdup("");
+ if (!x)
+ return -ENOMEM;
+ *ret = x;
+ return 0;
+ }
+
+ if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
return -EINVAL;
if (!endswith(unit, ".slice"))
return -EINVAL;
- p = unit_name_to_prefix(unit);
- if (!p)
- return -ENOMEM;
+ r = unit_name_to_prefix(unit, &p);
+ if (r < 0)
+ return r;
dash = strchr(p, '-');
+
+ /* Don't allow initial dashes */
+ if (dash == p)
+ return -EINVAL;
+
while (dash) {
_cleanup_free_ char *escaped = NULL;
char n[dash - p + sizeof(".slice")];
- strcpy(stpncpy(n, p, dash - p), ".slice");
+ /* Don't allow trailing or double dashes */
+ if (dash[1] == 0 || dash[1] == '-')
+ return -EINVAL;
- if (!unit_name_is_valid(n, TEMPLATE_INVALID))
+ strcpy(stpncpy(n, p, dash - p), ".slice");
+ if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
return -EINVAL;
escaped = cg_escape(n);
diff --git a/src/shared/cgroup-util.h b/src/shared/cgroup-util.h
index 96a3d3bafa..cbf7201370 100644
--- a/src/shared/cgroup-util.h
+++ b/src/shared/cgroup-util.h
@@ -104,6 +104,7 @@ int cg_path_get_unit(const char *path, char **unit);
int cg_path_get_user_unit(const char *path, char **unit);
int cg_path_get_machine_name(const char *path, char **machine);
int cg_path_get_slice(const char *path, char **slice);
+int cg_path_get_user_slice(const char *path, char **slice);
int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted);
int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup);
@@ -114,6 +115,7 @@ int cg_pid_get_unit(pid_t pid, char **unit);
int cg_pid_get_user_unit(pid_t pid, char **unit);
int cg_pid_get_machine_name(pid_t pid, char **machine);
int cg_pid_get_slice(pid_t pid, char **slice);
+int cg_pid_get_user_slice(pid_t pid, char **slice);
int cg_path_decode_unit(const char *cgroup, char **unit);
diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c
index 39ab645133..48b10865da 100644
--- a/src/shared/clean-ipc.c
+++ b/src/shared/clean-ipc.c
@@ -24,12 +24,12 @@
#include <sys/sem.h>
#include <sys/msg.h>
#include <sys/stat.h>
-#include <sys/mman.h>
#include <fcntl.h>
#include <dirent.h>
#include <mqueue.h>
#include "util.h"
+#include "formats-util.h"
#include "strv.h"
#include "clean-ipc.h"
diff --git a/src/shared/clock-util.c b/src/shared/clock-util.c
index 96684681a4..e4e03df1e4 100644
--- a/src/shared/clock-util.c
+++ b/src/shared/clock-util.c
@@ -19,29 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
-#include <string.h>
-#include <unistd.h>
#include <errno.h>
-#include <stdlib.h>
-#include <signal.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <sys/prctl.h>
#include <sys/time.h>
#include <linux/rtc.h>
#include "macro.h"
#include "util.h"
-#include "log.h"
-#include "strv.h"
#include "clock-util.h"
-#include "fileio.h"
int clock_get_hwclock(struct tm *tm) {
_cleanup_close_ int fd = -1;
diff --git a/src/shared/clock-util.h b/src/shared/clock-util.h
index 198a7b2753..8c2d235430 100644
--- a/src/shared/clock-util.h
+++ b/src/shared/clock-util.h
@@ -21,7 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
int clock_is_localtime(void);
int clock_set_timezone(int *min);
diff --git a/src/shared/condition.c b/src/shared/condition.c
index da7560f05f..24871b0dae 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -23,22 +23,21 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
-#include <sys/statvfs.h>
#include <fnmatch.h>
#include "sd-id128.h"
#include "util.h"
#include "virt.h"
#include "path-util.h"
-#include "fileio.h"
#include "architecture.h"
#include "smack-util.h"
#include "apparmor-util.h"
#include "ima-util.h"
#include "selinux-util.h"
#include "audit.h"
-#include "condition.h"
#include "cap-list.h"
+#include "hostname-util.h"
+#include "condition.h"
Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
Condition *c;
@@ -46,7 +45,7 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger
assert(type >= 0);
assert(type < _CONDITION_TYPE_MAX);
- assert(!parameter == (type == CONDITION_NULL));
+ assert((!parameter) == (type == CONDITION_NULL));
c = new0(Condition, 1);
if (!c)
@@ -102,7 +101,7 @@ static int condition_test_kernel_command_line(Condition *c) {
_cleanup_free_ char *word = NULL;
bool found;
- r = unquote_first_word(&p, &word, true);
+ r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
if (r < 0)
return r;
if (r == 0)
@@ -350,7 +349,7 @@ static int condition_test_path_is_mount_point(Condition *c) {
assert(c->parameter);
assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
- return path_is_mount_point(c->parameter, true) > 0;
+ return path_is_mount_point(c->parameter, AT_SYMLINK_FOLLOW) > 0;
}
static int condition_test_path_is_read_write(Condition *c) {
diff --git a/src/shared/conf-files.c b/src/shared/conf-files.c
index db4937db88..da8745b284 100644
--- a/src/shared/conf-files.c
+++ b/src/shared/conf-files.c
@@ -19,13 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
-#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
-#include <sys/stat.h>
#include <dirent.h>
#include "macro.h"
@@ -39,12 +36,13 @@
static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) {
_cleanup_closedir_ DIR *dir = NULL;
- char *dirpath;
+ const char *dirpath;
+ int r;
assert(path);
assert(suffix);
- dirpath = strjoina(root ? root : "", path);
+ dirpath = prefix_roota(root, path);
dir = opendir(dirpath);
if (!dir) {
@@ -56,7 +54,6 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char
for (;;) {
struct dirent *de;
char *p;
- int r;
errno = 0;
de = readdir(dir);
diff --git a/src/shared/conf-files.h b/src/shared/conf-files.h
index 368c112eb3..3169a907f1 100644
--- a/src/shared/conf-files.h
+++ b/src/shared/conf-files.h
@@ -22,7 +22,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "macro.h"
int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...);
int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs);
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 0b1af6c577..7370c786f9 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -22,9 +22,7 @@
#include <string.h>
#include <stdio.h>
#include <errno.h>
-#include <assert.h>
#include <stdlib.h>
-#include <netinet/ether.h>
#include "conf-parser.h"
#include "conf-files.h"
@@ -34,54 +32,8 @@
#include "log.h"
#include "utf8.h"
#include "path-util.h"
-#include "set.h"
-#include "exit-status.h"
#include "sd-messages.h"
-int log_syntax_internal(
- const char *unit,
- int level,
- const char *file,
- int line,
- const char *func,
- const char *config_file,
- unsigned config_line,
- int error,
- const char *format, ...) {
-
- _cleanup_free_ char *msg = NULL;
- int r;
- va_list ap;
-
- va_start(ap, format);
- r = vasprintf(&msg, format, ap);
- va_end(ap);
- if (r < 0)
- return log_oom();
-
- if (unit)
- r = log_struct_internal(level,
- error,
- file, line, func,
- getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit,
- LOG_MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR),
- "CONFIG_FILE=%s", config_file,
- "CONFIG_LINE=%u", config_line,
- LOG_MESSAGE("[%s:%u] %s", config_file, config_line, msg),
- NULL);
- else
- r = log_struct_internal(level,
- error,
- file, line, func,
- LOG_MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR),
- "CONFIG_FILE=%s", config_file,
- "CONFIG_LINE=%u", config_line,
- LOG_MESSAGE("[%s:%u] %s", config_file, config_line, msg),
- NULL);
-
- return r;
-}
-
int config_item_table_lookup(
const void *table,
const char *section,
@@ -492,7 +444,7 @@ int config_parse_many(const char *conf_file,
if (r < 0) \
log_syntax(unit, LOG_ERR, filename, line, -r, \
"Failed to parse %s value, ignoring: %s", \
- #vartype, rvalue); \
+ #type, rvalue); \
\
return 0; \
}
@@ -763,41 +715,30 @@ int config_parse_strv(const char *unit,
return 0;
}
-int config_parse_mode(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
+int config_parse_mode(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
mode_t *m = data;
- long l;
- char *x = NULL;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- errno = 0;
- l = strtol(rvalue, &x, 8);
- if (!x || x == rvalue || *x || errno) {
- log_syntax(unit, LOG_ERR, filename, line, errno,
- "Failed to parse mode value, ignoring: %s", rvalue);
- return 0;
- }
-
- if (l < 0000 || l > 07777) {
- log_syntax(unit, LOG_ERR, filename, line, ERANGE,
- "Mode value out of range, ignoring: %s", rvalue);
+ if (parse_mode(rvalue, m) < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse mode value, ignoring: %s", rvalue);
return 0;
}
- *m = (mode_t) l;
return 0;
}
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index 7a2f855f9f..6152ee33b9 100644
--- a/src/shared/conf-parser.h
+++ b/src/shared/conf-parser.h
@@ -119,23 +119,6 @@ int config_parse_mode(const char *unit, const char *filename, unsigned line, con
int config_parse_log_facility(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_log_level(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int log_syntax_internal(
- const char *unit,
- int level,
- const char *file,
- int line,
- const char *func,
- const char *config_file,
- unsigned config_line,
- int error,
- const char *format, ...) _printf_(9, 10);
-
-#define log_syntax(unit, level, config_file, config_line, error, ...) \
- log_syntax_internal(unit, level, \
- __FILE__, __LINE__, __func__, \
- config_file, config_line, \
- error, __VA_ARGS__)
-
#define log_invalid_utf8(unit, level, config_file, config_line, error, rvalue) \
do { \
_cleanup_free_ char *_p = utf8_escape_invalid(rvalue); \
diff --git a/src/shared/copy.c b/src/shared/copy.c
index 0239a58066..1282cb88be 100644
--- a/src/shared/copy.c
+++ b/src/shared/copy.c
@@ -285,7 +285,7 @@ static int fd_copy_directory(
else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
else
- q = -ENOTSUP;
+ q = -EOPNOTSUPP;
if (q == -EEXIST && merge)
q = 0;
@@ -317,7 +317,7 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge)
else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
return fd_copy_node(fdf, from, &st, fdt, to);
else
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
int copy_tree(const char *from, const char *to, bool merge) {
@@ -360,7 +360,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) {
}
int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
- int fdt, r;
+ int fdt = -1, r;
assert(from);
assert(to);
@@ -372,7 +372,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
}
if (chattr_flags != 0)
- (void) chattr_fd(fdt, true, chattr_flags);
+ (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
r = copy_file_fd(from, fdt, true);
if (r < 0) {
@@ -390,7 +390,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
}
int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
int r;
assert(from);
@@ -404,9 +404,15 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace
if (r < 0)
return r;
- if (renameat2(AT_FDCWD, t, AT_FDCWD, to, replace ? 0 : RENAME_NOREPLACE) < 0) {
- unlink_noerrno(t);
- return -errno;
+ if (replace) {
+ r = renameat(AT_FDCWD, t, AT_FDCWD, to);
+ if (r < 0)
+ r = -errno;
+ } else
+ r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
+ if (r < 0) {
+ (void) unlink_noerrno(t);
+ return r;
}
return 0;
@@ -415,7 +421,7 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace
int copy_times(int fdf, int fdt) {
struct timespec ut[2];
struct stat st;
- usec_t crtime;
+ usec_t crtime = 0;
assert(fdf >= 0);
assert(fdt >= 0);
diff --git a/src/shared/dev-setup.c b/src/shared/dev-setup.c
index e8b0810d23..25ad918b85 100644
--- a/src/shared/dev-setup.c
+++ b/src/shared/dev-setup.c
@@ -20,21 +20,15 @@
***/
#include <errno.h>
-#include <sys/stat.h>
#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
#include <unistd.h>
-#include "dev-setup.h"
-#include "log.h"
-#include "macro.h"
#include "util.h"
#include "label.h"
+#include "path-util.h"
+#include "dev-setup.h"
-int dev_setup(const char *prefix) {
- const char *j, *k;
-
+int dev_setup(const char *prefix, uid_t uid, gid_t gid) {
static const char symlinks[] =
"-/proc/kcore\0" "/dev/core\0"
"/proc/self/fd\0" "/dev/fd\0"
@@ -42,7 +36,13 @@ int dev_setup(const char *prefix) {
"/proc/self/fd/1\0" "/dev/stdout\0"
"/proc/self/fd/2\0" "/dev/stderr\0";
+ const char *j, *k;
+ int r;
+
NULSTR_FOREACH_PAIR(j, k, symlinks) {
+ _cleanup_free_ char *link_name = NULL;
+ const char *n;
+
if (j[0] == '-') {
j++;
@@ -51,15 +51,21 @@ int dev_setup(const char *prefix) {
}
if (prefix) {
- _cleanup_free_ char *link_name = NULL;
-
- link_name = strjoin(prefix, "/", k, NULL);
+ link_name = prefix_root(prefix, k);
if (!link_name)
return -ENOMEM;
- symlink_label(j, link_name);
+ n = link_name;
} else
- symlink_label(j, k);
+ n = k;
+
+ r = symlink_label(j, n);
+ if (r < 0)
+ log_debug_errno(r, "Failed to symlink %s to %s: %m", j, n);
+
+ if (uid != UID_INVALID || gid != GID_INVALID)
+ if (lchown(n, uid, gid) < 0)
+ log_debug_errno(errno, "Failed to chown %s: %m", n);
}
return 0;
diff --git a/src/shared/dev-setup.h b/src/shared/dev-setup.h
index d41b6eefba..ab2748db7f 100644
--- a/src/shared/dev-setup.h
+++ b/src/shared/dev-setup.h
@@ -21,4 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-int dev_setup(const char *pathprefix);
+#include <sys/types.h>
+
+int dev_setup(const char *prefix, uid_t uid, gid_t gid);
diff --git a/src/shared/device-nodes.c b/src/shared/device-nodes.c
index 73e9edd29d..9d5af72d27 100644
--- a/src/shared/device-nodes.c
+++ b/src/shared/device-nodes.c
@@ -19,21 +19,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <sys/types.h>
#include "device-nodes.h"
#include "utf8.h"
int whitelisted_char_for_devnode(char c, const char *white) {
+
if ((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
strchr("#+-.:=@_", c) != NULL ||
(white != NULL && strchr(white, c) != NULL))
return 1;
+
return 0;
}
@@ -48,27 +47,34 @@ int encode_devnode_name(const char *str, char *str_enc, size_t len) {
seqlen = utf8_encoded_valid_unichar(&str[i]);
if (seqlen > 1) {
+
if (len-j < (size_t)seqlen)
- goto err;
+ return -EINVAL;
+
memcpy(&str_enc[j], &str[i], seqlen);
j += seqlen;
i += (seqlen-1);
+
} else if (str[i] == '\\' || !whitelisted_char_for_devnode(str[i], NULL)) {
+
if (len-j < 4)
- goto err;
+ return -EINVAL;
+
sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
j += 4;
+
} else {
if (len-j < 1)
- goto err;
+ return -EINVAL;
+
str_enc[j] = str[i];
j++;
}
}
+
if (len-j < 1)
- goto err;
+ return -EINVAL;
+
str_enc[j] = '\0';
return 0;
-err:
- return -EINVAL;
}
diff --git a/src/shared/dropin.c b/src/shared/dropin.c
index d1baad6192..963d05d32e 100644
--- a/src/shared/dropin.c
+++ b/src/shared/dropin.c
@@ -164,7 +164,7 @@ static int iterate_dir(
}
int unit_file_process_dir(
- Set * unit_path_cache,
+ Set *unit_path_cache,
const char *unit_path,
const char *name,
const char *suffix,
@@ -174,6 +174,7 @@ int unit_file_process_dir(
char ***strv) {
_cleanup_free_ char *path = NULL;
+ int r;
assert(unit_path);
assert(name);
@@ -184,22 +185,22 @@ int unit_file_process_dir(
return log_oom();
if (!unit_path_cache || set_get(unit_path_cache, path))
- iterate_dir(path, dependency, consumer, arg, strv);
+ (void) iterate_dir(path, dependency, consumer, arg, strv);
- if (unit_name_is_instance(name)) {
+ if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) {
_cleanup_free_ char *template = NULL, *p = NULL;
/* Also try the template dir */
- template = unit_name_template(name);
- if (!template)
- return log_oom();
+ r = unit_name_template(name, &template);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate template from unit name: %m");
p = strjoin(unit_path, "/", template, suffix, NULL);
if (!p)
return log_oom();
if (!unit_path_cache || set_get(unit_path_cache, p))
- iterate_dir(p, dependency, consumer, arg, strv);
+ (void) iterate_dir(p, dependency, consumer, arg, strv);
}
return 0;
diff --git a/src/shared/efivars.c b/src/shared/efivars.c
index a319574527..d34d977b9a 100644
--- a/src/shared/efivars.c
+++ b/src/shared/efivars.c
@@ -22,15 +22,49 @@
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
-#include <ctype.h>
-#include "acpi-fpdt.h"
#include "util.h"
#include "utf8.h"
+#include "virt.h"
#include "efivars.h"
#ifdef ENABLE_EFI
+#define LOAD_OPTION_ACTIVE 0x00000001
+#define MEDIA_DEVICE_PATH 0x04
+#define MEDIA_HARDDRIVE_DP 0x01
+#define MEDIA_FILEPATH_DP 0x04
+#define SIGNATURE_TYPE_GUID 0x02
+#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02
+#define END_DEVICE_PATH_TYPE 0x7f
+#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff
+#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001
+
+struct boot_option {
+ uint32_t attr;
+ uint16_t path_len;
+ uint16_t title[];
+} _packed_;
+
+struct drive_path {
+ uint32_t part_nr;
+ uint64_t part_start;
+ uint64_t part_size;
+ char signature[16];
+ uint8_t mbr_type;
+ uint8_t signature_type;
+} _packed_;
+
+struct device_path {
+ uint8_t type;
+ uint8_t sub_type;
+ uint16_t length;
+ union {
+ uint16_t path[0];
+ struct drive_path drive;
+ };
+} _packed_;
+
bool is_efi_boot(void) {
return access("/sys/firmware/efi", F_OK) >= 0;
}
@@ -53,12 +87,82 @@ static int read_flag(const char *varname) {
return r;
}
-int is_efi_secure_boot(void) {
- return read_flag("SecureBoot");
+bool is_efi_secure_boot(void) {
+ return read_flag("SecureBoot") > 0;
+}
+
+bool is_efi_secure_boot_setup_mode(void) {
+ return read_flag("SetupMode") > 0;
+}
+
+int efi_reboot_to_firmware_supported(void) {
+ int r;
+ size_t s;
+ uint64_t b;
+ _cleanup_free_ void *v = NULL;
+
+ if (!is_efi_boot() || detect_container(NULL) > 0)
+ return -EOPNOTSUPP;
+
+ r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s);
+ if (r < 0)
+ return r;
+ else if (s != sizeof(uint64_t))
+ return -EINVAL;
+
+ b = *(uint64_t *)v;
+ b &= EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+ return b > 0 ? 0 : -EOPNOTSUPP;
+}
+
+static int get_os_indications(uint64_t *os_indication) {
+ int r;
+ size_t s;
+ _cleanup_free_ void *v = NULL;
+
+ r = efi_reboot_to_firmware_supported();
+ if (r < 0)
+ return r;
+
+ r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s);
+ if (r < 0)
+ return r;
+ else if (s != sizeof(uint64_t))
+ return -EINVAL;
+
+ *os_indication = *(uint64_t *)v;
+ return 0;
+}
+
+int efi_get_reboot_to_firmware(void) {
+ int r;
+ uint64_t b;
+
+ r = get_os_indications(&b);
+ if (r < 0)
+ return r;
+
+ return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI);
}
-int is_efi_secure_boot_setup_mode(void) {
- return read_flag("SetupMode");
+int efi_set_reboot_to_firmware(bool value) {
+ int r;
+ uint64_t b, b_new;
+
+ r = get_os_indications(&b);
+ if (r < 0)
+ return r;
+
+ if (value)
+ b_new = b | EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+ else
+ b_new = b & ~EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+
+ /* Avoid writing to efi vars store if we can due to firmware bugs. */
+ if (b != b_new)
+ return efi_set_variable(EFI_VENDOR_GLOBAL, "OsIndications", &b_new, sizeof(uint64_t));
+
+ return 0;
}
int efi_get_variable(
@@ -73,7 +177,7 @@ int efi_get_variable(
uint32_t a;
ssize_t n;
struct stat st;
- void *r;
+ _cleanup_free_ void *buf = NULL;
assert(name);
assert(value);
@@ -101,25 +205,22 @@ int efi_get_variable(
if (n != sizeof(a))
return -EIO;
- r = malloc(st.st_size - 4 + 2);
- if (!r)
+ buf = malloc(st.st_size - 4 + 2);
+ if (!buf)
return -ENOMEM;
- n = read(fd, r, (size_t) st.st_size - 4);
- if (n < 0) {
- free(r);
+ n = read(fd, buf, (size_t) st.st_size - 4);
+ if (n < 0)
return -errno;
- }
- if (n != (ssize_t) st.st_size - 4) {
- free(r);
+ if (n != (ssize_t) st.st_size - 4)
return -EIO;
- }
/* Always NUL terminate (2 bytes, to protect UTF-16) */
- ((char*) r)[st.st_size - 4] = 0;
- ((char*) r)[st.st_size - 4 + 1] = 0;
+ ((char*) buf)[st.st_size - 4] = 0;
+ ((char*) buf)[st.st_size - 4 + 1] = 0;
- *value = r;
+ *value = buf;
+ buf = NULL;
*size = (size_t) st.st_size - 4;
if (attribute)
@@ -128,6 +229,46 @@ int efi_get_variable(
return 0;
}
+int efi_set_variable(
+ sd_id128_t vendor,
+ const char *name,
+ const void *value,
+ size_t size) {
+
+ struct var {
+ uint32_t attr;
+ char buf[];
+ } _packed_ * _cleanup_free_ buf = NULL;
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1;
+
+ assert(name);
+
+ if (asprintf(&p,
+ "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ name, SD_ID128_FORMAT_VAL(vendor)) < 0)
+ return -ENOMEM;
+
+ if (size == 0) {
+ if (unlink(p) < 0)
+ return -errno;
+ return 0;
+ }
+
+ fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
+ if (fd < 0)
+ return -errno;
+
+ buf = malloc(sizeof(uint32_t) + size);
+ if (!buf)
+ return -ENOMEM;
+
+ buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
+ memcpy(buf->buf, value, size);
+
+ return loop_write(fd, buf, sizeof(uint32_t) + size, false);
+}
+
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
_cleanup_free_ void *s = NULL;
size_t ss = 0;
@@ -179,47 +320,22 @@ int efi_get_boot_option(
uint16_t id,
char **title,
sd_id128_t *part_uuid,
- char **path) {
-
- struct boot_option {
- uint32_t attr;
- uint16_t path_len;
- uint16_t title[];
- } _packed_;
-
- struct drive_path {
- uint32_t part_nr;
- uint64_t part_start;
- uint64_t part_size;
- char signature[16];
- uint8_t mbr_type;
- uint8_t signature_type;
- } _packed_;
-
- struct device_path {
- uint8_t type;
- uint8_t sub_type;
- uint16_t length;
- union {
- uint16_t path[0];
- struct drive_path drive;
- };
- } _packed_;
+ char **path,
+ bool *active) {
char boot_id[9];
_cleanup_free_ uint8_t *buf = NULL;
size_t l;
struct boot_option *header;
size_t title_size;
- char *s = NULL;
- char *p = NULL;
+ _cleanup_free_ char *s = NULL, *p = NULL;
sd_id128_t p_uuid = SD_ID128_NULL;
- int err;
+ int r;
- snprintf(boot_id, sizeof(boot_id), "Boot%04X", id);
- err = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
- if (err < 0)
- return err;
+ xsprintf(boot_id, "Boot%04X", id);
+ r = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
+ if (r < 0)
+ return r;
if (l < sizeof(struct boot_option))
return -ENOENT;
@@ -230,10 +346,8 @@ int efi_get_boot_option(
if (title) {
s = utf16_to_utf8(header->title, title_size);
- if (!s) {
- err = -ENOMEM;
- goto err;
- }
+ if (!s)
+ return -ENOMEM;
}
if (header->path_len > 0) {
@@ -250,23 +364,23 @@ int efi_get_boot_option(
break;
/* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
- if (dpath->type == 0x7f && dpath->sub_type == 0xff)
+ if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE)
break;
dnext += dpath->length;
/* Type 0x04 – Media Device Path */
- if (dpath->type != 0x04)
+ if (dpath->type != MEDIA_DEVICE_PATH)
continue;
/* Sub-Type 1 – Hard Drive */
- if (dpath->sub_type == 0x01) {
+ if (dpath->sub_type == MEDIA_HARDDRIVE_DP) {
/* 0x02 – GUID Partition Table */
- if (dpath->drive.mbr_type != 0x02)
+ if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER)
continue;
/* 0x02 – GUID signature */
- if (dpath->drive.signature_type != 0x02)
+ if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID)
continue;
if (part_uuid)
@@ -275,29 +389,135 @@ int efi_get_boot_option(
}
/* Sub-Type 4 – File Path */
- if (dpath->sub_type == 0x04 && !p && path) {
+ if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && path) {
p = utf16_to_utf8(dpath->path, dpath->length-4);
+ efi_tilt_backslashes(p);
continue;
}
}
}
- if (title)
+ if (title) {
*title = s;
+ s = NULL;
+ }
if (part_uuid)
*part_uuid = p_uuid;
- if (path)
+ if (path) {
*path = p;
+ p = NULL;
+ }
+ if (active)
+ *active = !!(header->attr & LOAD_OPTION_ACTIVE);
return 0;
-err:
- free(s);
- free(p);
- return err;
+}
+
+static void to_utf16(uint16_t *dest, const char *src) {
+ int i;
+
+ for (i = 0; src[i] != '\0'; i++)
+ dest[i] = src[i];
+ dest[i] = '\0';
+}
+
+struct guid {
+ uint32_t u1;
+ uint16_t u2;
+ uint16_t u3;
+ uint8_t u4[8];
+} _packed_;
+
+static void id128_to_efi_guid(sd_id128_t id, void *guid) {
+ struct guid *uuid = guid;
+
+ uuid->u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3];
+ uuid->u2 = id.bytes[4] << 8 | id.bytes[5];
+ uuid->u3 = id.bytes[6] << 8 | id.bytes[7];
+ memcpy(uuid->u4, id.bytes+8, sizeof(uuid->u4));
+}
+
+static uint16_t *tilt_slashes(uint16_t *s) {
+ uint16_t *p;
+
+ for (p = s; *p; p++)
+ if (*p == '/')
+ *p = '\\';
+
+ return s;
+}
+
+int efi_add_boot_option(uint16_t id, const char *title,
+ uint32_t part, uint64_t pstart, uint64_t psize,
+ sd_id128_t part_uuid, const char *path) {
+ char boot_id[9];
+ size_t size;
+ size_t title_len;
+ size_t path_len;
+ struct boot_option *option;
+ struct device_path *devicep;
+ _cleanup_free_ char *buf = NULL;
+
+ title_len = (strlen(title)+1) * 2;
+ path_len = (strlen(path)+1) * 2;
+
+ buf = calloc(sizeof(struct boot_option) + title_len +
+ sizeof(struct drive_path) +
+ sizeof(struct device_path) + path_len, 1);
+ if (!buf)
+ return -ENOMEM;
+
+ /* header */
+ option = (struct boot_option *)buf;
+ option->attr = LOAD_OPTION_ACTIVE;
+ option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) +
+ offsetof(struct device_path, path) + path_len +
+ offsetof(struct device_path, path);
+ to_utf16(option->title, title);
+ size = offsetof(struct boot_option, title) + title_len;
+
+ /* partition info */
+ devicep = (struct device_path *)(buf + size);
+ devicep->type = MEDIA_DEVICE_PATH;
+ devicep->sub_type = MEDIA_HARDDRIVE_DP;
+ devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path);
+ devicep->drive.part_nr = part;
+ devicep->drive.part_start = pstart;
+ devicep->drive.part_size = psize;
+ devicep->drive.signature_type = SIGNATURE_TYPE_GUID;
+ devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
+ id128_to_efi_guid(part_uuid, devicep->drive.signature);
+ size += devicep->length;
+
+ /* path to loader */
+ devicep = (struct device_path *)(buf + size);
+ devicep->type = MEDIA_DEVICE_PATH;
+ devicep->sub_type = MEDIA_FILEPATH_DP;
+ devicep->length = offsetof(struct device_path, path) + path_len;
+ to_utf16(devicep->path, path);
+ tilt_slashes(devicep->path);
+ size += devicep->length;
+
+ /* end of path */
+ devicep = (struct device_path *)(buf + size);
+ devicep->type = END_DEVICE_PATH_TYPE;
+ devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE;
+ devicep->length = offsetof(struct device_path, path);
+ size += devicep->length;
+
+ xsprintf(boot_id, "Boot%04X", id);
+ return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, buf, size);
+}
+
+int efi_remove_boot_option(uint16_t id) {
+ char boot_id[9];
+
+ xsprintf(boot_id, "Boot%04X", id);
+ return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0);
}
int efi_get_boot_order(uint16_t **order) {
- void *buf;
+ _cleanup_free_ void *buf = NULL;
size_t l;
int r;
@@ -305,21 +525,22 @@ int efi_get_boot_order(uint16_t **order) {
if (r < 0)
return r;
- if (l <= 0) {
- free(buf);
+ if (l <= 0)
return -ENOENT;
- }
- if ((l % sizeof(uint16_t) > 0) ||
- (l / sizeof(uint16_t) > INT_MAX)) {
- free(buf);
+ if (l % sizeof(uint16_t) > 0 ||
+ l / sizeof(uint16_t) > INT_MAX)
return -EINVAL;
- }
*order = buf;
+ buf = NULL;
return (int) (l / sizeof(uint16_t));
}
+int efi_set_boot_order(uint16_t *order, size_t n) {
+ return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t));
+}
+
static int boot_id_hex(const char s[4]) {
int i;
int id = 0;
@@ -344,8 +565,9 @@ static int cmp_uint16(const void *_a, const void *_b) {
int efi_get_boot_options(uint16_t **options) {
_cleanup_closedir_ DIR *dir = NULL;
struct dirent *de;
- uint16_t *list = NULL;
- int count = 0, r;
+ _cleanup_free_ uint16_t *list = NULL;
+ size_t alloc = 0;
+ int count = 0;
assert(options);
@@ -353,9 +575,8 @@ int efi_get_boot_options(uint16_t **options) {
if (!dir)
return -errno;
- FOREACH_DIRENT(de, dir, r = -errno; goto fail) {
+ FOREACH_DIRENT(de, dir, return -errno) {
int id;
- uint16_t *t;
if (strncmp(de->d_name, "Boot", 4) != 0)
continue;
@@ -370,24 +591,17 @@ int efi_get_boot_options(uint16_t **options) {
if (id < 0)
continue;
- t = realloc(list, (count + 1) * sizeof(uint16_t));
- if (!t) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!GREEDY_REALLOC(list, alloc, count + 1))
+ return -ENOMEM;
- list = t;
- list[count ++] = id;
+ list[count++] = id;
}
qsort_safe(list, count, sizeof(uint16_t), cmp_uint16);
*options = list;
+ list = NULL;
return count;
-
-fail:
- free(list);
- return r;
}
static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
@@ -463,3 +677,13 @@ int efi_loader_get_device_part_uuid(sd_id128_t *u) {
}
#endif
+
+char *efi_tilt_backslashes(char *s) {
+ char *p;
+
+ for (p = s; *p; p++)
+ if (*p == '\\')
+ *p = '/';
+
+ return s;
+}
diff --git a/src/shared/efivars.h b/src/shared/efivars.h
index 7921bedc9f..e953a12737 100644
--- a/src/shared/efivars.h
+++ b/src/shared/efivars.h
@@ -21,8 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
-#include <inttypes.h>
#include <stdbool.h>
#include "sd-id128.h"
@@ -30,17 +28,103 @@
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
+#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001
+#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
+#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
+
+#ifdef ENABLE_EFI
bool is_efi_boot(void);
-int is_efi_secure_boot(void);
-int is_efi_secure_boot_setup_mode(void);
+bool is_efi_secure_boot(void);
+bool is_efi_secure_boot_setup_mode(void);
+int efi_reboot_to_firmware_supported(void);
+int efi_get_reboot_to_firmware(void);
+int efi_set_reboot_to_firmware(bool value);
int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size);
+int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size);
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p);
-int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *partuuid, char **path);
+int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active);
+int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path);
+int efi_remove_boot_option(uint16_t id);
int efi_get_boot_order(uint16_t **order);
+int efi_set_boot_order(uint16_t *order, size_t n);
int efi_get_boot_options(uint16_t **options);
int efi_loader_get_device_part_uuid(sd_id128_t *u);
int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader);
+
+#else
+
+static inline bool is_efi_boot(void) {
+ return false;
+}
+
+static inline bool is_efi_secure_boot(void) {
+ return false;
+}
+
+static inline bool is_efi_secure_boot_setup_mode(void) {
+ return false;
+}
+
+static inline int efi_reboot_to_firmware_supported(void) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_get_reboot_to_firmware(void) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_set_reboot_to_firmware(bool value) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_remove_boot_option(uint16_t id) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_get_boot_order(uint16_t **order) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_set_boot_order(uint16_t *order, size_t n) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_get_boot_options(uint16_t **options) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_loader_get_device_part_uuid(sd_id128_t *u) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
+ return -EOPNOTSUPP;
+}
+
+#endif
+
+char *efi_tilt_backslashes(char *s);
diff --git a/src/shared/env-util.c b/src/shared/env-util.c
index 038246d21b..ac7bbdc711 100644
--- a/src/shared/env-util.c
+++ b/src/shared/env-util.c
@@ -20,7 +20,6 @@
***/
#include <limits.h>
-#include <sys/param.h>
#include <unistd.h>
#include "strv.h"
@@ -449,3 +448,147 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
return e;
}
+
+char *replace_env(const char *format, char **env) {
+ enum {
+ WORD,
+ CURLY,
+ VARIABLE
+ } state = WORD;
+
+ const char *e, *word = format;
+ char *r = NULL, *k;
+
+ assert(format);
+
+ for (e = format; *e; e ++) {
+
+ switch (state) {
+
+ case WORD:
+ if (*e == '$')
+ state = CURLY;
+ break;
+
+ case CURLY:
+ if (*e == '{') {
+ k = strnappend(r, word, e-word-1);
+ if (!k)
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e-1;
+ state = VARIABLE;
+
+ } else if (*e == '$') {
+ k = strnappend(r, word, e-word);
+ if (!k)
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e+1;
+ state = WORD;
+ } else
+ state = WORD;
+ break;
+
+ case VARIABLE:
+ if (*e == '}') {
+ const char *t;
+
+ t = strempty(strv_env_get_n(env, word+2, e-word-2));
+
+ k = strappend(r, t);
+ if (!k)
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e+1;
+ state = WORD;
+ }
+ break;
+ }
+ }
+
+ k = strnappend(r, word, e-word);
+ if (!k)
+ goto fail;
+
+ free(r);
+ return k;
+
+fail:
+ free(r);
+ return NULL;
+}
+
+char **replace_env_argv(char **argv, char **env) {
+ char **ret, **i;
+ unsigned k = 0, l = 0;
+
+ l = strv_length(argv);
+
+ ret = new(char*, l+1);
+ if (!ret)
+ return NULL;
+
+ STRV_FOREACH(i, argv) {
+
+ /* If $FOO appears as single word, replace it by the split up variable */
+ if ((*i)[0] == '$' && (*i)[1] != '{') {
+ char *e;
+ char **w, **m = NULL;
+ unsigned q;
+
+ e = strv_env_get(env, *i+1);
+ if (e) {
+ int r;
+
+ r = strv_split_quoted(&m, e, UNQUOTE_RELAX);
+ if (r < 0) {
+ ret[k] = NULL;
+ strv_free(ret);
+ return NULL;
+ }
+ } else
+ m = NULL;
+
+ q = strv_length(m);
+ l = l + q - 1;
+
+ w = realloc(ret, sizeof(char*) * (l+1));
+ if (!w) {
+ ret[k] = NULL;
+ strv_free(ret);
+ strv_free(m);
+ return NULL;
+ }
+
+ ret = w;
+ if (m) {
+ memcpy(ret + k, m, q * sizeof(char*));
+ free(m);
+ }
+
+ k += q;
+ continue;
+ }
+
+ /* If ${FOO} appears as part of a word, replace it by the variable as-is */
+ ret[k] = replace_env(*i, env);
+ if (!ret[k]) {
+ strv_free(ret);
+ return NULL;
+ }
+ k++;
+ }
+
+ ret[k] = NULL;
+ return ret;
+}
diff --git a/src/shared/env-util.h b/src/shared/env-util.h
index 618441a655..803aa61cad 100644
--- a/src/shared/env-util.h
+++ b/src/shared/env-util.h
@@ -22,7 +22,6 @@
***/
#include <stdbool.h>
-#include <sys/types.h>
#include "macro.h"
@@ -30,6 +29,9 @@ bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);
+char *replace_env(const char *format, char **env);
+char **replace_env_argv(char **argv, char **env);
+
bool strv_env_is_valid(char **e);
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
diff --git a/src/shared/errno-list.c b/src/shared/errno-list.c
index c63296f292..34d1331486 100644
--- a/src/shared/errno-list.c
+++ b/src/shared/errno-list.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
#include <string.h>
#include "util.h"
diff --git a/src/shared/exit-status.c b/src/shared/exit-status.c
index 5c73b4d3c0..c09efdd2cb 100644
--- a/src/shared/exit-status.c
+++ b/src/shared/exit-status.c
@@ -20,7 +20,6 @@
***/
#include <stdlib.h>
-#include <sys/wait.h>
#include "exit-status.h"
#include "set.h"
@@ -167,7 +166,7 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
return "NOPERMISSION";
case EXIT_NOTINSTALLED:
- return "NOTINSSTALLED";
+ return "NOTINSTALLED";
case EXIT_NOTCONFIGURED:
return "NOTCONFIGURED";
@@ -226,3 +225,17 @@ bool exit_status_set_is_empty(ExitStatusSet *x) {
return set_isempty(x->status) && set_isempty(x->signal);
}
+
+bool exit_status_set_test(ExitStatusSet *x, int code, int status) {
+
+ if (exit_status_set_is_empty(x))
+ return false;
+
+ if (code == CLD_EXITED && set_contains(x->status, INT_TO_PTR(status)))
+ return true;
+
+ if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && set_contains(x->signal, INT_TO_PTR(status)))
+ return true;
+
+ return false;
+}
diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h
index 1d774f25dc..7259cd1d18 100644
--- a/src/shared/exit-status.h
+++ b/src/shared/exit-status.h
@@ -100,3 +100,4 @@ bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status);
void exit_status_set_free(ExitStatusSet *x);
bool exit_status_set_is_empty(ExitStatusSet *x);
+bool exit_status_set_test(ExitStatusSet *x, int code, int status);
diff --git a/src/shared/fdset.c b/src/shared/fdset.c
index 9e35ce5cec..6101b628ec 100644
--- a/src/shared/fdset.c
+++ b/src/shared/fdset.c
@@ -22,7 +22,6 @@
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
-#include <unistd.h>
#include "set.h"
#include "util.h"
@@ -33,7 +32,7 @@
#define MAKE_SET(s) ((Set*) s)
#define MAKE_FDSET(s) ((FDSet*) s)
-/* Make sure we can distuingish fd 0 and NULL */
+/* Make sure we can distinguish fd 0 and NULL */
#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
diff --git a/src/shared/fdset.h b/src/shared/fdset.h
index c3c5e52286..340438d7c4 100644
--- a/src/shared/fdset.h
+++ b/src/shared/fdset.h
@@ -22,7 +22,6 @@
***/
#include "set.h"
-#include "util.h"
typedef struct FDSet FDSet;
diff --git a/src/shared/fileio-label.c b/src/shared/fileio-label.c
index 5fd69e0580..bec988ca78 100644
--- a/src/shared/fileio-label.c
+++ b/src/shared/fileio-label.c
@@ -19,13 +19,9 @@
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
#include "util.h"
#include "selinux-util.h"
-#include "label.h"
#include "fileio-label.h"
int write_string_file_atomic_label(const char *fn, const char *line) {
diff --git a/src/shared/formats-util.h b/src/shared/formats-util.h
new file mode 100644
index 0000000000..ce516b117d
--- /dev/null
+++ b/src/shared/formats-util.h
@@ -0,0 +1,63 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Ronny Chevalier
+
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#if SIZEOF_PID_T == 4
+# define PID_PRI PRIi32
+#elif SIZEOF_PID_T == 2
+# define PID_PRI PRIi16
+#else
+# error Unknown pid_t size
+#endif
+#define PID_FMT "%" PID_PRI
+
+#if SIZEOF_UID_T == 4
+# define UID_FMT "%" PRIu32
+#elif SIZEOF_UID_T == 2
+# define UID_FMT "%" PRIu16
+#else
+# error Unknown uid_t size
+#endif
+
+#if SIZEOF_GID_T == 4
+# define GID_FMT "%" PRIu32
+#elif SIZEOF_GID_T == 2
+# define GID_FMT "%" PRIu16
+#else
+# error Unknown gid_t size
+#endif
+
+#if SIZEOF_TIME_T == 8
+# define PRI_TIME PRIi64
+#elif SIZEOF_TIME_T == 4
+# define PRI_TIME PRIu32
+#else
+# error Unknown time_t size
+#endif
+
+#if SIZEOF_RLIM_T == 8
+# define RLIM_FMT "%" PRIu64
+#elif SIZEOF_RLIM_T == 4
+# define RLIM_FMT "%" PRIu32
+#else
+# error Unknown rlim_t size
+#endif
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
index cf317e17bd..e231a0ff80 100644
--- a/src/shared/fstab-util.c
+++ b/src/shared/fstab-util.c
@@ -125,6 +125,36 @@ answer:
return !!n;
}
+int fstab_extract_values(const char *opts, const char *name, char ***values) {
+ _cleanup_strv_free_ char **optsv = NULL, **res = NULL;
+ char **s;
+
+ assert(opts);
+ assert(name);
+ assert(values);
+
+ optsv = strv_split(opts, ",");
+ if (!optsv)
+ return -ENOMEM;
+
+ STRV_FOREACH(s, optsv) {
+ char *arg;
+ int r;
+
+ arg = startswith(*s, name);
+ if (!arg || *arg != '=')
+ continue;
+ r = strv_extend(&res, arg + 1);
+ if (r < 0)
+ return r;
+ }
+
+ *values = res;
+ res = NULL;
+
+ return !!*values;
+}
+
int fstab_find_pri(const char *options, int *ret) {
_cleanup_free_ char *opt = NULL;
int r;
diff --git a/src/shared/fstab-util.h b/src/shared/fstab-util.h
index 9f6b32eaf4..387c562a96 100644
--- a/src/shared/fstab-util.h
+++ b/src/shared/fstab-util.h
@@ -28,6 +28,8 @@
int fstab_filter_options(const char *opts, const char *names,
const char **namefound, char **value, char **filtered);
+int fstab_extract_values(const char *opts, const char *name, char ***values);
+
static inline bool fstab_test_option(const char *opts, const char *names) {
return !!fstab_filter_options(opts, names, NULL, NULL, NULL);
}
diff --git a/src/shared/fw-util.c b/src/shared/fw-util.c
index ceb1ae508c..6b3599d90d 100644
--- a/src/shared/fw-util.c
+++ b/src/shared/fw-util.c
@@ -91,10 +91,10 @@ int fw_add_masquerade(
int r;
if (af != AF_INET)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
if (protocol != 0 && protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
h = iptc_init("nat");
if (!h)
@@ -175,10 +175,10 @@ int fw_add_local_dnat(
assert(add || !previous_remote);
if (af != AF_INET)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
- return -ENOTSUP;
+ return -EOPNOTSUPP;
if (local_port <= 0)
return -EINVAL;
diff --git a/src/shared/fw-util.h b/src/shared/fw-util.h
index 698cc43daa..93152e3978 100644
--- a/src/shared/fw-util.h
+++ b/src/shared/fw-util.h
@@ -60,7 +60,7 @@ static inline int fw_add_masquerade(
const char *out_interface,
const union in_addr_union *destination,
unsigned destination_prefixlen) {
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
static inline int fw_add_local_dnat(
@@ -76,7 +76,7 @@ static inline int fw_add_local_dnat(
const union in_addr_union *remote,
uint16_t remote_port,
const union in_addr_union *previous_remote) {
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
#endif
diff --git a/src/shared/generator.c b/src/shared/generator.c
index 7f16d5cbef..807569a1b8 100644
--- a/src/shared/generator.c
+++ b/src/shared/generator.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
#include <unistd.h>
#include "util.h"
@@ -29,17 +28,63 @@
#include "generator.h"
#include "path-util.h"
#include "fstab-util.h"
+#include "fileio.h"
#include "dropin.h"
+static int write_fsck_sysroot_service(const char *dir, const char *what) {
+ const char *unit;
+ _cleanup_free_ char *device = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ unit = strjoina(dir, "/systemd-fsck-root.service");
+ log_debug("Creating %s", unit);
+
+ r = unit_name_from_path(what, ".device", &device);
+ if (r < 0)
+ return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what);
+
+ f = fopen(unit, "wxe");
+ if (!f)
+ return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
+
+ fprintf(f,
+ "# Automatically generated by %1$s\n\n"
+ "[Unit]\n"
+ "Documentation=man:systemd-fsck-root.service(8)\n"
+ "Description=File System Check on %2$s\n"
+ "DefaultDependencies=no\n"
+ "BindsTo=%3$s\n"
+ "After=%3$s\n"
+ "Before=shutdown.target\n"
+ "\n"
+ "[Service]\n"
+ "Type=oneshot\n"
+ "RemainAfterExit=yes\n"
+ "ExecStart=" SYSTEMD_FSCK_PATH " %2$s\n"
+ "TimeoutSec=0\n",
+ program_invocation_short_name,
+ what,
+ device);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write unit file %s: %m", unit);
+
+ return 0;
+}
+
int generator_write_fsck_deps(
FILE *f,
- const char *dest,
+ const char *dir,
const char *what,
const char *where,
const char *fstype) {
+ int r;
+
assert(f);
- assert(dest);
+ assert(dir);
assert(what);
assert(where);
@@ -49,7 +94,6 @@ int generator_write_fsck_deps(
}
if (!isempty(fstype) && !streq(fstype, "auto")) {
- int r;
r = fsck_exists(fstype);
if (r == -ENOENT) {
/* treat missing check as essentially OK */
@@ -59,34 +103,48 @@ int generator_write_fsck_deps(
return log_warning_errno(r, "Checking was requested for %s, but fsck.%s cannot be used: %m", what, fstype);
}
- if (streq(where, "/")) {
+ if (path_equal(where, "/")) {
char *lnk;
- lnk = strjoina(dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service");
+ lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service");
mkdir_parents(lnk, 0755);
if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-fsck-root.service", lnk) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
} else {
- _cleanup_free_ char *fsck = NULL;
+ _cleanup_free_ char *_fsck = NULL;
+ const char *fsck;
+
+ if (in_initrd() && path_equal(where, "/sysroot")) {
+ r = write_fsck_sysroot_service(dir, what);
+ if (r < 0)
+ return r;
+
+ fsck = "systemd-fsck-root.service";
+ } else {
+ r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create fsck service name: %m");
- fsck = unit_name_from_path_instance("systemd-fsck", what, ".service");
- if (!fsck)
- return log_oom();
+ fsck = _fsck;
+ }
fprintf(f,
- "RequiresOverridable=%s\n"
- "After=%s\n",
- fsck,
+ "RequiresOverridable=%1$s\n"
+ "After=%1$s\n",
fsck);
}
return 0;
}
-int generator_write_timeouts(const char *dir, const char *what, const char *where,
- const char *opts, char **filtered) {
+int generator_write_timeouts(
+ const char *dir,
+ const char *what,
+ const char *where,
+ const char *opts,
+ char **filtered) {
/* Allow configuration how long we wait for a device that
* backs a mount point to show up. This is useful to support
@@ -104,8 +162,7 @@ int generator_write_timeouts(const char *dir, const char *what, const char *wher
r = parse_sec(timeout, &u);
if (r < 0) {
- log_warning("Failed to parse timeout for %s, ignoring: %s",
- where, timeout);
+ log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout);
return 0;
}
@@ -113,9 +170,9 @@ int generator_write_timeouts(const char *dir, const char *what, const char *wher
if (!node)
return log_oom();
- unit = unit_name_from_path(node, ".device");
- if (!unit)
- return log_oom();
+ r = unit_name_from_path(node, ".device", &unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make unit name from path: %m");
return write_drop_in_format(dir, unit, 50, "device-timeout",
"# Automatically generated by %s\n\n"
diff --git a/src/shared/generator.h b/src/shared/generator.h
index 64bd28f596..6c3f38abba 100644
--- a/src/shared/generator.h
+++ b/src/shared/generator.h
@@ -23,7 +23,16 @@
#include <stdio.h>
-int generator_write_fsck_deps(FILE *f, const char *dest, const char *what, const char *where, const char *type);
-
-int generator_write_timeouts(const char *dir, const char *what, const char *where,
- const char *opts, char **filtered);
+int generator_write_fsck_deps(
+ FILE *f,
+ const char *dir,
+ const char *what,
+ const char *where,
+ const char *type);
+
+int generator_write_timeouts(
+ const char *dir,
+ const char *what,
+ const char *where,
+ const char *opts,
+ char **filtered);
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c
index e63ba4bb5a..20d599d04b 100644
--- a/src/shared/hashmap.c
+++ b/src/shared/hashmap.c
@@ -20,9 +20,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <stdlib.h>
-#include <string.h>
#include <errno.h>
#include "util.h"
@@ -31,8 +29,12 @@
#include "macro.h"
#include "siphash24.h"
#include "strv.h"
-#include "list.h"
#include "mempool.h"
+#include "random-util.h"
+
+#ifdef ENABLE_DEBUG_HASHMAP
+#include "list.h"
+#endif
/*
* Implementation of hashmaps.
@@ -130,10 +132,10 @@ struct swap_entries {
/* Distance from Initial Bucket */
typedef uint8_t dib_raw_t;
-#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */
-#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */
-#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */
-#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */
+#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */
+#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */
+#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */
+#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */
#define DIB_FREE UINT_MAX
@@ -771,7 +773,7 @@ static void reset_direct_storage(HashmapBase *h) {
memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets);
}
-static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
HashmapBase *h;
const struct hashmap_type_info *hi = &hashmap_type_info[type];
bool use_pool;
@@ -810,19 +812,19 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu
}
Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+ return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
}
OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+ return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
}
Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+ return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
}
static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops,
- enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+ enum HashmapType type HASHMAP_DEBUG_PARAMS) {
HashmapBase *q;
assert(h);
@@ -830,7 +832,7 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops
if (*h)
return 0;
- q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS);
+ q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS);
if (!q)
return -ENOMEM;
@@ -839,15 +841,15 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops
}
int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+ return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
}
int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+ return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
}
int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
- return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+ return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
}
static void hashmap_free_no_clear(HashmapBase *h) {
@@ -864,38 +866,41 @@ static void hashmap_free_no_clear(HashmapBase *h) {
free(h);
}
-void internal_hashmap_free(HashmapBase *h) {
+HashmapBase *internal_hashmap_free(HashmapBase *h) {
/* Free the hashmap, but nothing in it */
- if (!h)
- return;
+ if (h) {
+ internal_hashmap_clear(h);
+ hashmap_free_no_clear(h);
+ }
- internal_hashmap_clear(h);
- hashmap_free_no_clear(h);
+ return NULL;
}
-void internal_hashmap_free_free(HashmapBase *h) {
+HashmapBase *internal_hashmap_free_free(HashmapBase *h) {
/* Free the hashmap and all data objects in it, but not the
* keys */
- if (!h)
- return;
+ if (h) {
+ internal_hashmap_clear_free(h);
+ hashmap_free_no_clear(h);
+ }
- internal_hashmap_clear_free(h);
- hashmap_free_no_clear(h);
+ return NULL;
}
-void hashmap_free_free_free(Hashmap *h) {
+Hashmap *hashmap_free_free_free(Hashmap *h) {
/* Free the hashmap and all data and key objects in it */
- if (!h)
- return;
+ if (h) {
+ hashmap_clear_free_free(h);
+ hashmap_free_no_clear(HASHMAP_BASE(h));
+ }
- hashmap_clear_free_free(h);
- hashmap_free_no_clear(HASHMAP_BASE(h));
+ return NULL;
}
void internal_hashmap_clear(HashmapBase *h) {
@@ -990,7 +995,6 @@ static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx,
if (dib < distance) {
/* Found a wealthier entry. Go Robin Hood! */
-
bucket_set_dib(h, idx, distance);
/* swap the entries */
diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
index 894f67939e..a03ee5812a 100644
--- a/src/shared/hashmap.h
+++ b/src/shared/hashmap.h
@@ -144,25 +144,25 @@ OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HA
#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
-void internal_hashmap_free(HashmapBase *h);
-static inline void hashmap_free(Hashmap *h) {
- internal_hashmap_free(HASHMAP_BASE(h));
+HashmapBase *internal_hashmap_free(HashmapBase *h);
+static inline Hashmap *hashmap_free(Hashmap *h) {
+ return (void*)internal_hashmap_free(HASHMAP_BASE(h));
}
-static inline void ordered_hashmap_free(OrderedHashmap *h) {
- internal_hashmap_free(HASHMAP_BASE(h));
+static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
+ return (void*)internal_hashmap_free(HASHMAP_BASE(h));
}
-void internal_hashmap_free_free(HashmapBase *h);
-static inline void hashmap_free_free(Hashmap *h) {
- internal_hashmap_free_free(HASHMAP_BASE(h));
+HashmapBase *internal_hashmap_free_free(HashmapBase *h);
+static inline Hashmap *hashmap_free_free(Hashmap *h) {
+ return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
}
-static inline void ordered_hashmap_free_free(OrderedHashmap *h) {
- internal_hashmap_free_free(HASHMAP_BASE(h));
+static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
+ return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
}
-void hashmap_free_free_free(Hashmap *h);
-static inline void ordered_hashmap_free_free_free(OrderedHashmap *h) {
- hashmap_free_free_free(PLAIN_HASHMAP(h));
+Hashmap *hashmap_free_free_free(Hashmap *h);
+static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
+ return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
}
HashmapBase *internal_hashmap_copy(HashmapBase *h);
diff --git a/src/shared/hostname-util.c b/src/shared/hostname-util.c
new file mode 100644
index 0000000000..e336f269fa
--- /dev/null
+++ b/src/shared/hostname-util.c
@@ -0,0 +1,193 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/utsname.h>
+#include <ctype.h>
+
+#include "util.h"
+#include "hostname-util.h"
+
+bool hostname_is_set(void) {
+ struct utsname u;
+
+ assert_se(uname(&u) >= 0);
+
+ if (isempty(u.nodename))
+ return false;
+
+ /* This is the built-in kernel default host name */
+ if (streq(u.nodename, "(none)"))
+ return false;
+
+ return true;
+}
+
+char* gethostname_malloc(void) {
+ struct utsname u;
+
+ assert_se(uname(&u) >= 0);
+
+ if (isempty(u.nodename) || streq(u.nodename, "(none)"))
+ return strdup(u.sysname);
+
+ return strdup(u.nodename);
+}
+
+static bool hostname_valid_char(char c) {
+ return
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ c == '-' ||
+ c == '_' ||
+ c == '.';
+}
+
+bool hostname_is_valid(const char *s) {
+ const char *p;
+ bool dot;
+
+ if (isempty(s))
+ return false;
+
+ /* Doesn't accept empty hostnames, hostnames with trailing or
+ * leading dots, and hostnames with multiple dots in a
+ * sequence. Also ensures that the length stays below
+ * HOST_NAME_MAX. */
+
+ for (p = s, dot = true; *p; p++) {
+ if (*p == '.') {
+ if (dot)
+ return false;
+
+ dot = true;
+ } else {
+ if (!hostname_valid_char(*p))
+ return false;
+
+ dot = false;
+ }
+ }
+
+ if (dot)
+ return false;
+
+ if (p-s > HOST_NAME_MAX)
+ return false;
+
+ return true;
+}
+
+char* hostname_cleanup(char *s, bool lowercase) {
+ char *p, *d;
+ bool dot;
+
+ assert(s);
+
+ for (p = s, d = s, dot = true; *p; p++) {
+ if (*p == '.') {
+ if (dot)
+ continue;
+
+ *(d++) = '.';
+ dot = true;
+ } else if (hostname_valid_char(*p)) {
+ *(d++) = lowercase ? tolower(*p) : *p;
+ dot = false;
+ }
+
+ }
+
+ if (dot && d > s)
+ d[-1] = 0;
+ else
+ *d = 0;
+
+ strshorten(s, HOST_NAME_MAX);
+
+ return s;
+}
+
+bool is_localhost(const char *hostname) {
+ assert(hostname);
+
+ /* This tries to identify local host and domain names
+ * described in RFC6761 plus the redhatism of .localdomain */
+
+ return streq(hostname, "localhost") ||
+ streq(hostname, "localhost.") ||
+ streq(hostname, "localdomain.") ||
+ streq(hostname, "localdomain") ||
+ endswith(hostname, ".localhost") ||
+ endswith(hostname, ".localhost.") ||
+ endswith(hostname, ".localdomain") ||
+ endswith(hostname, ".localdomain.");
+}
+
+int sethostname_idempotent(const char *s) {
+ char buf[HOST_NAME_MAX + 1] = {};
+
+ assert(s);
+
+ if (gethostname(buf, sizeof(buf)) < 0)
+ return -errno;
+
+ if (streq(buf, s))
+ return 0;
+
+ if (sethostname(s, strlen(s)) < 0)
+ return -errno;
+
+ return 1;
+}
+
+int read_hostname_config(const char *path, char **hostname) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char l[LINE_MAX];
+ char *name = NULL;
+
+ assert(path);
+ assert(hostname);
+
+ f = fopen(path, "re");
+ if (!f)
+ return -errno;
+
+ /* may have comments, ignore them */
+ FOREACH_LINE(l, f, return -errno) {
+ truncate_nl(l);
+ if (l[0] != '\0' && l[0] != '#') {
+ /* found line with value */
+ name = hostname_cleanup(l, false);
+ name = strdup(name);
+ if (!name)
+ return -ENOMEM;
+ break;
+ }
+ }
+
+ if (!name)
+ /* no non-empty line found */
+ return -ENOENT;
+
+ *hostname = name;
+ return 0;
+}
diff --git a/src/shared/hostname-util.h b/src/shared/hostname-util.h
new file mode 100644
index 0000000000..0c4763cf5a
--- /dev/null
+++ b/src/shared/hostname-util.h
@@ -0,0 +1,39 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2015 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "macro.h"
+
+bool hostname_is_set(void);
+
+char* gethostname_malloc(void);
+
+bool hostname_is_valid(const char *s) _pure_;
+char* hostname_cleanup(char *s, bool lowercase);
+
+bool is_localhost(const char *hostname);
+
+int sethostname_idempotent(const char *s);
+
+int read_hostname_config(const char *path, char **hostname);
diff --git a/src/shared/import-util.c b/src/shared/import-util.c
index 660d92ac5d..001a8a37e8 100644
--- a/src/shared/import-util.c
+++ b/src/shared/import-util.c
@@ -150,6 +150,27 @@ int raw_strip_suffixes(const char *p, char **ret) {
return 0;
}
+bool dkr_digest_is_valid(const char *digest) {
+ /* 7 chars for prefix, 64 chars for the digest itself */
+ if (strlen(digest) != 71)
+ return false;
+
+ return startswith(digest, "sha256:") && in_charset(digest + 7, "0123456789abcdef");
+}
+
+bool dkr_ref_is_valid(const char *ref) {
+ const char *colon;
+
+ if (isempty(ref))
+ return false;
+
+ colon = strchr(ref, ':');
+ if (!colon)
+ return filename_is_valid(ref);
+
+ return dkr_digest_is_valid(ref);
+}
+
bool dkr_name_is_valid(const char *name) {
const char *slash, *p;
diff --git a/src/shared/import-util.h b/src/shared/import-util.h
index ff155b0ff2..7bf7d4ca40 100644
--- a/src/shared/import-util.h
+++ b/src/shared/import-util.h
@@ -44,4 +44,6 @@ int raw_strip_suffixes(const char *name, char **ret);
bool dkr_name_is_valid(const char *name);
bool dkr_id_is_valid(const char *id);
+bool dkr_ref_is_valid(const char *ref);
+bool dkr_digest_is_valid(const char *digest);
#define dkr_tag_is_valid(tag) filename_is_valid(tag)
diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c
index d853f17772..cbe984d2fb 100644
--- a/src/shared/install-printf.c
+++ b/src/shared/install-printf.c
@@ -19,44 +19,32 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <stdlib.h>
#include "specifier.h"
#include "unit-name.h"
#include "util.h"
#include "install-printf.h"
+#include "formats-util.h"
static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) {
- InstallInfo *i = userdata;
- char *n;
+ UnitFileInstallInfo *i = userdata;
assert(i);
- n = unit_name_to_prefix_and_instance(i->name);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 0;
+ return unit_name_to_prefix_and_instance(i->name, ret);
}
static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) {
- InstallInfo *i = userdata;
- char *n;
+ UnitFileInstallInfo *i = userdata;
assert(i);
- n = unit_name_to_prefix(i->name);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 0;
+ return unit_name_to_prefix(i->name, ret);
}
static int specifier_instance(char specifier, void *data, void *userdata, char **ret) {
- InstallInfo *i = userdata;
+ UnitFileInstallInfo *i = userdata;
char *instance;
int r;
@@ -77,7 +65,7 @@ static int specifier_instance(char specifier, void *data, void *userdata, char *
}
static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) {
- InstallInfo *i = userdata;
+ UnitFileInstallInfo *i = userdata;
const char *username;
_cleanup_free_ char *tmp = NULL;
char *printed = NULL;
@@ -114,7 +102,7 @@ static int specifier_user_name(char specifier, void *data, void *userdata, char
}
-int install_full_printf(InstallInfo *i, const char *format, char **ret) {
+int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) {
/* This is similar to unit_full_printf() but does not support
* anything path-related.
diff --git a/src/shared/install-printf.h b/src/shared/install-printf.h
index 6ffa488b1b..6550337824 100644
--- a/src/shared/install-printf.h
+++ b/src/shared/install-printf.h
@@ -22,4 +22,5 @@
#pragma once
#include "install.h"
-int install_full_printf(InstallInfo *i, const char *format, char **ret);
+
+int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret);
diff --git a/src/shared/install.c b/src/shared/install.c
index 65f1c245c6..6172c42d69 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -36,7 +36,6 @@
#include "install.h"
#include "conf-parser.h"
#include "conf-files.h"
-#include "specifier.h"
#include "install-printf.h"
#include "special.h"
@@ -113,51 +112,6 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d
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;
-
- path_kill_slashes(c[i].path);
-
- if (source) {
- c[i].source = strdup(source);
- if (!c[i].source) {
- free(c[i].path);
- return -ENOMEM;
- }
-
- path_kill_slashes(c[i].path);
- } else
- c[i].source = NULL;
-
- *n_changes = i+1;
- return 0;
-}
-
static int mark_symlink_for_removal(
Set **remove_symlinks_to,
const char *p) {
@@ -259,10 +213,10 @@ static int remove_marked_symlinks_fd(
int q;
bool found;
- if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
+ if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
- if (unit_name_is_instance(de->d_name) &&
+ if (unit_name_is_valid(de->d_name, UNIT_NAME_INSTANCE) &&
instance_whitelist &&
!strv_contains(instance_whitelist, de->d_name)) {
@@ -273,9 +227,9 @@ static int remove_marked_symlinks_fd(
* the template of it might be
* listed. */
- w = unit_name_template(de->d_name);
- if (!w)
- return -ENOMEM;
+ r = unit_name_template(de->d_name, &w);
+ if (r < 0)
+ return r;
if (!strv_contains(instance_whitelist, w))
continue;
@@ -310,7 +264,7 @@ static int remove_marked_symlinks_fd(
path_kill_slashes(p);
rmdir_parents(p, config_path);
- add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
if (!set_get(remove_symlinks_to, p)) {
@@ -515,7 +469,7 @@ static int find_symlinks_in_scope(
UnitFileState *state) {
int r;
- _cleanup_free_ char *path = NULL;
+ _cleanup_free_ char *normal_path = NULL, *runtime_path = NULL;
bool same_name_link_runtime = false, same_name_link = false;
assert(scope >= 0);
@@ -523,11 +477,11 @@ static int find_symlinks_in_scope(
assert(name);
/* First look in runtime config path */
- r = get_config_path(scope, true, root_dir, &path);
+ r = get_config_path(scope, true, root_dir, &normal_path);
if (r < 0)
return r;
- r = find_symlinks(name, path, &same_name_link_runtime);
+ r = find_symlinks(name, normal_path, &same_name_link_runtime);
if (r < 0)
return r;
else if (r > 0) {
@@ -536,11 +490,11 @@ static int find_symlinks_in_scope(
}
/* Then look in the normal config path */
- r = get_config_path(scope, false, root_dir, &path);
+ r = get_config_path(scope, false, root_dir, &runtime_path);
if (r < 0)
return r;
- r = find_symlinks(name, path, &same_name_link);
+ r = find_symlinks(name, runtime_path, &same_name_link);
if (r < 0)
return r;
else if (r > 0) {
@@ -584,7 +538,7 @@ int unit_file_mask(
STRV_FOREACH(i, files) {
_cleanup_free_ char *path = NULL;
- if (!unit_name_is_valid(*i, TEMPLATE_VALID)) {
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
if (r == 0)
r = -EINVAL;
continue;
@@ -597,7 +551,7 @@ int unit_file_mask(
}
if (symlink("/dev/null", path) >= 0) {
- add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
continue;
}
@@ -608,8 +562,8 @@ int unit_file_mask(
if (force) {
if (symlink_atomic("/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");
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
continue;
}
}
@@ -647,7 +601,7 @@ int unit_file_unmask(
STRV_FOREACH(i, files) {
_cleanup_free_ char *path = NULL;
- if (!unit_name_is_valid(*i, TEMPLATE_VALID)) {
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
if (r == 0)
r = -EINVAL;
continue;
@@ -665,7 +619,7 @@ int unit_file_unmask(
q = -errno;
else {
q = mark_symlink_for_removal(&remove_symlinks_to, path);
- add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
}
}
@@ -718,7 +672,7 @@ int unit_file_link(
fn = basename(*i);
if (!path_is_absolute(*i) ||
- !unit_name_is_valid(fn, TEMPLATE_VALID)) {
+ !unit_name_is_valid(fn, UNIT_NAME_ANY)) {
if (r == 0)
r = -EINVAL;
continue;
@@ -747,7 +701,7 @@ int unit_file_link(
return -ENOMEM;
if (symlink(*i, path) >= 0) {
- add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
continue;
}
@@ -766,8 +720,8 @@ int unit_file_link(
if (force) {
if (symlink_atomic(*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);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
continue;
}
}
@@ -794,6 +748,51 @@ void unit_file_list_free(Hashmap *h) {
hashmap_free(h);
}
+int unit_file_changes_add(
+ 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;
+
+ path_kill_slashes(c[i].path);
+
+ if (source) {
+ c[i].source = strdup(source);
+ if (!c[i].source) {
+ free(c[i].path);
+ return -ENOMEM;
+ }
+
+ path_kill_slashes(c[i].path);
+ } else
+ c[i].source = NULL;
+
+ *n_changes = i+1;
+ return 0;
+}
+
void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
unsigned i;
@@ -810,7 +809,7 @@ void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
free(changes);
}
-static void install_info_free(InstallInfo *i) {
+static void install_info_free(UnitFileInstallInfo *i) {
assert(i);
free(i->name);
@@ -824,7 +823,7 @@ static void install_info_free(InstallInfo *i) {
}
static void install_info_hashmap_free(OrderedHashmap *m) {
- InstallInfo *i;
+ UnitFileInstallInfo *i;
if (!m)
return;
@@ -848,7 +847,7 @@ static int install_info_add(
InstallContext *c,
const char *name,
const char *path) {
- InstallInfo *i = NULL;
+ UnitFileInstallInfo *i = NULL;
int r;
assert(c);
@@ -857,7 +856,7 @@ static int install_info_add(
if (!name)
name = basename(path);
- if (!unit_name_is_valid(name, TEMPLATE_VALID))
+ if (!unit_name_is_valid(name, UNIT_NAME_ANY))
return -EINVAL;
if (ordered_hashmap_get(c->have_installed, name) ||
@@ -868,7 +867,7 @@ static int install_info_add(
if (r < 0)
return r;
- i = new0(InstallInfo, 1);
+ i = new0(UnitFileInstallInfo, 1);
if (!i)
return -ENOMEM;
@@ -927,7 +926,7 @@ static int config_parse_also(
size_t l;
const char *word, *state;
InstallContext *c = data;
- InstallInfo *i = userdata;
+ UnitFileInstallInfo *i = userdata;
assert(filename);
assert(lvalue);
@@ -968,7 +967,7 @@ static int config_parse_user(
void *data,
void *userdata) {
- InstallInfo *i = data;
+ UnitFileInstallInfo *i = data;
char *printed;
int r;
@@ -998,7 +997,7 @@ static int config_parse_default_instance(
void *data,
void *userdata) {
- InstallInfo *i = data;
+ UnitFileInstallInfo *i = data;
char *printed;
int r;
@@ -1023,7 +1022,7 @@ static int config_parse_default_instance(
static int unit_file_load(
InstallContext *c,
- InstallInfo *info,
+ UnitFileInstallInfo *info,
const char *path,
const char *root_dir,
bool allow_symlink,
@@ -1083,8 +1082,8 @@ static int unit_file_load(
static int unit_file_search(
InstallContext *c,
- InstallInfo *info,
- LookupPaths *paths,
+ UnitFileInstallInfo *info,
+ const LookupPaths *paths,
const char *root_dir,
bool allow_symlink,
bool load,
@@ -1119,7 +1118,7 @@ static int unit_file_search(
return r;
}
- if (unit_name_is_instance(info->name)) {
+ if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
/* Unit file doesn't exist, however instance
* enablement was requested. We will check if it is
@@ -1127,9 +1126,9 @@ static int unit_file_search(
_cleanup_free_ char *template = NULL;
- template = unit_name_template(info->name);
- if (!template)
- return -ENOMEM;
+ r = unit_name_template(info->name, &template);
+ if (r < 0)
+ return r;
STRV_FOREACH(p, paths->unit_path) {
_cleanup_free_ char *path = NULL;
@@ -1153,14 +1152,14 @@ static int unit_file_search(
}
static int unit_file_can_install(
- LookupPaths *paths,
+ const LookupPaths *paths,
const char *root_dir,
const char *name,
bool allow_symlink,
bool *also) {
_cleanup_(install_context_done) InstallContext c = {};
- InstallInfo *i;
+ UnitFileInstallInfo *i;
int r;
assert(paths);
@@ -1199,7 +1198,7 @@ static int create_symlink(
mkdir_parents_label(new_path, 0755);
if (symlink(old_path, new_path) >= 0) {
- add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
return 0;
}
@@ -1220,14 +1219,14 @@ static int create_symlink(
if (r < 0)
return r;
- 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);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
return 0;
}
static int install_info_symlink_alias(
- InstallInfo *i,
+ UnitFileInstallInfo *i,
const char *config_path,
bool force,
UnitFileChange **changes,
@@ -1259,7 +1258,7 @@ static int install_info_symlink_alias(
}
static int install_info_symlink_wants(
- InstallInfo *i,
+ UnitFileInstallInfo *i,
const char *config_path,
char **list,
const char *suffix,
@@ -1275,7 +1274,7 @@ static int install_info_symlink_wants(
assert(i);
assert(config_path);
- if (unit_name_is_template(i->name)) {
+ if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
/* Don't install any symlink if there's no default
* instance configured */
@@ -1283,9 +1282,9 @@ static int install_info_symlink_wants(
if (!i->default_instance)
return 0;
- buf = unit_name_replace_instance(i->name, i->default_instance);
- if (!buf)
- return -ENOMEM;
+ r = unit_name_replace_instance(i->name, i->default_instance, &buf);
+ if (r < 0)
+ return r;
n = buf;
} else
@@ -1298,7 +1297,7 @@ static int install_info_symlink_wants(
if (q < 0)
return q;
- if (!unit_name_is_valid(dst, TEMPLATE_VALID)) {
+ if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) {
r = -EINVAL;
continue;
}
@@ -1316,8 +1315,8 @@ static int install_info_symlink_wants(
}
static int install_info_symlink_link(
- InstallInfo *i,
- LookupPaths *paths,
+ UnitFileInstallInfo *i,
+ const LookupPaths *paths,
const char *config_path,
const char *root_dir,
bool force,
@@ -1344,8 +1343,8 @@ static int install_info_symlink_link(
}
static int install_info_apply(
- InstallInfo *i,
- LookupPaths *paths,
+ UnitFileInstallInfo *i,
+ const LookupPaths *paths,
const char *config_path,
const char *root_dir,
bool force,
@@ -1377,14 +1376,14 @@ static int install_info_apply(
static int install_context_apply(
InstallContext *c,
- LookupPaths *paths,
+ const LookupPaths *paths,
const char *config_path,
const char *root_dir,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
- InstallInfo *i;
+ UnitFileInstallInfo *i;
int r, q;
assert(c);
@@ -1424,12 +1423,12 @@ static int install_context_apply(
static int install_context_mark_for_removal(
InstallContext *c,
- LookupPaths *paths,
+ const LookupPaths *paths,
Set **remove_symlinks_to,
const char *config_path,
const char *root_dir) {
- InstallInfo *i;
+ UnitFileInstallInfo *i;
int r, q;
assert(c);
@@ -1463,13 +1462,13 @@ static int install_context_mark_for_removal(
} else if (r >= 0)
r += q;
- if (unit_name_is_instance(i->name)) {
+ if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) {
char *unit_file;
if (i->path) {
unit_file = basename(i->path);
- if (unit_name_is_instance(unit_file))
+ if (unit_name_is_valid(unit_file, UNIT_NAME_INSTANCE))
/* unit file named as instance exists, thus all symlinks
* pointing to it will be removed */
q = mark_symlink_for_removal(remove_symlinks_to, i->name);
@@ -1481,9 +1480,9 @@ static int install_context_mark_for_removal(
/* If i->path is not set, it means that we didn't actually find
* the unit file. But we can still remove symlinks to the
* nonexistent template. */
- unit_file = unit_name_template(i->name);
- if (!unit_file)
- return log_oom();
+ r = unit_name_template(i->name, &unit_file);
+ if (r < 0)
+ return r;
q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
free(unit_file);
@@ -1514,7 +1513,7 @@ int unit_file_add_dependency(
_cleanup_free_ char *config_path = NULL;
char **i;
int r;
- InstallInfo *info;
+ UnitFileInstallInfo *info;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
@@ -1536,7 +1535,7 @@ int unit_file_add_dependency(
if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) {
log_error("Failed to enable unit: Unit %s is masked", *i);
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
r = install_info_add_auto(&c, *i);
@@ -1614,7 +1613,7 @@ int unit_file_enable(
state = unit_file_get_state(scope, root_dir, *i);
if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) {
log_error("Failed to enable unit: Unit %s is masked", *i);
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
r = install_info_add_auto(&c, *i);
@@ -1703,7 +1702,7 @@ int unit_file_set_default(
_cleanup_free_ char *config_path = NULL;
char *path;
int r;
- InstallInfo *i = NULL;
+ UnitFileInstallInfo *i = NULL;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
@@ -1785,39 +1784,28 @@ int unit_file_get_default(
return -ENOENT;
}
-UnitFileState unit_file_get_state(
+UnitFileState unit_file_lookup_state(
UnitFileScope scope,
const char *root_dir,
+ const LookupPaths *paths,
const char *name) {
- _cleanup_lookup_paths_free_ LookupPaths paths = {};
UnitFileState state = _UNIT_FILE_STATE_INVALID;
char **i;
_cleanup_free_ char *path = NULL;
- int r;
-
- assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
- assert(name);
+ int r = 0;
- if (root_dir && scope != UNIT_FILE_SYSTEM)
- return -EINVAL;
+ assert(paths);
- if (!unit_name_is_valid(name, TEMPLATE_VALID))
+ if (!unit_name_is_valid(name, UNIT_NAME_ANY))
return -EINVAL;
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
- if (r < 0)
- return r;
-
- STRV_FOREACH(i, paths.unit_path) {
+ STRV_FOREACH(i, paths->unit_path) {
struct stat st;
char *partial;
bool also = false;
free(path);
- path = NULL;
-
path = path_join(root_dir, *i, name);
if (!path)
return -ENOMEM;
@@ -1836,7 +1824,7 @@ UnitFileState unit_file_get_state(
if (errno != ENOENT)
return r;
- if (!unit_name_is_instance(name))
+ if (!unit_name_is_valid(name, UNIT_NAME_INSTANCE))
continue;
} else {
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
@@ -1846,8 +1834,7 @@ UnitFileState unit_file_get_state(
if (r < 0 && r != -ENOENT)
return r;
else if (r > 0) {
- state = path_startswith(*i, "/run") ?
- UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
+ state = path_startswith(*i, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
return state;
}
}
@@ -1858,7 +1845,7 @@ UnitFileState unit_file_get_state(
else if (r > 0)
return state;
- r = unit_file_can_install(&paths, root_dir, partial, true, &also);
+ r = unit_file_can_install(paths, root_dir, partial, true, &also);
if (r < 0 && errno != ENOENT)
return r;
else if (r > 0)
@@ -1873,6 +1860,28 @@ UnitFileState unit_file_get_state(
return r < 0 ? r : state;
}
+UnitFileState unit_file_get_state(
+ UnitFileScope scope,
+ const char *root_dir,
+ const char *name) {
+
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
+ int r;
+
+ assert(scope >= 0);
+ assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(name);
+
+ if (root_dir && scope != UNIT_FILE_SYSTEM)
+ return -EINVAL;
+
+ r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+ if (r < 0)
+ return r;
+
+ return unit_file_lookup_state(scope, root_dir, &paths, name);
+}
+
int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
_cleanup_strv_free_ char **files = NULL;
char **p;
@@ -1985,7 +1994,7 @@ int unit_file_preset(
STRV_FOREACH(i, files) {
- if (!unit_name_is_valid(*i, TEMPLATE_VALID))
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
return -EINVAL;
r = unit_file_query_preset(scope, root_dir, *i);
@@ -2081,7 +2090,7 @@ int unit_file_preset_all(
if (hidden_file(de->d_name))
continue;
- if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
+ if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
dirent_ensure_type(d, de);
@@ -2193,7 +2202,7 @@ int unit_file_get_list(
if (hidden_file(de->d_name))
continue;
- if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
+ if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
if (hashmap_get(h, de->d_name))
diff --git a/src/shared/install.h b/src/shared/install.h
index 357be0f92d..a9d77dd91b 100644
--- a/src/shared/install.h
+++ b/src/shared/install.h
@@ -21,18 +21,27 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+typedef enum UnitFileScope UnitFileScope;
+typedef enum UnitFileState UnitFileState;
+typedef enum UnitFilePresetMode UnitFilePresetMode;
+typedef enum UnitFileChangeType UnitFileChangeType;
+typedef struct UnitFileChange UnitFileChange;
+typedef struct UnitFileList UnitFileList;
+typedef struct UnitFileInstallInfo UnitFileInstallInfo;
+
#include "hashmap.h"
#include "unit-name.h"
+#include "path-lookup.h"
-typedef enum UnitFileScope {
+enum UnitFileScope {
UNIT_FILE_SYSTEM,
UNIT_FILE_GLOBAL,
UNIT_FILE_USER,
_UNIT_FILE_SCOPE_MAX,
_UNIT_FILE_SCOPE_INVALID = -1
-} UnitFileScope;
+};
-typedef enum UnitFileState {
+enum UnitFileState {
UNIT_FILE_ENABLED,
UNIT_FILE_ENABLED_RUNTIME,
UNIT_FILE_LINKED,
@@ -45,35 +54,35 @@ typedef enum UnitFileState {
UNIT_FILE_INVALID,
_UNIT_FILE_STATE_MAX,
_UNIT_FILE_STATE_INVALID = -1
-} UnitFileState;
+};
-typedef enum UnitFilePresetMode {
+enum UnitFilePresetMode {
UNIT_FILE_PRESET_FULL,
UNIT_FILE_PRESET_ENABLE_ONLY,
UNIT_FILE_PRESET_DISABLE_ONLY,
_UNIT_FILE_PRESET_MAX,
_UNIT_FILE_PRESET_INVALID = -1
-} UnitFilePresetMode;
+};
-typedef enum UnitFileChangeType {
+enum UnitFileChangeType {
UNIT_FILE_SYMLINK,
UNIT_FILE_UNLINK,
_UNIT_FILE_CHANGE_TYPE_MAX,
_UNIT_FILE_CHANGE_TYPE_INVALID = -1
-} UnitFileChangeType;
+};
-typedef struct UnitFileChange {
+struct UnitFileChange {
UnitFileChangeType type;
char *path;
char *source;
-} UnitFileChange;
+};
-typedef struct UnitFileList {
+struct UnitFileList {
char *path;
UnitFileState state;
-} UnitFileList;
+};
-typedef struct {
+struct UnitFileInstallInfo {
char *name;
char *path;
char *user;
@@ -84,7 +93,7 @@ typedef struct {
char **also;
char *default_instance;
-} InstallInfo;
+};
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);
@@ -98,11 +107,20 @@ int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char
int unit_file_get_default(UnitFileScope scope, const char *root_dir, char **name);
int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes);
-UnitFileState unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename);
+UnitFileState unit_file_lookup_state(
+ UnitFileScope scope,
+ const char *root_dir,
+ const LookupPaths *paths,
+ const char *name);
+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);
+int unit_file_changes_add(UnitFileChange **changes, unsigned *n_changes, UnitFileChangeType type, const char *path, const char *source);
void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes);
int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name);
diff --git a/src/shared/json.c b/src/shared/json.c
index bb3d26f0e5..be40a0d203 100644
--- a/src/shared/json.c
+++ b/src/shared/json.c
@@ -21,18 +21,178 @@
#include <sys/types.h>
#include <math.h>
-
#include "macro.h"
-#include "log.h"
-#include "util.h"
#include "utf8.h"
#include "json.h"
-enum {
- STATE_NULL,
- STATE_VALUE,
- STATE_VALUE_POST,
-};
+int json_variant_new(JsonVariant **ret, JsonVariantType type) {
+ JsonVariant *v;
+
+ v = new0(JsonVariant, 1);
+ if (!v)
+ return -ENOMEM;
+ v->type = type;
+ *ret = v;
+ return 0;
+}
+
+static int json_variant_deep_copy(JsonVariant *ret, JsonVariant *variant) {
+ int r;
+
+ assert(ret);
+ assert(variant);
+
+ ret->type = variant->type;
+ ret->size = variant->size;
+
+ if (variant->type == JSON_VARIANT_STRING) {
+ ret->string = memdup(variant->string, variant->size+1);
+ if (!ret->string)
+ return -ENOMEM;
+ } else if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT) {
+ size_t i;
+
+ ret->objects = new0(JsonVariant, variant->size);
+ if (!ret->objects)
+ return -ENOMEM;
+
+ for (i = 0; i < variant->size; ++i) {
+ r = json_variant_deep_copy(&ret->objects[i], &variant->objects[i]);
+ if (r < 0)
+ return r;
+ }
+ } else
+ ret->value = variant->value;
+
+ return 0;
+}
+
+static JsonVariant *json_object_unref(JsonVariant *variant);
+
+static JsonVariant *json_variant_unref_inner(JsonVariant *variant) {
+ if (!variant)
+ return NULL;
+
+ if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
+ return json_object_unref(variant);
+ else if (variant->type == JSON_VARIANT_STRING)
+ free(variant->string);
+
+ return NULL;
+}
+
+static JsonVariant *json_raw_unref(JsonVariant *variant, size_t size) {
+ if (!variant)
+ return NULL;
+
+ for (size_t i = 0; i < size; ++i)
+ json_variant_unref_inner(&variant[i]);
+
+ free(variant);
+ return NULL;
+}
+
+static JsonVariant *json_object_unref(JsonVariant *variant) {
+ size_t i;
+
+ assert(variant);
+
+ if (!variant->objects)
+ return NULL;
+
+ for (i = 0; i < variant->size; ++i)
+ json_variant_unref_inner(&variant->objects[i]);
+
+ free(variant->objects);
+ return NULL;
+}
+
+static JsonVariant **json_variant_array_unref(JsonVariant **variant) {
+ size_t i = 0;
+ JsonVariant *p = NULL;
+
+ if (!variant)
+ return NULL;
+
+ while((p = (variant[i++])) != NULL) {
+ if (p->type == JSON_VARIANT_STRING)
+ free(p->string);
+ free(p);
+ }
+
+ free(variant);
+
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant **, json_variant_array_unref);
+
+JsonVariant *json_variant_unref(JsonVariant *variant) {
+ if (!variant)
+ return NULL;
+
+ if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
+ json_object_unref(variant);
+ else if (variant->type == JSON_VARIANT_STRING)
+ free(variant->string);
+
+ free(variant);
+
+ return NULL;
+}
+
+char *json_variant_string(JsonVariant *variant){
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_STRING);
+
+ return variant->string;
+}
+
+bool json_variant_bool(JsonVariant *variant) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_BOOLEAN);
+
+ return variant->value.boolean;
+}
+
+intmax_t json_variant_integer(JsonVariant *variant) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_INTEGER);
+
+ return variant->value.integer;
+}
+
+double json_variant_real(JsonVariant *variant) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_REAL);
+
+ return variant->value.real;
+}
+
+JsonVariant *json_variant_element(JsonVariant *variant, unsigned index) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT);
+ assert(index < variant->size);
+ assert(variant->objects);
+
+ return &variant->objects[index];
+}
+
+JsonVariant *json_variant_value(JsonVariant *variant, const char *key) {
+ size_t i;
+
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_OBJECT);
+ assert(variant->objects);
+
+ for (i = 0; i < variant->size; i += 2) {
+ JsonVariant *p = &variant->objects[i];
+ if (p->type == JSON_VARIANT_STRING && streq(key, p->string))
+ return &variant->objects[i + 1];
+ }
+
+ return NULL;
+}
static void inc_lines(unsigned *line, const char *s, size_t n) {
const char *p = s;
@@ -286,9 +446,6 @@ static int json_parse_number(const char **p, union json_value *ret) {
} while (strchr("0123456789", *c) && *c != 0);
}
- if (*c != 0)
- return -EINVAL;
-
*p = c;
if (is_double) {
@@ -311,6 +468,12 @@ int json_tokenize(
int t;
int r;
+ enum {
+ STATE_NULL,
+ STATE_VALUE,
+ STATE_VALUE_POST,
+ };
+
assert(p);
assert(*p);
assert(ret_string);
@@ -444,3 +607,260 @@ int json_tokenize(
}
}
+
+static bool json_is_value(JsonVariant *var) {
+ assert(var);
+
+ return var->type != JSON_VARIANT_CONTROL;
+}
+
+static int json_scoped_parse(JsonVariant **tokens, size_t *i, size_t n, JsonVariant *scope) {
+ bool arr = scope->type == JSON_VARIANT_ARRAY;
+ int terminator = arr ? JSON_ARRAY_CLOSE : JSON_OBJECT_CLOSE;
+ size_t allocated = 0, size = 0;
+ JsonVariant *key = NULL, *value = NULL, *var = NULL, *items = NULL;
+ enum {
+ STATE_KEY,
+ STATE_COLON,
+ STATE_COMMA,
+ STATE_VALUE
+ } state = arr ? STATE_VALUE : STATE_KEY;
+
+ assert(tokens);
+ assert(i);
+ assert(scope);
+
+ while((var = *i < n ? tokens[(*i)++] : NULL) != NULL) {
+ bool stopper;
+ int r;
+
+ stopper = !json_is_value(var) && var->value.integer == terminator;
+
+ if (stopper) {
+ if (state != STATE_COMMA && size > 0)
+ goto error;
+
+ goto out;
+ }
+
+ if (state == STATE_KEY) {
+ if (var->type != JSON_VARIANT_STRING)
+ goto error;
+ else {
+ key = var;
+ state = STATE_COLON;
+ }
+ }
+ else if (state == STATE_COLON) {
+ if (key == NULL)
+ goto error;
+
+ if (json_is_value(var))
+ goto error;
+
+ if (var->value.integer != JSON_COLON)
+ goto error;
+
+ state = STATE_VALUE;
+ }
+ else if (state == STATE_VALUE) {
+ _cleanup_json_variant_unref_ JsonVariant *v = NULL;
+ size_t toadd = arr ? 1 : 2;
+
+ if (!json_is_value(var)) {
+ int type = (var->value.integer == JSON_ARRAY_OPEN) ? JSON_VARIANT_ARRAY : JSON_VARIANT_OBJECT;
+
+ r = json_variant_new(&v, type);
+ if (r < 0)
+ goto error;
+
+ r = json_scoped_parse(tokens, i, n, v);
+ if (r < 0)
+ goto error;
+
+ value = v;
+ }
+ else
+ value = var;
+
+ if(!GREEDY_REALLOC(items, allocated, size + toadd))
+ goto error;
+
+ if (arr) {
+ r = json_variant_deep_copy(&items[size], value);
+ if (r < 0)
+ goto error;
+ } else {
+ r = json_variant_deep_copy(&items[size], key);
+ if (r < 0)
+ goto error;
+
+ r = json_variant_deep_copy(&items[size+1], value);
+ if (r < 0)
+ goto error;
+ }
+
+ size += toadd;
+ state = STATE_COMMA;
+ }
+ else if (state == STATE_COMMA) {
+ if (json_is_value(var))
+ goto error;
+
+ if (var->value.integer != JSON_COMMA)
+ goto error;
+
+ key = NULL;
+ value = NULL;
+
+ state = arr ? STATE_VALUE : STATE_KEY;
+ }
+ }
+
+error:
+ json_raw_unref(items, size);
+ return -EBADMSG;
+
+out:
+ scope->size = size;
+ scope->objects = items;
+
+ return scope->type;
+}
+
+static int json_parse_tokens(JsonVariant **tokens, size_t ntokens, JsonVariant **rv) {
+ size_t it = 0;
+ int r;
+ JsonVariant *e;
+ _cleanup_json_variant_unref_ JsonVariant *p = NULL;
+
+ assert(tokens);
+ assert(ntokens);
+
+ e = tokens[it++];
+ r = json_variant_new(&p, JSON_VARIANT_OBJECT);
+ if (r < 0)
+ return r;
+
+ if (e->type != JSON_VARIANT_CONTROL && e->value.integer != JSON_OBJECT_OPEN)
+ return -EBADMSG;
+
+ r = json_scoped_parse(tokens, &it, ntokens, p);
+ if (r < 0)
+ return r;
+
+ *rv = p;
+ p = NULL;
+
+ return 0;
+}
+
+static int json_tokens(const char *string, size_t size, JsonVariant ***tokens, size_t *n) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_(json_variant_array_unrefp) JsonVariant **items = NULL;
+ union json_value v = {};
+ void *json_state = NULL;
+ const char *p;
+ int t, r;
+ size_t allocated = 0, s = 0;
+
+ assert(string);
+ assert(n);
+
+ if (size <= 0)
+ return -EBADMSG;
+
+ buf = strndup(string, size);
+ if (!buf)
+ return -ENOMEM;
+
+ p = buf;
+ for (;;) {
+ _cleanup_json_variant_unref_ JsonVariant *var = NULL;
+ _cleanup_free_ char *rstr = NULL;
+
+ t = json_tokenize(&p, &rstr, &v, &json_state, NULL);
+
+ if (t < 0)
+ return t;
+ else if (t == JSON_END)
+ break;
+
+ if (t <= JSON_ARRAY_CLOSE) {
+ r = json_variant_new(&var, JSON_VARIANT_CONTROL);
+ if (r < 0)
+ return r;
+ var->value.integer = t;
+ } else {
+ switch (t) {
+ case JSON_STRING:
+ r = json_variant_new(&var, JSON_VARIANT_STRING);
+ if (r < 0)
+ return r;
+ var->size = strlen(rstr);
+ var->string = strdup(rstr);
+ if (!var->string) {
+ return -ENOMEM;
+ }
+ break;
+ case JSON_INTEGER:
+ r = json_variant_new(&var, JSON_VARIANT_INTEGER);
+ if (r < 0)
+ return r;
+ var->value = v;
+ break;
+ case JSON_REAL:
+ r = json_variant_new(&var, JSON_VARIANT_REAL);
+ if (r < 0)
+ return r;
+ var->value = v;
+ break;
+ case JSON_BOOLEAN:
+ r = json_variant_new(&var, JSON_VARIANT_BOOLEAN);
+ if (r < 0)
+ return r;
+ var->value = v;
+ break;
+ case JSON_NULL:
+ r = json_variant_new(&var, JSON_VARIANT_NULL);
+ if (r < 0)
+ return r;
+ break;
+ }
+ }
+
+ if (!GREEDY_REALLOC(items, allocated, s+2))
+ return -ENOMEM;
+
+ items[s++] = var;
+ items[s] = NULL;
+ var = NULL;
+ }
+
+ *n = s;
+ *tokens = items;
+ items = NULL;
+
+ return 0;
+}
+
+int json_parse(const char *string, JsonVariant **rv) {
+ _cleanup_(json_variant_array_unrefp) JsonVariant **s = NULL;
+ JsonVariant *v = NULL;
+ size_t n = 0;
+ int r;
+
+ assert(string);
+ assert(rv);
+
+ r = json_tokens(string, strlen(string), &s, &n);
+ if (r < 0)
+ return r;
+
+ r = json_parse_tokens(s, n, &v);
+ if (r < 0)
+ return r;
+
+ *rv = v;
+ return 0;
+}
diff --git a/src/shared/json.h b/src/shared/json.h
index a8457132e7..e0b4d810b5 100644
--- a/src/shared/json.h
+++ b/src/shared/json.h
@@ -22,7 +22,7 @@
***/
#include <stdbool.h>
-#include <inttypes.h>
+#include "util.h"
enum {
JSON_END,
@@ -39,12 +39,50 @@ enum {
JSON_NULL,
};
+typedef enum {
+ JSON_VARIANT_CONTROL,
+ JSON_VARIANT_STRING,
+ JSON_VARIANT_INTEGER,
+ JSON_VARIANT_BOOLEAN,
+ JSON_VARIANT_REAL,
+ JSON_VARIANT_ARRAY,
+ JSON_VARIANT_OBJECT,
+ JSON_VARIANT_NULL
+} JsonVariantType;
+
union json_value {
bool boolean;
double real;
intmax_t integer;
};
+typedef struct JsonVariant {
+ JsonVariantType type;
+ size_t size;
+ union {
+ char *string;
+ struct JsonVariant *objects;
+ union json_value value;
+ };
+} JsonVariant;
+
+int json_variant_new(JsonVariant **ret, JsonVariantType type);
+JsonVariant *json_variant_unref(JsonVariant *v);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant *, json_variant_unref);
+#define _cleanup_json_variant_unref_ _cleanup_(json_variant_unrefp)
+
+char *json_variant_string(JsonVariant *v);
+bool json_variant_bool(JsonVariant *v);
+intmax_t json_variant_integer(JsonVariant *v);
+double json_variant_real(JsonVariant *v);
+
+JsonVariant *json_variant_element(JsonVariant *v, unsigned index);
+JsonVariant *json_variant_value(JsonVariant *v, const char *key);
+
#define JSON_VALUE_NULL ((union json_value) {})
int json_tokenize(const char **p, char **ret_string, union json_value *ret_value, void **state, unsigned *line);
+
+int json_parse(const char *string, JsonVariant **rv);
+int json_parse_measure(const char *string, size_t *size);
diff --git a/src/shared/lockfile-util.c b/src/shared/lockfile-util.c
new file mode 100644
index 0000000000..05e16d1caa
--- /dev/null
+++ b/src/shared/lockfile-util.c
@@ -0,0 +1,154 @@
+/*-*- 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <sys/file.h>
+
+#include "util.h"
+#include "lockfile-util.h"
+#include "fileio.h"
+
+int make_lock_file(const char *p, int operation, LockFile *ret) {
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ /*
+ * We use UNPOSIX locks if they are available. They have nice
+ * semantics, and are mostly compatible with NFS. However,
+ * they are only available on new kernels. When we detect we
+ * are running on an older kernel, then we fall back to good
+ * old BSD locks. They also have nice semantics, but are
+ * slightly problematic on NFS, where they are upgraded to
+ * POSIX locks, even though locally they are orthogonal to
+ * POSIX locks.
+ */
+
+ t = strdup(p);
+ if (!t)
+ return -ENOMEM;
+
+ for (;;) {
+ struct flock fl = {
+ .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK,
+ .l_whence = SEEK_SET,
+ };
+ struct stat st;
+
+ fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
+ if (fd < 0)
+ return -errno;
+
+ r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl);
+ if (r < 0) {
+
+ /* If the kernel is too old, use good old BSD locks */
+ if (errno == EINVAL)
+ r = flock(fd, operation);
+
+ if (r < 0)
+ return errno == EAGAIN ? -EBUSY : -errno;
+ }
+
+ /* If we acquired the lock, let's check if the file
+ * still exists in the file system. If not, then the
+ * previous exclusive owner removed it and then closed
+ * it. In such a case our acquired lock is worthless,
+ * hence try again. */
+
+ r = fstat(fd, &st);
+ if (r < 0)
+ return -errno;
+ if (st.st_nlink > 0)
+ break;
+
+ fd = safe_close(fd);
+ }
+
+ ret->path = t;
+ ret->fd = fd;
+ ret->operation = operation;
+
+ fd = -1;
+ t = NULL;
+
+ return r;
+}
+
+int make_lock_file_for(const char *p, int operation, LockFile *ret) {
+ const char *fn;
+ char *t;
+
+ assert(p);
+ assert(ret);
+
+ fn = basename(p);
+ if (!filename_is_valid(fn))
+ return -EINVAL;
+
+ t = newa(char, strlen(p) + 2 + 4 + 1);
+ stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck");
+
+ return make_lock_file(t, operation, ret);
+}
+
+void release_lock_file(LockFile *f) {
+ int r;
+
+ if (!f)
+ return;
+
+ if (f->path) {
+
+ /* If we are the exclusive owner we can safely delete
+ * the lock file itself. If we are not the exclusive
+ * owner, we can try becoming it. */
+
+ if (f->fd >= 0 &&
+ (f->operation & ~LOCK_NB) == LOCK_SH) {
+ static const struct flock fl = {
+ .l_type = F_WRLCK,
+ .l_whence = SEEK_SET,
+ };
+
+ r = fcntl(f->fd, F_OFD_SETLK, &fl);
+ if (r < 0 && errno == EINVAL)
+ r = flock(f->fd, LOCK_EX|LOCK_NB);
+
+ if (r >= 0)
+ f->operation = LOCK_EX|LOCK_NB;
+ }
+
+ if ((f->operation & ~LOCK_NB) == LOCK_EX)
+ unlink_noerrno(f->path);
+
+ free(f->path);
+ f->path = NULL;
+ }
+
+ f->fd = safe_close(f->fd);
+ f->operation = 0;
+}
diff --git a/src/shared/lockfile-util.h b/src/shared/lockfile-util.h
new file mode 100644
index 0000000000..38d47094bd
--- /dev/null
+++ b/src/shared/lockfile-util.h
@@ -0,0 +1,39 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "macro.h"
+#include "missing.h"
+
+typedef struct LockFile {
+ char *path;
+ int fd;
+ int operation;
+} LockFile;
+
+int make_lock_file(const char *p, int operation, LockFile *ret);
+int make_lock_file_for(const char *p, int operation, LockFile *ret);
+void release_lock_file(LockFile *f);
+
+#define _cleanup_release_lock_file_ _cleanup_(release_lock_file)
+
+#define LOCK_FILE_INIT { .fd = -1, .path = NULL }
diff --git a/src/shared/log.c b/src/shared/log.c
index 646a1d6389..b96afc4de4 100644
--- a/src/shared/log.c
+++ b/src/shared/log.c
@@ -29,11 +29,16 @@
#include <stddef.h>
#include <printf.h>
+#include "sd-messages.h"
#include "log.h"
#include "util.h"
#include "missing.h"
#include "macro.h"
#include "socket-util.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "signal-util.h"
#define SNDBUF_SIZE (8*1024*1024)
@@ -689,7 +694,8 @@ int log_object_internalv(
va_list ap) {
PROTECT_ERRNO;
- char buffer[LINE_MAX];
+ char *buffer, *b;
+ size_t l;
if (error < 0)
error = -error;
@@ -701,7 +707,21 @@ int log_object_internalv(
if (error != 0)
errno = error;
- vsnprintf(buffer, sizeof(buffer), format, ap);
+ /* Prepend the object name before the message */
+ if (object) {
+ size_t n;
+
+ n = strlen(object);
+ l = n + 2 + LINE_MAX;
+
+ buffer = newa(char, l);
+ b = stpcpy(stpcpy(buffer, object), ": ");
+ } else {
+ l = LINE_MAX;
+ b = buffer = newa(char, l);
+ }
+
+ vsnprintf(b, l, format, ap);
return log_dispatch(level, error, file, line, func, object_field, object, buffer);
}
@@ -1061,3 +1081,58 @@ void log_received_signal(int level, const struct signalfd_siginfo *si) {
void log_set_upgrade_syslog_to_journal(bool b) {
upgrade_syslog_to_journal = b;
}
+
+int log_syntax_internal(
+ const char *unit,
+ int level,
+ const char *config_file,
+ unsigned config_line,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format, ...) {
+
+ PROTECT_ERRNO;
+ char buffer[LINE_MAX];
+ int r;
+ va_list ap;
+
+ if (error < 0)
+ error = -error;
+
+ if (_likely_(LOG_PRI(level) > log_max_level))
+ return -error;
+
+ if (log_target == LOG_TARGET_NULL)
+ return -error;
+
+ if (error != 0)
+ errno = error;
+
+ va_start(ap, format);
+ vsnprintf(buffer, sizeof(buffer), format, ap);
+ va_end(ap);
+
+ if (unit)
+ r = log_struct_internal(
+ level, error,
+ file, line, func,
+ getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit,
+ LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
+ "CONFIG_FILE=%s", config_file,
+ "CONFIG_LINE=%u", config_line,
+ LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer),
+ NULL);
+ else
+ r = log_struct_internal(
+ level, error,
+ file, line, func,
+ LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
+ "CONFIG_FILE=%s", config_file,
+ "CONFIG_LINE=%u", config_line,
+ LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer),
+ NULL);
+
+ return r;
+}
diff --git a/src/shared/log.h b/src/shared/log.h
index 2889e1e77f..569762d083 100644
--- a/src/shared/log.h
+++ b/src/shared/log.h
@@ -23,14 +23,13 @@
#include <stdbool.h>
#include <stdarg.h>
+#include <stdlib.h>
#include <syslog.h>
#include <sys/signalfd.h>
-#include <sys/types.h>
-#include <unistd.h>
#include <errno.h>
-#include "macro.h"
#include "sd-id128.h"
+#include "macro.h"
typedef enum LogTarget{
LOG_TARGET_CONSOLE,
@@ -156,12 +155,12 @@ void log_assert_failed_return(
const char *func);
/* Logging with level */
-#define log_full_errno(level, error, ...) \
- ({ \
- int _l = (level), _e = (error); \
- (log_get_max_level() >= LOG_PRI(_l)) \
- ? log_internal(_l, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
- : -abs(_e); \
+#define log_full_errno(level, error, ...) \
+ ({ \
+ int _level = (level), _e = (error); \
+ (log_get_max_level() >= LOG_PRI(_level)) \
+ ? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
+ : -abs(_e); \
})
#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__)
@@ -205,8 +204,26 @@ LogTarget log_target_from_string(const char *s) _pure_;
/* Helpers to prepare various fields for structured logging */
#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x)
-#define LOG_ERRNO(error) "ERRNO=%i", abs(error)
void log_received_signal(int level, const struct signalfd_siginfo *si);
void log_set_upgrade_syslog_to_journal(bool b);
+
+int log_syntax_internal(
+ const char *unit,
+ int level,
+ const char *config_file,
+ unsigned config_line,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format, ...) _printf_(9, 10);
+
+#define log_syntax(unit, level, config_file, config_line, error, ...) \
+ ({ \
+ int _level = (level), _e = (error); \
+ (log_get_max_level() >= LOG_PRI(_level)) \
+ ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
+ : -abs(_e); \
+ })
diff --git a/src/shared/login-shared.c b/src/shared/login-shared.c
index 054c77503b..64650a9134 100644
--- a/src/shared/login-shared.c
+++ b/src/shared/login-shared.c
@@ -23,7 +23,9 @@
#include "def.h"
bool session_id_valid(const char *id) {
- assert(id);
- return id[0] && id[strspn(id, LETTERS DIGITS)] == '\0';
+ if (isempty(id))
+ return false;
+
+ return id[strspn(id, LETTERS DIGITS)] == '\0';
}
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index c2495056d7..ac5eb16f62 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -20,9 +20,7 @@
***/
#include <time.h>
-#include <assert.h>
#include <errno.h>
-#include <poll.h>
#include <sys/socket.h>
#include <string.h>
#include <fcntl.h>
@@ -32,8 +30,10 @@
#include "util.h"
#include "utf8.h"
#include "hashmap.h"
-#include "fileio.h"
#include "journal-internal.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
/* up to three lines (each up to 100 characters),
or 300 characters, whichever is less */
@@ -272,7 +272,7 @@ static int output_short(
}
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to get journal fields: %m");
if (!message)
return 0;
@@ -408,11 +408,9 @@ static int output_verbose(
r = sd_journal_get_data(j, "_SOURCE_REALTIME_TIMESTAMP", &data, &length);
if (r == -ENOENT)
log_debug("Source realtime timestamp not found");
- else if (r < 0) {
- log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
- "Failed to get source realtime timestamp: %s", strerror(-r));
- return r;
- } else {
+ else if (r < 0)
+ return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get source realtime timestamp: %m");
+ else {
_cleanup_free_ char *value = NULL;
size_t size;
@@ -428,11 +426,8 @@ static int output_verbose(
if (r < 0) {
r = sd_journal_get_realtime_usec(j, &realtime);
- if (r < 0) {
- log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
- "Failed to get realtime timestamp: %s", strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get realtime timestamp: %m");
}
r = sd_journal_get_cursor(j, &cursor);
@@ -682,7 +677,7 @@ static int output_json(
h = hashmap_new(&string_hash_ops);
if (!h)
- return -ENOMEM;
+ return log_oom();
/* First round, iterate through the entry and count how often each field appears */
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
@@ -700,7 +695,7 @@ static int output_json(
n = strndup(data, eq - (const char*) data);
if (!n) {
- r = -ENOMEM;
+ r = log_oom();
goto finish;
}
@@ -709,13 +704,16 @@ static int output_json(
r = hashmap_put(h, n, UINT_TO_PTR(1));
if (r < 0) {
free(n);
+ log_oom();
goto finish;
}
} else {
r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
free(n);
- if (r < 0)
+ if (r < 0) {
+ log_oom();
goto finish;
+ }
}
}
@@ -753,7 +751,7 @@ static int output_json(
n = strndup(data, m);
if (!n) {
- r = -ENOMEM;
+ r = log_oom();
goto finish;
}
@@ -948,11 +946,11 @@ static int show_journal(FILE *f,
/* Seek to end */
r = sd_journal_seek_tail(j);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to seek to tail: %m");
r = sd_journal_previous_skip(j, how_many);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to skip previous: %m");
for (;;) {
for (;;) {
@@ -961,7 +959,7 @@ static int show_journal(FILE *f,
if (need_seek) {
r = sd_journal_next(j);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to iterate through journal: %m");
}
if (r == 0)
@@ -977,7 +975,7 @@ static int show_journal(FILE *f,
if (r == -ESTALE)
continue;
else if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to get journal time: %m");
if (usec < not_before)
continue;
@@ -988,22 +986,22 @@ static int show_journal(FILE *f,
r = output_journal(f, j, mode, n_columns, flags, ellipsized);
if (r < 0)
- goto finish;
+ return r;
}
if (warn_cutoff && line < how_many && not_before > 0) {
sd_id128_t boot_id;
- usec_t cutoff;
+ usec_t cutoff = 0;
/* Check whether the cutoff line is too early */
r = sd_id128_get_boot(&boot_id);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to get boot id: %m");
r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to get journal cutoff time: %m");
if (r > 0 && not_before < cutoff) {
maybe_print_begin_newline(f, &flags);
@@ -1018,12 +1016,11 @@ static int show_journal(FILE *f,
r = sd_journal_wait(j, USEC_INFINITY);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to wait for journal: %m");
}
-finish:
- return r;
+ return 0;
}
int add_matches_for_unit(sd_journal *j, const char *unit) {
@@ -1166,9 +1163,9 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
if (fd < 0)
_exit(EXIT_FAILURE);
- k = loop_read(fd, buf, 36, false);
+ r = loop_read_exact(fd, buf, 36, false);
safe_close(fd);
- if (k != 36)
+ if (r < 0)
_exit(EXIT_FAILURE);
k = send(pair[1], buf, 36, MSG_NOSIGNAL);
@@ -1220,7 +1217,7 @@ int add_match_this_boot(sd_journal *j, const char *machine) {
r = sd_journal_add_conjunction(j);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add conjunction: %m");
return 0;
}
@@ -1250,7 +1247,7 @@ int show_journal_by_unit(
r = sd_journal_open(&j, journal_open_flags);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to open journal: %m");
r = add_match_this_boot(j, NULL);
if (r < 0)
@@ -1261,12 +1258,15 @@ int show_journal_by_unit(
else
r = add_matches_for_user_unit(j, unit, uid);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add unit matches: %m");
if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
_cleanup_free_ char *filter;
filter = journal_make_match_string(j);
+ if (!filter)
+ return log_oom();
+
log_debug("Journal filter: %s", filter);
}
diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h
index 8d9641e8ac..569e1faa55 100644
--- a/src/shared/logs-show.h
+++ b/src/shared/logs-show.h
@@ -22,7 +22,6 @@
***/
#include <stdbool.h>
-#include <unistd.h>
#include <sys/types.h>
#include "sd-journal.h"
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 8d61507e84..273dacff1f 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -23,12 +23,12 @@
#include <linux/fs.h>
#include <fcntl.h>
-#include "strv.h"
#include "utf8.h"
#include "btrfs-util.h"
#include "path-util.h"
#include "copy.h"
#include "mkdir.h"
+#include "rm-rf.h"
#include "machine-image.h"
static const char image_search_path[] =
@@ -136,12 +136,11 @@ static int image_make(
/* btrfs subvolumes have inode 256 */
if (st.st_ino == 256) {
- struct statfs sfs;
- if (fstatfs(fd, &sfs) < 0)
- return -errno;
-
- if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return r;
+ if (r) {
BtrfsSubvolInfo info;
BtrfsQuotaInfo quota;
@@ -164,10 +163,10 @@ static int image_make(
r = btrfs_subvol_get_quota_fd(fd, &quota);
if (r >= 0) {
- (*ret)->usage = quota.referred;
+ (*ret)->usage = quota.referenced;
(*ret)->usage_exclusive = quota.exclusive;
- (*ret)->limit = quota.referred_max;
+ (*ret)->limit = quota.referenced_max;
(*ret)->limit_exclusive = quota.exclusive_max;
}
@@ -358,19 +357,21 @@ int image_remove(Image *i) {
switch (i->type) {
case IMAGE_SUBVOLUME:
- return btrfs_subvol_remove(i->path);
+ return btrfs_subvol_remove(i->path, true);
case IMAGE_DIRECTORY:
/* Allow deletion of read-only directories */
(void) chattr_path(i->path, false, FS_IMMUTABLE_FL);
-
- /* fall through */
+ return rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
case IMAGE_RAW:
- return rm_rf_dangerous(i->path, false, true, false);
+ if (unlink(i->path) < 0)
+ return -errno;
+
+ return 0;
default:
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
}
@@ -431,7 +432,7 @@ int image_rename(Image *i, const char *new_name) {
}
default:
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
if (!new_path)
@@ -441,8 +442,9 @@ int image_rename(Image *i, const char *new_name) {
if (!nn)
return -ENOMEM;
- if (renameat2(AT_FDCWD, i->path, AT_FDCWD, new_path, RENAME_NOREPLACE) < 0)
- return -errno;
+ r = rename_noreplace(AT_FDCWD, i->path, AT_FDCWD, new_path);
+ if (r < 0)
+ return r;
/* Restore the immutable bit, if it was set before */
if (file_attr & FS_IMMUTABLE_FL)
@@ -488,7 +490,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
case IMAGE_DIRECTORY:
new_path = strjoina("/var/lib/machines/", new_name);
- r = btrfs_subvol_snapshot(i->path, new_path, read_only, true);
+ r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE);
break;
case IMAGE_RAW:
@@ -498,7 +500,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
break;
default:
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
if (r < 0)
@@ -563,7 +565,7 @@ int image_read_only(Image *i, bool b) {
}
default:
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
return 0;
@@ -601,7 +603,7 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
return r;
if (p) {
- mkdir_p("/run/systemd/nspawn/locks", 0600);
+ mkdir_p("/run/systemd/nspawn/locks", 0700);
r = make_lock_file(p, operation, global);
if (r < 0) {
@@ -614,6 +616,19 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
return 0;
}
+int image_set_limit(Image *i, uint64_t referenced_max) {
+ assert(i);
+
+ if (path_equal(i->path, "/") ||
+ path_startswith(i->path, "/usr"))
+ return -EROFS;
+
+ if (i->type != IMAGE_SUBVOLUME)
+ return -EOPNOTSUPP;
+
+ return btrfs_quota_limit(i->path, referenced_max);
+}
+
int image_name_lock(const char *name, int operation, LockFile *ret) {
const char *p;
@@ -628,7 +643,7 @@ int image_name_lock(const char *name, int operation, LockFile *ret) {
if (streq(name, ".host"))
return -EBUSY;
- mkdir_p("/run/systemd/nspawn/locks", 0600);
+ mkdir_p("/run/systemd/nspawn/locks", 0700);
p = strjoina("/run/systemd/nspawn/locks/name-", name);
return make_lock_file(p, operation, ret);
diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h
index 75fa5f4533..f041600fbf 100644
--- a/src/shared/machine-image.h
+++ b/src/shared/machine-image.h
@@ -22,6 +22,7 @@
***/
#include "time-util.h"
+#include "lockfile-util.h"
#include "hashmap.h"
typedef enum ImageType {
@@ -45,6 +46,8 @@ typedef struct Image {
uint64_t usage_exclusive;
uint64_t limit;
uint64_t limit_exclusive;
+
+ void *userdata;
} Image;
Image *image_unref(Image *i);
@@ -68,3 +71,5 @@ bool image_name_is_valid(const char *s) _pure_;
int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local);
int image_name_lock(const char *name, int operation, LockFile *ret);
+
+int image_set_limit(Image *i, uint64_t referenced_max);
diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c
new file mode 100644
index 0000000000..d27931cb4a
--- /dev/null
+++ b/src/shared/machine-pool.c
@@ -0,0 +1,379 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/prctl.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/mount.h>
+
+#include "util.h"
+#include "process-util.h"
+#include "lockfile-util.h"
+#include "mkdir.h"
+#include "btrfs-util.h"
+#include "path-util.h"
+#include "signal-util.h"
+#include "machine-pool.h"
+
+#define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
+#define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
+
+static int check_btrfs(void) {
+ struct statfs sfs;
+
+ if (statfs("/var/lib/machines", &sfs) < 0) {
+ if (errno != ENOENT)
+ return -errno;
+
+ if (statfs("/var/lib", &sfs) < 0)
+ return -errno;
+ }
+
+ return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
+}
+
+static int setup_machine_raw(uint64_t size, sd_bus_error *error) {
+ _cleanup_free_ char *tmp = NULL;
+ _cleanup_close_ int fd = -1;
+ struct statvfs ss;
+ pid_t pid = 0;
+ siginfo_t si;
+ int r;
+
+ /* We want to be able to make use of btrfs-specific file
+ * system features, in particular subvolumes, reflinks and
+ * quota. Hence, if we detect that /var/lib/machines.raw is
+ * not located on btrfs, let's create a loopback file, place a
+ * btrfs file system into it, and mount it to
+ * /var/lib/machines. */
+
+ fd = open("/var/lib/machines.raw", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+ if (fd >= 0) {
+ r = fd;
+ fd = -1;
+ return r;
+ }
+
+ if (errno != ENOENT)
+ return sd_bus_error_set_errnof(error, errno, "Failed to open /var/lib/machines.raw: %m");
+
+ r = tempfn_xxxxxx("/var/lib/machines.raw", &tmp);
+ if (r < 0)
+ return r;
+
+ (void) mkdir_p_label("/var/lib", 0755);
+ fd = open(tmp, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600);
+ if (fd < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to create /var/lib/machines.raw: %m");
+
+ if (fstatvfs(fd, &ss) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to determine free space on /var/lib/machines.raw: %m");
+ goto fail;
+ }
+
+ if (ss.f_bsize * ss.f_bavail < VAR_LIB_MACHINES_FREE_MIN) {
+ r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Not enough free disk space to set up /var/lib/machines.");
+ goto fail;
+ }
+
+ if (ftruncate(fd, size) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to enlarge /var/lib/machines.raw: %m");
+ goto fail;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to fork mkfs.btrfs: %m");
+ goto fail;
+ }
+
+ if (pid == 0) {
+
+ /* Child */
+
+ reset_all_signal_handlers();
+ reset_signal_mask();
+ assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+
+ fd = safe_close(fd);
+
+ execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL);
+ if (errno == ENOENT)
+ return 99;
+
+ _exit(EXIT_FAILURE);
+ }
+
+ r = wait_for_terminate(pid, &si);
+ if (r < 0) {
+ sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m");
+ goto fail;
+ }
+
+ pid = 0;
+
+ if (si.si_code != CLD_EXITED) {
+ r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally.");
+ goto fail;
+ }
+ if (si.si_status == 99) {
+ r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
+ goto fail;
+ }
+ if (si.si_status != 0) {
+ r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", si.si_status);
+ goto fail;
+ }
+
+ r = rename_noreplace(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw");
+ if (r < 0) {
+ sd_bus_error_set_errnof(error, r, "Failed to move /var/lib/machines.raw into place: %m");
+ goto fail;
+ }
+
+ r = fd;
+ fd = -1;
+
+ return r;
+
+fail:
+ unlink_noerrno(tmp);
+
+ if (pid > 1)
+ kill_and_sigcont(pid, SIGKILL);
+
+ return r;
+}
+
+int setup_machine_directory(uint64_t size, sd_bus_error *error) {
+ _cleanup_release_lock_file_ LockFile lock_file = LOCK_FILE_INIT;
+ struct loop_info64 info = {
+ .lo_flags = LO_FLAGS_AUTOCLEAR,
+ };
+ _cleanup_close_ int fd = -1, control = -1, loop = -1;
+ _cleanup_free_ char* loopdev = NULL;
+ char tmpdir[] = "/tmp/import-mount.XXXXXX", *mntdir = NULL;
+ bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false;
+ char buf[FORMAT_BYTES_MAX];
+ int r, nr = -1;
+
+ /* btrfs cannot handle file systems < 16M, hence use this as minimum */
+ if (size == (uint64_t) -1)
+ size = VAR_LIB_MACHINES_SIZE_START;
+ else if (size < 16*1024*1024)
+ size = 16*1024*1024;
+
+ /* Make sure we only set the directory up once at a time */
+ r = make_lock_file("/run/systemd/machines.lock", LOCK_EX, &lock_file);
+ if (r < 0)
+ return r;
+
+ r = check_btrfs();
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
+ if (r > 0) {
+ (void) btrfs_subvol_make_label("/var/lib/machines");
+
+ r = btrfs_quota_enable("/var/lib/machines", true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to enable quota, ignoring: %m");
+
+ return 0;
+ }
+
+ if (path_is_mount_point("/var/lib/machines", AT_SYMLINK_FOLLOW) > 0 ||
+ dir_is_empty("/var/lib/machines") == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "/var/lib/machines is not a btrfs file system. Operation is not supported on legacy file systems.");
+
+ fd = setup_machine_raw(size, error);
+ if (fd < 0)
+ return fd;
+
+ control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (control < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m");
+
+ nr = ioctl(control, LOOP_CTL_GET_FREE);
+ if (nr < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m");
+
+ if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK);
+ if (loop < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m");
+ goto fail;
+ }
+
+ if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m");
+ goto fail;
+ }
+
+ if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to enable auto-clear for loopback device: %m");
+ goto fail;
+ }
+
+ /* We need to make sure the new /var/lib/machines directory
+ * has an access mode of 0700 at the time it is first made
+ * available. mkfs will create it with 0755 however. Hence,
+ * let's mount the directory into an inaccessible directory
+ * below /tmp first, fix the access mode, and move it to the
+ * public place then. */
+
+ if (!mkdtemp(tmpdir)) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m");
+ goto fail;
+ }
+ tmpdir_made = true;
+
+ mntdir = strjoina(tmpdir, "/mnt");
+ if (mkdir(mntdir, 0700) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount directory: %m");
+ goto fail;
+ }
+ mntdir_made = true;
+
+ if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m");
+ goto fail;
+ }
+ mntdir_mounted = true;
+
+ r = btrfs_quota_enable(mntdir, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to enable quota, ignoring: %m");
+
+ if (chmod(mntdir, 0700) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m");
+ goto fail;
+ }
+
+ (void) mkdir_p_label("/var/lib/machines", 0700);
+
+ if (mount(mntdir, "/var/lib/machines", NULL, MS_BIND, NULL) < 0) {
+ r = sd_bus_error_set_errnof(error, errno, "Failed to mount directory into right place: %m");
+ goto fail;
+ }
+
+ (void) syncfs(fd);
+
+ log_info("Set up /var/lib/machines as btrfs loopback file system of size %s mounted on /var/lib/machines.raw.", format_bytes(buf, sizeof(buf), size));
+
+ (void) umount2(mntdir, MNT_DETACH);
+ (void) rmdir(mntdir);
+ (void) rmdir(tmpdir);
+
+ return 0;
+
+fail:
+ if (mntdir_mounted)
+ (void) umount2(mntdir, MNT_DETACH);
+
+ if (mntdir_made)
+ (void) rmdir(mntdir);
+ if (tmpdir_made)
+ (void) rmdir(tmpdir);
+
+ if (loop >= 0) {
+ (void) ioctl(loop, LOOP_CLR_FD);
+ loop = safe_close(loop);
+ }
+
+ if (control >= 0 && nr >= 0)
+ (void) ioctl(control, LOOP_CTL_REMOVE, nr);
+
+ return r;
+}
+
+static int sync_path(const char *p) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return -errno;
+
+ if (syncfs(fd) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int grow_machine_directory(void) {
+ char buf[FORMAT_BYTES_MAX];
+ struct statvfs a, b;
+ uint64_t old_size, new_size, max_add;
+ int r;
+
+ /* Ensure the disk space data is accurate */
+ sync_path("/var/lib/machines");
+ sync_path("/var/lib/machines.raw");
+
+ if (statvfs("/var/lib/machines.raw", &a) < 0)
+ return -errno;
+
+ if (statvfs("/var/lib/machines", &b) < 0)
+ return -errno;
+
+ /* Don't grow if not enough disk space is available on the host */
+ if (((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) <= VAR_LIB_MACHINES_FREE_MIN)
+ return 0;
+
+ /* Don't grow if at least 1/3th of the fs is still free */
+ if (b.f_bavail > b.f_blocks / 3)
+ return 0;
+
+ /* Calculate how much we are willing to add at maximum */
+ max_add = ((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) - VAR_LIB_MACHINES_FREE_MIN;
+
+ /* Calculate the old size */
+ old_size = (uint64_t) b.f_blocks * (uint64_t) b.f_bsize;
+
+ /* Calculate the new size as three times the size of what is used right now */
+ new_size = ((uint64_t) b.f_blocks - (uint64_t) b.f_bavail) * (uint64_t) b.f_bsize * 3;
+
+ /* Always, grow at least to the start size */
+ if (new_size < VAR_LIB_MACHINES_SIZE_START)
+ new_size = VAR_LIB_MACHINES_SIZE_START;
+
+ /* If the new size is smaller than the old size, don't grow */
+ if (new_size < old_size)
+ return 0;
+
+ /* Ensure we never add more than the maximum */
+ if (new_size > old_size + max_add)
+ new_size = old_size + max_add;
+
+ r = btrfs_resize_loopback("/var/lib/machines", new_size, true);
+ if (r <= 0)
+ return r;
+
+ r = btrfs_quota_limit("/var/lib/machines", new_size);
+ if (r < 0)
+ return r;
+
+ log_info("Grew /var/lib/machines btrfs loopback file system to %s.", format_bytes(buf, sizeof(buf), new_size));
+ return 1;
+}
diff --git a/src/shared/machine-pool.h b/src/shared/machine-pool.h
new file mode 100644
index 0000000000..fe01d3d47c
--- /dev/null
+++ b/src/shared/machine-pool.h
@@ -0,0 +1,30 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+/* Grow the /var/lib/machines directory after each 10MiB written */
+#define GROW_INTERVAL_BYTES (UINT64_C(10) * UINT64_C(1024) * UINT64_C(1024))
+
+int setup_machine_directory(uint64_t size, sd_bus_error *error);
+int grow_machine_directory(void);
diff --git a/src/shared/macro.h b/src/shared/macro.h
index 7f89951d62..7ae1ed80b6 100644
--- a/src/shared/macro.h
+++ b/src/shared/macro.h
@@ -256,6 +256,15 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
} \
} while (false)
+#define assert_return_errno(expr, r, err) \
+ do { \
+ if (_unlikely_(!(expr))) { \
+ log_assert_failed_return(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ errno = err; \
+ return (r); \
+ } \
+ } while (false)
+
#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
#define INT_TO_PTR(u) ((void *) ((intptr_t) (u)))
#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
diff --git a/src/shared/memfd-util.c b/src/shared/memfd-util.c
index 6624c5e7db..e99a738e1f 100644
--- a/src/shared/memfd-util.c
+++ b/src/shared/memfd-util.c
@@ -21,7 +21,6 @@
#include <stdio.h>
#include <fcntl.h>
-#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/prctl.h>
@@ -30,7 +29,6 @@
#endif
#include "util.h"
-#include "bus-label.h"
#include "memfd-util.h"
#include "utf8.h"
#include "missing.h"
diff --git a/src/shared/memfd-util.h b/src/shared/memfd-util.h
index cf588fe02f..3ed551fb37 100644
--- a/src/shared/memfd-util.h
+++ b/src/shared/memfd-util.h
@@ -21,12 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <inttypes.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include "macro.h"
-#include "util.h"
int memfd_new(const char *name);
int memfd_new_and_map(const char *name, size_t sz, void **p);
diff --git a/src/shared/missing.h b/src/shared/missing.h
index b33a70cb2c..be7f6186fc 100644
--- a/src/shared/missing.h
+++ b/src/shared/missing.h
@@ -35,6 +35,7 @@
#include <linux/loop.h>
#include <linux/audit.h>
#include <linux/capability.h>
+#include <linux/neighbour.h>
#ifdef HAVE_AUDIT
#include <libaudit.h>
@@ -179,6 +180,16 @@ static inline int memfd_create(const char *name, unsigned int flags) {
# define __NR_getrandom 349
# elif defined(__powerpc__)
# define __NR_getrandom 359
+# elif defined _MIPS_SIM
+# if _MIPS_SIM == _MIPS_SIM_ABI32
+# define __NR_getrandom 4353
+# endif
+# if _MIPS_SIM == _MIPS_SIM_NABI32
+# define __NR_getrandom 6317
+# endif
+# if _MIPS_SIM == _MIPS_SIM_ABI64
+# define __NR_getrandom 5313
+# endif
# else
# warning "__NR_getrandom unknown for your architecture"
# define __NR_getrandom 0xffffffff
@@ -219,12 +230,64 @@ static inline int getrandom(void *buffer, size_t count, unsigned flags) {
#define BTRFS_UUID_SIZE 16
#endif
+#ifndef BTRFS_SUBVOL_RDONLY
+#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
+#endif
+
+#ifndef BTRFS_SUBVOL_NAME_MAX
+#define BTRFS_SUBVOL_NAME_MAX 4039
+#endif
+
+#ifndef BTRFS_INO_LOOKUP_PATH_MAX
+#define BTRFS_INO_LOOKUP_PATH_MAX 4080
+#endif
+
+#ifndef BTRFS_SEARCH_ARGS_BUFSIZE
+#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
+#endif
+
#ifndef HAVE_LINUX_BTRFS_H
struct btrfs_ioctl_vol_args {
int64_t fd;
char name[BTRFS_PATH_NAME_MAX + 1];
};
+struct btrfs_qgroup_limit {
+ __u64 flags;
+ __u64 max_rfer;
+ __u64 max_excl;
+ __u64 rsv_rfer;
+ __u64 rsv_excl;
+};
+
+struct btrfs_qgroup_inherit {
+ __u64 flags;
+ __u64 num_qgroups;
+ __u64 num_ref_copies;
+ __u64 num_excl_copies;
+ struct btrfs_qgroup_limit lim;
+ __u64 qgroups[0];
+};
+
+struct btrfs_ioctl_qgroup_limit_args {
+ __u64 qgroupid;
+ struct btrfs_qgroup_limit lim;
+};
+
+struct btrfs_ioctl_vol_args_v2 {
+ __s64 fd;
+ __u64 transid;
+ __u64 flags;
+ union {
+ struct {
+ __u64 size;
+ struct btrfs_qgroup_inherit *qgroup_inherit;
+ };
+ __u64 unused[4];
+ };
+ char name[BTRFS_SUBVOL_NAME_MAX + 1];
+};
+
struct btrfs_ioctl_dev_info_args {
uint64_t devid; /* in/out */
uint8_t uuid[BTRFS_UUID_SIZE]; /* in/out */
@@ -240,6 +303,76 @@ struct btrfs_ioctl_fs_info_args {
uint8_t fsid[BTRFS_FSID_SIZE]; /* out */
uint64_t reserved[124]; /* pad to 1k */
};
+
+struct btrfs_ioctl_ino_lookup_args {
+ __u64 treeid;
+ __u64 objectid;
+ char name[BTRFS_INO_LOOKUP_PATH_MAX];
+};
+
+struct btrfs_ioctl_search_key {
+ /* which root are we searching. 0 is the tree of tree roots */
+ __u64 tree_id;
+
+ /* keys returned will be >= min and <= max */
+ __u64 min_objectid;
+ __u64 max_objectid;
+
+ /* keys returned will be >= min and <= max */
+ __u64 min_offset;
+ __u64 max_offset;
+
+ /* max and min transids to search for */
+ __u64 min_transid;
+ __u64 max_transid;
+
+ /* keys returned will be >= min and <= max */
+ __u32 min_type;
+ __u32 max_type;
+
+ /*
+ * how many items did userland ask for, and how many are we
+ * returning
+ */
+ __u32 nr_items;
+
+ /* align to 64 bits */
+ __u32 unused;
+
+ /* some extra for later */
+ __u64 unused1;
+ __u64 unused2;
+ __u64 unused3;
+ __u64 unused4;
+};
+
+struct btrfs_ioctl_search_header {
+ __u64 transid;
+ __u64 objectid;
+ __u64 offset;
+ __u32 type;
+ __u32 len;
+};
+
+
+struct btrfs_ioctl_search_args {
+ struct btrfs_ioctl_search_key key;
+ char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
+};
+
+struct btrfs_ioctl_clone_range_args {
+ __s64 src_fd;
+ __u64 src_offset, src_length;
+ __u64 dest_offset;
+};
+
+#define BTRFS_QUOTA_CTL_ENABLE 1
+#define BTRFS_QUOTA_CTL_DISABLE 2
+#define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3
+struct btrfs_ioctl_quota_ctl_args {
+ __u64 cmd;
+ __u64 status;
+};
#endif
#ifndef BTRFS_IOC_DEFRAG
@@ -247,6 +380,53 @@ struct btrfs_ioctl_fs_info_args {
struct btrfs_ioctl_vol_args)
#endif
+#ifndef BTRFS_IOC_RESIZE
+#define BTRFS_IOC_RESIZE _IOW(BTRFS_IOCTL_MAGIC, 3, \
+ struct btrfs_ioctl_vol_args)
+#endif
+
+#ifndef BTRFS_IOC_CLONE
+#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
+#endif
+
+#ifndef BTRFS_IOC_CLONE_RANGE
+#define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
+ struct btrfs_ioctl_clone_range_args)
+#endif
+
+#ifndef BTRFS_IOC_SUBVOL_CREATE
+#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
+ struct btrfs_ioctl_vol_args)
+#endif
+
+#ifndef BTRFS_IOC_SNAP_DESTROY
+#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
+ struct btrfs_ioctl_vol_args)
+#endif
+
+#ifndef BTRFS_IOC_TREE_SEARCH
+#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
+ struct btrfs_ioctl_search_args)
+#endif
+
+#ifndef BTRFS_IOC_INO_LOOKUP
+#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \
+ struct btrfs_ioctl_ino_lookup_args)
+#endif
+
+#ifndef BTRFS_IOC_SNAP_CREATE_V2
+#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
+ struct btrfs_ioctl_vol_args_v2)
+#endif
+
+#ifndef BTRFS_IOC_SUBVOL_GETFLAGS
+#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, __u64)
+#endif
+
+#ifndef BTRFS_IOC_SUBVOL_SETFLAGS
+#define BTRFS_IOC_SUBVOL_SETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 26, __u64)
+#endif
+
#ifndef BTRFS_IOC_DEV_INFO
#define BTRFS_IOC_DEV_INFO _IOWR(BTRFS_IOCTL_MAGIC, 30, \
struct btrfs_ioctl_dev_info_args)
@@ -262,10 +442,24 @@ struct btrfs_ioctl_fs_info_args {
struct btrfs_ioctl_vol_args)
#endif
+#ifndef BTRFS_IOC_QUOTA_CTL
+#define BTRFS_IOC_QUOTA_CTL _IOWR(BTRFS_IOCTL_MAGIC, 40, \
+ struct btrfs_ioctl_quota_ctl_args)
+#endif
+
+#ifndef BTRFS_IOC_QGROUP_LIMIT
+#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \
+ struct btrfs_ioctl_qgroup_limit_args)
+#endif
+
#ifndef BTRFS_FIRST_FREE_OBJECTID
#define BTRFS_FIRST_FREE_OBJECTID 256
#endif
+#ifndef BTRFS_LAST_FREE_OBJECTID
+#define BTRFS_LAST_FREE_OBJECTID -256ULL
+#endif
+
#ifndef BTRFS_ROOT_TREE_OBJECTID
#define BTRFS_ROOT_TREE_OBJECTID 1
#endif
@@ -290,6 +484,10 @@ struct btrfs_ioctl_fs_info_args {
#define BTRFS_QGROUP_LIMIT_KEY 244
#endif
+#ifndef BTRFS_ROOT_BACKREF_KEY
+#define BTRFS_ROOT_BACKREF_KEY 144
+#endif
+
#ifndef BTRFS_SUPER_MAGIC
#define BTRFS_SUPER_MAGIC 0x9123683E
#endif
@@ -543,7 +741,7 @@ static inline int setns(int fd, int nstype) {
#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1)
#endif
-#if !HAVE_DECL_IFLA_VXLAN_LOCAL6
+#if !HAVE_DECL_IFLA_VXLAN_REMCSUM_NOPARTIAL
#define IFLA_VXLAN_UNSPEC 0
#define IFLA_VXLAN_ID 1
#define IFLA_VXLAN_GROUP 2
@@ -562,7 +760,14 @@ static inline int setns(int fd, int nstype) {
#define IFLA_VXLAN_PORT 15
#define IFLA_VXLAN_GROUP6 16
#define IFLA_VXLAN_LOCAL6 17
-#define __IFLA_VXLAN_MAX 18
+#define IFLA_VXLAN_UDP_CSUM 18
+#define IFLA_VXLAN_UDP_ZERO_CSUM6_TX 19
+#define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20
+#define IFLA_VXLAN_REMCSUM_TX 21
+#define IFLA_VXLAN_REMCSUM_RX 22
+#define IFLA_VXLAN_GBP 23
+#define IFLA_VXLAN_REMCSUM_NOPARTIAL 24
+#define __IFLA_VXLAN_MAX 25
#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
#endif
@@ -613,6 +818,21 @@ static inline int setns(int fd, int nstype) {
#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
#endif
+#if !HAVE_DECL_NDA_IFINDEX
+#define NDA_UNSPEC 0
+#define NDA_DST 1
+#define NDA_LLADDR 2
+#define NDA_CACHEINFO 3
+#define NDA_PROBES 4
+#define NDA_VLAN 5
+#define NDA_PORT 6
+#define NDA_VNI 7
+#define NDA_IFINDEX 8
+#define __NDA_MAX 9
+
+#define NDA_MAX (__NDA_MAX - 1)
+#endif
+
#ifndef IPV6_UNICAST_IF
#define IPV6_UNICAST_IF 76
#endif
@@ -674,6 +894,14 @@ static inline int setns(int fd, int nstype) {
#define LOOPBACK_IFINDEX 1
#endif
+#if !HAVE_DECL_IFA_FLAGS
+#define IFA_FLAGS 8
+#endif
+
+#ifndef IFA_F_NOPREFIXROUTE
+#define IFA_F_NOPREFIXROUTE 0x200
+#endif
+
#ifndef MAX_AUDIT_MESSAGE_LENGTH
#define MAX_AUDIT_MESSAGE_LENGTH 8970
#endif
@@ -763,3 +991,11 @@ static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, uns
#ifndef KCMP_FILE
#define KCMP_FILE 0
#endif
+
+#ifndef INPUT_PROP_POINTING_STICK
+#define INPUT_PROP_POINTING_STICK 0x05
+#endif
+
+#ifndef INPUT_PROP_ACCELEROMETER
+#define INPUT_PROP_ACCELEROMETER 0x06
+#endif
diff --git a/src/shared/mkdir-label.c b/src/shared/mkdir-label.c
index ee11ac06ff..76bbc1edda 100644
--- a/src/shared/mkdir-label.c
+++ b/src/shared/mkdir-label.c
@@ -20,16 +20,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
-#include <string.h>
#include <unistd.h>
-#include <errno.h>
-#include <stdlib.h>
#include <stdio.h>
#include "label.h"
-#include "util.h"
-#include "path-util.h"
#include "mkdir.h"
int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid) {
diff --git a/src/shared/mkdir.c b/src/shared/mkdir.c
index beefd1052a..7ee4546988 100644
--- a/src/shared/mkdir.c
+++ b/src/shared/mkdir.c
@@ -19,14 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
-#include <unistd.h>
#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include "label.h"
#include "util.h"
#include "path-util.h"
#include "mkdir.h"
@@ -46,10 +41,8 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkd
(st.st_mode & 0700) > (mode & 0700) ||
(uid != UID_INVALID && st.st_uid != uid) ||
(gid != GID_INVALID && st.st_gid != gid) ||
- !S_ISDIR(st.st_mode)) {
- errno = EEXIST;
- return -errno;
- }
+ !S_ISDIR(st.st_mode))
+ return -EEXIST;
return 0;
}
diff --git a/src/shared/mkdir.h b/src/shared/mkdir.h
index e317df300e..2392d1fd1b 100644
--- a/src/shared/mkdir.h
+++ b/src/shared/mkdir.h
@@ -22,7 +22,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdbool.h>
#include <sys/types.h>
int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid);
diff --git a/src/shared/ordered-set.h b/src/shared/ordered-set.h
new file mode 100644
index 0000000000..766a1f2e83
--- /dev/null
+++ b/src/shared/ordered-set.h
@@ -0,0 +1,59 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "hashmap.h"
+
+typedef struct OrderedSet OrderedSet;
+
+static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) {
+ return (OrderedSet*) ordered_hashmap_new(ops);
+}
+
+static inline OrderedSet* ordered_set_free(OrderedSet *s) {
+ ordered_hashmap_free((OrderedHashmap*) s);
+ return NULL;
+}
+
+static inline OrderedSet* ordered_set_free_free(OrderedSet *s) {
+ ordered_hashmap_free_free((OrderedHashmap*) s);
+ return NULL;
+}
+
+static inline int ordered_set_put(OrderedSet *s, void *p) {
+ return ordered_hashmap_put((OrderedHashmap*) s, p, p);
+}
+
+static inline bool ordered_set_isempty(OrderedSet *s) {
+ return ordered_hashmap_isempty((OrderedHashmap*) s);
+}
+
+static inline void *ordered_set_iterate(OrderedSet *s, Iterator *i) {
+ return ordered_hashmap_iterate((OrderedHashmap*) s, i, NULL);
+}
+
+#define ORDERED_SET_FOREACH(e, s, i) \
+ for ((i) = ITERATOR_FIRST, (e) = ordered_set_iterate((s), &(i)); (e); (e) = ordered_set_iterate((s), &(i)))
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free);
+
+#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep)
diff --git a/src/shared/pager.c b/src/shared/pager.c
index 8635d9a600..58b62fdccf 100644
--- a/src/shared/pager.c
+++ b/src/shared/pager.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
@@ -28,7 +27,9 @@
#include "pager.h"
#include "util.h"
+#include "process-util.h"
#include "macro.h"
+#include "terminal-util.h"
static pid_t pager_pid = 0;
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index 291a2f4054..f6a127174c 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -19,18 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <unistd.h>
#include <errno.h>
#include "util.h"
-#include "mkdir.h"
#include "strv.h"
#include "path-util.h"
#include "path-lookup.h"
+#include "install.h"
int user_config_home(char **config_home) {
const char *e;
@@ -220,8 +218,8 @@ static char** user_dirs(
return tmp;
}
-char **generator_paths(SystemdRunningAs running_as) {
- if (running_as == SYSTEMD_USER)
+char **generator_paths(ManagerRunningAs running_as) {
+ if (running_as == MANAGER_USER)
return strv_new("/run/systemd/user-generators",
"/etc/systemd/user-generators",
"/usr/local/lib/systemd/user-generators",
@@ -237,7 +235,7 @@ char **generator_paths(SystemdRunningAs running_as) {
int lookup_paths_init(
LookupPaths *p,
- SystemdRunningAs running_as,
+ ManagerRunningAs running_as,
bool personal,
const char *root_dir,
const char *generator,
@@ -279,7 +277,7 @@ int lookup_paths_init(
* we include /lib in the search path for the system
* stuff but avoid it for user stuff. */
- if (running_as == SYSTEMD_USER) {
+ if (running_as == MANAGER_USER) {
if (personal)
unit_path = user_dirs(generator, generator_early, generator_late);
else
@@ -339,7 +337,7 @@ int lookup_paths_init(
p->unit_path = NULL;
}
- if (running_as == SYSTEMD_SYSTEM) {
+ if (running_as == MANAGER_SYSTEM) {
#ifdef HAVE_SYSV_COMPAT
/* /etc/init.d/ compatibility does not matter to users */
@@ -439,7 +437,7 @@ int lookup_paths_init_from_scope(LookupPaths *paths,
zero(*paths);
return lookup_paths_init(paths,
- scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
+ scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
scope == UNIT_FILE_USER,
root_dir,
NULL, NULL, NULL);
diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h
index 2ec888da81..e35c8d3c04 100644
--- a/src/shared/path-lookup.h
+++ b/src/shared/path-lookup.h
@@ -22,7 +22,6 @@
***/
#include "macro.h"
-#include "install.h"
typedef struct LookupPaths {
char **unit_path;
@@ -32,28 +31,31 @@ typedef struct LookupPaths {
#endif
} LookupPaths;
-typedef enum SystemdRunningAs {
- SYSTEMD_SYSTEM,
- SYSTEMD_USER,
- _SYSTEMD_RUNNING_AS_MAX,
- _SYSTEMD_RUNNING_AS_INVALID = -1
-} SystemdRunningAs;
+typedef enum ManagerRunningAs {
+ MANAGER_SYSTEM,
+ MANAGER_USER,
+ _MANAGER_RUNNING_AS_MAX,
+ _MANAGER_RUNNING_AS_INVALID = -1
+} ManagerRunningAs;
int user_config_home(char **config_home);
int user_runtime_dir(char **runtime_dir);
-char **generator_paths(SystemdRunningAs running_as);
+char **generator_paths(ManagerRunningAs running_as);
int lookup_paths_init(LookupPaths *p,
- SystemdRunningAs running_as,
+ ManagerRunningAs running_as,
bool personal,
const char *root_dir,
const char *generator,
const char *generator_early,
const char *generator_late);
-void lookup_paths_free(LookupPaths *p);
+
+#include "install.h"
+
int lookup_paths_init_from_scope(LookupPaths *paths,
UnitFileScope scope,
const char *root_dir);
+void lookup_paths_free(LookupPaths *p);
#define _cleanup_lookup_paths_free_ _cleanup_(lookup_paths_free)
diff --git a/src/shared/path-util.c b/src/shared/path-util.c
index b9db7f1047..be50a1865d 100644
--- a/src/shared/path-util.c
+++ b/src/shared/path-util.c
@@ -19,15 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
-#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
-#include <dirent.h>
#include <sys/statvfs.h>
#include "macro.h"
@@ -36,6 +33,7 @@
#include "strv.h"
#include "path-util.h"
#include "missing.h"
+#include "fileio.h"
bool path_is_absolute(const char *p) {
return p[0] == '/';
@@ -403,12 +401,18 @@ char* path_startswith(const char *path, const char *prefix) {
}
}
-bool path_equal(const char *a, const char *b) {
+int path_compare(const char *a, const char *b) {
+ int d;
+
assert(a);
assert(b);
- if ((a[0] == '/') != (b[0] == '/'))
- return false;
+ /* A relative path and an abolute path must not compare as equal.
+ * Which one is sorted before the other does not really matter.
+ * Here a relative path is ordered before an absolute path. */
+ d = (a[0] == '/') - (b[0] == '/');
+ if (d)
+ return d;
for (;;) {
size_t j, k;
@@ -417,25 +421,40 @@ bool path_equal(const char *a, const char *b) {
b += strspn(b, "/");
if (*a == 0 && *b == 0)
- return true;
+ return 0;
- if (*a == 0 || *b == 0)
- return false;
+ /* Order prefixes first: "/foo" before "/foo/bar" */
+ if (*a == 0)
+ return -1;
+ if (*b == 0)
+ return 1;
j = strcspn(a, "/");
k = strcspn(b, "/");
- if (j != k)
- return false;
+ /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
+ d = memcmp(a, b, MIN(j, k));
+ if (d)
+ return (d > 0) - (d < 0); /* sign of d */
- if (memcmp(a, b, j) != 0)
- return false;
+ /* Sort "/foo/a" before "/foo/aaa" */
+ d = (j > k) - (j < k); /* sign of (j - k) */
+ if (d)
+ return d;
a += j;
b += k;
}
}
+bool path_equal(const char *a, const char *b) {
+ return path_compare(a, b) == 0;
+}
+
+bool path_equal_or_files_same(const char *a, const char *b) {
+ return path_equal(a, b) || files_same(a, b) > 0;
+}
+
char* path_join(const char *root, const char *path, const char *rest) {
assert(path);
@@ -452,87 +471,192 @@ char* path_join(const char *root, const char *path, const char *rest) {
NULL);
}
-int path_is_mount_point(const char *t, bool allow_symlink) {
+static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
+ char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
+ _cleanup_free_ char *fdinfo = NULL;
+ _cleanup_close_ int subfd = -1;
+ char *p;
+ int r;
+
+ if ((flags & AT_EMPTY_PATH) && isempty(filename))
+ xsprintf(path, "/proc/self/fdinfo/%i", fd);
+ else {
+ subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
+ if (subfd < 0)
+ return -errno;
+
+ xsprintf(path, "/proc/self/fdinfo/%i", subfd);
+ }
+
+ r = read_full_file(path, &fdinfo, NULL);
+ if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
+ return -EOPNOTSUPP;
+ if (r < 0)
+ return -errno;
+
+ p = startswith(fdinfo, "mnt_id:");
+ if (!p) {
+ p = strstr(fdinfo, "\nmnt_id:");
+ if (!p) /* The mnt_id field is a relatively new addition */
+ return -EOPNOTSUPP;
+
+ p += 8;
+ }
- union file_handle_union h = FILE_HANDLE_INIT;
+ p += strspn(p, WHITESPACE);
+ p[strcspn(p, WHITESPACE)] = 0;
+
+ return safe_atoi(p, mnt_id);
+}
+
+int fd_is_mount_point(int fd, const char *filename, int flags) {
+ union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
int mount_id = -1, mount_id_parent = -1;
- _cleanup_free_ char *parent = NULL;
+ bool nosupp = false, check_st_dev = true;
struct stat a, b;
int r;
- bool nosupp = false;
- /* We are not actually interested in the file handles, but
- * name_to_handle_at() also passes us the mount ID, hence use
- * it but throw the handle away */
+ assert(fd >= 0);
+ assert(filename);
- if (path_equal(t, "/"))
- return 1;
-
- r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
+ /* First we will try the name_to_handle_at() syscall, which
+ * tells us the mount id and an opaque file "handle". It is
+ * not supported everywhere though (kernel compile-time
+ * option, not all file systems are hooked up). If it works
+ * the mount id is usually good enough to tell us whether
+ * something is a mount point.
+ *
+ * If that didn't work we will try to read the mount id from
+ * /proc/self/fdinfo/<fd>. This is almost as good as
+ * name_to_handle_at(), however, does not return the the
+ * opaque file handle. The opaque file handle is pretty useful
+ * to detect the root directory, which we should always
+ * consider a mount point. Hence we use this only as
+ * fallback. Exporting the mnt_id in fdinfo is a pretty recent
+ * kernel addition.
+ *
+ * As last fallback we do traditional fstat() based st_dev
+ * comparisons. This is how things were traditionally done,
+ * but unionfs breaks breaks this since it exposes file
+ * systems with a variety of st_dev reported. Also, btrfs
+ * subvolumes have different st_dev, even though they aren't
+ * real mounts of their own. */
+
+ r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
if (r < 0) {
if (errno == ENOSYS)
/* This kernel does not support name_to_handle_at()
- * fall back to the traditional stat() logic. */
- goto fallback;
+ * fall back to simpler logic. */
+ goto fallback_fdinfo;
else if (errno == EOPNOTSUPP)
/* This kernel or file system does not support
- * name_to_handle_at(), hence fallback to the
+ * name_to_handle_at(), hence let's see if the
+ * upper fs supports it (in which case it is a
+ * mount point), otherwise fallback to the
* traditional stat() logic */
nosupp = true;
- else if (errno == ENOENT)
- return 0;
else
return -errno;
}
- r = path_get_parent(t, &parent);
- if (r < 0)
- return r;
-
- h.handle.handle_bytes = MAX_HANDLE_SZ;
- r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, AT_SYMLINK_FOLLOW);
- if (r < 0)
- if (errno == EOPNOTSUPP)
+ r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
+ if (r < 0) {
+ if (errno == EOPNOTSUPP) {
if (nosupp)
/* Neither parent nor child do name_to_handle_at()?
We have no choice but to fall back. */
- goto fallback;
+ goto fallback_fdinfo;
else
- /* The parent can't do name_to_handle_at() but
- * the directory we are interested in can?
- * Or the other way around?
+ /* The parent can't do name_to_handle_at() but the
+ * directory we are interested in can?
* If so, it must be a mount point. */
return 1;
- else
+ } else
return -errno;
- else
- return mount_id != mount_id_parent;
+ }
-fallback:
- if (allow_symlink)
- r = stat(t, &a);
- else
- r = lstat(t, &a);
+ /* The parent can do name_to_handle_at() but the
+ * directory we are interested in can't? If so, it
+ * must be a mount point. */
+ if (nosupp)
+ return 1;
- if (r < 0) {
- if (errno == ENOENT)
- return 0;
+ /* If the file handle for the directory we are
+ * interested in and its parent are identical, we
+ * assume this is the root directory, which is a mount
+ * point. */
+
+ if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
+ h.handle.handle_type == h_parent.handle.handle_type &&
+ memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
+ return 1;
+
+ return mount_id != mount_id_parent;
+
+fallback_fdinfo:
+ r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
+ if (r == -EOPNOTSUPP)
+ goto fallback_fstat;
+ if (r < 0)
+ return r;
+
+ r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
+ if (r < 0)
+ return r;
+
+ if (mount_id != mount_id_parent)
+ return 1;
+ /* Hmm, so, the mount ids are the same. This leaves one
+ * special case though for the root file system. For that,
+ * let's see if the parent directory has the same inode as we
+ * are interested in. Hence, let's also do fstat() checks now,
+ * too, but avoid the st_dev comparisons, since they aren't
+ * that useful on unionfs mounts. */
+ check_st_dev = false;
+
+fallback_fstat:
+ /* yay for fstatat() taking a different set of flags than the other
+ * _at() above */
+ if (flags & AT_SYMLINK_FOLLOW)
+ flags &= ~AT_SYMLINK_FOLLOW;
+ else
+ flags |= AT_SYMLINK_NOFOLLOW;
+ if (fstatat(fd, filename, &a, flags) < 0)
return -errno;
- }
- free(parent);
- parent = NULL;
+ if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
+ return -errno;
+
+ /* A directory with same device and inode as its parent? Must
+ * be the root directory */
+ if (a.st_dev == b.st_dev &&
+ a.st_ino == b.st_ino)
+ return 1;
+
+ return check_st_dev && (a.st_dev != b.st_dev);
+}
+
+/* flags can be AT_SYMLINK_FOLLOW or 0 */
+int path_is_mount_point(const char *t, int flags) {
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *parent = NULL;
+ int r;
+
+ assert(t);
+
+ if (path_equal(t, "/"))
+ return 1;
r = path_get_parent(t, &parent);
if (r < 0)
return r;
- r = stat(parent, &b);
- if (r < 0)
+ fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
+ if (fd < 0)
return -errno;
- return a.st_dev != b.st_dev;
+ return fd_is_mount_point(fd, basename(t), flags);
}
int path_is_read_only_fs(const char *path) {
@@ -683,3 +807,37 @@ int fsck_exists(const char *fstype) {
return 0;
}
+
+char *prefix_root(const char *root, const char *path) {
+ char *n, *p;
+ size_t l;
+
+ /* If root is passed, prefixes path with it. Otherwise returns
+ * it as is. */
+
+ assert(path);
+
+ /* First, drop duplicate prefixing slashes from the path */
+ while (path[0] == '/' && path[1] == '/')
+ path++;
+
+ if (isempty(root) || path_equal(root, "/"))
+ return strdup(path);
+
+ l = strlen(root) + 1 + strlen(path) + 1;
+
+ n = new(char, l);
+ if (!n)
+ return NULL;
+
+ p = stpcpy(n, root);
+
+ while (p > n && p[-1] == '/')
+ p--;
+
+ if (path[0] != '/')
+ *(p++) = '/';
+
+ strcpy(p, path);
+ return n;
+}
diff --git a/src/shared/path-util.h b/src/shared/path-util.h
index bd0d32473f..1eac89c51b 100644
--- a/src/shared/path-util.h
+++ b/src/shared/path-util.h
@@ -44,14 +44,17 @@ char* path_make_absolute_cwd(const char *p);
int path_make_relative(const char *from_dir, const char *to_path, char **_r);
char* path_kill_slashes(char *path);
char* path_startswith(const char *path, const char *prefix) _pure_;
+int path_compare(const char *a, const char *b) _pure_;
bool path_equal(const char *a, const char *b) _pure_;
+bool path_equal_or_files_same(const char *a, const char *b);
char* path_join(const char *root, const char *path, const char *rest);
char** path_strv_make_absolute_cwd(char **l);
char** path_strv_resolve(char **l, const char *prefix);
char** path_strv_resolve_uniq(char **l, const char *prefix);
-int path_is_mount_point(const char *path, bool allow_symlink);
+int fd_is_mount_point(int fd, const char *filename, int flags);
+int path_is_mount_point(const char *path, int flags);
int path_is_read_only_fs(const char *path);
int path_is_os_tree(const char *path);
@@ -70,3 +73,30 @@ int fsck_exists(const char *fstype);
/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */
#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
+
+char *prefix_root(const char *root, const char *path);
+
+/* Similar to prefix_root(), but returns an alloca() buffer, or
+ * possibly a const pointer into the path parameter */
+#define prefix_roota(root, path) \
+ ({ \
+ const char* _path = (path), *_root = (root), *_ret; \
+ char *_p, *_n; \
+ size_t _l; \
+ while (_path[0] == '/' && _path[1] == '/') \
+ _path ++; \
+ if (isempty(_root) || path_equal(_root, "/")) \
+ _ret = _path; \
+ else { \
+ _l = strlen(_root) + 1 + strlen(_path) + 1; \
+ _n = alloca(_l); \
+ _p = stpcpy(_n, _root); \
+ while (_p > _n && _p[-1] == '/') \
+ _p--; \
+ if (_path[0] != '/') \
+ *(_p++) = '/'; \
+ strcpy(_p, _path); \
+ _ret = _n; \
+ } \
+ _ret; \
+ })
diff --git a/src/shared/prioq.c b/src/shared/prioq.c
index 8af4c51c2f..b89888be0e 100644
--- a/src/shared/prioq.c
+++ b/src/shared/prioq.c
@@ -45,12 +45,14 @@ Prioq *prioq_new(compare_func_t compare_func) {
return q;
}
-void prioq_free(Prioq *q) {
+Prioq* prioq_free(Prioq *q) {
if (!q)
- return;
+ return NULL;
free(q->items);
free(q);
+
+ return NULL;
}
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
diff --git a/src/shared/prioq.h b/src/shared/prioq.h
index d836b36cd9..1c044b135c 100644
--- a/src/shared/prioq.h
+++ b/src/shared/prioq.h
@@ -28,7 +28,7 @@ typedef struct Prioq Prioq;
#define PRIOQ_IDX_NULL ((unsigned) -1)
Prioq *prioq_new(compare_func_t compare);
-void prioq_free(Prioq *q);
+Prioq *prioq_free(Prioq *q);
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
int prioq_put(Prioq *q, void *data, unsigned *idx);
diff --git a/src/shared/process-util.c b/src/shared/process-util.c
new file mode 100644
index 0000000000..cfc876567d
--- /dev/null
+++ b/src/shared/process-util.c
@@ -0,0 +1,539 @@
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <ctype.h>
+
+#include "fileio.h"
+#include "util.h"
+#include "log.h"
+#include "signal-util.h"
+#include "process-util.h"
+
+int get_process_state(pid_t pid) {
+ const char *p;
+ char state;
+ int r;
+ _cleanup_free_ char *line = NULL;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "stat");
+ r = read_one_line_file(p, &line);
+ if (r < 0)
+ return r;
+
+ p = strrchr(line, ')');
+ if (!p)
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " %c", &state) != 1)
+ return -EIO;
+
+ return (unsigned char) state;
+}
+
+int get_process_comm(pid_t pid, char **name) {
+ const char *p;
+ int r;
+
+ assert(name);
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "comm");
+
+ r = read_one_line_file(p, name);
+ if (r == -ENOENT)
+ return -ESRCH;
+
+ return r;
+}
+
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char *r = NULL, *k;
+ const char *p;
+ int c;
+
+ assert(line);
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "cmdline");
+
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ if (max_length == 0) {
+ size_t len = 0, allocated = 0;
+
+ while ((c = getc(f)) != EOF) {
+
+ if (!GREEDY_REALLOC(r, allocated, len+2)) {
+ free(r);
+ return -ENOMEM;
+ }
+
+ r[len++] = isprint(c) ? c : ' ';
+ }
+
+ if (len > 0)
+ r[len-1] = 0;
+
+ } else {
+ bool space = false;
+ size_t left;
+
+ r = new(char, max_length);
+ if (!r)
+ return -ENOMEM;
+
+ k = r;
+ left = max_length;
+ while ((c = getc(f)) != EOF) {
+
+ if (isprint(c)) {
+ if (space) {
+ if (left <= 4)
+ break;
+
+ *(k++) = ' ';
+ left--;
+ space = false;
+ }
+
+ if (left <= 4)
+ break;
+
+ *(k++) = (char) c;
+ left--;
+ } else
+ space = true;
+ }
+
+ if (left <= 4) {
+ size_t n = MIN(left-1, 3U);
+ memcpy(k, "...", n);
+ k[n] = 0;
+ } else
+ *k = 0;
+ }
+
+ /* Kernel threads have no argv[] */
+ if (isempty(r)) {
+ _cleanup_free_ char *t = NULL;
+ int h;
+
+ free(r);
+
+ if (!comm_fallback)
+ return -ENOENT;
+
+ h = get_process_comm(pid, &t);
+ if (h < 0)
+ return h;
+
+ r = strjoin("[", t, "]", NULL);
+ if (!r)
+ return -ENOMEM;
+ }
+
+ *line = r;
+ return 0;
+}
+
+int is_kernel_thread(pid_t pid) {
+ const char *p;
+ size_t count;
+ char c;
+ bool eof;
+ FILE *f;
+
+ if (pid == 0)
+ return 0;
+
+ assert(pid > 0);
+
+ p = procfs_file_alloca(pid, "cmdline");
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ count = fread(&c, 1, 1, f);
+ eof = feof(f);
+ fclose(f);
+
+ /* Kernel threads have an empty cmdline */
+
+ if (count <= 0)
+ return eof ? 1 : -errno;
+
+ return 0;
+}
+
+int get_process_capeff(pid_t pid, char **capeff) {
+ const char *p;
+
+ assert(capeff);
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "status");
+
+ return get_status_field(p, "\nCapEff:", capeff);
+}
+
+static int get_process_link_contents(const char *proc_file, char **name) {
+ int r;
+
+ assert(proc_file);
+ assert(name);
+
+ r = readlink_malloc(proc_file, name);
+ if (r < 0)
+ return r == -ENOENT ? -ESRCH : r;
+
+ return 0;
+}
+
+int get_process_exe(pid_t pid, char **name) {
+ const char *p;
+ char *d;
+ int r;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "exe");
+ r = get_process_link_contents(p, name);
+ if (r < 0)
+ return r;
+
+ d = endswith(*name, " (deleted)");
+ if (d)
+ *d = '\0';
+
+ return 0;
+}
+
+static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX];
+ const char *p;
+
+ assert(field);
+ assert(uid);
+
+ if (pid == 0)
+ return getuid();
+
+ p = procfs_file_alloca(pid, "status");
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ FOREACH_LINE(line, f, return -errno) {
+ char *l;
+
+ l = strstrip(line);
+
+ if (startswith(l, field)) {
+ l += strlen(field);
+ l += strspn(l, WHITESPACE);
+
+ l[strcspn(l, WHITESPACE)] = 0;
+
+ return parse_uid(l, uid);
+ }
+ }
+
+ return -EIO;
+}
+
+int get_process_uid(pid_t pid, uid_t *uid) {
+ return get_process_id(pid, "Uid:", uid);
+}
+
+int get_process_gid(pid_t pid, gid_t *gid) {
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+ return get_process_id(pid, "Gid:", gid);
+}
+
+int get_process_cwd(pid_t pid, char **cwd) {
+ const char *p;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "cwd");
+
+ return get_process_link_contents(p, cwd);
+}
+
+int get_process_root(pid_t pid, char **root) {
+ const char *p;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "root");
+
+ return get_process_link_contents(p, root);
+}
+
+int get_process_environ(pid_t pid, char **env) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *outcome = NULL;
+ int c;
+ const char *p;
+ size_t allocated = 0, sz = 0;
+
+ assert(pid >= 0);
+ assert(env);
+
+ p = procfs_file_alloca(pid, "environ");
+
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ while ((c = fgetc(f)) != EOF) {
+ if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
+ return -ENOMEM;
+
+ if (c == '\0')
+ outcome[sz++] = '\n';
+ else
+ sz += cescape_char(c, outcome + sz);
+ }
+
+ outcome[sz] = '\0';
+ *env = outcome;
+ outcome = NULL;
+
+ return 0;
+}
+
+int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
+ int r;
+ _cleanup_free_ char *line = NULL;
+ long unsigned ppid;
+ const char *p;
+
+ assert(pid >= 0);
+ assert(_ppid);
+
+ if (pid == 0) {
+ *_ppid = getppid();
+ return 0;
+ }
+
+ p = procfs_file_alloca(pid, "stat");
+ r = read_one_line_file(p, &line);
+ if (r < 0)
+ return r;
+
+ /* Let's skip the pid and comm fields. The latter is enclosed
+ * in () but does not escape any () in its value, so let's
+ * skip over it manually */
+
+ p = strrchr(line, ')');
+ if (!p)
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " "
+ "%*c " /* state */
+ "%lu ", /* ppid */
+ &ppid) != 1)
+ return -EIO;
+
+ if ((long unsigned) (pid_t) ppid != ppid)
+ return -ERANGE;
+
+ *_ppid = (pid_t) ppid;
+
+ return 0;
+}
+
+int wait_for_terminate(pid_t pid, siginfo_t *status) {
+ siginfo_t dummy;
+
+ assert(pid >= 1);
+
+ if (!status)
+ status = &dummy;
+
+ for (;;) {
+ zero(*status);
+
+ if (waitid(P_PID, pid, status, WEXITED) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ return -errno;
+ }
+
+ return 0;
+ }
+}
+
+/*
+ * Return values:
+ * < 0 : wait_for_terminate() failed to get the state of the
+ * process, the process was terminated by a signal, or
+ * failed for an unknown reason.
+ * >=0 : The process terminated normally, and its exit code is
+ * returned.
+ *
+ * That is, success is indicated by a return value of zero, and an
+ * error is indicated by a non-zero value.
+ *
+ * A warning is emitted if the process terminates abnormally,
+ * and also if it returns non-zero unless check_exit_code is true.
+ */
+int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) {
+ int r;
+ siginfo_t status;
+
+ assert(name);
+ assert(pid > 1);
+
+ r = wait_for_terminate(pid, &status);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to wait for %s: %m", name);
+
+ if (status.si_code == CLD_EXITED) {
+ if (status.si_status != 0)
+ log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG,
+ "%s failed with error code %i.", name, status.si_status);
+ else
+ log_debug("%s succeeded.", name);
+
+ return status.si_status;
+ } else if (status.si_code == CLD_KILLED ||
+ status.si_code == CLD_DUMPED) {
+
+ log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
+ return -EPROTO;
+ }
+
+ log_warning("%s failed due to unknown reason.", name);
+ return -EPROTO;
+}
+
+int kill_and_sigcont(pid_t pid, int sig) {
+ int r;
+
+ r = kill(pid, sig) < 0 ? -errno : 0;
+
+ if (r >= 0)
+ kill(pid, SIGCONT);
+
+ return r;
+}
+
+int getenv_for_pid(pid_t pid, const char *field, char **_value) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char *value = NULL;
+ int r;
+ bool done = false;
+ size_t l;
+ const char *path;
+
+ assert(pid >= 0);
+ assert(field);
+ assert(_value);
+
+ path = procfs_file_alloca(pid, "environ");
+
+ f = fopen(path, "re");
+ if (!f)
+ return -errno;
+
+ l = strlen(field);
+ r = 0;
+
+ 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 (memcmp(line, field, l) == 0 && line[l] == '=') {
+ value = strdup(line + l + 1);
+ if (!value)
+ return -ENOMEM;
+
+ r = 1;
+ break;
+ }
+
+ } while (!done);
+
+ *_value = value;
+ return r;
+}
+
+bool pid_is_unwaited(pid_t pid) {
+ /* Checks whether a PID is still valid at all, including a zombie */
+
+ if (pid <= 0)
+ return false;
+
+ if (kill(pid, 0) >= 0)
+ return true;
+
+ return errno != ESRCH;
+}
+
+bool pid_is_alive(pid_t pid) {
+ int r;
+
+ /* Checks whether a PID is still valid and not a zombie */
+
+ if (pid <= 0)
+ return false;
+
+ r = get_process_state(pid);
+ if (r == -ENOENT || r == 'Z')
+ return false;
+
+ return true;
+}
diff --git a/src/shared/process-util.h b/src/shared/process-util.h
new file mode 100644
index 0000000000..07431d043b
--- /dev/null
+++ b/src/shared/process-util.h
@@ -0,0 +1,65 @@
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <alloca.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+#include "formats-util.h"
+
+#define procfs_file_alloca(pid, field) \
+ ({ \
+ pid_t _pid_ = (pid); \
+ const char *_r_; \
+ if (_pid_ == 0) { \
+ _r_ = ("/proc/self/" field); \
+ } else { \
+ _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
+ sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \
+ } \
+ _r_; \
+ })
+
+int get_process_state(pid_t pid);
+int get_process_comm(pid_t pid, char **name);
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
+int get_process_exe(pid_t pid, char **name);
+int get_process_uid(pid_t pid, uid_t *uid);
+int get_process_gid(pid_t pid, gid_t *gid);
+int get_process_capeff(pid_t pid, char **capeff);
+int get_process_cwd(pid_t pid, char **cwd);
+int get_process_root(pid_t pid, char **root);
+int get_process_environ(pid_t pid, char **environ);
+
+int wait_for_terminate(pid_t pid, siginfo_t *status);
+int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
+
+int kill_and_sigcont(pid_t pid, int sig);
+pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
+void rename_process(const char name[8]);
+int is_kernel_thread(pid_t pid);
+int getenv_for_pid(pid_t pid, const char *field, char **_value);
+
+bool pid_is_alive(pid_t pid);
+bool pid_is_unwaited(pid_t pid);
diff --git a/src/shared/pty.c b/src/shared/pty.c
index fbe6295ea5..119d66e9a2 100644
--- a/src/shared/pty.c
+++ b/src/shared/pty.c
@@ -44,18 +44,12 @@
#include <errno.h>
#include <fcntl.h>
-#include <limits.h>
-#include <linux/ioctl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/epoll.h>
-#include <sys/eventfd.h>
#include <sys/ioctl.h>
-#include <sys/types.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <termios.h>
@@ -63,9 +57,10 @@
#include "barrier.h"
#include "macro.h"
-#include "pty.h"
#include "ring.h"
#include "util.h"
+#include "signal-util.h"
+#include "pty.h"
#define PTY_BUFSIZE 4096
diff --git a/src/shared/pty.h b/src/shared/pty.h
index a87ceb58ca..63c7db2833 100644
--- a/src/shared/pty.h
+++ b/src/shared/pty.h
@@ -21,17 +21,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
#include <unistd.h>
#include "barrier.h"
#include "macro.h"
#include "sd-event.h"
-#include "util.h"
typedef struct Pty Pty;
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
index 31274a1418..789f217efc 100644
--- a/src/shared/ptyfwd.c
+++ b/src/shared/ptyfwd.c
@@ -20,7 +20,6 @@
***/
#include <sys/epoll.h>
-#include <sys/signalfd.h>
#include <sys/ioctl.h>
#include <limits.h>
#include <termios.h>
@@ -42,6 +41,8 @@ struct PTYForward {
struct termios saved_stdin_attr;
struct termios saved_stdout_attr;
+ bool read_only:1;
+
bool saved_stdin:1;
bool saved_stdout:1;
@@ -298,7 +299,13 @@ static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo *
return 0;
}
-int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, PTYForward **ret) {
+int pty_forward_new(
+ sd_event *event,
+ int master,
+ bool ignore_vhangup,
+ bool read_only,
+ PTYForward **ret) {
+
_cleanup_(pty_forward_freep) PTYForward *f = NULL;
struct winsize ws;
int r;
@@ -307,6 +314,7 @@ int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, PTYForward
if (!f)
return -ENOMEM;
+ f->read_only = read_only;
f->ignore_vhangup = ignore_vhangup;
if (event)
@@ -317,13 +325,15 @@ int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, PTYForward
return r;
}
- r = fd_nonblock(STDIN_FILENO, true);
- if (r < 0)
- return r;
+ if (!read_only) {
+ r = fd_nonblock(STDIN_FILENO, true);
+ if (r < 0)
+ return r;
- r = fd_nonblock(STDOUT_FILENO, true);
- if (r < 0)
- return r;
+ r = fd_nonblock(STDOUT_FILENO, true);
+ if (r < 0)
+ return r;
+ }
r = fd_nonblock(master, true);
if (r < 0)
@@ -332,38 +342,36 @@ int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, PTYForward
f->master = master;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
- (void)ioctl(master, TIOCSWINSZ, &ws);
-
- if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) {
- struct termios raw_stdin_attr;
+ (void) ioctl(master, TIOCSWINSZ, &ws);
- f->saved_stdin = true;
+ if (!read_only) {
+ if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) {
+ struct termios raw_stdin_attr;
- raw_stdin_attr = f->saved_stdin_attr;
- cfmakeraw(&raw_stdin_attr);
- raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag;
- tcsetattr(STDIN_FILENO, TCSANOW, &raw_stdin_attr);
- }
+ f->saved_stdin = true;
- if (tcgetattr(STDOUT_FILENO, &f->saved_stdout_attr) >= 0) {
- struct termios raw_stdout_attr;
+ raw_stdin_attr = f->saved_stdin_attr;
+ cfmakeraw(&raw_stdin_attr);
+ raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag;
+ tcsetattr(STDIN_FILENO, TCSANOW, &raw_stdin_attr);
+ }
- f->saved_stdout = true;
+ if (tcgetattr(STDOUT_FILENO, &f->saved_stdout_attr) >= 0) {
+ struct termios raw_stdout_attr;
- raw_stdout_attr = f->saved_stdout_attr;
- cfmakeraw(&raw_stdout_attr);
- raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag;
- raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag;
- tcsetattr(STDOUT_FILENO, TCSANOW, &raw_stdout_attr);
- }
+ f->saved_stdout = true;
- r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLIN|EPOLLOUT|EPOLLET, on_master_event, f);
- if (r < 0)
- return r;
+ raw_stdout_attr = f->saved_stdout_attr;
+ cfmakeraw(&raw_stdout_attr);
+ raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag;
+ raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag;
+ tcsetattr(STDOUT_FILENO, TCSANOW, &raw_stdout_attr);
+ }
- r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO, EPOLLIN|EPOLLET, on_stdin_event, f);
- if (r < 0 && r != -EPERM)
- return r;
+ r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO, EPOLLIN|EPOLLET, on_stdin_event, f);
+ if (r < 0 && r != -EPERM)
+ return r;
+ }
r = sd_event_add_io(f->event, &f->stdout_event_source, STDOUT_FILENO, EPOLLOUT|EPOLLET, on_stdout_event, f);
if (r == -EPERM)
@@ -372,6 +380,10 @@ int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, PTYForward
else if (r < 0)
return r;
+ r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLIN|EPOLLOUT|EPOLLET, on_master_event, f);
+ if (r < 0)
+ return r;
+
r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH, on_sigwinch_event, f);
if (r < 0)
return r;
diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h
index d3e229bd70..6f84e4036a 100644
--- a/src/shared/ptyfwd.h
+++ b/src/shared/ptyfwd.h
@@ -21,16 +21,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
-#include <signal.h>
#include <stdbool.h>
-#include "util.h"
#include "sd-event.h"
typedef struct PTYForward PTYForward;
-int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, PTYForward **f);
+int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, bool read_only, PTYForward **f);
PTYForward *pty_forward_free(PTYForward *f);
int pty_forward_get_last_char(PTYForward *f, char *ch);
diff --git a/src/shared/random-util.c b/src/shared/random-util.c
new file mode 100644
index 0000000000..b230044f50
--- /dev/null
+++ b/src/shared/random-util.c
@@ -0,0 +1,129 @@
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_SYS_AUXV_H
+#include <sys/auxv.h>
+#endif
+#include <linux/random.h>
+
+#include "random-util.h"
+#include "time-util.h"
+#include "missing.h"
+#include "util.h"
+
+int dev_urandom(void *p, size_t n) {
+ static int have_syscall = -1;
+
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ /* Gathers some randomness from the kernel. This call will
+ * never block, and will always return some data from the
+ * kernel, regardless if the random pool is fully initialized
+ * or not. It thus makes no guarantee for the quality of the
+ * returned entropy, but is good enough for or usual usecases
+ * of seeding the hash functions for hashtable */
+
+ /* Use the getrandom() syscall unless we know we don't have
+ * it, or when the requested size is too large for it. */
+ if (have_syscall != 0 || (size_t) (int) n != n) {
+ r = getrandom(p, n, GRND_NONBLOCK);
+ if (r == (int) n) {
+ have_syscall = true;
+ return 0;
+ }
+
+ if (r < 0) {
+ if (errno == ENOSYS)
+ /* we lack the syscall, continue with
+ * reading from /dev/urandom */
+ have_syscall = false;
+ else if (errno == EAGAIN)
+ /* not enough entropy for now. Let's
+ * remember to use the syscall the
+ * next time, again, but also read
+ * from /dev/urandom for now, which
+ * doesn't care about the current
+ * amount of entropy. */
+ have_syscall = true;
+ else
+ return -errno;
+ } else
+ /* too short read? */
+ return -ENODATA;
+ }
+
+ fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return errno == ENOENT ? -ENOSYS : -errno;
+
+ return loop_read_exact(fd, p, n, true);
+}
+
+void initialize_srand(void) {
+ static bool srand_called = false;
+ unsigned x;
+#ifdef HAVE_SYS_AUXV_H
+ void *auxv;
+#endif
+
+ if (srand_called)
+ return;
+
+ x = 0;
+
+#ifdef HAVE_SYS_AUXV_H
+ /* The kernel provides us with a bit of entropy in auxv, so
+ * let's try to make use of that to seed the pseudo-random
+ * generator. It's better than nothing... */
+
+ auxv = (void*) getauxval(AT_RANDOM);
+ if (auxv)
+ x ^= *(unsigned*) auxv;
+#endif
+
+ x ^= (unsigned) now(CLOCK_REALTIME);
+ x ^= (unsigned) gettid();
+
+ srand(x);
+ srand_called = true;
+}
+
+void random_bytes(void *p, size_t n) {
+ uint8_t *q;
+ int r;
+
+ r = dev_urandom(p, n);
+ if (r >= 0)
+ return;
+
+ /* If some idiot made /dev/urandom unavailable to us, he'll
+ * get a PRNG instead. */
+
+ initialize_srand();
+
+ for (q = p; q < (uint8_t*) p + n; q ++)
+ *q = rand();
+}
diff --git a/src/shared/random-util.h b/src/shared/random-util.h
new file mode 100644
index 0000000000..f7862c8c8b
--- /dev/null
+++ b/src/shared/random-util.h
@@ -0,0 +1,38 @@
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdint.h>
+
+int dev_urandom(void *p, size_t n);
+void random_bytes(void *p, size_t n);
+void initialize_srand(void);
+
+static inline uint64_t random_u64(void) {
+ uint64_t u;
+ random_bytes(&u, sizeof(u));
+ return u;
+}
+
+static inline uint32_t random_u32(void) {
+ uint32_t u;
+ random_bytes(&u, sizeof(u));
+ return u;
+}
diff --git a/src/shared/ratelimit.c b/src/shared/ratelimit.c
index 01b62b7b38..81fc9c19ff 100644
--- a/src/shared/ratelimit.c
+++ b/src/shared/ratelimit.c
@@ -19,10 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include "ratelimit.h"
-#include "log.h"
/* Modelled after Linux' lib/ratelimit.c by Dave Young
* <hidave.darkstar@gmail.com>, which is licensed GPLv2. */
diff --git a/src/shared/ring.c b/src/shared/ring.c
index 309075e348..6814918464 100644
--- a/src/shared/ring.c
+++ b/src/shared/ring.c
@@ -20,7 +20,6 @@
***/
#include <errno.h>
-#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
diff --git a/src/shared/ring.h b/src/shared/ring.h
index 1210aabdf6..a7c44d1b56 100644
--- a/src/shared/ring.h
+++ b/src/shared/ring.h
@@ -21,11 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/uio.h>
typedef struct Ring Ring;
diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c
new file mode 100644
index 0000000000..bafd483be2
--- /dev/null
+++ b/src/shared/rm-rf.c
@@ -0,0 +1,224 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "path-util.h"
+#include "btrfs-util.h"
+#include "rm-rf.h"
+
+int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
+ _cleanup_closedir_ DIR *d = NULL;
+ int ret = 0, r;
+
+ assert(fd >= 0);
+
+ /* This returns the first error we run into, but nevertheless
+ * tries to go on. This closes the passed fd. */
+
+ if (!(flags & REMOVE_PHYSICAL)) {
+
+ r = fd_is_temporary_fs(fd);
+ if (r < 0) {
+ safe_close(fd);
+ return r;
+ }
+
+ if (!r) {
+ /* We refuse to clean physical file systems
+ * with this call, unless explicitly
+ * requested. This is extra paranoia just to
+ * be sure we never ever remove non-state
+ * data */
+
+ log_error("Attempted to remove disk file system, and we can't allow that.");
+ safe_close(fd);
+ return -EPERM;
+ }
+ }
+
+ d = fdopendir(fd);
+ if (!d) {
+ safe_close(fd);
+ return errno == ENOENT ? 0 : -errno;
+ }
+
+ for (;;) {
+ struct dirent *de;
+ bool is_dir;
+ struct stat st;
+
+ errno = 0;
+ de = readdir(d);
+ if (!de) {
+ if (errno != 0 && ret == 0)
+ ret = -errno;
+ return ret;
+ }
+
+ if (streq(de->d_name, ".") || streq(de->d_name, ".."))
+ continue;
+
+ if (de->d_type == DT_UNKNOWN ||
+ (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
+ if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ continue;
+ }
+
+ is_dir = S_ISDIR(st.st_mode);
+ } else
+ is_dir = de->d_type == DT_DIR;
+
+ if (is_dir) {
+ int subdir_fd;
+
+ /* if root_dev is set, remove subdirectories only if device is same */
+ if (root_dev && st.st_dev != root_dev->st_dev)
+ continue;
+
+ subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+ if (subdir_fd < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ continue;
+ }
+
+ /* Stop at mount points */
+ r = fd_is_mount_point(fd, de->d_name, 0);
+ if (r < 0) {
+ if (ret == 0 && r != -ENOENT)
+ ret = r;
+
+ safe_close(subdir_fd);
+ continue;
+ }
+ if (r) {
+ safe_close(subdir_fd);
+ continue;
+ }
+
+ if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) {
+
+ /* This could be a subvolume, try to remove it */
+
+ r = btrfs_subvol_remove_fd(fd, de->d_name, true);
+ if (r < 0) {
+ if (r != -ENOTTY && r != -EINVAL) {
+ if (ret == 0)
+ ret = r;
+
+ safe_close(subdir_fd);
+ continue;
+ }
+
+ /* ENOTTY, then it wasn't a
+ * btrfs subvolume, continue
+ * below. */
+ } else {
+ /* It was a subvolume, continue. */
+ safe_close(subdir_fd);
+ continue;
+ }
+ }
+
+ /* We pass REMOVE_PHYSICAL here, to avoid
+ * doing the fstatfs() to check the file
+ * system type again for each directory */
+ r = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev);
+ if (r < 0 && ret == 0)
+ ret = r;
+
+ if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ }
+
+ } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
+
+ if (unlinkat(fd, de->d_name, 0) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ }
+ }
+ }
+}
+
+int rm_rf(const char *path, RemoveFlags flags) {
+ int fd, r;
+ struct statfs s;
+
+ assert(path);
+
+ /* We refuse to clean the root file system with this
+ * call. This is extra paranoia to never cause a really
+ * seriously broken system. */
+ if (path_equal(path, "/")) {
+ log_error("Attempted to remove entire root file system, and we can't allow that.");
+ return -EPERM;
+ }
+
+ if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) {
+ /* Try to remove as subvolume first */
+ r = btrfs_subvol_remove(path, true);
+ if (r >= 0)
+ return r;
+
+ if (r != -ENOTTY && r != -EINVAL)
+ return r;
+
+ /* Not btrfs or not a subvolume */
+ }
+
+ fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+ if (fd < 0) {
+
+ if (errno != ENOTDIR && errno != ELOOP)
+ return -errno;
+
+ if (!(flags & REMOVE_PHYSICAL)) {
+ if (statfs(path, &s) < 0)
+ return -errno;
+
+ if (!is_temporary_fs(&s)) {
+ log_error("Attempted to remove disk file system, and we can't allow that.");
+ return -EPERM;
+ }
+ }
+
+ if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES))
+ if (unlink(path) < 0 && errno != ENOENT)
+ return -errno;
+
+ return 0;
+ }
+
+ r = rm_rf_children(fd, flags, NULL);
+
+ if (flags & REMOVE_ROOT) {
+ if (rmdir(path) < 0) {
+ if (r == 0 && errno != ENOENT)
+ r = -errno;
+ }
+ }
+
+ return r;
+}
diff --git a/src/shared/rm-rf.h b/src/shared/rm-rf.h
new file mode 100644
index 0000000000..96579eb182
--- /dev/null
+++ b/src/shared/rm-rf.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/stat.h>
+
+typedef enum RemoveFlags {
+ REMOVE_ONLY_DIRECTORIES = 1,
+ REMOVE_ROOT = 2,
+ REMOVE_PHYSICAL = 4, /* if not set, only removes files on tmpfs, never physical file systems */
+ REMOVE_SUBVOLUME = 8,
+} RemoveFlags;
+
+int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
+int rm_rf(const char *path, RemoveFlags flags);
diff --git a/src/shared/selinux-util.c b/src/shared/selinux-util.c
index a2233e0cfb..7c58985cd2 100644
--- a/src/shared/selinux-util.c
+++ b/src/shared/selinux-util.c
@@ -20,7 +20,6 @@
***/
#include <errno.h>
-#include <unistd.h>
#include <malloc.h>
#include <sys/un.h>
@@ -117,6 +116,7 @@ void mac_selinux_finish(void) {
return;
selabel_close(label_hnd);
+ label_hnd = NULL;
#endif
}
@@ -146,7 +146,7 @@ int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
r = lsetfilecon(path, fcon);
/* If the FS doesn't support labels, then exit without warning */
- if (r < 0 && errno == ENOTSUP)
+ if (r < 0 && errno == EOPNOTSUPP)
return 0;
}
}
diff --git a/src/shared/selinux-util.h b/src/shared/selinux-util.h
index a694441000..8467185291 100644
--- a/src/shared/selinux-util.h
+++ b/src/shared/selinux-util.h
@@ -22,7 +22,6 @@
***/
#include <sys/socket.h>
-#include <stdio.h>
#include <stdbool.h>
bool mac_selinux_use(void);
diff --git a/src/shared/set.h b/src/shared/set.h
index 2b49e2f287..4dffecd39d 100644
--- a/src/shared/set.h
+++ b/src/shared/set.h
@@ -57,7 +57,7 @@ static inline bool set_contains(Set *s, const void *key) {
return internal_hashmap_contains(HASHMAP_BASE(s), key);
}
-static inline void *set_remove(Set *s, void *key) {
+static inline void *set_remove(Set *s, const void *key) {
return internal_hashmap_remove(HASHMAP_BASE(s), key);
}
diff --git a/src/shared/signal-util.c b/src/shared/signal-util.c
new file mode 100644
index 0000000000..9a2973b6fd
--- /dev/null
+++ b/src/shared/signal-util.c
@@ -0,0 +1,228 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "signal-util.h"
+
+int reset_all_signal_handlers(void) {
+ int sig, r = 0;
+
+ for (sig = 1; sig < _NSIG; sig++) {
+ static const struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ .sa_flags = SA_RESTART,
+ };
+
+ /* These two cannot be caught... */
+ if (sig == SIGKILL || sig == SIGSTOP)
+ continue;
+
+ /* On Linux the first two RT signals are reserved by
+ * glibc, and sigaction() will return EINVAL for them. */
+ if ((sigaction(sig, &sa, NULL) < 0))
+ if (errno != EINVAL && r == 0)
+ r = -errno;
+ }
+
+ return r;
+}
+
+int reset_signal_mask(void) {
+ sigset_t ss;
+
+ if (sigemptyset(&ss) < 0)
+ return -errno;
+
+ if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int sigaction_many(const struct sigaction *sa, ...) {
+ va_list ap;
+ int r = 0, sig;
+
+ va_start(ap, sa);
+ while ((sig = va_arg(ap, int)) > 0)
+ if (sigaction(sig, sa, NULL) < 0)
+ r = -errno;
+ va_end(ap);
+
+ return r;
+}
+
+int ignore_signals(int sig, ...) {
+ static const struct sigaction sa = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_RESTART,
+ };
+ va_list ap;
+ int r = 0;
+
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+
+ va_start(ap, sig);
+ while ((sig = va_arg(ap, int)) > 0)
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+ va_end(ap);
+
+ return r;
+}
+
+int default_signals(int sig, ...) {
+ static const struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ .sa_flags = SA_RESTART,
+ };
+ va_list ap;
+ int r = 0;
+
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+
+ va_start(ap, sig);
+ while ((sig = va_arg(ap, int)) > 0)
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+ va_end(ap);
+
+ return r;
+}
+
+void sigset_add_many(sigset_t *ss, ...) {
+ va_list ap;
+ int sig;
+
+ assert(ss);
+
+ va_start(ap, ss);
+ while ((sig = va_arg(ap, int)) > 0)
+ assert_se(sigaddset(ss, sig) == 0);
+ va_end(ap);
+}
+
+int sigprocmask_many(int how, ...) {
+ va_list ap;
+ sigset_t ss;
+ int sig;
+
+ assert_se(sigemptyset(&ss) == 0);
+
+ va_start(ap, how);
+ while ((sig = va_arg(ap, int)) > 0)
+ assert_se(sigaddset(&ss, sig) == 0);
+ va_end(ap);
+
+ if (sigprocmask(how, &ss, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static const char *const __signal_table[] = {
+ [SIGHUP] = "HUP",
+ [SIGINT] = "INT",
+ [SIGQUIT] = "QUIT",
+ [SIGILL] = "ILL",
+ [SIGTRAP] = "TRAP",
+ [SIGABRT] = "ABRT",
+ [SIGBUS] = "BUS",
+ [SIGFPE] = "FPE",
+ [SIGKILL] = "KILL",
+ [SIGUSR1] = "USR1",
+ [SIGSEGV] = "SEGV",
+ [SIGUSR2] = "USR2",
+ [SIGPIPE] = "PIPE",
+ [SIGALRM] = "ALRM",
+ [SIGTERM] = "TERM",
+#ifdef SIGSTKFLT
+ [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
+#endif
+ [SIGCHLD] = "CHLD",
+ [SIGCONT] = "CONT",
+ [SIGSTOP] = "STOP",
+ [SIGTSTP] = "TSTP",
+ [SIGTTIN] = "TTIN",
+ [SIGTTOU] = "TTOU",
+ [SIGURG] = "URG",
+ [SIGXCPU] = "XCPU",
+ [SIGXFSZ] = "XFSZ",
+ [SIGVTALRM] = "VTALRM",
+ [SIGPROF] = "PROF",
+ [SIGWINCH] = "WINCH",
+ [SIGIO] = "IO",
+ [SIGPWR] = "PWR",
+ [SIGSYS] = "SYS"
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
+
+const char *signal_to_string(int signo) {
+ static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
+ const char *name;
+
+ name = __signal_to_string(signo);
+ if (name)
+ return name;
+
+ if (signo >= SIGRTMIN && signo <= SIGRTMAX)
+ snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
+ else
+ snprintf(buf, sizeof(buf), "%d", signo);
+
+ return buf;
+}
+
+int signal_from_string(const char *s) {
+ int signo;
+ int offset = 0;
+ unsigned u;
+
+ signo = __signal_from_string(s);
+ if (signo > 0)
+ return signo;
+
+ if (startswith(s, "RTMIN+")) {
+ s += 6;
+ offset = SIGRTMIN;
+ }
+ if (safe_atou(s, &u) >= 0) {
+ signo = (int) u + offset;
+ if (signo > 0 && signo < _NSIG)
+ return signo;
+ }
+ return -EINVAL;
+}
+
+int signal_from_string_try_harder(const char *s) {
+ int signo;
+ assert(s);
+
+ signo = signal_from_string(s);
+ if (signo <= 0)
+ if (startswith(s, "SIG"))
+ return signal_from_string(s+3);
+
+ return signo;
+}
diff --git a/src/shared/signal-util.h b/src/shared/signal-util.h
new file mode 100644
index 0000000000..ddf64cda76
--- /dev/null
+++ b/src/shared/signal-util.h
@@ -0,0 +1,41 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2015 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <signal.h>
+
+#include "macro.h"
+
+int reset_all_signal_handlers(void);
+int reset_signal_mask(void);
+
+int ignore_signals(int sig, ...);
+int default_signals(int sig, ...);
+int sigaction_many(const struct sigaction *sa, ...);
+
+void sigset_add_many(sigset_t *ss, ...);
+int sigprocmask_many(int how, ...);
+
+const char *signal_to_string(int i) _const_;
+int signal_from_string(const char *s) _pure_;
+
+int signal_from_string_try_harder(const char *s);
diff --git a/src/shared/smack-util.c b/src/shared/smack-util.c
index 64e213489e..2e24b1ea99 100644
--- a/src/shared/smack-util.c
+++ b/src/shared/smack-util.c
@@ -24,6 +24,7 @@
#include <sys/xattr.h>
#include "util.h"
+#include "process-util.h"
#include "path-util.h"
#include "fileio.h"
#include "smack-util.h"
@@ -187,7 +188,7 @@ int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
r = lsetxattr(path, "security.SMACK64", label, strlen(label), 0);
/* If the FS doesn't support labels, then exit without warning */
- if (r < 0 && errno == ENOTSUP)
+ if (r < 0 && errno == EOPNOTSUPP)
return 0;
}
diff --git a/src/shared/socket-label.c b/src/shared/socket-label.c
index 6806c51158..cbe3ff216e 100644
--- a/src/shared/socket-label.c
+++ b/src/shared/socket-label.c
@@ -19,24 +19,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
-#include <stdlib.h>
-#include <arpa/inet.h>
-#include <stdio.h>
-#include <net/if.h>
-#include <sys/types.h>
#include <sys/stat.h>
#include <stddef.h>
-#include <sys/ioctl.h>
#include "macro.h"
#include "util.h"
#include "mkdir.h"
#include "missing.h"
-#include "label.h"
#include "selinux-util.h"
#include "socket-util.h"
@@ -117,9 +109,6 @@ int socket_address_listen(
/* Enforce the right access mode for the socket */
old_mask = umask(~ socket_mode);
- /* Include the original umask in our mask */
- umask(~socket_mode | old_mask);
-
r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
if (r < 0 && errno == EADDRINUSE) {
diff --git a/src/shared/socket-util.c b/src/shared/socket-util.c
index c6f64876be..e8bb10dc9b 100644
--- a/src/shared/socket-util.c
+++ b/src/shared/socket-util.c
@@ -19,27 +19,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
-#include <stdlib.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <net/if.h>
#include <sys/types.h>
-#include <sys/stat.h>
#include <stddef.h>
-#include <sys/ioctl.h>
#include <netdb.h>
#include "macro.h"
-#include "util.h"
-#include "mkdir.h"
#include "path-util.h"
+#include "util.h"
#include "socket-util.h"
#include "missing.h"
#include "fileio.h"
+#include "formats-util.h"
int socket_address_parse(SocketAddress *a, const char *s) {
char *e, *n;
@@ -55,11 +51,6 @@ int socket_address_parse(SocketAddress *a, const char *s) {
if (*s == '[') {
/* IPv6 in [x:.....:z]:p notation */
- if (!socket_ipv6_is_supported()) {
- log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
- return -EAFNOSUPPORT;
- }
-
e = strchr(s+1, ']');
if (!e)
return -EINVAL;
@@ -144,11 +135,6 @@ int socket_address_parse(SocketAddress *a, const char *s) {
if (idx == 0)
return -EINVAL;
- if (!socket_ipv6_is_supported()) {
- log_warning("Binding to interface is not available since kernel does not support IPv6.");
- return -EAFNOSUPPORT;
- }
-
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
a->sockaddr.in6.sin6_scope_id = idx;
@@ -182,6 +168,25 @@ int socket_address_parse(SocketAddress *a, const char *s) {
return 0;
}
+int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
+ SocketAddress b;
+ int r;
+
+ /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
+
+ r = socket_address_parse(&b, s);
+ if (r < 0)
+ return r;
+
+ if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
+ log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
+ return -EAFNOSUPPORT;
+ }
+
+ *a = b;
+ return 0;
+}
+
int socket_address_parse_netlink(SocketAddress *a, const char *s) {
int family;
unsigned group = 0;
@@ -302,7 +307,7 @@ int socket_address_print(const SocketAddress *a, char **ret) {
return 0;
}
- return sockaddr_pretty(&a->sockaddr.sa, a->size, false, ret);
+ return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret);
}
bool socket_address_can_accept(const SocketAddress *a) {
@@ -325,9 +330,6 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
if (a->type != b->type)
return false;
- if (a->size != b->size)
- return false;
-
if (socket_address_family(a) != socket_address_family(b))
return false;
@@ -352,14 +354,20 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
break;
case AF_UNIX:
+ if (a->size <= offsetof(struct sockaddr_un, sun_path) ||
+ b->size <= offsetof(struct sockaddr_un, sun_path))
+ return false;
if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
return false;
if (a->sockaddr.un.sun_path[0]) {
- if (!strneq(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)))
+ if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path))
return false;
} else {
+ if (a->size != b->size)
+ return false;
+
if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
return false;
}
@@ -367,7 +375,6 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
break;
case AF_NETLINK:
-
if (a->protocol != b->protocol)
return false;
@@ -437,57 +444,55 @@ bool socket_ipv6_is_supported(void) {
}
bool socket_address_matches_fd(const SocketAddress *a, int fd) {
- union sockaddr_union sa;
- socklen_t salen = sizeof(sa), solen;
- int protocol, type;
+ SocketAddress b;
+ socklen_t solen;
assert(a);
assert(fd >= 0);
- if (getsockname(fd, &sa.sa, &salen) < 0)
+ b.size = sizeof(b.sockaddr);
+ if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0)
return false;
- if (sa.sa.sa_family != a->sockaddr.sa.sa_family)
+ if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family)
return false;
- solen = sizeof(type);
- if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &solen) < 0)
+ solen = sizeof(b.type);
+ if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0)
return false;
- if (type != a->type)
+ if (b.type != a->type)
return false;
if (a->protocol != 0) {
- solen = sizeof(protocol);
- if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &solen) < 0)
+ solen = sizeof(b.protocol);
+ if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0)
return false;
- if (protocol != a->protocol)
+ if (b.protocol != a->protocol)
return false;
}
- switch (sa.sa.sa_family) {
-
- case AF_INET:
- return sa.in.sin_port == a->sockaddr.in.sin_port &&
- sa.in.sin_addr.s_addr == a->sockaddr.in.sin_addr.s_addr;
+ return socket_address_equal(a, &b);
+}
- case AF_INET6:
- return sa.in6.sin6_port == a->sockaddr.in6.sin6_port &&
- memcmp(&sa.in6.sin6_addr, &a->sockaddr.in6.sin6_addr, sizeof(struct in6_addr)) == 0;
+int sockaddr_port(const struct sockaddr *_sa) {
+ union sockaddr_union *sa = (union sockaddr_union*) _sa;
- case AF_UNIX:
- return salen == a->size &&
- memcmp(sa.un.sun_path, a->sockaddr.un.sun_path, salen - offsetof(struct sockaddr_un, sun_path)) == 0;
+ assert(sa);
- }
+ if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
+ return -EAFNOSUPPORT;
- return false;
+ return ntohs(sa->sa.sa_family == AF_INET6 ?
+ sa->in6.sin6_port :
+ sa->in.sin_port);
}
-int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, char **ret) {
+int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
union sockaddr_union *sa = (union sockaddr_union*) _sa;
char *p;
+ int r;
assert(sa);
assert(salen >= sizeof(sa->sa.sa_family));
@@ -499,12 +504,17 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
a = ntohl(sa->in.sin_addr.s_addr);
- if (asprintf(&p,
- "%u.%u.%u.%u:%u",
- a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
- ntohs(sa->in.sin_port)) < 0)
+ if (include_port)
+ r = asprintf(&p,
+ "%u.%u.%u.%u:%u",
+ a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
+ ntohs(sa->in.sin_port));
+ else
+ r = asprintf(&p,
+ "%u.%u.%u.%u",
+ a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF);
+ if (r < 0)
return -ENOMEM;
-
break;
}
@@ -513,22 +523,37 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
};
- if (translate_ipv6 && memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
+ if (translate_ipv6 &&
+ memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
const uint8_t *a = sa->in6.sin6_addr.s6_addr+12;
-
- if (asprintf(&p,
- "%u.%u.%u.%u:%u",
- a[0], a[1], a[2], a[3],
- ntohs(sa->in6.sin6_port)) < 0)
+ if (include_port)
+ r = asprintf(&p,
+ "%u.%u.%u.%u:%u",
+ a[0], a[1], a[2], a[3],
+ ntohs(sa->in6.sin6_port));
+ else
+ r = asprintf(&p,
+ "%u.%u.%u.%u",
+ a[0], a[1], a[2], a[3]);
+ if (r < 0)
return -ENOMEM;
} else {
char a[INET6_ADDRSTRLEN];
- if (asprintf(&p,
- "[%s]:%u",
- inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)),
- ntohs(sa->in6.sin6_port)) < 0)
- return -ENOMEM;
+ inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
+
+ if (include_port) {
+ r = asprintf(&p,
+ "[%s]:%u",
+ a,
+ ntohs(sa->in6.sin6_port));
+ if (r < 0)
+ return -ENOMEM;
+ } else {
+ p = strdup(a);
+ if (!p)
+ return -ENOMEM;
+ }
}
break;
@@ -565,7 +590,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
break;
default:
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
@@ -603,7 +628,7 @@ int getpeername_pretty(int fd, char **ret) {
/* For remote sockets we translate IPv6 addresses back to IPv4
* if applicable, since that's nicer. */
- return sockaddr_pretty(&sa.sa, salen, true, ret);
+ return sockaddr_pretty(&sa.sa, salen, true, true, ret);
}
int getsockname_pretty(int fd, char **ret) {
@@ -621,7 +646,7 @@ int getsockname_pretty(int fd, char **ret) {
* listening sockets where the difference between IPv4 and
* IPv6 matters. */
- return sockaddr_pretty(&sa.sa, salen, false, ret);
+ return sockaddr_pretty(&sa.sa, salen, false, true, ret);
}
int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
@@ -635,7 +660,7 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret)
if (r != 0) {
int saved_errno = errno;
- r = sockaddr_pretty(&sa->sa, salen, true, &ret);
+ r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
if (r < 0)
return log_error_errno(r, "sockadd_pretty() failed: %m");
diff --git a/src/shared/socket-util.h b/src/shared/socket-util.h
index 07d0aff72b..538cf59174 100644
--- a/src/shared/socket-util.h
+++ b/src/shared/socket-util.h
@@ -25,7 +25,6 @@
#include <netinet/in.h>
#include <netinet/ether.h>
#include <sys/un.h>
-#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/if_packet.h>
@@ -67,6 +66,7 @@ typedef enum SocketAddressBindIPv6Only {
#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
int socket_address_parse(SocketAddress *a, const char *s);
+int socket_address_parse_and_warn(SocketAddress *a, const char *s);
int socket_address_parse_netlink(SocketAddress *a, const char *s);
int socket_address_print(const SocketAddress *a, char **p);
int socket_address_verify(const SocketAddress *a) _pure_;
@@ -98,7 +98,9 @@ const char* socket_address_get_path(const SocketAddress *a);
bool socket_ipv6_is_supported(void);
-int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, char **ret);
+int sockaddr_port(const struct sockaddr *_sa) _pure_;
+
+int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
int getpeername_pretty(int fd, char **ret);
int getsockname_pretty(int fd, char **ret);
diff --git a/src/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c
index ee267833e6..70466d17e5 100644
--- a/src/shared/spawn-ask-password-agent.c
+++ b/src/shared/spawn-ask-password-agent.c
@@ -19,16 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
-#include <string.h>
-#include <sys/prctl.h>
#include <signal.h>
-#include <fcntl.h>
#include "log.h"
#include "util.h"
+#include "process-util.h"
#include "spawn-ask-password-agent.h"
static pid_t agent_pid = 0;
diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c
index 8f259a8f39..4db249e1ca 100644
--- a/src/shared/spawn-polkit-agent.c
+++ b/src/shared/spawn-polkit-agent.c
@@ -19,18 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
-#include <string.h>
-#include <sys/prctl.h>
#include <signal.h>
-#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include "log.h"
#include "util.h"
+#include "process-util.h"
#include "spawn-polkit-agent.h"
#ifdef ENABLE_POLKIT
diff --git a/src/shared/special.h b/src/shared/special.h
index b045047d36..e51310eb6d 100644
--- a/src/shared/special.h
+++ b/src/shared/special.h
@@ -42,6 +42,8 @@
/* Special boot targets */
#define SPECIAL_RESCUE_TARGET "rescue.target"
#define SPECIAL_EMERGENCY_TARGET "emergency.target"
+#define SPECIAL_MULTI_USER_TARGET "multi-user.target"
+#define SPECIAL_GRAPHICAL_TARGET "graphical.target"
/* Early boot targets */
#define SPECIAL_SYSINIT_TARGET "sysinit.target"
@@ -108,13 +110,6 @@
#define SPECIAL_SIGPWR_TARGET "sigpwr.target"
#define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target"
-/* For SysV compatibility. Usually an alias for a saner target. On
- * SysV-free systems this doesn't exist. */
-#define SPECIAL_RUNLEVEL2_TARGET "runlevel2.target"
-#define SPECIAL_RUNLEVEL3_TARGET "runlevel3.target"
-#define SPECIAL_RUNLEVEL4_TARGET "runlevel4.target"
-#define SPECIAL_RUNLEVEL5_TARGET "runlevel5.target"
-
/* Where we add all our system units, users and machines by default */
#define SPECIAL_SYSTEM_SLICE "system.slice"
#define SPECIAL_USER_SLICE "user.slice"
diff --git a/src/shared/specifier.c b/src/shared/specifier.c
index 8fbf6db5df..85bd477f2d 100644
--- a/src/shared/specifier.c
+++ b/src/shared/specifier.c
@@ -24,6 +24,7 @@
#include "macro.h"
#include "util.h"
+#include "hostname-util.h"
#include "specifier.h"
/*
diff --git a/src/shared/strbuf.h b/src/shared/strbuf.h
index 2347fd4328..fbc4e5f2a1 100644
--- a/src/shared/strbuf.h
+++ b/src/shared/strbuf.h
@@ -21,9 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdarg.h>
#include <stdint.h>
-#include <stdbool.h>
struct strbuf {
char *buf;
diff --git a/src/shared/strv.c b/src/shared/strv.c
index e27ac68151..d44a72fc48 100644
--- a/src/shared/strv.c
+++ b/src/shared/strv.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
@@ -81,9 +80,10 @@ void strv_clear(char **l) {
*l = NULL;
}
-void strv_free(char **l) {
+char **strv_free(char **l) {
strv_clear(l);
free(l);
+ return NULL;
}
char **strv_copy(char * const *l) {
@@ -278,7 +278,7 @@ char **strv_split_newlines(const char *s) {
return l;
}
-int strv_split_quoted(char ***t, const char *s, bool relax) {
+int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags) {
size_t n = 0, allocated = 0;
_cleanup_strv_free_ char **l = NULL;
int r;
@@ -289,7 +289,7 @@ int strv_split_quoted(char ***t, const char *s, bool relax) {
for (;;) {
_cleanup_free_ char *word = NULL;
- r = unquote_first_word(&s, &word, relax);
+ r = unquote_first_word(&s, &word, flags);
if (r < 0)
return r;
if (r == 0)
diff --git a/src/shared/strv.h b/src/shared/strv.h
index 518c4c2aa8..22f8f98fda 100644
--- a/src/shared/strv.h
+++ b/src/shared/strv.h
@@ -31,7 +31,7 @@ char *strv_find(char **l, const char *name) _pure_;
char *strv_find_prefix(char **l, const char *name) _pure_;
char *strv_find_startswith(char **l, const char *name) _pure_;
-void strv_free(char **l);
+char **strv_free(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
#define _cleanup_strv_free_ _cleanup_(strv_freep)
@@ -73,7 +73,7 @@ static inline bool strv_isempty(char * const *l) {
char **strv_split(const char *s, const char *separator);
char **strv_split_newlines(const char *s);
-int strv_split_quoted(char ***t, const char *s, bool relax);
+int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags);
char *strv_join(char **l, const char *separator);
char *strv_join_quoted(char **l);
diff --git a/src/shared/strxcpyx.h b/src/shared/strxcpyx.h
index 7be246d570..ccc7e52f37 100644
--- a/src/shared/strxcpyx.h
+++ b/src/shared/strxcpyx.h
@@ -21,8 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdarg.h>
-#include <stdbool.h>
#include "macro.h"
diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c
index 813641ad44..b12189cd10 100644
--- a/src/shared/switch-root.c
+++ b/src/shared/switch-root.c
@@ -29,10 +29,11 @@
#include "util.h"
#include "path-util.h"
-#include "switch-root.h"
#include "mkdir.h"
+#include "rm-rf.h"
#include "base-filesystem.h"
#include "missing.h"
+#include "switch-root.h"
int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags) {
@@ -104,7 +105,7 @@ int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot,
* to look like. They might even boot, if they are RO and
* don't have the FS layout. Just ignore the error and
* switch_root() nevertheless. */
- (void) base_filesystem_create(new_root);
+ (void) base_filesystem_create(new_root, UID_INVALID, GID_INVALID);
if (chdir(new_root) < 0)
return log_error_errno(errno, "Failed to change directory to %s: %m", new_root);
@@ -142,7 +143,7 @@ int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot,
if (fstat(old_root_fd, &rb) < 0)
log_warning_errno(errno, "Failed to stat old root directory, leaving: %m");
else {
- rm_rf_children(old_root_fd, false, false, &rb);
+ (void) rm_rf_children(old_root_fd, 0, &rb);
old_root_fd = -1;
}
}
diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c
new file mode 100644
index 0000000000..55f4e48601
--- /dev/null
+++ b/src/shared/sysctl-util.c
@@ -0,0 +1,80 @@
+/*-*- 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <getopt.h>
+
+#include "log.h"
+#include "util.h"
+#include "fileio.h"
+#include "build.h"
+#include "sysctl-util.h"
+
+char *sysctl_normalize(char *s) {
+ char *n;
+
+ n = strpbrk(s, "/.");
+ /* If the first separator is a slash, the path is
+ * assumed to be normalized and slashes remain slashes
+ * and dots remains dots. */
+ if (!n || *n == '/')
+ return s;
+
+ /* Otherwise, dots become slashes and slashes become
+ * dots. Fun. */
+ while (n) {
+ if (*n == '.')
+ *n = '/';
+ else
+ *n = '.';
+
+ n = strpbrk(n + 1, "/.");
+ }
+
+ return s;
+}
+
+int sysctl_write(const char *property, const char *value) {
+ char *p;
+
+ assert(property);
+ assert(value);
+
+ log_debug("Setting '%s' to '%s'", property, value);
+
+ p = strjoina("/proc/sys/", property);
+ return write_string_file(p, value);
+}
+
+int sysctl_read(const char *property, char **content) {
+ char *p;
+
+ assert(property);
+ assert(content);
+
+ p = strjoina("/proc/sys/", property);
+ return read_full_file(p, content, NULL);
+}
diff --git a/src/shared/time-dst.h b/src/shared/sysctl-util.h
index 536b6bbdfb..2ee6454e52 100644
--- a/src/shared/time-dst.h
+++ b/src/shared/sysctl-util.h
@@ -5,7 +5,7 @@
/***
This file is part of systemd.
- Copyright 2012 Kay Sievers
+ 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
@@ -21,6 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-int time_get_dst(time_t date, const char *tzfile,
- time_t *switch_cur, char **zone_cur, bool *dst_cur,
- time_t *switch_next, int *delta_next, char **zone_next, bool *dst_next);
+char *sysctl_normalize(char *s);
+int sysctl_read(const char *property, char **value);
+int sysctl_write(const char *property, const char *value);
+
diff --git a/src/shared/terminal-util.c b/src/shared/terminal-util.c
new file mode 100644
index 0000000000..042b88f222
--- /dev/null
+++ b/src/shared/terminal-util.c
@@ -0,0 +1,1072 @@
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <assert.h>
+#include <poll.h>
+#include <linux/vt.h>
+#include <linux/tiocl.h>
+#include <linux/kd.h>
+
+#include "terminal-util.h"
+#include "time-util.h"
+#include "process-util.h"
+#include "util.h"
+#include "fileio.h"
+#include "path-util.h"
+
+static volatile unsigned cached_columns = 0;
+static volatile unsigned cached_lines = 0;
+
+int chvt(int vt) {
+ _cleanup_close_ int fd;
+
+ fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (vt < 0) {
+ int tiocl[2] = {
+ TIOCL_GETKMSGREDIRECT,
+ 0
+ };
+
+ if (ioctl(fd, TIOCLINUX, tiocl) < 0)
+ return -errno;
+
+ vt = tiocl[0] <= 0 ? 1 : tiocl[0];
+ }
+
+ if (ioctl(fd, VT_ACTIVATE, vt) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
+ struct termios old_termios, new_termios;
+ char c, line[LINE_MAX];
+
+ assert(f);
+ assert(ret);
+
+ if (tcgetattr(fileno(f), &old_termios) >= 0) {
+ new_termios = old_termios;
+
+ new_termios.c_lflag &= ~ICANON;
+ new_termios.c_cc[VMIN] = 1;
+ new_termios.c_cc[VTIME] = 0;
+
+ if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
+ size_t k;
+
+ if (t != USEC_INFINITY) {
+ if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
+ tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+ return -ETIMEDOUT;
+ }
+ }
+
+ k = fread(&c, 1, 1, f);
+
+ tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+
+ if (k <= 0)
+ return -EIO;
+
+ if (need_nl)
+ *need_nl = c != '\n';
+
+ *ret = c;
+ return 0;
+ }
+ }
+
+ if (t != USEC_INFINITY) {
+ if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
+ return -ETIMEDOUT;
+ }
+
+ errno = 0;
+ if (!fgets(line, sizeof(line), f))
+ return errno ? -errno : -EIO;
+
+ truncate_nl(line);
+
+ if (strlen(line) != 1)
+ return -EBADMSG;
+
+ if (need_nl)
+ *need_nl = false;
+
+ *ret = line[0];
+ return 0;
+}
+
+int ask_char(char *ret, const char *replies, const char *text, ...) {
+ int r;
+
+ assert(ret);
+ assert(replies);
+ assert(text);
+
+ for (;;) {
+ va_list ap;
+ char c;
+ bool need_nl = true;
+
+ if (on_tty())
+ fputs(ANSI_HIGHLIGHT_ON, stdout);
+
+ va_start(ap, text);
+ vprintf(text, ap);
+ va_end(ap);
+
+ if (on_tty())
+ fputs(ANSI_HIGHLIGHT_OFF, stdout);
+
+ fflush(stdout);
+
+ r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl);
+ if (r < 0) {
+
+ if (r == -EBADMSG) {
+ puts("Bad input, please try again.");
+ continue;
+ }
+
+ putchar('\n');
+ return r;
+ }
+
+ if (need_nl)
+ putchar('\n');
+
+ if (strchr(replies, c)) {
+ *ret = c;
+ return 0;
+ }
+
+ puts("Read unexpected character, please try again.");
+ }
+}
+
+int ask_string(char **ret, const char *text, ...) {
+ assert(ret);
+ assert(text);
+
+ for (;;) {
+ char line[LINE_MAX];
+ va_list ap;
+
+ if (on_tty())
+ fputs(ANSI_HIGHLIGHT_ON, stdout);
+
+ va_start(ap, text);
+ vprintf(text, ap);
+ va_end(ap);
+
+ if (on_tty())
+ fputs(ANSI_HIGHLIGHT_OFF, stdout);
+
+ fflush(stdout);
+
+ errno = 0;
+ if (!fgets(line, sizeof(line), stdin))
+ return errno ? -errno : -EIO;
+
+ if (!endswith(line, "\n"))
+ putchar('\n');
+ else {
+ char *s;
+
+ if (isempty(line))
+ continue;
+
+ truncate_nl(line);
+ s = strdup(line);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+ }
+ }
+}
+
+int reset_terminal_fd(int fd, bool switch_to_text) {
+ struct termios termios;
+ int r = 0;
+
+ /* Set terminal to some sane defaults */
+
+ assert(fd >= 0);
+
+ /* We leave locked terminal attributes untouched, so that
+ * Plymouth may set whatever it wants to set, and we don't
+ * interfere with that. */
+
+ /* Disable exclusive mode, just in case */
+ ioctl(fd, TIOCNXCL);
+
+ /* Switch to text mode */
+ if (switch_to_text)
+ ioctl(fd, KDSETMODE, KD_TEXT);
+
+ /* Enable console unicode mode */
+ ioctl(fd, KDSKBMODE, K_UNICODE);
+
+ if (tcgetattr(fd, &termios) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ /* We only reset the stuff that matters to the software. How
+ * hardware is set up we don't touch assuming that somebody
+ * else will do that for us */
+
+ termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
+ termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
+ termios.c_oflag |= ONLCR;
+ termios.c_cflag |= CREAD;
+ termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
+
+ termios.c_cc[VINTR] = 03; /* ^C */
+ termios.c_cc[VQUIT] = 034; /* ^\ */
+ termios.c_cc[VERASE] = 0177;
+ termios.c_cc[VKILL] = 025; /* ^X */
+ termios.c_cc[VEOF] = 04; /* ^D */
+ termios.c_cc[VSTART] = 021; /* ^Q */
+ termios.c_cc[VSTOP] = 023; /* ^S */
+ termios.c_cc[VSUSP] = 032; /* ^Z */
+ termios.c_cc[VLNEXT] = 026; /* ^V */
+ termios.c_cc[VWERASE] = 027; /* ^W */
+ termios.c_cc[VREPRINT] = 022; /* ^R */
+ termios.c_cc[VEOL] = 0;
+ termios.c_cc[VEOL2] = 0;
+
+ termios.c_cc[VTIME] = 0;
+ termios.c_cc[VMIN] = 1;
+
+ if (tcsetattr(fd, TCSANOW, &termios) < 0)
+ r = -errno;
+
+finish:
+ /* Just in case, flush all crap out */
+ tcflush(fd, TCIOFLUSH);
+
+ return r;
+}
+
+int reset_terminal(const char *name) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ return reset_terminal_fd(fd, true);
+}
+
+int open_terminal(const char *name, int mode) {
+ int fd, r;
+ unsigned c = 0;
+
+ /*
+ * If a TTY is in the process of being closed opening it might
+ * cause EIO. This is horribly awful, but unlikely to be
+ * changed in the kernel. Hence we work around this problem by
+ * retrying a couple of times.
+ *
+ * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
+ */
+
+ assert(!(mode & O_CREAT));
+
+ for (;;) {
+ fd = open(name, mode, 0);
+ if (fd >= 0)
+ break;
+
+ if (errno != EIO)
+ return -errno;
+
+ /* Max 1s in total */
+ if (c >= 20)
+ return -errno;
+
+ usleep(50 * USEC_PER_MSEC);
+ c++;
+ }
+
+ r = isatty(fd);
+ if (r < 0) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ if (!r) {
+ safe_close(fd);
+ return -ENOTTY;
+ }
+
+ return fd;
+}
+
+int acquire_terminal(
+ const char *name,
+ bool fail,
+ bool force,
+ bool ignore_tiocstty_eperm,
+ usec_t timeout) {
+
+ int fd = -1, notify = -1, r = 0, wd = -1;
+ usec_t ts = 0;
+
+ assert(name);
+
+ /* We use inotify to be notified when the tty is closed. We
+ * create the watch before checking if we can actually acquire
+ * it, so that we don't lose any event.
+ *
+ * Note: strictly speaking this actually watches for the
+ * device being closed, it does *not* really watch whether a
+ * tty loses its controlling process. However, unless some
+ * rogue process uses TIOCNOTTY on /dev/tty *after* closing
+ * its tty otherwise this will not become a problem. As long
+ * as the administrator makes sure not configure any service
+ * on the same tty as an untrusted user this should not be a
+ * problem. (Which he probably should not do anyway.) */
+
+ if (timeout != USEC_INFINITY)
+ ts = now(CLOCK_MONOTONIC);
+
+ if (!fail && !force) {
+ notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0));
+ if (notify < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ wd = inotify_add_watch(notify, name, IN_CLOSE);
+ if (wd < 0) {
+ r = -errno;
+ goto fail;
+ }
+ }
+
+ for (;;) {
+ struct sigaction sa_old, sa_new = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_RESTART,
+ };
+
+ if (notify >= 0) {
+ r = flush_fd(notify);
+ if (r < 0)
+ goto fail;
+ }
+
+ /* We pass here O_NOCTTY only so that we can check the return
+ * value TIOCSCTTY and have a reliable way to figure out if we
+ * successfully became the controlling process of the tty */
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
+ * if we already own the tty. */
+ assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
+
+ /* First, try to get the tty */
+ if (ioctl(fd, TIOCSCTTY, force) < 0)
+ r = -errno;
+
+ assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
+
+ /* Sometimes it makes sense to ignore TIOCSCTTY
+ * returning EPERM, i.e. when very likely we already
+ * are have this controlling terminal. */
+ if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
+ r = 0;
+
+ if (r < 0 && (force || fail || r != -EPERM)) {
+ goto fail;
+ }
+
+ if (r >= 0)
+ break;
+
+ assert(!fail);
+ assert(!force);
+ assert(notify >= 0);
+
+ for (;;) {
+ union inotify_event_buffer buffer;
+ struct inotify_event *e;
+ ssize_t l;
+
+ if (timeout != USEC_INFINITY) {
+ usec_t n;
+
+ n = now(CLOCK_MONOTONIC);
+ if (ts + timeout < n) {
+ r = -ETIMEDOUT;
+ goto fail;
+ }
+
+ r = fd_wait_for_event(fd, POLLIN, ts + timeout - n);
+ if (r < 0)
+ goto fail;
+
+ if (r == 0) {
+ r = -ETIMEDOUT;
+ goto fail;
+ }
+ }
+
+ l = read(notify, &buffer, sizeof(buffer));
+ if (l < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+
+ r = -errno;
+ goto fail;
+ }
+
+ FOREACH_INOTIFY_EVENT(e, buffer, l) {
+ if (e->wd != wd || !(e->mask & IN_CLOSE)) {
+ r = -EIO;
+ goto fail;
+ }
+ }
+
+ break;
+ }
+
+ /* We close the tty fd here since if the old session
+ * ended our handle will be dead. It's important that
+ * we do this after sleeping, so that we don't enter
+ * an endless loop. */
+ fd = safe_close(fd);
+ }
+
+ safe_close(notify);
+
+ r = reset_terminal_fd(fd, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to reset terminal: %m");
+
+ return fd;
+
+fail:
+ safe_close(fd);
+ safe_close(notify);
+
+ return r;
+}
+
+int release_terminal(void) {
+ static const struct sigaction sa_new = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_RESTART,
+ };
+
+ _cleanup_close_ int fd = -1;
+ struct sigaction sa_old;
+ int r = 0;
+
+ fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
+ * by our own TIOCNOTTY */
+ assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
+
+ if (ioctl(fd, TIOCNOTTY) < 0)
+ r = -errno;
+
+ assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
+
+ return r;
+}
+
+int terminal_vhangup_fd(int fd) {
+ assert(fd >= 0);
+
+ if (ioctl(fd, TIOCVHANGUP) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int terminal_vhangup(const char *name) {
+ _cleanup_close_ int fd;
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ return terminal_vhangup_fd(fd);
+}
+
+int vt_disallocate(const char *name) {
+ int fd, r;
+ unsigned u;
+
+ /* Deallocate the VT if possible. If not possible
+ * (i.e. because it is the active one), at least clear it
+ * entirely (including the scrollback buffer) */
+
+ if (!startswith(name, "/dev/"))
+ return -EINVAL;
+
+ if (!tty_is_vc(name)) {
+ /* So this is not a VT. I guess we cannot deallocate
+ * it then. But let's at least clear the screen */
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ loop_write(fd,
+ "\033[r" /* clear scrolling region */
+ "\033[H" /* move home */
+ "\033[2J", /* clear screen */
+ 10, false);
+ safe_close(fd);
+
+ return 0;
+ }
+
+ if (!startswith(name, "/dev/tty"))
+ return -EINVAL;
+
+ r = safe_atou(name+8, &u);
+ if (r < 0)
+ return r;
+
+ if (u <= 0)
+ return -EINVAL;
+
+ /* Try to deallocate */
+ fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ r = ioctl(fd, VT_DISALLOCATE, u);
+ safe_close(fd);
+
+ if (r >= 0)
+ return 0;
+
+ if (errno != EBUSY)
+ return -errno;
+
+ /* Couldn't deallocate, so let's clear it fully with
+ * scrollback */
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ loop_write(fd,
+ "\033[r" /* clear scrolling region */
+ "\033[H" /* move home */
+ "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
+ 10, false);
+ safe_close(fd);
+
+ return 0;
+}
+
+void warn_melody(void) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return;
+
+ /* Yeah, this is synchronous. Kinda sucks. But well... */
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/440));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/220));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/220));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, 0);
+}
+
+int make_console_stdio(void) {
+ int fd, r;
+
+ /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
+
+ fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY);
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to acquire terminal: %m");
+
+ r = make_stdio(fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to duplicate terminal fd: %m");
+
+ return 0;
+}
+
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) {
+ static const char status_indent[] = " "; /* "[" STATUS "] " */
+ _cleanup_free_ char *s = NULL;
+ _cleanup_close_ int fd = -1;
+ struct iovec iovec[6] = {};
+ int n = 0;
+ static bool prev_ephemeral;
+
+ assert(format);
+
+ /* This is independent of logging, as status messages are
+ * optional and go exclusively to the console. */
+
+ if (vasprintf(&s, format, ap) < 0)
+ return log_oom();
+
+ fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ if (ellipse) {
+ char *e;
+ size_t emax, sl;
+ int c;
+
+ c = fd_columns(fd);
+ if (c <= 0)
+ c = 80;
+
+ sl = status ? sizeof(status_indent)-1 : 0;
+
+ emax = c - sl - 1;
+ if (emax < 3)
+ emax = 3;
+
+ e = ellipsize(s, emax, 50);
+ if (e) {
+ free(s);
+ s = e;
+ }
+ }
+
+ if (prev_ephemeral)
+ IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE);
+ prev_ephemeral = ephemeral;
+
+ if (status) {
+ if (!isempty(status)) {
+ IOVEC_SET_STRING(iovec[n++], "[");
+ IOVEC_SET_STRING(iovec[n++], status);
+ IOVEC_SET_STRING(iovec[n++], "] ");
+ } else
+ IOVEC_SET_STRING(iovec[n++], status_indent);
+ }
+
+ IOVEC_SET_STRING(iovec[n++], s);
+ if (!ephemeral)
+ IOVEC_SET_STRING(iovec[n++], "\n");
+
+ if (writev(fd, iovec, n) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) {
+ va_list ap;
+ int r;
+
+ assert(format);
+
+ va_start(ap, format);
+ r = status_vprintf(status, ellipse, ephemeral, format, ap);
+ va_end(ap);
+
+ return r;
+}
+
+bool tty_is_vc(const char *tty) {
+ assert(tty);
+
+ return vtnr_from_tty(tty) >= 0;
+}
+
+bool tty_is_console(const char *tty) {
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ return streq(tty, "console");
+}
+
+int vtnr_from_tty(const char *tty) {
+ int i, r;
+
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ if (!startswith(tty, "tty") )
+ return -EINVAL;
+
+ if (tty[3] < '0' || tty[3] > '9')
+ return -EINVAL;
+
+ r = safe_atoi(tty+3, &i);
+ if (r < 0)
+ return r;
+
+ if (i < 0 || i > 63)
+ return -EINVAL;
+
+ return i;
+}
+
+char *resolve_dev_console(char **active) {
+ char *tty;
+
+ /* Resolve where /dev/console is pointing to, if /sys is actually ours
+ * (i.e. not read-only-mounted which is a sign for container setups) */
+
+ if (path_is_read_only_fs("/sys") > 0)
+ return NULL;
+
+ if (read_one_line_file("/sys/class/tty/console/active", active) < 0)
+ return NULL;
+
+ /* If multiple log outputs are configured the last one is what
+ * /dev/console points to */
+ tty = strrchr(*active, ' ');
+ if (tty)
+ tty++;
+ else
+ tty = *active;
+
+ if (streq(tty, "tty0")) {
+ char *tmp;
+
+ /* Get the active VC (e.g. tty1) */
+ if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) {
+ free(*active);
+ tty = *active = tmp;
+ }
+ }
+
+ return tty;
+}
+
+bool tty_is_vc_resolve(const char *tty) {
+ _cleanup_free_ char *active = NULL;
+
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ if (streq(tty, "console")) {
+ tty = resolve_dev_console(&active);
+ if (!tty)
+ return false;
+ }
+
+ return tty_is_vc(tty);
+}
+
+const char *default_term_for_tty(const char *tty) {
+ assert(tty);
+
+ return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
+}
+
+int fd_columns(int fd) {
+ struct winsize ws = {};
+
+ if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+ return -errno;
+
+ if (ws.ws_col <= 0)
+ return -EIO;
+
+ return ws.ws_col;
+}
+
+unsigned columns(void) {
+ const char *e;
+ int c;
+
+ if (_likely_(cached_columns > 0))
+ return cached_columns;
+
+ c = 0;
+ e = getenv("COLUMNS");
+ if (e)
+ (void) safe_atoi(e, &c);
+
+ if (c <= 0)
+ c = fd_columns(STDOUT_FILENO);
+
+ if (c <= 0)
+ c = 80;
+
+ cached_columns = c;
+ return cached_columns;
+}
+
+int fd_lines(int fd) {
+ struct winsize ws = {};
+
+ if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+ return -errno;
+
+ if (ws.ws_row <= 0)
+ return -EIO;
+
+ return ws.ws_row;
+}
+
+unsigned lines(void) {
+ const char *e;
+ int l;
+
+ if (_likely_(cached_lines > 0))
+ return cached_lines;
+
+ l = 0;
+ e = getenv("LINES");
+ if (e)
+ (void) safe_atoi(e, &l);
+
+ if (l <= 0)
+ l = fd_lines(STDOUT_FILENO);
+
+ if (l <= 0)
+ l = 24;
+
+ cached_lines = l;
+ return cached_lines;
+}
+
+/* intended to be used as a SIGWINCH sighandler */
+void columns_lines_cache_reset(int signum) {
+ cached_columns = 0;
+ cached_lines = 0;
+}
+
+bool on_tty(void) {
+ static int cached_on_tty = -1;
+
+ if (_unlikely_(cached_on_tty < 0))
+ cached_on_tty = isatty(STDOUT_FILENO) > 0;
+
+ return cached_on_tty;
+}
+
+int make_stdio(int fd) {
+ int r, s, t;
+
+ assert(fd >= 0);
+
+ r = dup2(fd, STDIN_FILENO);
+ s = dup2(fd, STDOUT_FILENO);
+ t = dup2(fd, STDERR_FILENO);
+
+ if (fd >= 3)
+ safe_close(fd);
+
+ if (r < 0 || s < 0 || t < 0)
+ return -errno;
+
+ /* Explicitly unset O_CLOEXEC, since if fd was < 3, then
+ * dup2() was a NOP and the bit hence possibly set. */
+ fd_cloexec(STDIN_FILENO, false);
+ fd_cloexec(STDOUT_FILENO, false);
+ fd_cloexec(STDERR_FILENO, false);
+
+ return 0;
+}
+
+int make_null_stdio(void) {
+ int null_fd;
+
+ null_fd = open("/dev/null", O_RDWR|O_NOCTTY);
+ if (null_fd < 0)
+ return -errno;
+
+ return make_stdio(null_fd);
+}
+
+int getttyname_malloc(int fd, char **ret) {
+ size_t l = 100;
+ int r;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ for (;;) {
+ char path[l];
+
+ r = ttyname_r(fd, path, sizeof(path));
+ if (r == 0) {
+ const char *p;
+ char *c;
+
+ p = startswith(path, "/dev/");
+ c = strdup(p ?: path);
+ if (!c)
+ return -ENOMEM;
+
+ *ret = c;
+ return 0;
+ }
+
+ if (r != ERANGE)
+ return -r;
+
+ l *= 2;
+ }
+
+ return 0;
+}
+
+int getttyname_harder(int fd, char **r) {
+ int k;
+ char *s = NULL;
+
+ k = getttyname_malloc(fd, &s);
+ if (k < 0)
+ return k;
+
+ if (streq(s, "tty")) {
+ free(s);
+ return get_ctty(0, NULL, r);
+ }
+
+ *r = s;
+ return 0;
+}
+
+int get_ctty_devnr(pid_t pid, dev_t *d) {
+ int r;
+ _cleanup_free_ char *line = NULL;
+ const char *p;
+ unsigned long ttynr;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "stat");
+ r = read_one_line_file(p, &line);
+ if (r < 0)
+ return r;
+
+ p = strrchr(line, ')');
+ if (!p)
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " "
+ "%*c " /* state */
+ "%*d " /* ppid */
+ "%*d " /* pgrp */
+ "%*d " /* session */
+ "%lu ", /* ttynr */
+ &ttynr) != 1)
+ return -EIO;
+
+ if (major(ttynr) == 0 && minor(ttynr) == 0)
+ return -ENXIO;
+
+ if (d)
+ *d = (dev_t) ttynr;
+
+ return 0;
+}
+
+int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
+ char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
+ _cleanup_free_ char *s = NULL;
+ const char *p;
+ dev_t devnr;
+ int k;
+
+ assert(r);
+
+ k = get_ctty_devnr(pid, &devnr);
+ if (k < 0)
+ return k;
+
+ sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
+
+ k = readlink_malloc(fn, &s);
+ if (k < 0) {
+
+ if (k != -ENOENT)
+ return k;
+
+ /* This is an ugly hack */
+ if (major(devnr) == 136) {
+ if (asprintf(&b, "pts/%u", minor(devnr)) < 0)
+ return -ENOMEM;
+ } else {
+ /* Probably something like the ptys which have no
+ * symlink in /dev/char. Let's return something
+ * vaguely useful. */
+
+ b = strdup(fn + 5);
+ if (!b)
+ return -ENOMEM;
+ }
+ } else {
+ if (startswith(s, "/dev/"))
+ p = s + 5;
+ else if (startswith(s, "../"))
+ p = s + 3;
+ else
+ p = s;
+
+ b = strdup(p);
+ if (!b)
+ return -ENOMEM;
+ }
+
+ *r = b;
+ if (_devnr)
+ *_devnr = devnr;
+
+ return 0;
+}
diff --git a/src/shared/terminal-util.h b/src/shared/terminal-util.h
new file mode 100644
index 0000000000..188714f228
--- /dev/null
+++ b/src/shared/terminal-util.h
@@ -0,0 +1,109 @@
+#pragma once
+
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "macro.h"
+#include "time-util.h"
+
+#define ANSI_HIGHLIGHT_ON "\x1B[1;39m"
+#define ANSI_RED_ON "\x1B[31m"
+#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m"
+#define ANSI_GREEN_ON "\x1B[32m"
+#define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m"
+#define ANSI_HIGHLIGHT_YELLOW_ON "\x1B[1;33m"
+#define ANSI_HIGHLIGHT_BLUE_ON "\x1B[1;34m"
+#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
+#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
+
+int reset_terminal_fd(int fd, bool switch_to_text);
+int reset_terminal(const char *name);
+
+int open_terminal(const char *name, int mode);
+int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm, usec_t timeout);
+int release_terminal(void);
+
+int terminal_vhangup_fd(int fd);
+int terminal_vhangup(const char *name);
+
+int chvt(int vt);
+
+int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
+int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4);
+int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
+
+int vt_disallocate(const char *name);
+
+char *resolve_dev_console(char **active);
+bool tty_is_vc(const char *tty);
+bool tty_is_vc_resolve(const char *tty);
+bool tty_is_console(const char *tty) _pure_;
+int vtnr_from_tty(const char *tty);
+const char *default_term_for_tty(const char *tty);
+
+void warn_melody(void);
+
+int make_stdio(int fd);
+int make_null_stdio(void);
+int make_console_stdio(void);
+
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0);
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_(4,5);
+
+int fd_columns(int fd);
+unsigned columns(void);
+int fd_lines(int fd);
+unsigned lines(void);
+void columns_lines_cache_reset(int _unused_ signum);
+
+bool on_tty(void);
+
+static inline const char *ansi_highlight(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_ON : "";
+}
+
+static inline const char *ansi_highlight_red(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_RED_ON : "";
+}
+
+static inline const char *ansi_highlight_green(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_GREEN_ON : "";
+}
+
+static inline const char *ansi_highlight_yellow(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_YELLOW_ON : "";
+}
+
+static inline const char *ansi_highlight_blue(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_BLUE_ON : "";
+}
+
+static inline const char *ansi_highlight_off(void) {
+ return on_tty() ? ANSI_HIGHLIGHT_OFF : "";
+}
+
+int get_ctty_devnr(pid_t pid, dev_t *d);
+int get_ctty(pid_t, dev_t *_devnr, char **r);
+
+int getttyname_malloc(int fd, char **r);
+int getttyname_harder(int fd, char **r);
diff --git a/src/shared/time-dst.c b/src/shared/time-dst.c
deleted file mode 100644
index 1ce6f721b7..0000000000
--- a/src/shared/time-dst.c
+++ /dev/null
@@ -1,335 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Timezone file reading code from glibc 2.16.
-
- Copyright (C) 1991-2012 Free Software Foundation, Inc.
- Copyright 2012 Kay Sievers
-
- 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 <http://www.gnu.org/licenses/>.
-***/
-#include <ctype.h>
-#include <errno.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <endian.h>
-#include <byteswap.h>
-#include <assert.h>
-#include <limits.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <sys/stat.h>
-
-#include "time-dst.h"
-#include "util.h"
-
-/*
- * If tzh_version is '2' or greater, the above is followed by a second instance
- * of tzhead and a second instance of the data in which each coded transition
- * time uses 8 rather than 4 chars, then a POSIX-TZ-environment-variable-style
- * string for use in handling instants after the last transition time stored in
- * the file * (with nothing between the newlines if there is no POSIX
- * representation for such instants).
- */
-#define TZ_MAGIC "TZif"
-struct tzhead {
- char tzh_magic[4]; /* TZ_MAGIC */
- char tzh_version[1]; /* '\0' or '2' as of 2005 */
- char tzh_reserved[15]; /* reserved--must be zero */
- char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
- char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
- char tzh_leapcnt[4]; /* coded number of leap seconds */
- char tzh_timecnt[4]; /* coded number of transition times */
- char tzh_typecnt[4]; /* coded number of local time types */
- char tzh_charcnt[4]; /* coded number of abbr. chars */
-};
-
-struct ttinfo {
- long int offset; /* Seconds east of GMT. */
- unsigned char isdst; /* Used to set tm_isdst. */
- unsigned char idx; /* Index into `zone_names'. */
- unsigned char isstd; /* Transition times are in standard time. */
- unsigned char isgmt; /* Transition times are in GMT. */
-};
-
-struct leap {
- time_t transition; /* Time the transition takes effect. */
- long int change; /* Seconds of correction to apply. */
-};
-
-static inline int decode(const void *ptr) {
- return be32toh(*(int *)ptr);
-}
-
-static inline int64_t decode64(const void *ptr) {
- return be64toh(*(int64_t *)ptr);
-}
-
-int time_get_dst(time_t date, const char *tzfile,
- time_t *switch_cur, char **zone_cur, bool *dst_cur,
- time_t *switch_next, int *delta_next, char **zone_next, bool *dst_next) {
- unsigned char *type_idxs = 0;
- size_t num_types = 0;
- struct ttinfo *types = NULL;
- char *zone_names = NULL;
- struct stat st;
- size_t num_isstd, num_isgmt;
- struct tzhead tzhead;
- size_t chars;
- size_t i;
- size_t total_size;
- size_t types_idx;
- int trans_width = 4;
- size_t tzspec_len;
- size_t num_leaps;
- size_t lo, hi;
- size_t num_transitions = 0;
- _cleanup_free_ time_t *transitions = NULL;
- _cleanup_fclose_ FILE *f;
-
- f = fopen(tzfile, "re");
- if (f == NULL)
- return -errno;
-
- if (fstat(fileno(f), &st) < 0)
- return -errno;
-
-read_again:
- if (fread((void *)&tzhead, sizeof(tzhead), 1, f) != 1 ||
- memcmp(tzhead.tzh_magic, TZ_MAGIC, sizeof(tzhead.tzh_magic)) != 0)
- return -EINVAL;
-
- num_transitions = (size_t)decode(tzhead.tzh_timecnt);
- num_types = (size_t)decode(tzhead.tzh_typecnt);
- chars = (size_t)decode(tzhead.tzh_charcnt);
- num_leaps = (size_t)decode(tzhead.tzh_leapcnt);
- num_isstd = (size_t)decode(tzhead.tzh_ttisstdcnt);
- num_isgmt = (size_t)decode(tzhead.tzh_ttisgmtcnt);
-
- /* For platforms with 64-bit time_t we use the new format if available. */
- if (sizeof(time_t) == 8 && trans_width == 4 && tzhead.tzh_version[0] != '\0') {
- size_t to_skip;
-
- /* We use the 8-byte format. */
- trans_width = 8;
-
- /* Position the stream before the second header. */
- to_skip = (num_transitions * (4 + 1)
- + num_types * 6
- + chars
- + num_leaps * 8 + num_isstd + num_isgmt);
- if (fseek(f, to_skip, SEEK_CUR) != 0)
- return -EINVAL;
-
- goto read_again;
- }
-
- if (num_transitions > ((SIZE_MAX - (__alignof__(struct ttinfo) - 1)) / (sizeof(time_t) + 1)))
- return -EINVAL;
-
- total_size = num_transitions * (sizeof(time_t) + 1);
- total_size = ((total_size + __alignof__(struct ttinfo) - 1) & ~(__alignof__(struct ttinfo) - 1));
- types_idx = total_size;
- if (num_leaps > (SIZE_MAX - total_size) / sizeof(struct ttinfo))
- return -EINVAL;
-
- total_size += num_types * sizeof(struct ttinfo);
- if (chars > SIZE_MAX - total_size)
- return -EINVAL;
-
- total_size += chars;
- if (__alignof__(struct leap) - 1 > SIZE_MAX - total_size)
- return -EINVAL;
-
- total_size = ((total_size + __alignof__(struct leap) - 1) & ~(__alignof__(struct leap) - 1));
- if (num_leaps > (SIZE_MAX - total_size) / sizeof(struct leap))
- return -EINVAL;
-
- total_size += num_leaps * sizeof(struct leap);
- tzspec_len = 0;
- if (sizeof(time_t) == 8 && trans_width == 8) {
- off_t rem = st.st_size - ftello(f);
-
- if (rem < 0 || (size_t) rem < (num_transitions * (8 + 1) + num_types * 6 + chars))
- return -EINVAL;
- tzspec_len = (size_t) rem - (num_transitions * (8 + 1) + num_types * 6 + chars);
- if (num_leaps > SIZE_MAX / 12 || tzspec_len < num_leaps * 12)
- return -EINVAL;
- tzspec_len -= num_leaps * 12;
- if (tzspec_len < num_isstd)
- return -EINVAL;
- tzspec_len -= num_isstd;
- if (tzspec_len == 0 || tzspec_len - 1 < num_isgmt)
- return -EINVAL;
- tzspec_len -= num_isgmt + 1;
- if (SIZE_MAX - total_size < tzspec_len)
- return -EINVAL;
- }
-
- /* leave space for additional zone_names zero terminator */
- transitions = malloc0(total_size + tzspec_len + 1);
- if (transitions == NULL)
- return -EINVAL;
-
- type_idxs = (unsigned char *)transitions + (num_transitions
- * sizeof(time_t));
- types = (struct ttinfo *)((char *)transitions + types_idx);
- zone_names = (char *)types + num_types * sizeof(struct ttinfo);
-
- if (sizeof(time_t) == 4 || trans_width == 8) {
- if (fread(transitions, trans_width + 1, num_transitions, f) != num_transitions)
- return -EINVAL;
- } else {
- if (fread(transitions, 4, num_transitions, f) != num_transitions ||
- fread(type_idxs, 1, num_transitions, f) != num_transitions)
- return -EINVAL;
- }
-
- /* Check for bogus indices in the data file, so we can hereafter
- safely use type_idxs[T] as indices into `types' and never crash. */
- for (i = 0; i < num_transitions; ++i)
- if (type_idxs[i] >= num_types)
- return -EINVAL;
-
- if (__BYTE_ORDER == __BIG_ENDIAN ? sizeof(time_t) == 8 && trans_width == 4
- : sizeof(time_t) == 4 || trans_width == 4) {
- /* Decode the transition times, stored as 4-byte integers in
- network (big-endian) byte order. We work from the end of
- the array so as not to clobber the next element to be
- processed when sizeof (time_t) > 4. */
- i = num_transitions;
- while (i-- > 0)
- transitions[i] = decode((char *)transitions + i * 4);
- } else if (__BYTE_ORDER != __BIG_ENDIAN && sizeof(time_t) == 8) {
- /* Decode the transition times, stored as 8-byte integers in
- network (big-endian) byte order. */
- for (i = 0; i < num_transitions; ++i)
- transitions[i] = decode64((char *)transitions + i * 8);
- }
-
- for (i = 0; i < num_types; ++i) {
- unsigned char x[4];
- int c;
-
- if (fread(x, 1, sizeof(x), f) != sizeof(x))
- return -EINVAL;
- c = getc(f);
- if ((unsigned int)c > 1u)
- return -EINVAL;
- types[i].isdst = c;
- c = getc(f);
- if ((size_t) c > chars)
- /* Bogus index in data file. */
- return -EINVAL;
- types[i].idx = c;
- types[i].offset = (long int)decode(x);
- }
-
- if (fread(zone_names, 1, chars, f) != chars)
- return -EINVAL;
-
- zone_names[chars] = '\0';
-
- for (i = 0; i < num_isstd; ++i) {
- int c = getc(f);
- if (c == EOF)
- return -EINVAL;
- types[i].isstd = c != 0;
- }
-
- while (i < num_types)
- types[i++].isstd = 0;
-
- for (i = 0; i < num_isgmt; ++i) {
- int c = getc(f);
- if (c == EOF)
- return -EINVAL;
- types[i].isgmt = c != 0;
- }
-
- while (i < num_types)
- types[i++].isgmt = 0;
-
- if (num_transitions == 0)
- return -EINVAL;
-
- if (date < transitions[0] || date >= transitions[num_transitions - 1])
- return -EINVAL;
-
- /* Find the first transition after TIMER, and
- then pick the type of the transition before it. */
- lo = 0;
- hi = num_transitions - 1;
-
- /* Assume that DST is changing twice a year and guess initial
- search spot from it.
- Half of a gregorian year has on average 365.2425 * 86400 / 2
- = 15778476 seconds. */
- i = (transitions[num_transitions - 1] - date) / 15778476;
- if (i < num_transitions) {
- i = num_transitions - 1 - i;
- if (date < transitions[i]) {
- if (i < 10 || date >= transitions[i - 10]) {
- /* Linear search. */
- while (date < transitions[i - 1])
- i--;
- goto found;
- }
- hi = i - 10;
- } else {
- if (i + 10 >= num_transitions || date < transitions[i + 10]) {
- /* Linear search. */
- while (date >= transitions[i])
- i++;
- goto found;
- }
- lo = i + 10;
- }
- }
-
- /* Binary search. */
- while (lo + 1 < hi) {
- i = (lo + hi) / 2;
- if (date < transitions[i])
- hi = i;
- else
- lo = i;
- }
- i = hi;
-
-found:
- if (switch_cur)
- *switch_cur = transitions[i-1];
- if (zone_cur)
- *zone_cur = strdup(&zone_names[types[type_idxs[i - 1]].idx]);
- if (dst_cur)
- *dst_cur = types[type_idxs[i-1]].isdst;
-
- if (switch_next)
- *switch_next = transitions[i];
- if (delta_next)
- *delta_next = (types[type_idxs[i]].offset - types[type_idxs[i-1]].offset) / 60;
- if (zone_next)
- *zone_next = strdup(&zone_names[types[type_idxs[i]].idx]);
- if (dst_next)
- *dst_next = types[type_idxs[i]].isdst;
-
- return 0;
-}
diff --git a/src/shared/time-util.c b/src/shared/time-util.c
index 947ac1fcfb..12f1b193be 100644
--- a/src/shared/time-util.c
+++ b/src/shared/time-util.c
@@ -398,18 +398,21 @@ void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
t->monotonic);
}
-void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
+int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
unsigned long long a, b;
assert(value);
assert(t);
- if (sscanf(value, "%llu %llu", &a, &b) != 2)
- log_debug("Failed to parse finish timestamp value %s", value);
- else {
- t->realtime = a;
- t->monotonic = b;
+ if (sscanf(value, "%llu %llu", &a, &b) != 2) {
+ log_debug("Failed to parse finish timestamp value %s.", value);
+ return -EINVAL;
}
+
+ t->realtime = a;
+ t->monotonic = b;
+
+ return 0;
}
int parse_timestamp(const char *t, usec_t *usec) {
@@ -786,7 +789,7 @@ int parse_nsec(const char *t, nsec_t *nsec) {
s = startswith(p, "infinity");
if (s) {
s += strspn(s, WHITESPACE);
- if (!*s != 0)
+ if (*s != 0)
return -EINVAL;
*nsec = NSEC_INFINITY;
diff --git a/src/shared/time-util.h b/src/shared/time-util.h
index fca8a4db9b..7a64d454a0 100644
--- a/src/shared/time-util.h
+++ b/src/shared/time-util.h
@@ -94,7 +94,7 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t);
char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t);
-void dual_timestamp_deserialize(const char *value, dual_timestamp *t);
+int dual_timestamp_deserialize(const char *value, dual_timestamp *t);
int parse_timestamp(const char *t, usec_t *usec);
diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h
index 5f09ce181f..f758ce13e4 100644
--- a/src/shared/udev-util.h
+++ b/src/shared/udev-util.h
@@ -30,6 +30,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_enumerate*, udev_enumerate_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_event*, udev_event_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_rules*, udev_rules_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl*, udev_ctrl_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_connection*, udev_ctrl_connection_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_msg*, udev_ctrl_msg_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_monitor*, udev_monitor_unref);
#define _cleanup_udev_unref_ _cleanup_(udev_unrefp)
@@ -38,5 +40,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_monitor*, udev_monitor_unref);
#define _cleanup_udev_event_unref_ _cleanup_(udev_event_unrefp)
#define _cleanup_udev_rules_unref_ _cleanup_(udev_rules_unrefp)
#define _cleanup_udev_ctrl_unref_ _cleanup_(udev_ctrl_unrefp)
+#define _cleanup_udev_ctrl_connection_unref_ _cleanup_(udev_ctrl_connection_unrefp)
+#define _cleanup_udev_ctrl_msg_unref_ _cleanup_(udev_ctrl_msg_unrefp)
#define _cleanup_udev_monitor_unref_ _cleanup_(udev_monitor_unrefp)
#define _cleanup_udev_list_cleanup_ _cleanup_(udev_list_cleanup)
diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c
index 21b66913c9..bf52463d81 100644
--- a/src/shared/unit-name.c
+++ b/src/shared/unit-name.c
@@ -21,7 +21,6 @@
#include <errno.h>
#include <string.h>
-#include <assert.h>
#include "path-util.h"
#include "bus-label.h"
@@ -34,45 +33,13 @@
DIGITS LETTERS \
":-_.\\"
-static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
- [UNIT_SERVICE] = "service",
- [UNIT_SOCKET] = "socket",
- [UNIT_BUSNAME] = "busname",
- [UNIT_TARGET] = "target",
- [UNIT_SNAPSHOT] = "snapshot",
- [UNIT_DEVICE] = "device",
- [UNIT_MOUNT] = "mount",
- [UNIT_AUTOMOUNT] = "automount",
- [UNIT_SWAP] = "swap",
- [UNIT_TIMER] = "timer",
- [UNIT_PATH] = "path",
- [UNIT_SLICE] = "slice",
- [UNIT_SCOPE] = "scope"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
-
-static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
- [UNIT_STUB] = "stub",
- [UNIT_LOADED] = "loaded",
- [UNIT_NOT_FOUND] = "not-found",
- [UNIT_ERROR] = "error",
- [UNIT_MERGED] = "merged",
- [UNIT_MASKED] = "masked"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
-
-bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
+bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
const char *e, *i, *at;
- /* Valid formats:
- *
- * string@instance.suffix
- * string.suffix
- */
+ assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
- assert(IN_SET(template_ok, TEMPLATE_VALID, TEMPLATE_INVALID));
+ if (_unlikely_(flags == 0))
+ return false;
if (isempty(n))
return false;
@@ -96,15 +63,32 @@ bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
return false;
}
- if (at) {
- if (at == n)
- return false;
+ if (at == n)
+ return false;
- if (!template_ok == TEMPLATE_VALID && at+1 == e)
- return false;
- }
+ if (flags & UNIT_NAME_PLAIN)
+ if (!at)
+ return true;
- return true;
+ if (flags & UNIT_NAME_INSTANCE)
+ if (at && e > at + 1)
+ return true;
+
+ if (flags & UNIT_NAME_TEMPLATE)
+ if (at && e == at + 1)
+ return true;
+
+ return false;
+}
+
+bool unit_prefix_is_valid(const char *p) {
+
+ /* We don't allow additional @ in the prefix string */
+
+ if (isempty(p))
+ return false;
+
+ return in_charset(p, VALID_CHARS);
}
bool unit_instance_is_valid(const char *i) {
@@ -121,14 +105,41 @@ bool unit_instance_is_valid(const char *i) {
return in_charset(i, "@" VALID_CHARS);
}
-bool unit_prefix_is_valid(const char *p) {
+bool unit_suffix_is_valid(const char *s) {
+ if (isempty(s))
+ return false;
- /* We don't allow additional @ in the instance string */
+ if (s[0] != '.')
+ return false;
- if (isempty(p))
+ if (unit_type_from_string(s + 1) < 0)
return false;
- return in_charset(p, VALID_CHARS);
+ return true;
+}
+
+int unit_name_to_prefix(const char *n, char **ret) {
+ const char *p;
+ char *s;
+
+ assert(n);
+ assert(ret);
+
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ p = strchr(n, '@');
+ if (!p)
+ p = strrchr(n, '.');
+
+ assert_se(p);
+
+ s = strndup(n, p - n);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
}
int unit_name_to_instance(const char *n, char **instance) {
@@ -138,6 +149,9 @@ int unit_name_to_instance(const char *n, char **instance) {
assert(n);
assert(instance);
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return -EINVAL;
+
/* Everything past the first @ and before the last . is the instance */
p = strchr(n, '@');
if (!p) {
@@ -145,13 +159,13 @@ int unit_name_to_instance(const char *n, char **instance) {
return 0;
}
- d = strrchr(n, '.');
+ p++;
+
+ d = strrchr(p, '.');
if (!d)
return -EINVAL;
- if (d < p)
- return -EINVAL;
- i = strndup(p+1, d-p-1);
+ i = strndup(p, d-p);
if (!i)
return -ENOMEM;
@@ -159,55 +173,95 @@ int unit_name_to_instance(const char *n, char **instance) {
return 1;
}
-char *unit_name_to_prefix_and_instance(const char *n) {
+int unit_name_to_prefix_and_instance(const char *n, char **ret) {
const char *d;
+ char *s;
assert(n);
+ assert(ret);
- assert_se(d = strrchr(n, '.'));
- return strndup(n, d - n);
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ d = strrchr(n, '.');
+ if (!d)
+ return -EINVAL;
+
+ s = strndup(n, d - n);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
}
-char *unit_name_to_prefix(const char *n) {
- const char *p;
+UnitType unit_name_to_type(const char *n) {
+ const char *e;
assert(n);
- p = strchr(n, '@');
- if (p)
- return strndup(n, p - n);
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return _UNIT_TYPE_INVALID;
+
+ assert_se(e = strrchr(n, '.'));
- return unit_name_to_prefix_and_instance(n);
+ return unit_type_from_string(e + 1);
}
-char *unit_name_change_suffix(const char *n, const char *suffix) {
- char *e, *r;
+int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
+ char *e, *s;
size_t a, b;
assert(n);
assert(suffix);
- assert(suffix[0] == '.');
+ assert(ret);
+
+ if (!unit_name_is_valid(n, UNIT_NAME_ANY))
+ return -EINVAL;
+
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
assert_se(e = strrchr(n, '.'));
+
a = e - n;
b = strlen(suffix);
- r = new(char, a + b + 1);
- if (!r)
- return NULL;
+ s = new(char, a + b + 1);
+ if (!s)
+ return -ENOMEM;
- strcpy(mempcpy(r, n, a), suffix);
- return r;
+ strcpy(mempcpy(s, n, a), suffix);
+ *ret = s;
+
+ return 0;
}
-char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
+int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
+ char *s;
+
assert(prefix);
assert(suffix);
+ assert(ret);
+
+ if (!unit_prefix_is_valid(prefix))
+ return -EINVAL;
+
+ if (instance && !unit_instance_is_valid(instance))
+ return -EINVAL;
+
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
if (!instance)
- return strappend(prefix, suffix);
+ s = strappend(prefix, suffix);
+ else
+ s = strjoin(prefix, "@", instance, suffix, NULL);
+ if (!s)
+ return -ENOMEM;
- return strjoin(prefix, "@", instance, suffix, NULL);
+ *ret = s;
+ return 0;
}
static char *do_escape_char(char c, char *t) {
@@ -243,30 +297,6 @@ static char *do_escape(const char *f, char *t) {
return t;
}
-static char *do_escape_mangle(const char *f, enum unit_name_mangle allow_globs, char *t) {
- const char *valid_chars;
-
- assert(f);
- assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
- assert(t);
-
- /* We'll only escape the obvious characters here, to play
- * safe. */
-
- valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
-
- for (; *f; f++) {
- if (*f == '/')
- *(t++) = '-';
- else if (!strchr(valid_chars, *f))
- t = do_escape_char(*f, t);
- else
- *(t++) = *f;
- }
-
- return t;
-}
-
char *unit_name_escape(const char *f) {
char *r, *t;
@@ -282,14 +312,15 @@ char *unit_name_escape(const char *f) {
return r;
}
-char *unit_name_unescape(const char *f) {
- char *r, *t;
+int unit_name_unescape(const char *f, char **ret) {
+ _cleanup_free_ char *r = NULL;
+ char *t;
assert(f);
r = strdup(f);
if (!r)
- return NULL;
+ return -ENOMEM;
for (t = r; *f; f++) {
if (*f == '-')
@@ -297,180 +328,234 @@ char *unit_name_unescape(const char *f) {
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;
- }
+ if (f[1] != 'x')
+ return -EINVAL;
+
+ a = unhexchar(f[2]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unhexchar(f[3]);
+ if (b < 0)
+ return -EINVAL;
+
+ *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
+ f += 3;
} else
*(t++) = *f;
}
*t = 0;
- return r;
+ *ret = r;
+ r = NULL;
+
+ return 0;
}
-char *unit_name_path_escape(const char *f) {
- _cleanup_free_ char *p = NULL;
+int unit_name_path_escape(const char *f, char **ret) {
+ char *p, *s;
assert(f);
+ assert(ret);
- p = strdup(f);
+ p = strdupa(f);
if (!p)
- return NULL;
+ return -ENOMEM;
path_kill_slashes(p);
if (STR_IN_SET(p, "/", ""))
- return strdup("-");
-
- return unit_name_escape(p[0] == '/' ? p + 1 : p);
-}
+ s = strdup("-");
+ else {
+ char *e;
-char *unit_name_path_unescape(const char *f) {
- char *e, *w;
+ if (!path_is_safe(p))
+ return -EINVAL;
- assert(f);
+ /* Truncate trailing slashes */
+ e = endswith(p, "/");
+ if (e)
+ *e = 0;
- e = unit_name_unescape(f);
- if (!e)
- return NULL;
+ /* Truncate leading slashes */
+ if (p[0] == '/')
+ p++;
- if (e[0] != '/') {
- w = strappend("/", e);
- free(e);
- return w;
+ s = unit_name_escape(p);
}
+ if (!s)
+ return -ENOMEM;
- return e;
+ *ret = s;
+ return 0;
}
-bool unit_name_is_template(const char *n) {
- const char *p, *e;
+int unit_name_path_unescape(const char *f, char **ret) {
+ char *s;
+ int r;
- assert(n);
+ assert(f);
- p = strchr(n, '@');
- if (!p)
- return false;
+ if (isempty(f))
+ return -EINVAL;
- e = strrchr(p+1, '.');
- if (!e)
- return false;
+ if (streq(f, "-")) {
+ s = strdup("/");
+ if (!s)
+ return -ENOMEM;
+ } else {
+ char *w;
- return e == p + 1;
-}
+ r = unit_name_unescape(f, &w);
+ if (r < 0)
+ return r;
-bool unit_name_is_instance(const char *n) {
- const char *p, *e;
+ /* Don't accept trailing or leading slashes */
+ if (startswith(w, "/") || endswith(w, "/")) {
+ free(w);
+ return -EINVAL;
+ }
- assert(n);
+ /* Prefix a slash again */
+ s = strappend("/", w);
+ free(w);
+ if (!s)
+ return -ENOMEM;
- p = strchr(n, '@');
- if (!p)
- return false;
+ if (!path_is_safe(s)) {
+ free(s);
+ return -EINVAL;
+ }
+ }
- e = strrchr(p+1, '.');
- if (!e)
- return false;
+ if (ret)
+ *ret = s;
+ else
+ free(s);
- return e > p + 1;
+ return 0;
}
-char *unit_name_replace_instance(const char *f, const char *i) {
+int unit_name_replace_instance(const char *f, const char *i, char **ret) {
const char *p, *e;
- char *r;
+ char *s;
size_t a, b;
assert(f);
assert(i);
+ assert(ret);
- p = strchr(f, '@');
- if (!p)
- return strdup(f);
+ if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
+ return -EINVAL;
+ if (!unit_instance_is_valid(i))
+ return -EINVAL;
- e = strrchr(f, '.');
- if (!e)
- e = strchr(f, 0);
+ assert_se(p = strchr(f, '@'));
+ assert_se(e = strrchr(f, '.'));
a = p - f;
b = strlen(i);
- r = new(char, a + 1 + b + strlen(e) + 1);
- if (!r)
- return NULL;
+ s = new(char, a + 1 + b + strlen(e) + 1);
+ if (!s)
+ return -ENOMEM;
- strcpy(mempcpy(mempcpy(r, f, a + 1), i, b), e);
- return r;
+ strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
+
+ *ret = s;
+ return 0;
}
-char *unit_name_template(const char *f) {
+int unit_name_template(const char *f, char **ret) {
const char *p, *e;
- char *r;
+ char *s;
size_t a;
assert(f);
+ assert(ret);
- p = strchr(f, '@');
- if (!p)
- return strdup(f);
+ if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
+ return -EINVAL;
- e = strrchr(f, '.');
- if (!e)
- e = strchr(f, 0);
+ assert_se(p = strchr(f, '@'));
+ assert_se(e = strrchr(f, '.'));
a = p - f;
- r = new(char, a + 1 + strlen(e) + 1);
- if (!r)
- return NULL;
+ s = new(char, a + 1 + strlen(e) + 1);
+ if (!s)
+ return -ENOMEM;
- strcpy(mempcpy(r, f, a + 1), e);
- return r;
+ strcpy(mempcpy(s, f, a + 1), e);
+
+ *ret = s;
+ return 0;
}
-char *unit_name_from_path(const char *path, const char *suffix) {
+int unit_name_from_path(const char *path, const char *suffix, char **ret) {
_cleanup_free_ char *p = NULL;
+ char *s = NULL;
+ int r;
assert(path);
assert(suffix);
+ assert(ret);
- p = unit_name_path_escape(path);
- if (!p)
- return NULL;
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
+
+ r = unit_name_path_escape(path, &p);
+ if (r < 0)
+ return r;
- return strappend(p, suffix);
+ s = strappend(p, suffix);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
}
-char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
+int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
_cleanup_free_ char *p = NULL;
+ char *s;
+ int r;
assert(prefix);
assert(path);
assert(suffix);
+ assert(ret);
- p = unit_name_path_escape(path);
- if (!p)
- return NULL;
+ if (!unit_prefix_is_valid(prefix))
+ return -EINVAL;
+
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
+
+ r = unit_name_path_escape(path, &p);
+ if (r < 0)
+ return r;
+
+ s = strjoin(prefix, "@", p, suffix, NULL);
+ if (!s)
+ return -ENOMEM;
- return strjoin(prefix, "@", p, suffix, NULL);
+ *ret = s;
+ return 0;
}
-char *unit_name_to_path(const char *name) {
- _cleanup_free_ char *w = NULL;
+int unit_name_to_path(const char *name, char **ret) {
+ _cleanup_free_ char *prefix = NULL;
+ int r;
assert(name);
- w = unit_name_to_prefix(name);
- if (!w)
- return NULL;
+ r = unit_name_to_prefix(name, &prefix);
+ if (r < 0)
+ return r;
- return unit_name_path_unescape(w);
+ return unit_name_path_unescape(prefix, ret);
}
char *unit_dbus_path_from_name(const char *name) {
@@ -501,6 +586,30 @@ int unit_name_from_dbus_path(const char *path, char **name) {
return 0;
}
+static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
+ const char *valid_chars;
+
+ assert(f);
+ assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
+ assert(t);
+
+ /* We'll only escape the obvious characters here, to play
+ * safe. */
+
+ valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
+
+ for (; *f; f++) {
+ if (*f == '/')
+ *(t++) = '-';
+ else if (!strchr(valid_chars, *f))
+ t = do_escape_char(*f, t);
+ else
+ *(t++) = *f;
+ }
+
+ return t;
+}
+
/**
* Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
* /blah/blah is converted to blah-blah.mount, anything else is left alone,
@@ -508,72 +617,191 @@ int unit_name_from_dbus_path(const char *path, char **name) {
*
* If @allow_globs, globs characters are preserved. Otherwise they are escaped.
*/
-char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
- char *r, *t;
+int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
+ char *s, *t;
+ int r;
assert(name);
assert(suffix);
- assert(suffix[0] == '.');
+ assert(ret);
+
+ if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
+ return -EINVAL;
- if (is_device_path(name))
- return unit_name_from_path(name, ".device");
+ if (!unit_suffix_is_valid(suffix))
+ return -EINVAL;
- if (path_is_absolute(name))
- return unit_name_from_path(name, ".mount");
+ if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
+ /* No mangling necessary... */
+ s = strdup(name);
+ if (!s)
+ return -ENOMEM;
- r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
- if (!r)
- return NULL;
+ *ret = s;
+ return 0;
+ }
- t = do_escape_mangle(name, allow_globs, r);
+ if (is_device_path(name)) {
+ r = unit_name_from_path(name, ".device", ret);
+ if (r >= 0)
+ return 1;
+ if (r != -EINVAL)
+ return r;
+ }
+
+ if (path_is_absolute(name)) {
+ r = unit_name_from_path(name, ".mount", ret);
+ if (r >= 0)
+ return 1;
+ if (r != -EINVAL)
+ return r;
+ }
- if (unit_name_to_type(name) < 0)
+ s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
+ if (!s)
+ return -ENOMEM;
+
+ t = do_escape_mangle(name, allow_globs, s);
+ *t = 0;
+
+ if (unit_name_to_type(s) < 0)
strcpy(t, suffix);
- else
- *t = 0;
- return r;
+ *ret = s;
+ return 1;
}
-UnitType unit_name_to_type(const char *n) {
- const char *e;
+int slice_build_parent_slice(const char *slice, char **ret) {
+ char *s, *dash;
- assert(n);
+ assert(slice);
+ assert(ret);
- e = strrchr(n, '.');
- if (!e)
- return _UNIT_TYPE_INVALID;
+ if (!slice_name_is_valid(slice))
+ return -EINVAL;
- return unit_type_from_string(e + 1);
+ if (streq(slice, "-.slice")) {
+ *ret = NULL;
+ return 0;
+ }
+
+ s = strdup(slice);
+ if (!s)
+ return -ENOMEM;
+
+ dash = strrchr(s, '-');
+ if (dash)
+ strcpy(dash, ".slice");
+ else {
+ free(s);
+
+ s = strdup("-.slice");
+ if (!s)
+ return -ENOMEM;
+ }
+
+ *ret = s;
+ return 1;
}
-int build_subslice(const char *slice, const char*name, char **subslice) {
- char *ret;
+int slice_build_subslice(const char *slice, const char*name, char **ret) {
+ char *subslice;
assert(slice);
assert(name);
- assert(subslice);
+ assert(ret);
+
+ if (!slice_name_is_valid(slice))
+ return -EINVAL;
+
+ if (!unit_prefix_is_valid(name))
+ return -EINVAL;
if (streq(slice, "-.slice"))
- ret = strappend(name, ".slice");
+ subslice = strappend(name, ".slice");
else {
char *e;
- e = endswith(slice, ".slice");
- if (!e)
- return -EINVAL;
+ assert_se(e = endswith(slice, ".slice"));
- ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
- if (!ret)
+ subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
+ if (!subslice)
return -ENOMEM;
- stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
+ stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
}
- *subslice = ret;
+ *ret = subslice;
return 0;
}
+bool slice_name_is_valid(const char *name) {
+ const char *p, *e;
+ bool dash = false;
+
+ if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
+ return false;
+
+ if (streq(name, "-.slice"))
+ return true;
+
+ e = endswith(name, ".slice");
+ if (!e)
+ return false;
+
+ for (p = name; p < e; p++) {
+
+ if (*p == '-') {
+
+ /* Don't allow initial dash */
+ if (p == name)
+ return false;
+
+ /* Don't allow multiple dashes */
+ if (dash)
+ return false;
+
+ dash = true;
+ } else
+ dash = false;
+ }
+
+ /* Don't allow trailing hash */
+ if (dash)
+ return false;
+
+ return true;
+}
+
+static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
+ [UNIT_SERVICE] = "service",
+ [UNIT_SOCKET] = "socket",
+ [UNIT_BUSNAME] = "busname",
+ [UNIT_TARGET] = "target",
+ [UNIT_SNAPSHOT] = "snapshot",
+ [UNIT_DEVICE] = "device",
+ [UNIT_MOUNT] = "mount",
+ [UNIT_AUTOMOUNT] = "automount",
+ [UNIT_SWAP] = "swap",
+ [UNIT_TIMER] = "timer",
+ [UNIT_PATH] = "path",
+ [UNIT_SLICE] = "slice",
+ [UNIT_SCOPE] = "scope"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
+
+static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
+ [UNIT_STUB] = "stub",
+ [UNIT_LOADED] = "loaded",
+ [UNIT_NOT_FOUND] = "not-found",
+ [UNIT_ERROR] = "error",
+ [UNIT_MERGED] = "merged",
+ [UNIT_MASKED] = "masked"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
+
static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_REQUIRES] = "Requires",
[UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
@@ -584,6 +812,8 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_PART_OF] = "PartOf",
[UNIT_REQUIRED_BY] = "RequiredBy",
[UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
+ [UNIT_REQUISITE_OF] = "RequisiteOf",
+ [UNIT_REQUISITE_OF_OVERRIDABLE] = "RequisiteOfOverridable",
[UNIT_WANTED_BY] = "WantedBy",
[UNIT_BOUND_BY] = "BoundBy",
[UNIT_CONSISTS_OF] = "ConsistsOf",
diff --git a/src/shared/unit-name.h b/src/shared/unit-name.h
index 6f139cc4c4..b2043d0870 100644
--- a/src/shared/unit-name.h
+++ b/src/shared/unit-name.h
@@ -71,8 +71,10 @@ enum UnitDependency {
UNIT_PART_OF,
/* Inverse of the above */
- UNIT_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */
- UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'requires_overridable' and 'requisite_overridable' is 'soft_required_by' */
+ UNIT_REQUIRED_BY, /* inverse of 'requires' is 'required_by' */
+ UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'requires_overridable' is 'required_by_overridable' */
+ UNIT_REQUISITE_OF, /* inverse of 'requisite' is 'requisite_of' */
+ UNIT_REQUISITE_OF_OVERRIDABLE,/* inverse of 'requisite_overridable' is 'requisite_of_overridable' */
UNIT_WANTED_BY, /* inverse of 'wants' */
UNIT_BOUND_BY, /* inverse of 'binds_to' */
UNIT_CONSISTS_OF, /* inverse of 'part_of' */
@@ -107,61 +109,69 @@ enum UnitDependency {
_UNIT_DEPENDENCY_INVALID = -1
};
-const char *unit_type_to_string(UnitType i) _const_;
-UnitType unit_type_from_string(const char *s) _pure_;
-
-const char *unit_load_state_to_string(UnitLoadState i) _const_;
-UnitLoadState unit_load_state_from_string(const char *s) _pure_;
+typedef enum UnitNameFlags {
+ UNIT_NAME_PLAIN = 1, /* Allow foo.service */
+ UNIT_NAME_INSTANCE = 2, /* Allow foo@bar.service */
+ UNIT_NAME_TEMPLATE = 4, /* Allow foo@.service */
+ UNIT_NAME_ANY = UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE,
+} UnitNameFlags;
-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);
-
-enum template_valid {
- TEMPLATE_INVALID,
- TEMPLATE_VALID,
-};
-
-bool unit_name_is_valid(const char *n, enum template_valid template_ok) _pure_;
+bool unit_name_is_valid(const char *n, UnitNameFlags flags) _pure_;
bool unit_prefix_is_valid(const char *p) _pure_;
bool unit_instance_is_valid(const char *i) _pure_;
+bool unit_suffix_is_valid(const char *s) _pure_;
+
+static inline int unit_prefix_and_instance_is_valid(const char *p) {
+ /* For prefix+instance and instance the same rules apply */
+ return unit_instance_is_valid(p);
+}
+
+int unit_name_to_prefix(const char *n, char **prefix);
+int unit_name_to_instance(const char *n, char **instance);
+int unit_name_to_prefix_and_instance(const char *n, char **ret);
UnitType unit_name_to_type(const char *n) _pure_;
-char *unit_name_change_suffix(const char *n, const char *suffix);
+int unit_name_change_suffix(const char *n, const char *suffix, char **ret);
-char *unit_name_build(const char *prefix, const char *instance, const char *suffix);
+int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret);
char *unit_name_escape(const char *f);
-char *unit_name_unescape(const char *f);
-char *unit_name_path_escape(const char *f);
-char *unit_name_path_unescape(const char *f);
-
-bool unit_name_is_template(const char *n) _pure_;
-bool unit_name_is_instance(const char *n) _pure_;
+int unit_name_unescape(const char *f, char **ret);
+int unit_name_path_escape(const char *f, char **ret);
+int unit_name_path_unescape(const char *f, char **ret);
-char *unit_name_replace_instance(const char *f, const char *i);
+int unit_name_replace_instance(const char *f, const char *i, char **ret);
-char *unit_name_template(const char *f);
+int unit_name_template(const char *f, char **ret);
-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);
+int unit_name_from_path(const char *path, const char *suffix, char **ret);
+int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret);
+int unit_name_to_path(const char *name, char **ret);
char *unit_dbus_path_from_name(const char *name);
int unit_name_from_dbus_path(const char *path, char **name);
-enum unit_name_mangle {
- MANGLE_NOGLOB,
- MANGLE_GLOB,
-};
+typedef enum UnitNameMangle {
+ UNIT_NAME_NOGLOB,
+ UNIT_NAME_GLOB,
+} UnitNameMangle;
+
+int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret);
-char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix);
-static inline char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
- return unit_name_mangle_with_suffix(name, allow_globs, ".service");
+static inline int unit_name_mangle(const char *name, UnitNameMangle allow_globs, char **ret) {
+ return unit_name_mangle_with_suffix(name, allow_globs, ".service", ret);
}
-int build_subslice(const char *slice, const char*name, char **subslice);
+int slice_build_parent_slice(const char *slice, char **ret);
+int slice_build_subslice(const char *slice, const char*name, char **subslice);
+bool slice_name_is_valid(const char *name);
+
+const char *unit_type_to_string(UnitType i) _const_;
+UnitType unit_type_from_string(const char *s) _pure_;
+
+const char *unit_load_state_to_string(UnitLoadState i) _const_;
+UnitLoadState unit_load_state_from_string(const char *s) _pure_;
const char *unit_dependency_to_string(UnitDependency i) _const_;
UnitDependency unit_dependency_from_string(const char *s) _pure_;
diff --git a/src/shared/utf8.c b/src/shared/utf8.c
index 013c110f07..800884ffee 100644
--- a/src/shared/utf8.c
+++ b/src/shared/utf8.c
@@ -52,7 +52,7 @@
#include "utf8.h"
#include "util.h"
-static inline bool is_unicode_valid(uint32_t ch) {
+bool unichar_is_valid(uint32_t ch) {
if (ch >= 0x110000) /* End of unicode space */
return false;
@@ -66,7 +66,7 @@ static inline bool is_unicode_valid(uint32_t ch) {
return true;
}
-static bool is_unicode_control(uint32_t ch) {
+static bool unichar_is_control(uint32_t ch) {
/*
0 to ' '-1 is the C0 range.
@@ -156,7 +156,7 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
val = utf8_encoded_to_unichar(p);
if (val < 0 ||
- is_unicode_control(val) ||
+ unichar_is_control(val) ||
(!newline && val == '\n'))
return false;
@@ -276,6 +276,7 @@ char *ascii_is_valid(const char *str) {
* occupy.
*/
size_t utf8_encode_unichar(char *out_utf8, uint32_t g) {
+
if (g < (1 << 7)) {
if (out_utf8)
out_utf8[0] = g & 0x7f;
@@ -301,9 +302,9 @@ size_t utf8_encode_unichar(char *out_utf8, uint32_t g) {
out_utf8[3] = 0x80 | (g & 0x3f);
}
return 4;
- } else {
- return 0;
}
+
+ return 0;
}
char *utf16_to_utf8(const void *s, size_t length) {
@@ -394,7 +395,7 @@ int utf8_encoded_valid_unichar(const char *str) {
return -EINVAL;
/* check if value has valid range */
- if (!is_unicode_valid(unichar))
+ if (!unichar_is_valid(unichar))
return -EINVAL;
return len;
diff --git a/src/shared/utf8.h b/src/shared/utf8.h
index 77f663438e..e745649f06 100644
--- a/src/shared/utf8.h
+++ b/src/shared/utf8.h
@@ -27,6 +27,8 @@
#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd"
+bool unichar_is_valid(uint32_t c);
+
const char *utf8_is_valid(const char *s) _pure_;
char *ascii_is_valid(const char *s) _pure_;
diff --git a/src/shared/util.c b/src/shared/util.c
index ba035caed0..311acbb349 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -19,12 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
+#include <libintl.h>
#include <stdio.h>
#include <syslog.h>
#include <sched.h>
@@ -35,9 +35,6 @@
#include <fcntl.h>
#include <dirent.h>
#include <sys/ioctl.h>
-#include <linux/vt.h>
-#include <linux/tiocl.h>
-#include <termios.h>
#include <stdarg.h>
#include <poll.h>
#include <ctype.h>
@@ -45,8 +42,6 @@
#include <sys/utsname.h>
#include <pwd.h>
#include <netinet/ip.h>
-#include <linux/kd.h>
-#include <dlfcn.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <glob.h>
@@ -74,13 +69,13 @@
#include <sys/auxv.h>
#endif
+#include "config.h"
#include "macro.h"
#include "util.h"
#include "ioprio.h"
#include "missing.h"
#include "log.h"
#include "strv.h"
-#include "label.h"
#include "mkdir.h"
#include "path-util.h"
#include "exit-status.h"
@@ -93,13 +88,19 @@
#include "virt.h"
#include "def.h"
#include "sparse-endian.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "terminal-util.h"
+#include "hostname-util.h"
+#include "signal-util.h"
+
+/* Put this test here for a lack of better place */
+assert_cc(EAGAIN == EWOULDBLOCK);
int saved_argc = 0;
char **saved_argv = NULL;
-static volatile unsigned cached_columns = 0;
-static volatile unsigned cached_lines = 0;
-
size_t page_size(void) {
static thread_local size_t pgsz = 0;
long r;
@@ -148,6 +149,27 @@ char* endswith(const char *s, const char *postfix) {
return (char*) s + sl - pl;
}
+char* endswith_no_case(const char *s, const char *postfix) {
+ size_t sl, pl;
+
+ assert(s);
+ assert(postfix);
+
+ sl = strlen(s);
+ pl = strlen(postfix);
+
+ if (pl == 0)
+ return (char*) s + sl;
+
+ if (sl < pl)
+ return NULL;
+
+ if (strcasecmp(s + sl - pl, postfix) != 0)
+ return NULL;
+
+ return (char*) s + sl - pl;
+}
+
char* first_word(const char *s, const char *word) {
size_t sl, wl;
const char *p;
@@ -182,7 +204,7 @@ char* first_word(const char *s, const char *word) {
return (char*) p;
}
-static size_t cescape_char(char c, char *buf) {
+size_t cescape_char(char c, char *buf) {
char * buf_old = buf;
switch (c) {
@@ -351,7 +373,6 @@ int parse_uid(const char *s, uid_t* ret_uid) {
int r;
assert(s);
- assert(ret_uid);
r = safe_atolu(s, &ul);
if (r < 0)
@@ -370,7 +391,9 @@ int parse_uid(const char *s, uid_t* ret_uid) {
if (uid == (uid_t) 0xFFFF)
return -ENXIO;
- *ret_uid = uid;
+ if (ret_uid)
+ *ret_uid = uid;
+
return 0;
}
@@ -571,13 +594,12 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
char quotechars[2] = {*current, '\0'};
*l = strcspn_escaped(current + 1, quotechars);
- if (current[*l + 1] == '\0' ||
+ if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
(current[*l + 2] && !strchr(separator, current[*l + 2]))) {
/* right quote missing or garbage at the end */
*state = current;
return NULL;
}
- assert(current[*l + 1] == quotechars[0]);
*state = current++ + *l + 2;
} else if (quoted) {
*l = strcspn_escaped(current, separator);
@@ -595,49 +617,6 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
return current;
}
-int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
- int r;
- _cleanup_free_ char *line = NULL;
- long unsigned ppid;
- const char *p;
-
- assert(pid >= 0);
- assert(_ppid);
-
- if (pid == 0) {
- *_ppid = getppid();
- return 0;
- }
-
- p = procfs_file_alloca(pid, "stat");
- r = read_one_line_file(p, &line);
- if (r < 0)
- return r;
-
- /* Let's skip the pid and comm fields. The latter is enclosed
- * in () but does not escape any () in its value, so let's
- * skip over it manually */
-
- p = strrchr(line, ')');
- if (!p)
- return -EIO;
-
- p++;
-
- if (sscanf(p, " "
- "%*c " /* state */
- "%lu ", /* ppid */
- &ppid) != 1)
- return -EIO;
-
- if ((long unsigned) (pid_t) ppid != ppid)
- return -ERANGE;
-
- *_ppid = (pid_t) ppid;
-
- return 0;
-}
-
int fchmod_umask(int fd, mode_t m) {
mode_t u;
int r;
@@ -656,308 +635,6 @@ char *truncate_nl(char *s) {
return s;
}
-int get_process_state(pid_t pid) {
- const char *p;
- char state;
- int r;
- _cleanup_free_ char *line = NULL;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "stat");
- r = read_one_line_file(p, &line);
- if (r < 0)
- return r;
-
- p = strrchr(line, ')');
- if (!p)
- return -EIO;
-
- p++;
-
- if (sscanf(p, " %c", &state) != 1)
- return -EIO;
-
- return (unsigned char) state;
-}
-
-int get_process_comm(pid_t pid, char **name) {
- const char *p;
- int r;
-
- assert(name);
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "comm");
-
- r = read_one_line_file(p, name);
- if (r == -ENOENT)
- return -ESRCH;
-
- return r;
-}
-
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
- _cleanup_fclose_ FILE *f = NULL;
- char *r = NULL, *k;
- const char *p;
- int c;
-
- assert(line);
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "cmdline");
-
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- if (max_length == 0) {
- size_t len = 0, allocated = 0;
-
- while ((c = getc(f)) != EOF) {
-
- if (!GREEDY_REALLOC(r, allocated, len+2)) {
- free(r);
- return -ENOMEM;
- }
-
- r[len++] = isprint(c) ? c : ' ';
- }
-
- if (len > 0)
- r[len-1] = 0;
-
- } else {
- bool space = false;
- size_t left;
-
- r = new(char, max_length);
- if (!r)
- return -ENOMEM;
-
- k = r;
- left = max_length;
- while ((c = getc(f)) != EOF) {
-
- if (isprint(c)) {
- if (space) {
- if (left <= 4)
- break;
-
- *(k++) = ' ';
- left--;
- space = false;
- }
-
- if (left <= 4)
- break;
-
- *(k++) = (char) c;
- left--;
- } else
- space = true;
- }
-
- if (left <= 4) {
- size_t n = MIN(left-1, 3U);
- memcpy(k, "...", n);
- k[n] = 0;
- } else
- *k = 0;
- }
-
- /* Kernel threads have no argv[] */
- if (isempty(r)) {
- _cleanup_free_ char *t = NULL;
- int h;
-
- free(r);
-
- if (!comm_fallback)
- return -ENOENT;
-
- h = get_process_comm(pid, &t);
- if (h < 0)
- return h;
-
- r = strjoin("[", t, "]", NULL);
- if (!r)
- return -ENOMEM;
- }
-
- *line = r;
- return 0;
-}
-
-int is_kernel_thread(pid_t pid) {
- const char *p;
- size_t count;
- char c;
- bool eof;
- FILE *f;
-
- if (pid == 0)
- return 0;
-
- assert(pid > 0);
-
- p = procfs_file_alloca(pid, "cmdline");
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- count = fread(&c, 1, 1, f);
- eof = feof(f);
- fclose(f);
-
- /* Kernel threads have an empty cmdline */
-
- if (count <= 0)
- return eof ? 1 : -errno;
-
- return 0;
-}
-
-int get_process_capeff(pid_t pid, char **capeff) {
- const char *p;
-
- assert(capeff);
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "status");
-
- return get_status_field(p, "\nCapEff:", capeff);
-}
-
-static int get_process_link_contents(const char *proc_file, char **name) {
- int r;
-
- assert(proc_file);
- assert(name);
-
- r = readlink_malloc(proc_file, name);
- if (r < 0)
- return r == -ENOENT ? -ESRCH : r;
-
- return 0;
-}
-
-int get_process_exe(pid_t pid, char **name) {
- const char *p;
- char *d;
- int r;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "exe");
- r = get_process_link_contents(p, name);
- if (r < 0)
- return r;
-
- d = endswith(*name, " (deleted)");
- if (d)
- *d = '\0';
-
- return 0;
-}
-
-static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
- _cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
- const char *p;
-
- assert(field);
- assert(uid);
-
- if (pid == 0)
- return getuid();
-
- p = procfs_file_alloca(pid, "status");
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- FOREACH_LINE(line, f, return -errno) {
- char *l;
-
- l = strstrip(line);
-
- if (startswith(l, field)) {
- l += strlen(field);
- l += strspn(l, WHITESPACE);
-
- l[strcspn(l, WHITESPACE)] = 0;
-
- return parse_uid(l, uid);
- }
- }
-
- return -EIO;
-}
-
-int get_process_uid(pid_t pid, uid_t *uid) {
- return get_process_id(pid, "Uid:", uid);
-}
-
-int get_process_gid(pid_t pid, gid_t *gid) {
- assert_cc(sizeof(uid_t) == sizeof(gid_t));
- return get_process_id(pid, "Gid:", gid);
-}
-
-int get_process_cwd(pid_t pid, char **cwd) {
- const char *p;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "cwd");
-
- return get_process_link_contents(p, cwd);
-}
-
-int get_process_root(pid_t pid, char **root) {
- const char *p;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "root");
-
- return get_process_link_contents(p, root);
-}
-
-int get_process_environ(pid_t pid, char **env) {
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *outcome = NULL;
- int c;
- const char *p;
- size_t allocated = 0, sz = 0;
-
- assert(pid >= 0);
- assert(env);
-
- p = procfs_file_alloca(pid, "environ");
-
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- while ((c = fgetc(f)) != EOF) {
- if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
- return -ENOMEM;
-
- if (c == '\0')
- outcome[sz++] = '\n';
- else
- sz += cescape_char(c, outcome + sz);
- }
-
- outcome[sz] = '\0';
- *env = outcome;
- outcome = NULL;
-
- return 0;
-}
-
char *strnappend(const char *s, const char *suffix, size_t b) {
size_t a;
char *r;
@@ -1095,41 +772,6 @@ int readlink_and_canonicalize(const char *p, char **r) {
return 0;
}
-int reset_all_signal_handlers(void) {
- int sig, r = 0;
-
- for (sig = 1; sig < _NSIG; sig++) {
- struct sigaction sa = {
- .sa_handler = SIG_DFL,
- .sa_flags = SA_RESTART,
- };
-
- /* These two cannot be caught... */
- if (sig == SIGKILL || sig == SIGSTOP)
- continue;
-
- /* On Linux the first two RT signals are reserved by
- * glibc, and sigaction() will return EINVAL for them. */
- if ((sigaction(sig, &sa, NULL) < 0))
- if (errno != EINVAL && r == 0)
- r = -errno;
- }
-
- return r;
-}
-
-int reset_signal_mask(void) {
- sigset_t ss;
-
- if (sigemptyset(&ss) < 0)
- return -errno;
-
- if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0)
- return -errno;
-
- return 0;
-}
-
char *strstrip(char *s) {
char *e;
@@ -1330,7 +972,8 @@ char *cescape(const char *s) {
assert(s);
- /* Does C style string escaping. */
+ /* Does C style string escaping. May be reversed with
+ * cunescape(). */
r = new(char, strlen(s)*4 + 1);
if (!r)
@@ -1344,12 +987,214 @@ char *cescape(const char *s) {
return r;
}
-char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
+static int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) {
+ int r = 1;
+
+ assert(p);
+ assert(*p);
+ assert(ret);
+
+ /* Unescapes C style. Returns the unescaped character in ret,
+ * unless we encountered a \u sequence in which case the full
+ * unicode character is returned in ret_unicode, instead. */
+
+ if (length != (size_t) -1 && length < 1)
+ return -EINVAL;
+
+ switch (p[0]) {
+
+ case 'a':
+ *ret = '\a';
+ break;
+ case 'b':
+ *ret = '\b';
+ break;
+ case 'f':
+ *ret = '\f';
+ break;
+ case 'n':
+ *ret = '\n';
+ break;
+ case 'r':
+ *ret = '\r';
+ break;
+ case 't':
+ *ret = '\t';
+ break;
+ case 'v':
+ *ret = '\v';
+ break;
+ case '\\':
+ *ret = '\\';
+ break;
+ case '"':
+ *ret = '"';
+ break;
+ case '\'':
+ *ret = '\'';
+ break;
+
+ case 's':
+ /* This is an extension of the XDG syntax files */
+ *ret = ' ';
+ break;
+
+ case 'x': {
+ /* hexadecimal encoding */
+ int a, b;
+
+ if (length != (size_t) -1 && length < 3)
+ return -EINVAL;
+
+ a = unhexchar(p[1]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unhexchar(p[2]);
+ if (b < 0)
+ return -EINVAL;
+
+ /* Don't allow NUL bytes */
+ if (a == 0 && b == 0)
+ return -EINVAL;
+
+ *ret = (char) ((a << 4U) | b);
+ r = 3;
+ break;
+ }
+
+ case 'u': {
+ /* C++11 style 16bit unicode */
+
+ int a[4];
+ unsigned i;
+ uint32_t c;
+
+ if (length != (size_t) -1 && length < 5)
+ return -EINVAL;
+
+ for (i = 0; i < 4; i++) {
+ a[i] = unhexchar(p[1 + i]);
+ if (a[i] < 0)
+ return a[i];
+ }
+
+ c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
+
+ /* Don't allow 0 chars */
+ if (c == 0)
+ return -EINVAL;
+
+ if (c < 128)
+ *ret = c;
+ else {
+ if (!ret_unicode)
+ return -EINVAL;
+
+ *ret = 0;
+ *ret_unicode = c;
+ }
+
+ r = 5;
+ break;
+ }
+
+ case 'U': {
+ /* C++11 style 32bit unicode */
+
+ int a[8];
+ unsigned i;
+ uint32_t c;
+
+ if (length != (size_t) -1 && length < 9)
+ return -EINVAL;
+
+ for (i = 0; i < 8; i++) {
+ a[i] = unhexchar(p[1 + i]);
+ if (a[i] < 0)
+ return a[i];
+ }
+
+ c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
+ ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7];
+
+ /* Don't allow 0 chars */
+ if (c == 0)
+ return -EINVAL;
+
+ /* Don't allow invalid code points */
+ if (!unichar_is_valid(c))
+ return -EINVAL;
+
+ if (c < 128)
+ *ret = c;
+ else {
+ if (!ret_unicode)
+ return -EINVAL;
+
+ *ret = 0;
+ *ret_unicode = c;
+ }
+
+ r = 9;
+ break;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7': {
+ /* octal encoding */
+ int a, b, c;
+ uint32_t m;
+
+ if (length != (size_t) -1 && length < 3)
+ return -EINVAL;
+
+ a = unoctchar(p[0]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unoctchar(p[1]);
+ if (b < 0)
+ return -EINVAL;
+
+ c = unoctchar(p[2]);
+ if (c < 0)
+ return -EINVAL;
+
+ /* don't allow NUL bytes */
+ if (a == 0 && b == 0 && c == 0)
+ return -EINVAL;
+
+ /* Don't allow bytes above 255 */
+ m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
+ if (m > 255)
+ return -EINVAL;
+
+ *ret = m;
+ r = 3;
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ return r;
+}
+
+int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
char *r, *t;
const char *f;
size_t pl;
assert(s);
+ assert(ret);
/* Undoes C style string escaping, and optionally prefixes it. */
@@ -1357,135 +1202,71 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
r = new(char, pl+length+1);
if (!r)
- return NULL;
+ return -ENOMEM;
if (prefix)
memcpy(r, prefix, pl);
for (f = s, t = r + pl; f < s + length; f++) {
- size_t remaining = s + length - f;
+ size_t remaining;
+ uint32_t u;
+ char c;
+ int k;
+
+ remaining = s + length - f;
assert(remaining > 0);
- if (*f != '\\') { /* a literal literal */
+ if (*f != '\\') {
+ /* A literal literal, copy verbatim */
*(t++) = *f;
continue;
}
- if (--remaining == 0) { /* copy trailing backslash verbatim */
- *(t++) = *f;
- break;
- }
-
- f++;
-
- switch (*f) {
-
- case 'a':
- *(t++) = '\a';
- break;
- case 'b':
- *(t++) = '\b';
- break;
- case 'f':
- *(t++) = '\f';
- break;
- case 'n':
- *(t++) = '\n';
- break;
- case 'r':
- *(t++) = '\r';
- break;
- case 't':
- *(t++) = '\t';
- break;
- case 'v':
- *(t++) = '\v';
- break;
- case '\\':
- *(t++) = '\\';
- break;
- case '"':
- *(t++) = '"';
- break;
- case '\'':
- *(t++) = '\'';
- break;
-
- case 's':
- /* This is an extension of the XDG syntax files */
- *(t++) = ' ';
- break;
-
- case 'x': {
- /* hexadecimal encoding */
- int a = -1, b = -1;
-
- if (remaining >= 2) {
- a = unhexchar(f[1]);
- b = unhexchar(f[2]);
- }
-
- if (a < 0 || b < 0 || (a == 0 && b == 0)) {
- /* Invalid escape code, let's take it literal then */
- *(t++) = '\\';
- *(t++) = 'x';
- } else {
- *(t++) = (char) ((a << 4) | b);
- f += 2;
+ if (remaining == 1) {
+ if (flags & UNESCAPE_RELAX) {
+ /* A trailing backslash, copy verbatim */
+ *(t++) = *f;
+ continue;
}
- break;
+ free(r);
+ return -EINVAL;
}
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7': {
- /* octal encoding */
- int a = -1, b = -1, c = -1;
-
- if (remaining >= 3) {
- a = unoctchar(f[0]);
- b = unoctchar(f[1]);
- c = unoctchar(f[2]);
- }
-
- if (a < 0 || b < 0 || c < 0 || (a == 0 && b == 0 && c == 0)) {
+ k = cunescape_one(f + 1, remaining - 1, &c, &u);
+ if (k < 0) {
+ if (flags & UNESCAPE_RELAX) {
/* Invalid escape code, let's take it literal then */
*(t++) = '\\';
- *(t++) = f[0];
- } else {
- *(t++) = (char) ((a << 6) | (b << 3) | c);
- f += 2;
+ continue;
}
- break;
+ free(r);
+ return k;
}
- default:
- /* Invalid escape code, let's take it literal then */
- *(t++) = '\\';
- *(t++) = *f;
- break;
- }
+ if (c != 0)
+ /* Non-Unicode? Let's encode this directly */
+ *(t++) = c;
+ else
+ /* Unicode? Then let's encode this in UTF-8 */
+ t += utf8_encode_unichar(t, u);
+
+ f += k;
}
*t = 0;
- return r;
-}
-char *cunescape_length(const char *s, size_t length) {
- return cunescape_length_with_prefix(s, length, NULL);
+ *ret = r;
+ return t - r;
}
-char *cunescape(const char *s) {
- assert(s);
+int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
+ return cunescape_length_with_prefix(s, length, NULL, flags, ret);
+}
- return cunescape_length(s, strlen(s));
+int cunescape(const char *s, UnescapeFlags flags, char **ret) {
+ return cunescape_length(s, strlen(s), flags, ret);
}
char *xescape(const char *s, const char *bad) {
@@ -1494,7 +1275,7 @@ char *xescape(const char *s, const char *bad) {
/* Escapes all chars in bad, in addition to \ and all special
* chars, in \xFF style escaping. May be reversed with
- * cunescape. */
+ * cunescape(). */
r = new(char, strlen(s) * 4 + 1);
if (!r)
@@ -1689,6 +1470,7 @@ bool chars_intersect(const char *a, const char *b) {
bool fstype_is_network(const char *fstype) {
static const char table[] =
+ "afs\0"
"cifs\0"
"smbfs\0"
"sshfs\0"
@@ -1709,301 +1491,6 @@ bool fstype_is_network(const char *fstype) {
return nulstr_contains(table, fstype);
}
-int chvt(int vt) {
- _cleanup_close_ int fd;
-
- fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- if (vt < 0) {
- int tiocl[2] = {
- TIOCL_GETKMSGREDIRECT,
- 0
- };
-
- if (ioctl(fd, TIOCLINUX, tiocl) < 0)
- return -errno;
-
- vt = tiocl[0] <= 0 ? 1 : tiocl[0];
- }
-
- if (ioctl(fd, VT_ACTIVATE, vt) < 0)
- return -errno;
-
- return 0;
-}
-
-int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
- struct termios old_termios, new_termios;
- char c, line[LINE_MAX];
-
- assert(f);
- assert(ret);
-
- if (tcgetattr(fileno(f), &old_termios) >= 0) {
- new_termios = old_termios;
-
- new_termios.c_lflag &= ~ICANON;
- new_termios.c_cc[VMIN] = 1;
- new_termios.c_cc[VTIME] = 0;
-
- if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
- size_t k;
-
- if (t != USEC_INFINITY) {
- if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
- tcsetattr(fileno(f), TCSADRAIN, &old_termios);
- return -ETIMEDOUT;
- }
- }
-
- k = fread(&c, 1, 1, f);
-
- tcsetattr(fileno(f), TCSADRAIN, &old_termios);
-
- if (k <= 0)
- return -EIO;
-
- if (need_nl)
- *need_nl = c != '\n';
-
- *ret = c;
- return 0;
- }
- }
-
- if (t != USEC_INFINITY) {
- if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
- return -ETIMEDOUT;
- }
-
- errno = 0;
- if (!fgets(line, sizeof(line), f))
- return errno ? -errno : -EIO;
-
- truncate_nl(line);
-
- if (strlen(line) != 1)
- return -EBADMSG;
-
- if (need_nl)
- *need_nl = false;
-
- *ret = line[0];
- return 0;
-}
-
-int ask_char(char *ret, const char *replies, const char *text, ...) {
- int r;
-
- assert(ret);
- assert(replies);
- assert(text);
-
- for (;;) {
- va_list ap;
- char c;
- bool need_nl = true;
-
- if (on_tty())
- fputs(ANSI_HIGHLIGHT_ON, stdout);
-
- va_start(ap, text);
- vprintf(text, ap);
- va_end(ap);
-
- if (on_tty())
- fputs(ANSI_HIGHLIGHT_OFF, stdout);
-
- fflush(stdout);
-
- r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl);
- if (r < 0) {
-
- if (r == -EBADMSG) {
- puts("Bad input, please try again.");
- continue;
- }
-
- putchar('\n');
- return r;
- }
-
- if (need_nl)
- putchar('\n');
-
- if (strchr(replies, c)) {
- *ret = c;
- return 0;
- }
-
- puts("Read unexpected character, please try again.");
- }
-}
-
-int ask_string(char **ret, const char *text, ...) {
- assert(ret);
- assert(text);
-
- for (;;) {
- char line[LINE_MAX];
- va_list ap;
-
- if (on_tty())
- fputs(ANSI_HIGHLIGHT_ON, stdout);
-
- va_start(ap, text);
- vprintf(text, ap);
- va_end(ap);
-
- if (on_tty())
- fputs(ANSI_HIGHLIGHT_OFF, stdout);
-
- fflush(stdout);
-
- errno = 0;
- if (!fgets(line, sizeof(line), stdin))
- return errno ? -errno : -EIO;
-
- if (!endswith(line, "\n"))
- putchar('\n');
- else {
- char *s;
-
- if (isempty(line))
- continue;
-
- truncate_nl(line);
- s = strdup(line);
- if (!s)
- return -ENOMEM;
-
- *ret = s;
- return 0;
- }
- }
-}
-
-int reset_terminal_fd(int fd, bool switch_to_text) {
- struct termios termios;
- int r = 0;
-
- /* Set terminal to some sane defaults */
-
- assert(fd >= 0);
-
- /* We leave locked terminal attributes untouched, so that
- * Plymouth may set whatever it wants to set, and we don't
- * interfere with that. */
-
- /* Disable exclusive mode, just in case */
- ioctl(fd, TIOCNXCL);
-
- /* Switch to text mode */
- if (switch_to_text)
- ioctl(fd, KDSETMODE, KD_TEXT);
-
- /* Enable console unicode mode */
- ioctl(fd, KDSKBMODE, K_UNICODE);
-
- if (tcgetattr(fd, &termios) < 0) {
- r = -errno;
- goto finish;
- }
-
- /* We only reset the stuff that matters to the software. How
- * hardware is set up we don't touch assuming that somebody
- * else will do that for us */
-
- termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
- termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
- termios.c_oflag |= ONLCR;
- termios.c_cflag |= CREAD;
- termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
-
- termios.c_cc[VINTR] = 03; /* ^C */
- termios.c_cc[VQUIT] = 034; /* ^\ */
- termios.c_cc[VERASE] = 0177;
- termios.c_cc[VKILL] = 025; /* ^X */
- termios.c_cc[VEOF] = 04; /* ^D */
- termios.c_cc[VSTART] = 021; /* ^Q */
- termios.c_cc[VSTOP] = 023; /* ^S */
- termios.c_cc[VSUSP] = 032; /* ^Z */
- termios.c_cc[VLNEXT] = 026; /* ^V */
- termios.c_cc[VWERASE] = 027; /* ^W */
- termios.c_cc[VREPRINT] = 022; /* ^R */
- termios.c_cc[VEOL] = 0;
- termios.c_cc[VEOL2] = 0;
-
- termios.c_cc[VTIME] = 0;
- termios.c_cc[VMIN] = 1;
-
- if (tcsetattr(fd, TCSANOW, &termios) < 0)
- r = -errno;
-
-finish:
- /* Just in case, flush all crap out */
- tcflush(fd, TCIOFLUSH);
-
- return r;
-}
-
-int reset_terminal(const char *name) {
- _cleanup_close_ int fd = -1;
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- return reset_terminal_fd(fd, true);
-}
-
-int open_terminal(const char *name, int mode) {
- int fd, r;
- unsigned c = 0;
-
- /*
- * If a TTY is in the process of being closed opening it might
- * cause EIO. This is horribly awful, but unlikely to be
- * changed in the kernel. Hence we work around this problem by
- * retrying a couple of times.
- *
- * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
- */
-
- assert(!(mode & O_CREAT));
-
- for (;;) {
- fd = open(name, mode, 0);
- if (fd >= 0)
- break;
-
- if (errno != EIO)
- return -errno;
-
- /* Max 1s in total */
- if (c >= 20)
- return -errno;
-
- usleep(50 * USEC_PER_MSEC);
- c++;
- }
-
- r = isatty(fd);
- if (r < 0) {
- safe_close(fd);
- return -errno;
- }
-
- if (!r) {
- safe_close(fd);
- return -ENOTTY;
- }
-
- return fd;
-}
-
int flush_fd(int fd) {
struct pollfd pollfd = {
.fd = fd,
@@ -2040,238 +1527,6 @@ int flush_fd(int fd) {
}
}
-int acquire_terminal(
- const char *name,
- bool fail,
- bool force,
- bool ignore_tiocstty_eperm,
- usec_t timeout) {
-
- int fd = -1, notify = -1, r = 0, wd = -1;
- usec_t ts = 0;
-
- assert(name);
-
- /* We use inotify to be notified when the tty is closed. We
- * create the watch before checking if we can actually acquire
- * it, so that we don't lose any event.
- *
- * Note: strictly speaking this actually watches for the
- * device being closed, it does *not* really watch whether a
- * tty loses its controlling process. However, unless some
- * rogue process uses TIOCNOTTY on /dev/tty *after* closing
- * its tty otherwise this will not become a problem. As long
- * as the administrator makes sure not configure any service
- * on the same tty as an untrusted user this should not be a
- * problem. (Which he probably should not do anyway.) */
-
- if (timeout != USEC_INFINITY)
- ts = now(CLOCK_MONOTONIC);
-
- if (!fail && !force) {
- notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0));
- if (notify < 0) {
- r = -errno;
- goto fail;
- }
-
- wd = inotify_add_watch(notify, name, IN_CLOSE);
- if (wd < 0) {
- r = -errno;
- goto fail;
- }
- }
-
- for (;;) {
- struct sigaction sa_old, sa_new = {
- .sa_handler = SIG_IGN,
- .sa_flags = SA_RESTART,
- };
-
- if (notify >= 0) {
- r = flush_fd(notify);
- if (r < 0)
- goto fail;
- }
-
- /* We pass here O_NOCTTY only so that we can check the return
- * value TIOCSCTTY and have a reliable way to figure out if we
- * successfully became the controlling process of the tty */
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
- * if we already own the tty. */
- assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
-
- /* First, try to get the tty */
- if (ioctl(fd, TIOCSCTTY, force) < 0)
- r = -errno;
-
- assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
-
- /* Sometimes it makes sense to ignore TIOCSCTTY
- * returning EPERM, i.e. when very likely we already
- * are have this controlling terminal. */
- if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
- r = 0;
-
- if (r < 0 && (force || fail || r != -EPERM)) {
- goto fail;
- }
-
- if (r >= 0)
- break;
-
- assert(!fail);
- assert(!force);
- assert(notify >= 0);
-
- for (;;) {
- union inotify_event_buffer buffer;
- struct inotify_event *e;
- ssize_t l;
-
- if (timeout != USEC_INFINITY) {
- usec_t n;
-
- n = now(CLOCK_MONOTONIC);
- if (ts + timeout < n) {
- r = -ETIMEDOUT;
- goto fail;
- }
-
- r = fd_wait_for_event(fd, POLLIN, ts + timeout - n);
- if (r < 0)
- goto fail;
-
- if (r == 0) {
- r = -ETIMEDOUT;
- goto fail;
- }
- }
-
- l = read(notify, &buffer, sizeof(buffer));
- if (l < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
-
- r = -errno;
- goto fail;
- }
-
- FOREACH_INOTIFY_EVENT(e, buffer, l) {
- if (e->wd != wd || !(e->mask & IN_CLOSE)) {
- r = -EIO;
- goto fail;
- }
- }
-
- break;
- }
-
- /* We close the tty fd here since if the old session
- * ended our handle will be dead. It's important that
- * we do this after sleeping, so that we don't enter
- * an endless loop. */
- fd = safe_close(fd);
- }
-
- safe_close(notify);
-
- r = reset_terminal_fd(fd, true);
- if (r < 0)
- log_warning_errno(r, "Failed to reset terminal: %m");
-
- return fd;
-
-fail:
- safe_close(fd);
- safe_close(notify);
-
- return r;
-}
-
-int release_terminal(void) {
- static const struct sigaction sa_new = {
- .sa_handler = SIG_IGN,
- .sa_flags = SA_RESTART,
- };
-
- _cleanup_close_ int fd = -1;
- struct sigaction sa_old;
- int r = 0;
-
- fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
- * by our own TIOCNOTTY */
- assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
-
- if (ioctl(fd, TIOCNOTTY) < 0)
- r = -errno;
-
- assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
-
- return r;
-}
-
-int sigaction_many(const struct sigaction *sa, ...) {
- va_list ap;
- int r = 0, sig;
-
- va_start(ap, sa);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
-int ignore_signals(int sig, ...) {
- struct sigaction sa = {
- .sa_handler = SIG_IGN,
- .sa_flags = SA_RESTART,
- };
- va_list ap;
- int r = 0;
-
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
-
- va_start(ap, sig);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
-int default_signals(int sig, ...) {
- struct sigaction sa = {
- .sa_handler = SIG_DFL,
- .sa_flags = SA_RESTART,
- };
- va_list ap;
- int r = 0;
-
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
-
- va_start(ap, sig);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
void safe_close_pair(int p[]) {
assert(p);
@@ -2325,6 +1580,17 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
return n;
}
+int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
+ ssize_t n;
+
+ n = loop_read(fd, buf, nbytes, do_poll);
+ if (n < 0)
+ return n;
+ if ((size_t) n != nbytes)
+ return -EIO;
+ return 0;
+}
+
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
const uint8_t *p = buf;
@@ -2333,7 +1599,7 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
errno = 0;
- while (nbytes > 0) {
+ do {
ssize_t k;
k = write(fd, p, nbytes);
@@ -2353,23 +1619,23 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
return -errno;
}
- if (k == 0) /* Can't really happen */
+ if (nbytes > 0 && k == 0) /* Can't really happen */
return -EIO;
p += k;
nbytes -= k;
- }
+ } while (nbytes > 0);
return 0;
}
int parse_size(const char *t, off_t base, off_t *size) {
- /* Soo, sometimes we want to parse IEC binary suffxies, and
+ /* Soo, sometimes we want to parse IEC binary suffixes, and
* sometimes SI decimal suffixes. This function can parse
* both. Which one is the right way depends on the
* context. Wikipedia suggests that SI is customary for
- * hardrware metrics and network speeds, while IEC is
+ * hardware metrics and network speeds, while IEC is
* customary for most data sizes used by software and volatile
* (RAM) memory. Hence be careful which one you pick!
*
@@ -2492,40 +1758,6 @@ int parse_size(const char *t, off_t base, off_t *size) {
return 0;
}
-int make_stdio(int fd) {
- int r, s, t;
-
- assert(fd >= 0);
-
- r = dup2(fd, STDIN_FILENO);
- s = dup2(fd, STDOUT_FILENO);
- t = dup2(fd, STDERR_FILENO);
-
- if (fd >= 3)
- safe_close(fd);
-
- if (r < 0 || s < 0 || t < 0)
- return -errno;
-
- /* Explicitly unset O_CLOEXEC, since if fd was < 3, then
- * dup2() was a NOP and the bit hence possibly set. */
- fd_cloexec(STDIN_FILENO, false);
- fd_cloexec(STDOUT_FILENO, false);
- fd_cloexec(STDERR_FILENO, false);
-
- return 0;
-}
-
-int make_null_stdio(void) {
- int null_fd;
-
- null_fd = open("/dev/null", O_RDWR|O_NOCTTY);
- if (null_fd < 0)
- return -errno;
-
- return make_stdio(null_fd);
-}
-
bool is_device_path(const char *path) {
/* Returns true on paths that refer to a device, either in
@@ -2577,108 +1809,6 @@ char* dirname_malloc(const char *path) {
return dir;
}
-int dev_urandom(void *p, size_t n) {
- static int have_syscall = -1;
- int r, fd;
- ssize_t k;
-
- /* Gathers some randomness from the kernel. This call will
- * never block, and will always return some data from the
- * kernel, regardless if the random pool is fully initialized
- * or not. It thus makes no guarantee for the quality of the
- * returned entropy, but is good enough for or usual usecases
- * of seeding the hash functions for hashtable */
-
- /* Use the getrandom() syscall unless we know we don't have
- * it, or when the requested size is too large for it. */
- if (have_syscall != 0 || (size_t) (int) n != n) {
- r = getrandom(p, n, GRND_NONBLOCK);
- if (r == (int) n) {
- have_syscall = true;
- return 0;
- }
-
- if (r < 0) {
- if (errno == ENOSYS)
- /* we lack the syscall, continue with
- * reading from /dev/urandom */
- have_syscall = false;
- else if (errno == EAGAIN)
- /* not enough entropy for now. Let's
- * remember to use the syscall the
- * next time, again, but also read
- * from /dev/urandom for now, which
- * doesn't care about the current
- * amount of entropy. */
- have_syscall = true;
- else
- return -errno;
- } else
- /* too short read? */
- return -EIO;
- }
-
- fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return errno == ENOENT ? -ENOSYS : -errno;
-
- k = loop_read(fd, p, n, true);
- safe_close(fd);
-
- if (k < 0)
- return (int) k;
- if ((size_t) k != n)
- return -EIO;
-
- return 0;
-}
-
-void initialize_srand(void) {
- static bool srand_called = false;
- unsigned x;
-#ifdef HAVE_SYS_AUXV_H
- void *auxv;
-#endif
-
- if (srand_called)
- return;
-
- x = 0;
-
-#ifdef HAVE_SYS_AUXV_H
- /* The kernel provides us with a bit of entropy in auxv, so
- * let's try to make use of that to seed the pseudo-random
- * generator. It's better than nothing... */
-
- auxv = (void*) getauxval(AT_RANDOM);
- if (auxv)
- x ^= *(unsigned*) auxv;
-#endif
-
- x ^= (unsigned) now(CLOCK_REALTIME);
- x ^= (unsigned) gettid();
-
- srand(x);
- srand_called = true;
-}
-
-void random_bytes(void *p, size_t n) {
- uint8_t *q;
- int r;
-
- r = dev_urandom(p, n);
- if (r >= 0)
- return;
-
- /* If some idiot made /dev/urandom unavailable to us, he'll
- * get a PRNG instead. */
-
- initialize_srand();
-
- for (q = p; q < (uint8_t*) p + n; q ++)
- *q = rand();
-}
-
void rename_process(const char name[8]) {
assert(name);
@@ -2710,55 +1840,6 @@ void rename_process(const char name[8]) {
}
}
-void sigset_add_many(sigset_t *ss, ...) {
- va_list ap;
- int sig;
-
- assert(ss);
-
- va_start(ap, ss);
- while ((sig = va_arg(ap, int)) > 0)
- assert_se(sigaddset(ss, sig) == 0);
- va_end(ap);
-}
-
-int sigprocmask_many(int how, ...) {
- va_list ap;
- sigset_t ss;
- int sig;
-
- assert_se(sigemptyset(&ss) == 0);
-
- va_start(ap, how);
- while ((sig = va_arg(ap, int)) > 0)
- assert_se(sigaddset(&ss, sig) == 0);
- va_end(ap);
-
- if (sigprocmask(how, &ss, NULL) < 0)
- return -errno;
-
- return 0;
-}
-
-char* gethostname_malloc(void) {
- struct utsname u;
-
- assert_se(uname(&u) >= 0);
-
- if (!isempty(u.nodename) && !streq(u.nodename, "(none)"))
- return strdup(u.nodename);
-
- return strdup(u.sysname);
-}
-
-bool hostname_is_set(void) {
- struct utsname u;
-
- assert_se(uname(&u) >= 0);
-
- return !isempty(u.nodename) && !streq(u.nodename, "(none)");
-}
-
char *lookup_uid(uid_t uid) {
long bufsize;
char *name;
@@ -2808,243 +1889,14 @@ char *getusername_malloc(void) {
return lookup_uid(getuid());
}
-int getttyname_malloc(int fd, char **ret) {
- size_t l = 100;
- int r;
-
- assert(fd >= 0);
- assert(ret);
-
- for (;;) {
- char path[l];
-
- r = ttyname_r(fd, path, sizeof(path));
- if (r == 0) {
- const char *p;
- char *c;
-
- p = startswith(path, "/dev/");
- c = strdup(p ?: path);
- if (!c)
- return -ENOMEM;
-
- *ret = c;
- return 0;
- }
-
- if (r != ERANGE)
- return -r;
-
- l *= 2;
- }
-
- return 0;
-}
-
-int getttyname_harder(int fd, char **r) {
- int k;
- char *s;
-
- k = getttyname_malloc(fd, &s);
- if (k < 0)
- return k;
-
- if (streq(s, "tty")) {
- free(s);
- return get_ctty(0, NULL, r);
- }
-
- *r = s;
- return 0;
-}
-
-int get_ctty_devnr(pid_t pid, dev_t *d) {
- int r;
- _cleanup_free_ char *line = NULL;
- const char *p;
- unsigned long ttynr;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "stat");
- r = read_one_line_file(p, &line);
- if (r < 0)
- return r;
-
- p = strrchr(line, ')');
- if (!p)
- return -EIO;
-
- p++;
-
- if (sscanf(p, " "
- "%*c " /* state */
- "%*d " /* ppid */
- "%*d " /* pgrp */
- "%*d " /* session */
- "%lu ", /* ttynr */
- &ttynr) != 1)
- return -EIO;
-
- if (major(ttynr) == 0 && minor(ttynr) == 0)
- return -ENOENT;
-
- if (d)
- *d = (dev_t) ttynr;
-
- return 0;
-}
-
-int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
- char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
- _cleanup_free_ char *s = NULL;
- const char *p;
- dev_t devnr;
- int k;
-
- assert(r);
-
- k = get_ctty_devnr(pid, &devnr);
- if (k < 0)
- return k;
-
- sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
-
- k = readlink_malloc(fn, &s);
- if (k < 0) {
-
- if (k != -ENOENT)
- return k;
-
- /* This is an ugly hack */
- if (major(devnr) == 136) {
- asprintf(&b, "pts/%u", minor(devnr));
- goto finish;
- }
-
- /* Probably something like the ptys which have no
- * symlink in /dev/char. Let's return something
- * vaguely useful. */
-
- b = strdup(fn + 5);
- goto finish;
- }
-
- if (startswith(s, "/dev/"))
- p = s + 5;
- else if (startswith(s, "../"))
- p = s + 3;
- else
- p = s;
-
- b = strdup(p);
-
-finish:
- if (!b)
- return -ENOMEM;
-
- *r = b;
- if (_devnr)
- *_devnr = devnr;
-
- return 0;
-}
-
-int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
- _cleanup_closedir_ DIR *d = NULL;
- int ret = 0;
-
- assert(fd >= 0);
-
- /* This returns the first error we run into, but nevertheless
- * tries to go on. This closes the passed fd. */
-
- d = fdopendir(fd);
- if (!d) {
- safe_close(fd);
-
- return errno == ENOENT ? 0 : -errno;
- }
-
- for (;;) {
- struct dirent *de;
- bool is_dir, keep_around;
- struct stat st;
- int r;
-
- errno = 0;
- de = readdir(d);
- if (!de) {
- if (errno != 0 && ret == 0)
- ret = -errno;
- return ret;
- }
-
- if (streq(de->d_name, ".") || streq(de->d_name, ".."))
- continue;
-
- if (de->d_type == DT_UNKNOWN ||
- honour_sticky ||
- (de->d_type == DT_DIR && root_dev)) {
- if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- is_dir = S_ISDIR(st.st_mode);
- keep_around =
- honour_sticky &&
- (st.st_uid == 0 || st.st_uid == getuid()) &&
- (st.st_mode & S_ISVTX);
- } else {
- is_dir = de->d_type == DT_DIR;
- keep_around = false;
- }
-
- if (is_dir) {
- int subdir_fd;
-
- /* if root_dev is set, remove subdirectories only, if device is same as dir */
- if (root_dev && st.st_dev != root_dev->st_dev)
- continue;
-
- subdir_fd = openat(fd, de->d_name,
- O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
- if (subdir_fd < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- r = rm_rf_children_dangerous(subdir_fd, only_dirs, honour_sticky, root_dev);
- if (r < 0 && ret == 0)
- ret = r;
-
- if (!keep_around)
- if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- }
-
- } else if (!only_dirs && !keep_around) {
-
- if (unlinkat(fd, de->d_name, 0) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- }
- }
- }
-}
-
-_pure_ static int is_temporary_fs(struct statfs *s) {
+bool is_temporary_fs(const struct statfs *s) {
assert(s);
return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
}
-int is_fd_on_temporary_fs(int fd) {
+int fd_is_temporary_fs(int fd) {
struct statfs s;
if (fstatfs(fd, &s) < 0)
@@ -3053,114 +1905,6 @@ int is_fd_on_temporary_fs(int fd) {
return is_temporary_fs(&s);
}
-int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
- struct statfs s;
-
- assert(fd >= 0);
-
- if (fstatfs(fd, &s) < 0) {
- safe_close(fd);
- return -errno;
- }
-
- /* We refuse to clean disk file systems with this call. This
- * is extra paranoia just to be sure we never ever remove
- * non-state data */
- if (!is_temporary_fs(&s)) {
- log_error("Attempted to remove disk file system, and we can't allow that.");
- safe_close(fd);
- return -EPERM;
- }
-
- return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
-}
-
-static int file_is_priv_sticky(const char *p) {
- struct stat st;
-
- assert(p);
-
- if (lstat(p, &st) < 0)
- return -errno;
-
- return
- (st.st_uid == 0 || st.st_uid == getuid()) &&
- (st.st_mode & S_ISVTX);
-}
-
-static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
- int fd, r;
- struct statfs s;
-
- assert(path);
-
- /* We refuse to clean the root file system with this
- * call. This is extra paranoia to never cause a really
- * seriously broken system. */
- if (path_equal(path, "/")) {
- log_error("Attempted to remove entire root file system, and we can't allow that.");
- return -EPERM;
- }
-
- fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
- if (fd < 0) {
-
- if (errno != ENOTDIR && errno != ELOOP)
- return -errno;
-
- if (!dangerous) {
- if (statfs(path, &s) < 0)
- return -errno;
-
- if (!is_temporary_fs(&s)) {
- log_error("Attempted to remove disk file system, and we can't allow that.");
- return -EPERM;
- }
- }
-
- if (delete_root && !only_dirs)
- if (unlink(path) < 0 && errno != ENOENT)
- return -errno;
-
- return 0;
- }
-
- if (!dangerous) {
- if (fstatfs(fd, &s) < 0) {
- safe_close(fd);
- return -errno;
- }
-
- if (!is_temporary_fs(&s)) {
- log_error("Attempted to remove disk file system, and we can't allow that.");
- safe_close(fd);
- return -EPERM;
- }
- }
-
- r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL);
- if (delete_root) {
-
- if (honour_sticky && file_is_priv_sticky(path) > 0)
- return r;
-
- if (rmdir(path) < 0 && errno != ENOENT) {
- if (r == 0)
- r = -errno;
- }
- }
-
- return r;
-}
-
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
- return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false);
-}
-
-int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
- return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true);
-}
-
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
assert(path);
@@ -3225,311 +1969,6 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
}
}
-int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) {
- static const char status_indent[] = " "; /* "[" STATUS "] " */
- _cleanup_free_ char *s = NULL;
- _cleanup_close_ int fd = -1;
- struct iovec iovec[6] = {};
- int n = 0;
- static bool prev_ephemeral;
-
- assert(format);
-
- /* This is independent of logging, as status messages are
- * optional and go exclusively to the console. */
-
- if (vasprintf(&s, format, ap) < 0)
- return log_oom();
-
- fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- if (ellipse) {
- char *e;
- size_t emax, sl;
- int c;
-
- c = fd_columns(fd);
- if (c <= 0)
- c = 80;
-
- sl = status ? sizeof(status_indent)-1 : 0;
-
- emax = c - sl - 1;
- if (emax < 3)
- emax = 3;
-
- e = ellipsize(s, emax, 50);
- if (e) {
- free(s);
- s = e;
- }
- }
-
- if (prev_ephemeral)
- IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE);
- prev_ephemeral = ephemeral;
-
- if (status) {
- if (!isempty(status)) {
- IOVEC_SET_STRING(iovec[n++], "[");
- IOVEC_SET_STRING(iovec[n++], status);
- IOVEC_SET_STRING(iovec[n++], "] ");
- } else
- IOVEC_SET_STRING(iovec[n++], status_indent);
- }
-
- IOVEC_SET_STRING(iovec[n++], s);
- if (!ephemeral)
- IOVEC_SET_STRING(iovec[n++], "\n");
-
- if (writev(fd, iovec, n) < 0)
- return -errno;
-
- return 0;
-}
-
-int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) {
- va_list ap;
- int r;
-
- assert(format);
-
- va_start(ap, format);
- r = status_vprintf(status, ellipse, ephemeral, format, ap);
- va_end(ap);
-
- return r;
-}
-
-char *replace_env(const char *format, char **env) {
- enum {
- WORD,
- CURLY,
- VARIABLE
- } state = WORD;
-
- const char *e, *word = format;
- char *r = NULL, *k;
-
- assert(format);
-
- for (e = format; *e; e ++) {
-
- switch (state) {
-
- case WORD:
- if (*e == '$')
- state = CURLY;
- break;
-
- case CURLY:
- if (*e == '{') {
- k = strnappend(r, word, e-word-1);
- if (!k)
- goto fail;
-
- free(r);
- r = k;
-
- word = e-1;
- state = VARIABLE;
-
- } else if (*e == '$') {
- k = strnappend(r, word, e-word);
- if (!k)
- goto fail;
-
- free(r);
- r = k;
-
- word = e+1;
- state = WORD;
- } else
- state = WORD;
- break;
-
- case VARIABLE:
- if (*e == '}') {
- const char *t;
-
- t = strempty(strv_env_get_n(env, word+2, e-word-2));
-
- k = strappend(r, t);
- if (!k)
- goto fail;
-
- free(r);
- r = k;
-
- word = e+1;
- state = WORD;
- }
- break;
- }
- }
-
- k = strnappend(r, word, e-word);
- if (!k)
- goto fail;
-
- free(r);
- return k;
-
-fail:
- free(r);
- return NULL;
-}
-
-char **replace_env_argv(char **argv, char **env) {
- char **ret, **i;
- unsigned k = 0, l = 0;
-
- l = strv_length(argv);
-
- ret = new(char*, l+1);
- if (!ret)
- return NULL;
-
- STRV_FOREACH(i, argv) {
-
- /* If $FOO appears as single word, replace it by the split up variable */
- if ((*i)[0] == '$' && (*i)[1] != '{') {
- char *e;
- char **w, **m;
- unsigned q;
-
- e = strv_env_get(env, *i+1);
- if (e) {
- int r;
-
- r = strv_split_quoted(&m, e, true);
- if (r < 0) {
- ret[k] = NULL;
- strv_free(ret);
- return NULL;
- }
- } else
- m = NULL;
-
- q = strv_length(m);
- l = l + q - 1;
-
- w = realloc(ret, sizeof(char*) * (l+1));
- if (!w) {
- ret[k] = NULL;
- strv_free(ret);
- strv_free(m);
- return NULL;
- }
-
- ret = w;
- if (m) {
- memcpy(ret + k, m, q * sizeof(char*));
- free(m);
- }
-
- k += q;
- continue;
- }
-
- /* If ${FOO} appears as part of a word, replace it by the variable as-is */
- ret[k] = replace_env(*i, env);
- if (!ret[k]) {
- strv_free(ret);
- return NULL;
- }
- k++;
- }
-
- ret[k] = NULL;
- return ret;
-}
-
-int fd_columns(int fd) {
- struct winsize ws = {};
-
- if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
- return -errno;
-
- if (ws.ws_col <= 0)
- return -EIO;
-
- return ws.ws_col;
-}
-
-unsigned columns(void) {
- const char *e;
- int c;
-
- if (_likely_(cached_columns > 0))
- return cached_columns;
-
- c = 0;
- e = getenv("COLUMNS");
- if (e)
- (void) safe_atoi(e, &c);
-
- if (c <= 0)
- c = fd_columns(STDOUT_FILENO);
-
- if (c <= 0)
- c = 80;
-
- cached_columns = c;
- return cached_columns;
-}
-
-int fd_lines(int fd) {
- struct winsize ws = {};
-
- if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
- return -errno;
-
- if (ws.ws_row <= 0)
- return -EIO;
-
- return ws.ws_row;
-}
-
-unsigned lines(void) {
- const char *e;
- int l;
-
- if (_likely_(cached_lines > 0))
- return cached_lines;
-
- l = 0;
- e = getenv("LINES");
- if (e)
- (void) safe_atoi(e, &l);
-
- if (l <= 0)
- l = fd_lines(STDOUT_FILENO);
-
- if (l <= 0)
- l = 24;
-
- cached_lines = l;
- return cached_lines;
-}
-
-/* intended to be used as a SIGWINCH sighandler */
-void columns_lines_cache_reset(int signum) {
- cached_columns = 0;
- cached_lines = 0;
-}
-
-bool on_tty(void) {
- static int cached_on_tty = -1;
-
- if (_unlikely_(cached_on_tty < 0))
- cached_on_tty = isatty(STDOUT_FILENO) > 0;
-
- return cached_on_tty;
-}
-
int files_same(const char *filea, const char *fileb) {
struct stat a, b;
@@ -3705,14 +2144,15 @@ int touch(const char *path) {
return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
}
-char *unquote(const char *s, const char* quotes) {
+static char *unquote(const char *s, const char* quotes) {
size_t l;
assert(s);
/* This is rather stupid, simply removes the heading and
* trailing quotes if there is one. Doesn't care about
- * escaping or anything. We should make this smarter one
- * day... */
+ * escaping or anything.
+ *
+ * DON'T USE THIS FOR NEW CODE ANYMORE!*/
l = strlen(s);
if (l < 2)
@@ -3724,103 +2164,6 @@ char *unquote(const char *s, const char* quotes) {
return strdup(s);
}
-char *normalize_env_assignment(const char *s) {
- _cleanup_free_ char *value = NULL;
- const char *eq;
- char *p, *name;
-
- eq = strchr(s, '=');
- if (!eq) {
- char *r, *t;
-
- r = strdup(s);
- if (!r)
- return NULL;
-
- t = strstrip(r);
- if (t != r)
- memmove(r, t, strlen(t) + 1);
-
- return r;
- }
-
- name = strndupa(s, eq - s);
- p = strdupa(eq + 1);
-
- value = unquote(strstrip(p), QUOTES);
- if (!value)
- return NULL;
-
- return strjoin(strstrip(name), "=", value, NULL);
-}
-
-int wait_for_terminate(pid_t pid, siginfo_t *status) {
- siginfo_t dummy;
-
- assert(pid >= 1);
-
- if (!status)
- status = &dummy;
-
- for (;;) {
- zero(*status);
-
- if (waitid(P_PID, pid, status, WEXITED) < 0) {
-
- if (errno == EINTR)
- continue;
-
- return -errno;
- }
-
- return 0;
- }
-}
-
-/*
- * Return values:
- * < 0 : wait_for_terminate() failed to get the state of the
- * process, the process was terminated by a signal, or
- * failed for an unknown reason.
- * >=0 : The process terminated normally, and its exit code is
- * returned.
- *
- * That is, success is indicated by a return value of zero, and an
- * error is indicated by a non-zero value.
- *
- * A warning is emitted if the process terminates abnormally,
- * and also if it returns non-zero unless check_exit_code is true.
- */
-int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) {
- int r;
- siginfo_t status;
-
- assert(name);
- assert(pid > 1);
-
- r = wait_for_terminate(pid, &status);
- if (r < 0)
- return log_warning_errno(r, "Failed to wait for %s: %m", name);
-
- if (status.si_code == CLD_EXITED) {
- if (status.si_status != 0)
- log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG,
- "%s failed with error code %i.", name, status.si_status);
- else
- log_debug("%s succeeded.", name);
-
- return status.si_status;
- } else if (status.si_code == CLD_KILLED ||
- status.si_code == CLD_DUMPED) {
-
- log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
- return -EPROTO;
- }
-
- log_warning("%s failed due to unknown reason.", name);
- return -EPROTO;
-}
-
noreturn void freeze(void) {
/* Make sure nobody waits for us on a socket anymore */
@@ -3885,23 +2228,11 @@ DIR *xopendirat(int fd, const char *name, int flags) {
return d;
}
-int signal_from_string_try_harder(const char *s) {
- int signo;
- assert(s);
-
- signo = signal_from_string(s);
- if (signo <= 0)
- if (startswith(s, "SIG"))
- return signal_from_string(s+3);
-
- return signo;
-}
-
static char *tag_to_udev_node(const char *tagvalue, const char *by) {
_cleanup_free_ char *t = NULL, *u = NULL;
size_t enc_len;
- u = unquote(tagvalue, "\"\'");
+ u = unquote(tagvalue, QUOTES);
if (!u)
return NULL;
@@ -3934,101 +2265,6 @@ char *fstab_node_to_udev_node(const char *p) {
return strdup(p);
}
-bool tty_is_vc(const char *tty) {
- assert(tty);
-
- return vtnr_from_tty(tty) >= 0;
-}
-
-bool tty_is_console(const char *tty) {
- assert(tty);
-
- if (startswith(tty, "/dev/"))
- tty += 5;
-
- return streq(tty, "console");
-}
-
-int vtnr_from_tty(const char *tty) {
- int i, r;
-
- assert(tty);
-
- if (startswith(tty, "/dev/"))
- tty += 5;
-
- if (!startswith(tty, "tty") )
- return -EINVAL;
-
- if (tty[3] < '0' || tty[3] > '9')
- return -EINVAL;
-
- r = safe_atoi(tty+3, &i);
- if (r < 0)
- return r;
-
- if (i < 0 || i > 63)
- return -EINVAL;
-
- return i;
-}
-
-char *resolve_dev_console(char **active) {
- char *tty;
-
- /* Resolve where /dev/console is pointing to, if /sys is actually ours
- * (i.e. not read-only-mounted which is a sign for container setups) */
-
- if (path_is_read_only_fs("/sys") > 0)
- return NULL;
-
- if (read_one_line_file("/sys/class/tty/console/active", active) < 0)
- return NULL;
-
- /* If multiple log outputs are configured the last one is what
- * /dev/console points to */
- tty = strrchr(*active, ' ');
- if (tty)
- tty++;
- else
- tty = *active;
-
- if (streq(tty, "tty0")) {
- char *tmp;
-
- /* Get the active VC (e.g. tty1) */
- if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) {
- free(*active);
- tty = *active = tmp;
- }
- }
-
- return tty;
-}
-
-bool tty_is_vc_resolve(const char *tty) {
- _cleanup_free_ char *active = NULL;
-
- assert(tty);
-
- if (startswith(tty, "/dev/"))
- tty += 5;
-
- if (streq(tty, "console")) {
- tty = resolve_dev_console(&active);
- if (!tty)
- return false;
- }
-
- return tty_is_vc(tty);
-}
-
-const char *default_term_for_tty(const char *tty) {
- assert(tty);
-
- return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
-}
-
bool dirent_is_file(const struct dirent *de) {
assert(de);
@@ -4114,8 +2350,7 @@ static int do_execute(char **directories, usec_t timeout, char *argv[]) {
if (null_or_empty_path(path)) {
log_debug("%s is empty (a mask).", path);
continue;
- } else
- log_debug("%s will be executed.", path);
+ }
pid = fork();
if (pid < 0) {
@@ -4198,17 +2433,6 @@ void execute_directories(const char* const* directories, usec_t timeout, char *a
wait_for_terminate_and_warn(name, executor_pid, true);
}
-int kill_and_sigcont(pid_t pid, int sig) {
- int r;
-
- r = kill(pid, sig) < 0 ? -errno : 0;
-
- if (r >= 0)
- kill(pid, SIGCONT);
-
- return r;
-}
-
bool nulstr_contains(const char*nulstr, const char *needle) {
const char *i;
@@ -4235,79 +2459,6 @@ char* strshorten(char *s, size_t l) {
return s;
}
-static bool hostname_valid_char(char c) {
- return
- (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9') ||
- c == '-' ||
- c == '_' ||
- c == '.';
-}
-
-bool hostname_is_valid(const char *s) {
- const char *p;
- bool dot;
-
- if (isempty(s))
- return false;
-
- /* Doesn't accept empty hostnames, hostnames with trailing or
- * leading dots, and hostnames with multiple dots in a
- * sequence. Also ensures that the length stays below
- * HOST_NAME_MAX. */
-
- for (p = s, dot = true; *p; p++) {
- if (*p == '.') {
- if (dot)
- return false;
-
- dot = true;
- } else {
- if (!hostname_valid_char(*p))
- return false;
-
- dot = false;
- }
- }
-
- if (dot)
- return false;
-
- if (p-s > HOST_NAME_MAX)
- return false;
-
- return true;
-}
-
-char* hostname_cleanup(char *s, bool lowercase) {
- char *p, *d;
- bool dot;
-
- for (p = s, d = s, dot = true; *p; p++) {
- if (*p == '.') {
- if (dot)
- continue;
-
- *(d++) = '.';
- dot = true;
- } else if (hostname_valid_char(*p)) {
- *(d++) = lowercase ? tolower(*p) : *p;
- dot = false;
- }
-
- }
-
- if (dot && d > s)
- d[-1] = 0;
- else
- *d = 0;
-
- strshorten(s, HOST_NAME_MAX);
-
- return s;
-}
-
bool machine_name_is_valid(const char *s) {
if (!hostname_is_valid(s))
@@ -4393,111 +2544,45 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
return 0;
}
-int terminal_vhangup_fd(int fd) {
- assert(fd >= 0);
-
- if (ioctl(fd, TIOCVHANGUP) < 0)
- return -errno;
-
- return 0;
-}
-
-int terminal_vhangup(const char *name) {
- _cleanup_close_ int fd;
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- return terminal_vhangup_fd(fd);
-}
-
-int vt_disallocate(const char *name) {
- int fd, r;
- unsigned u;
-
- /* Deallocate the VT if possible. If not possible
- * (i.e. because it is the active one), at least clear it
- * entirely (including the scrollback buffer) */
-
- if (!startswith(name, "/dev/"))
- return -EINVAL;
-
- if (!tty_is_vc(name)) {
- /* So this is not a VT. I guess we cannot deallocate
- * it then. But let's at least clear the screen */
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- loop_write(fd,
- "\033[r" /* clear scrolling region */
- "\033[H" /* move home */
- "\033[2J", /* clear screen */
- 10, false);
- safe_close(fd);
-
- return 0;
- }
+int symlink_atomic(const char *from, const char *to) {
+ _cleanup_free_ char *t = NULL;
+ int r;
- if (!startswith(name, "/dev/tty"))
- return -EINVAL;
+ assert(from);
+ assert(to);
- r = safe_atou(name+8, &u);
+ r = tempfn_random(to, &t);
if (r < 0)
return r;
- if (u <= 0)
- return -EINVAL;
-
- /* Try to deallocate */
- fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- r = ioctl(fd, VT_DISALLOCATE, u);
- safe_close(fd);
-
- if (r >= 0)
- return 0;
-
- if (errno != EBUSY)
+ if (symlink(from, t) < 0)
return -errno;
- /* Couldn't deallocate, so let's clear it fully with
- * scrollback */
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- loop_write(fd,
- "\033[r" /* clear scrolling region */
- "\033[H" /* move home */
- "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
- 10, false);
- safe_close(fd);
+ if (rename(t, to) < 0) {
+ unlink_noerrno(t);
+ return -errno;
+ }
return 0;
}
-int symlink_atomic(const char *from, const char *to) {
- _cleanup_free_ char *t = NULL;
+int symlink_idempotent(const char *from, const char *to) {
+ _cleanup_free_ char *p = NULL;
int r;
assert(from);
assert(to);
- r = tempfn_random(to, &t);
- if (r < 0)
- return r;
+ if (symlink(from, to) < 0) {
+ if (errno != EEXIST)
+ return -errno;
- if (symlink(from, t) < 0)
- return -errno;
+ r = readlink_malloc(to, &p);
+ if (r < 0)
+ return r;
- if (rename(t, to) < 0) {
- unlink_noerrno(t);
- return -errno;
+ if (!streq(p, from))
+ return -EINVAL;
}
return 0;
@@ -5099,81 +3184,6 @@ static const char* const ip_tos_table[] = {
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
-static const char *const __signal_table[] = {
- [SIGHUP] = "HUP",
- [SIGINT] = "INT",
- [SIGQUIT] = "QUIT",
- [SIGILL] = "ILL",
- [SIGTRAP] = "TRAP",
- [SIGABRT] = "ABRT",
- [SIGBUS] = "BUS",
- [SIGFPE] = "FPE",
- [SIGKILL] = "KILL",
- [SIGUSR1] = "USR1",
- [SIGSEGV] = "SEGV",
- [SIGUSR2] = "USR2",
- [SIGPIPE] = "PIPE",
- [SIGALRM] = "ALRM",
- [SIGTERM] = "TERM",
-#ifdef SIGSTKFLT
- [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
-#endif
- [SIGCHLD] = "CHLD",
- [SIGCONT] = "CONT",
- [SIGSTOP] = "STOP",
- [SIGTSTP] = "TSTP",
- [SIGTTIN] = "TTIN",
- [SIGTTOU] = "TTOU",
- [SIGURG] = "URG",
- [SIGXCPU] = "XCPU",
- [SIGXFSZ] = "XFSZ",
- [SIGVTALRM] = "VTALRM",
- [SIGPROF] = "PROF",
- [SIGWINCH] = "WINCH",
- [SIGIO] = "IO",
- [SIGPWR] = "PWR",
- [SIGSYS] = "SYS"
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
-
-const char *signal_to_string(int signo) {
- static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
- const char *name;
-
- name = __signal_to_string(signo);
- if (name)
- return name;
-
- if (signo >= SIGRTMIN && signo <= SIGRTMAX)
- snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
- else
- snprintf(buf, sizeof(buf), "%d", signo);
-
- return buf;
-}
-
-int signal_from_string(const char *s) {
- int signo;
- int offset = 0;
- unsigned u;
-
- signo = __signal_from_string(s);
- if (signo > 0)
- return signo;
-
- if (startswith(s, "RTMIN+")) {
- s += 6;
- offset = SIGRTMIN;
- }
- if (safe_atou(s, &u) >= 0) {
- signo = (int) u + offset;
- if (signo > 0 && signo < _NSIG)
- return signo;
- }
- return -EINVAL;
-}
-
bool kexec_loaded(void) {
bool loaded = false;
char *s;
@@ -5417,60 +3427,6 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) {
return 0;
}
-int getenv_for_pid(pid_t pid, const char *field, char **_value) {
- _cleanup_fclose_ FILE *f = NULL;
- char *value = NULL;
- int r;
- bool done = false;
- size_t l;
- const char *path;
-
- assert(pid >= 0);
- assert(field);
- assert(_value);
-
- path = procfs_file_alloca(pid, "environ");
-
- f = fopen(path, "re");
- if (!f)
- return -errno;
-
- l = strlen(field);
- r = 0;
-
- 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 (memcmp(line, field, l) == 0 && line[l] == '=') {
- value = strdup(line + l + 1);
- if (!value)
- return -ENOMEM;
-
- r = 1;
- break;
- }
-
- } while (!done);
-
- *_value = value;
- return r;
-}
-
bool http_etag_is_valid(const char *etag) {
if (isempty(etag))
return false;
@@ -5547,43 +3503,6 @@ bool in_initrd(void) {
return saved;
}
-void warn_melody(void) {
- _cleanup_close_ int fd = -1;
-
- fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return;
-
- /* Yeah, this is synchronous. Kinda sucks. But well... */
-
- ioctl(fd, KIOCSOUND, (int)(1193180/440));
- usleep(125*USEC_PER_MSEC);
-
- ioctl(fd, KIOCSOUND, (int)(1193180/220));
- usleep(125*USEC_PER_MSEC);
-
- ioctl(fd, KIOCSOUND, (int)(1193180/220));
- usleep(125*USEC_PER_MSEC);
-
- ioctl(fd, KIOCSOUND, 0);
-}
-
-int make_console_stdio(void) {
- int fd, r;
-
- /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
-
- fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY);
- if (fd < 0)
- return log_error_errno(fd, "Failed to acquire terminal: %m");
-
- r = make_stdio(fd);
- if (r < 0)
- return log_error_errno(r, "Failed to duplicate terminal fd: %m");
-
- return 0;
-}
-
int get_home_dir(char **_h) {
struct passwd *p;
const char *e;
@@ -5746,7 +3665,7 @@ bool path_is_safe(const char *p) {
if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
return false;
- if (strlen(p) > PATH_MAX)
+ if (strlen(p)+1 > PATH_MAX)
return false;
/* The following two checks are not really dangerous, but hey, they still are confusing */
@@ -5782,6 +3701,11 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
return NULL;
}
+void init_gettext(void) {
+ setlocale(LC_ALL, "");
+ textdomain(GETTEXT_PACKAGE);
+}
+
bool is_locale_utf8(void) {
const char *set;
static int cached_answer = -1;
@@ -5993,7 +3917,7 @@ int on_ac_power(void) {
d = opendir("/sys/class/power_supply");
if (!d)
- return -errno;
+ return errno == ENOENT ? true : -errno;
for (;;) {
struct dirent *de;
@@ -6371,7 +4295,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
_cleanup_free_ char *word = NULL;
char *value = NULL;
- r = unquote_first_word(&p, &word, true);
+ r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
if (r < 0)
return r;
if (r == 0)
@@ -6411,7 +4335,7 @@ int get_proc_cmdline_key(const char *key, char **value) {
_cleanup_free_ char *word = NULL;
const char *e;
- r = unquote_first_word(&p, &word, true);
+ r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
if (r < 0)
return r;
if (r == 0)
@@ -6559,43 +4483,7 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
return -errno;
}
- if (setresgid(0, 0, 0) < 0)
- return -errno;
-
- if (setgroups(0, NULL) < 0)
- return -errno;
-
- if (setresuid(0, 0, 0) < 0)
- return -errno;
-
- return 0;
-}
-
-bool pid_is_unwaited(pid_t pid) {
- /* Checks whether a PID is still valid at all, including a zombie */
-
- if (pid <= 0)
- return false;
-
- if (kill(pid, 0) >= 0)
- return true;
-
- return errno != ESRCH;
-}
-
-bool pid_is_alive(pid_t pid) {
- int r;
-
- /* Checks whether a PID is still valid and not a zombie */
-
- if (pid <= 0)
- return false;
-
- r = get_process_state(pid);
- if (r == -ENOENT || r == 'Z')
- return false;
-
- return true;
+ return reset_uid_gid();
}
int getpeercred(int fd, struct ucred *ucred) {
@@ -6658,7 +4546,7 @@ int getpeersec(int fd, char **ret) {
if (isempty(s)) {
free(s);
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
*ret = s;
@@ -6689,7 +4577,7 @@ int open_tmpfile(const char *path, int flags) {
#ifdef O_TMPFILE
/* Try O_TMPFILE first, if it is supported */
- fd = open(path, flags|O_TMPFILE, S_IRUSR|S_IWUSR);
+ fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
if (fd >= 0)
return fd;
#endif
@@ -6746,10 +4634,7 @@ unsigned long personality_from_string(const char *p) {
return PER_LINUX;
#endif
- /* personality(7) documents that 0xffffffffUL is used for
- * querying the current personality, hence let's use that here
- * as error indicator. */
- return 0xffffffffUL;
+ return PERSONALITY_INVALID;
}
const char* personality_to_string(unsigned long p) {
@@ -6884,9 +4769,9 @@ int umount_recursive(const char *prefix, int flags) {
continue;
}
- p = cunescape(path);
- if (!p)
- return -ENOMEM;
+ r = cunescape(path, UNESCAPE_RELAX, &p);
+ if (r < 0)
+ return r;
if (!path_startswith(p, prefix))
continue;
@@ -6986,9 +4871,9 @@ int bind_remount_recursive(const char *prefix, bool ro) {
continue;
}
- p = cunescape(path);
- if (!p)
- return -ENOMEM;
+ r = cunescape(path, UNESCAPE_RELAX, &p);
+ if (r < 0)
+ return r;
/* Let's ignore autofs mounts. If they aren't
* triggered yet, we want to avoid triggering
@@ -7184,23 +5069,6 @@ int tempfn_random_child(const char *p, char **ret) {
return 0;
}
-/* make sure the hostname is not "localhost" */
-bool is_localhost(const char *hostname) {
- assert(hostname);
-
- /* This tries to identify local host and domain names
- * described in RFC6761 plus the redhatism of .localdomain */
-
- return streq(hostname, "localhost") ||
- streq(hostname, "localhost.") ||
- streq(hostname, "localdomain.") ||
- streq(hostname, "localdomain") ||
- endswith(hostname, ".localhost") ||
- endswith(hostname, ".localhost.") ||
- endswith(hostname, ".localdomain") ||
- endswith(hostname, ".localdomain.");
-}
-
int take_password_lock(const char *root) {
struct flock flock = {
@@ -7264,9 +5132,19 @@ int is_dir(const char* path, bool follow) {
return !!S_ISDIR(st.st_mode);
}
-int unquote_first_word(const char **p, char **ret, bool relax) {
+int is_device_node(const char *path) {
+ struct stat info;
+
+ if (lstat(path, &info) < 0)
+ return -errno;
+
+ return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
+}
+
+int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
_cleanup_free_ char *s = NULL;
size_t allocated = 0, sz = 0;
+ int r;
enum {
START,
@@ -7324,22 +5202,36 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
case VALUE_ESCAPE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
- if (!GREEDY_REALLOC(s, allocated, sz+2))
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
return -ENOMEM;
- s[sz++] = c;
- state = VALUE;
+ if (flags & UNQUOTE_CUNESCAPE) {
+ uint32_t u;
+
+ r = cunescape_one(*p, (size_t) -1, &c, &u);
+ if (r < 0)
+ return -EINVAL;
+ (*p) += r - 1;
+
+ if (c != 0)
+ s[sz++] = c; /* normal explicit char */
+ else
+ sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */
+ } else
+ s[sz++] = c;
+
+ state = VALUE;
break;
case SINGLE_QUOTE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
} else if (c == '\'')
@@ -7357,15 +5249,30 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
case SINGLE_QUOTE_ESCAPE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
- if (!GREEDY_REALLOC(s, allocated, sz+2))
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
return -ENOMEM;
- s[sz++] = c;
+ if (flags & UNQUOTE_CUNESCAPE) {
+ uint32_t u;
+
+ r = cunescape_one(*p, (size_t) -1, &c, &u);
+ if (r < 0)
+ return -EINVAL;
+
+ (*p) += r - 1;
+
+ if (c != 0)
+ s[sz++] = c;
+ else
+ sz += utf8_encode_unichar(s + sz, u);
+ } else
+ s[sz++] = c;
+
state = SINGLE_QUOTE;
break;
@@ -7387,15 +5294,30 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
case DOUBLE_QUOTE_ESCAPE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
- if (!GREEDY_REALLOC(s, allocated, sz+2))
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
return -ENOMEM;
- s[sz++] = c;
+ if (flags & UNQUOTE_CUNESCAPE) {
+ uint32_t u;
+
+ r = cunescape_one(*p, (size_t) -1, &c, &u);
+ if (r < 0)
+ return -EINVAL;
+
+ (*p) += r - 1;
+
+ if (c != 0)
+ s[sz++] = c;
+ else
+ sz += utf8_encode_unichar(s + sz, u);
+ } else
+ s[sz++] = c;
+
state = DOUBLE_QUOTE;
break;
@@ -7424,7 +5346,7 @@ finish:
return 1;
}
-int unquote_many_words(const char **p, ...) {
+int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
va_list ap;
char **l;
int n = 0, i, c, r;
@@ -7435,7 +5357,7 @@ int unquote_many_words(const char **p, ...) {
assert(p);
/* Count how many words are expected */
- va_start(ap, p);
+ va_start(ap, flags);
for (;;) {
if (!va_arg(ap, char **))
break;
@@ -7450,7 +5372,7 @@ int unquote_many_words(const char **p, ...) {
l = newa0(char*, n);
for (c = 0; c < n; c++) {
- r = unquote_first_word(p, &l[c], false);
+ r = unquote_first_word(p, &l[c], flags);
if (r < 0) {
int j;
@@ -7466,7 +5388,7 @@ int unquote_many_words(const char **p, ...) {
/* If we managed to parse all words, return them in the passed
* in parameters */
- va_start(ap, p);
+ va_start(ap, flags);
for (i = 0; i < n; i++) {
char **v;
@@ -7488,6 +5410,9 @@ int free_and_strdup(char **p, const char *s) {
/* Replaces a string pointer with an strdup()ed new string,
* possibly freeing the old one. */
+ if (streq_ptr(*p, s))
+ return 0;
+
if (s) {
t = strdup(s);
if (!t)
@@ -7498,26 +5423,6 @@ int free_and_strdup(char **p, const char *s) {
free(*p);
*p = t;
- return 0;
-}
-
-int sethostname_idempotent(const char *s) {
- int r;
- char buf[HOST_NAME_MAX + 1] = {};
-
- assert(s);
-
- r = gethostname(buf, sizeof(buf));
- if (r < 0)
- return -errno;
-
- if (streq(buf, s))
- return 0;
-
- r = sethostname(s, strlen(s));
- if (r < 0)
- return -errno;
-
return 1;
}
@@ -7779,7 +5684,7 @@ int same_fd(int a, int b) {
/* The fds refer to the same inode on disk, let's also check
* if they have the same fd flags. This is useful to
- * distuingish the read and write side of a pipe created with
+ * distinguish the read and write side of a pipe created with
* pipe(). */
fa = fcntl(a, F_GETFL);
if (fa < 0)
@@ -7792,32 +5697,41 @@ int same_fd(int a, int b) {
return fa == fb;
}
-int chattr_fd(int fd, bool b, unsigned mask) {
+int chattr_fd(int fd, unsigned value, unsigned mask) {
unsigned old_attr, new_attr;
+ struct stat st;
assert(fd >= 0);
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ /* Explicitly check whether this is a regular file or
+ * directory. If it is anything else (such as a device node or
+ * fifo), then the ioctl will not hit the file systems but
+ * possibly drivers, where the ioctl might have different
+ * effects. Notably, DRM is using the same ioctl() number. */
+
+ if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
+ return -ENOTTY;
+
if (mask == 0)
return 0;
if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0)
return -errno;
- if (b)
- new_attr = old_attr | mask;
- else
- new_attr = old_attr & ~mask;
-
+ new_attr = (old_attr & ~mask) | (value & mask);
if (new_attr == old_attr)
return 0;
if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0)
return -errno;
- return 0;
+ return 1;
}
-int chattr_path(const char *p, bool b, unsigned mask) {
+int chattr_path(const char *p, unsigned value, unsigned mask) {
_cleanup_close_ int fd = -1;
assert(p);
@@ -7829,12 +5743,20 @@ int chattr_path(const char *p, bool b, unsigned mask) {
if (fd < 0)
return -errno;
- return chattr_fd(fd, b, mask);
+ return chattr_fd(fd, value, mask);
}
int read_attr_fd(int fd, unsigned *ret) {
+ struct stat st;
+
assert(fd >= 0);
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
+ return -ENOTTY;
+
if (ioctl(fd, FS_IOC_GETFLAGS, ret) < 0)
return -errno;
@@ -7854,128 +5776,6 @@ int read_attr_path(const char *p, unsigned *ret) {
return read_attr_fd(fd, ret);
}
-int make_lock_file(const char *p, int operation, LockFile *ret) {
- _cleanup_close_ int fd = -1;
- _cleanup_free_ char *t = NULL;
- int r;
-
- /*
- * We use UNPOSIX locks if they are available. They have nice
- * semantics, and are mostly compatible with NFS. However,
- * they are only available on new kernels. When we detect we
- * are running on an older kernel, then we fall back to good
- * old BSD locks. They also have nice semantics, but are
- * slightly problematic on NFS, where they are upgraded to
- * POSIX locks, even though locally they are orthogonal to
- * POSIX locks.
- */
-
- t = strdup(p);
- if (!t)
- return -ENOMEM;
-
- for (;;) {
- struct flock fl = {
- .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK,
- .l_whence = SEEK_SET,
- };
- struct stat st;
-
- fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
- if (fd < 0)
- return -errno;
-
- r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl);
- if (r < 0) {
-
- /* If the kernel is too old, use good old BSD locks */
- if (errno == EINVAL)
- r = flock(fd, operation);
-
- if (r < 0)
- return errno == EAGAIN ? -EBUSY : -errno;
- }
-
- /* If we acquired the lock, let's check if the file
- * still exists in the file system. If not, then the
- * previous exclusive owner removed it and then closed
- * it. In such a case our acquired lock is worthless,
- * hence try again. */
-
- r = fstat(fd, &st);
- if (r < 0)
- return -errno;
- if (st.st_nlink > 0)
- break;
-
- fd = safe_close(fd);
- }
-
- ret->path = t;
- ret->fd = fd;
- ret->operation = operation;
-
- fd = -1;
- t = NULL;
-
- return r;
-}
-
-int make_lock_file_for(const char *p, int operation, LockFile *ret) {
- const char *fn;
- char *t;
-
- assert(p);
- assert(ret);
-
- fn = basename(p);
- if (!filename_is_valid(fn))
- return -EINVAL;
-
- t = newa(char, strlen(p) + 2 + 4 + 1);
- stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck");
-
- return make_lock_file(t, operation, ret);
-}
-
-void release_lock_file(LockFile *f) {
- int r;
-
- if (!f)
- return;
-
- if (f->path) {
-
- /* If we are the exclusive owner we can safely delete
- * the lock file itself. If we are not the exclusive
- * owner, we can try becoming it. */
-
- if (f->fd >= 0 &&
- (f->operation & ~LOCK_NB) == LOCK_SH) {
- static const struct flock fl = {
- .l_type = F_WRLCK,
- .l_whence = SEEK_SET,
- };
-
- r = fcntl(f->fd, F_OFD_SETLK, &fl);
- if (r < 0 && errno == EINVAL)
- r = flock(f->fd, LOCK_EX|LOCK_NB);
-
- if (r >= 0)
- f->operation = LOCK_EX|LOCK_NB;
- }
-
- if ((f->operation & ~LOCK_NB) == LOCK_EX)
- unlink_noerrno(f->path);
-
- free(f->path);
- f->path = NULL;
- }
-
- f->fd = safe_close(f->fd);
- f->operation = 0;
-}
-
static size_t nul_length(const uint8_t *p, size_t sz) {
size_t n = 0;
@@ -8102,3 +5902,147 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
return -1;
}
+
+void cmsg_close_all(struct msghdr *mh) {
+ struct cmsghdr *cmsg;
+
+ assert(mh);
+
+ for (cmsg = CMSG_FIRSTHDR(mh); cmsg; cmsg = CMSG_NXTHDR(mh, cmsg))
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
+ close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+}
+
+int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
+ struct stat buf;
+ int ret;
+
+ ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
+ if (ret >= 0)
+ return 0;
+
+ /* Even though renameat2() exists since Linux 3.15, btrfs added
+ * support for it later. If it is not implemented, fallback to another
+ * method. */
+ if (errno != EINVAL)
+ return -errno;
+
+ /* The link()/unlink() fallback does not work on directories. But
+ * renameat() without RENAME_NOREPLACE gives the same semantics on
+ * directories, except when newpath is an *empty* directory. This is
+ * good enough. */
+ ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW);
+ if (ret >= 0 && S_ISDIR(buf.st_mode)) {
+ ret = renameat(olddirfd, oldpath, newdirfd, newpath);
+ return ret >= 0 ? 0 : -errno;
+ }
+
+ /* If it is not a directory, use the link()/unlink() fallback. */
+ ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0);
+ if (ret < 0)
+ return -errno;
+
+ ret = unlinkat(olddirfd, oldpath, 0);
+ if (ret < 0) {
+ /* backup errno before the following unlinkat() alters it */
+ ret = errno;
+ (void) unlinkat(newdirfd, newpath, 0);
+ errno = ret;
+ return -errno;
+ }
+
+ return 0;
+}
+
+char *shell_maybe_quote(const char *s) {
+ const char *p;
+ char *r, *t;
+
+ assert(s);
+
+ /* Encloses a string in double quotes if necessary to make it
+ * OK as shell string. */
+
+ for (p = s; *p; p++)
+ if (*p <= ' ' ||
+ *p >= 127 ||
+ strchr(SHELL_NEED_QUOTES, *p))
+ break;
+
+ if (!*p)
+ return strdup(s);
+
+ r = new(char, 1+strlen(s)*2+1+1);
+ if (!r)
+ return NULL;
+
+ t = r;
+ *(t++) = '"';
+ t = mempcpy(t, s, p - s);
+
+ for (; *p; p++) {
+
+ if (strchr(SHELL_NEED_ESCAPE, *p))
+ *(t++) = '\\';
+
+ *(t++) = *p;
+ }
+
+ *(t++)= '"';
+ *t = 0;
+
+ return r;
+}
+
+int parse_mode(const char *s, mode_t *ret) {
+ char *x;
+ long l;
+
+ assert(s);
+ assert(ret);
+
+ errno = 0;
+ l = strtol(s, &x, 8);
+ if (errno != 0)
+ return -errno;
+
+ if (!x || x == s || *x)
+ return -EINVAL;
+ if (l < 0 || l > 07777)
+ return -ERANGE;
+
+ *ret = (mode_t) l;
+ return 0;
+}
+
+int mount_move_root(const char *path) {
+ assert(path);
+
+ if (chdir(path) < 0)
+ return -errno;
+
+ if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
+ return -errno;
+
+ if (chroot(".") < 0)
+ return -errno;
+
+ if (chdir("/") < 0)
+ return -errno;
+
+ return 0;
+}
+
+int reset_uid_gid(void) {
+
+ if (setgroups(0, NULL) < 0)
+ return -errno;
+
+ if (setresgid(0, 0, 0) < 0)
+ return -errno;
+
+ if (setresuid(0, 0, 0) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/shared/util.h b/src/shared/util.h
index a83b588221..467ae234a0 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -25,69 +25,27 @@
#include <fcntl.h>
#include <inttypes.h>
#include <time.h>
-#include <sys/time.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
-#include <signal.h>
#include <sched.h>
#include <limits.h>
#include <sys/types.h>
+#include <sys/socket.h>
#include <sys/stat.h>
#include <dirent.h>
-#include <sys/resource.h>
#include <stddef.h>
#include <unistd.h>
#include <locale.h>
#include <mntent.h>
-#include <sys/socket.h>
#include <sys/inotify.h>
-
-#if SIZEOF_PID_T == 4
-# define PID_PRI PRIi32
-#elif SIZEOF_PID_T == 2
-# define PID_PRI PRIi16
-#else
-# error Unknown pid_t size
-#endif
-#define PID_FMT "%" PID_PRI
-
-#if SIZEOF_UID_T == 4
-# define UID_FMT "%" PRIu32
-#elif SIZEOF_UID_T == 2
-# define UID_FMT "%" PRIu16
-#else
-# error Unknown uid_t size
-#endif
-
-#if SIZEOF_GID_T == 4
-# define GID_FMT "%" PRIu32
-#elif SIZEOF_GID_T == 2
-# define GID_FMT "%" PRIu16
-#else
-# error Unknown gid_t size
-#endif
-
-#if SIZEOF_TIME_T == 8
-# define PRI_TIME PRIi64
-#elif SIZEOF_TIME_T == 4
-# define PRI_TIME PRIu32
-#else
-# error Unknown time_t size
-#endif
-
-#if SIZEOF_RLIM_T == 8
-# define RLIM_FMT "%" PRIu64
-#elif SIZEOF_RLIM_T == 4
-# define RLIM_FMT "%" PRIu32
-#else
-# error Unknown rlim_t size
-#endif
+#include <sys/statfs.h>
#include "macro.h"
#include "missing.h"
#include "time-util.h"
+#include "formats-util.h"
/* What is interpreted as whitespace? */
#define WHITESPACE " \t\n\r"
@@ -104,16 +62,6 @@
#define FORMAT_BYTES_MAX 8
-#define ANSI_HIGHLIGHT_ON "\x1B[1;39m"
-#define ANSI_RED_ON "\x1B[31m"
-#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m"
-#define ANSI_GREEN_ON "\x1B[32m"
-#define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m"
-#define ANSI_HIGHLIGHT_YELLOW_ON "\x1B[1;33m"
-#define ANSI_HIGHLIGHT_BLUE_ON "\x1B[1;34m"
-#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
-#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
-
size_t page_size(void) _pure_;
#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
@@ -185,6 +133,7 @@ static inline char *startswith_no_case(const char *s, const char *prefix) {
}
char *endswith(const char *s, const char *postfix) _pure_;
+char *endswith_no_case(const char *s, const char *postfix) _pure_;
char *first_word(const char *s, const char *word) _pure_;
@@ -268,23 +217,15 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
#define _FOREACH_WORD(word, length, s, separator, quoted, state) \
for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted)))
-pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
-
char *strappend(const char *s, const char *suffix);
char *strnappend(const char *s, const char *suffix, size_t length);
-char *replace_env(const char *format, char **env);
-char **replace_env_argv(char **argv, char **env);
-
int readlinkat_malloc(int fd, const char *p, char **ret);
int readlink_malloc(const char *p, char **r);
int readlink_value(const char *p, char **ret);
int readlink_and_make_absolute(const char *p, char **r);
int readlink_and_canonicalize(const char *p, char **r);
-int reset_all_signal_handlers(void);
-int reset_signal_mask(void);
-
char *strstrip(char *s);
char *delete_chars(char *s, const char *bad);
char *truncate_nl(char *s);
@@ -293,17 +234,6 @@ char *file_in_same_dir(const char *path, const char *filename);
int rmdir_parents(const char *path, const char *stop);
-int get_process_state(pid_t pid);
-int get_process_comm(pid_t pid, char **name);
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
-int get_process_exe(pid_t pid, char **name);
-int get_process_uid(pid_t pid, uid_t *uid);
-int get_process_gid(pid_t pid, gid_t *gid);
-int get_process_capeff(pid_t pid, char **capeff);
-int get_process_cwd(pid_t pid, char **cwd);
-int get_process_root(pid_t pid, char **root);
-int get_process_environ(pid_t pid, char **environ);
-
char hexchar(int x) _const_;
int unhexchar(char c) _const_;
char octchar(int x) _const_;
@@ -312,9 +242,15 @@ char decchar(int x) _const_;
int undecchar(char c) _const_;
char *cescape(const char *s);
-char *cunescape(const char *s);
-char *cunescape_length(const char *s, size_t length);
-char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix);
+size_t cescape_char(char c, char *buf);
+
+typedef enum UnescapeFlags {
+ UNESCAPE_RELAX = 1,
+} UnescapeFlags;
+
+int cunescape(const char *s, UnescapeFlags flags, char **ret);
+int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret);
+int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
char *xescape(const char *s, const char *bad);
@@ -327,26 +263,6 @@ bool hidden_file(const char *filename) _pure_;
bool chars_intersect(const char *a, const char *b) _pure_;
-int make_stdio(int fd);
-int make_null_stdio(void);
-int make_console_stdio(void);
-
-int dev_urandom(void *p, size_t n);
-void random_bytes(void *p, size_t n);
-void initialize_srand(void);
-
-static inline uint64_t random_u64(void) {
- uint64_t u;
- random_bytes(&u, sizeof(u));
- return u;
-}
-
-static inline uint32_t random_u32(void) {
- uint32_t u;
- random_bytes(&u, sizeof(u));
- return u;
-}
-
/* For basic lookup tables with strictly enumerated entries */
#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
scope const char *name##_to_string(type i) { \
@@ -412,28 +328,12 @@ int close_all_fds(const int except[], unsigned n_except);
bool fstype_is_network(const char *fstype);
-int chvt(int vt);
-
-int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
-int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4);
-int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
-
-int reset_terminal_fd(int fd, bool switch_to_text);
-int reset_terminal(const char *name);
-
-int open_terminal(const char *name, int mode);
-int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm, usec_t timeout);
-int release_terminal(void);
-
int flush_fd(int fd);
-int ignore_signals(int sig, ...);
-int default_signals(int sig, ...);
-int sigaction_many(const struct sigaction *sa, ...);
-
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
+int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll);
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
bool is_device_path(const char *path);
@@ -441,75 +341,22 @@ bool is_device_path(const char *path);
int dir_is_empty(const char *path);
char* dirname_malloc(const char *path);
-void rename_process(const char name[8]);
-
-void sigset_add_many(sigset_t *ss, ...);
-int sigprocmask_many(int how, ...);
-
-bool hostname_is_set(void);
-
char* lookup_uid(uid_t uid);
-char* gethostname_malloc(void);
char* getlogname_malloc(void);
char* getusername_malloc(void);
-int getttyname_malloc(int fd, char **r);
-int getttyname_harder(int fd, char **r);
-
-int get_ctty_devnr(pid_t pid, dev_t *d);
-int get_ctty(pid_t, dev_t *_devnr, char **r);
-
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
-int is_fd_on_temporary_fs(int fd);
-
-int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
-int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
-int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
+bool is_temporary_fs(const struct statfs *s) _pure_;
+int fd_is_temporary_fs(int fd);
int pipe_eof(int fd);
cpu_set_t* cpu_set_malloc(unsigned *ncpus);
-int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0);
-int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_(4,5);
-
#define xsprintf(buf, fmt, ...) assert_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf))
-int fd_columns(int fd);
-unsigned columns(void);
-int fd_lines(int fd);
-unsigned lines(void);
-void columns_lines_cache_reset(int _unused_ signum);
-
-bool on_tty(void);
-
-static inline const char *ansi_highlight(void) {
- return on_tty() ? ANSI_HIGHLIGHT_ON : "";
-}
-
-static inline const char *ansi_highlight_red(void) {
- return on_tty() ? ANSI_HIGHLIGHT_RED_ON : "";
-}
-
-static inline const char *ansi_highlight_green(void) {
- return on_tty() ? ANSI_HIGHLIGHT_GREEN_ON : "";
-}
-
-static inline const char *ansi_highlight_yellow(void) {
- return on_tty() ? ANSI_HIGHLIGHT_YELLOW_ON : "";
-}
-
-static inline const char *ansi_highlight_blue(void) {
- return on_tty() ? ANSI_HIGHLIGHT_BLUE_ON : "";
-}
-
-static inline const char *ansi_highlight_off(void) {
- return on_tty() ? ANSI_HIGHLIGHT_OFF : "";
-}
-
int files_same(const char *filea, const char *fileb);
int running_in_chroot(void);
@@ -521,12 +368,6 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
int touch(const char *path);
-char *unquote(const char *s, const char *quotes);
-char *normalize_env_assignment(const char *s);
-
-int wait_for_terminate(pid_t pid, siginfo_t *status);
-int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
-
noreturn void freeze(void);
bool null_or_empty(struct stat *st) _pure_;
@@ -537,32 +378,17 @@ DIR *xopendirat(int dirfd, const char *name, int flags);
char *fstab_node_to_udev_node(const char *p);
-char *resolve_dev_console(char **active);
-bool tty_is_vc(const char *tty);
-bool tty_is_vc_resolve(const char *tty);
-bool tty_is_console(const char *tty) _pure_;
-int vtnr_from_tty(const char *tty);
-const char *default_term_for_tty(const char *tty);
-
void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
-int kill_and_sigcont(pid_t pid, int sig);
-
bool nulstr_contains(const char*nulstr, const char *needle);
bool plymouth_running(void);
-bool hostname_is_valid(const char *s) _pure_;
-char* hostname_cleanup(char *s, bool lowercase);
-
bool machine_name_is_valid(const char *s) _pure_;
char* strshorten(char *s, size_t l);
-int terminal_vhangup_fd(int fd);
-int terminal_vhangup(const char *name);
-
-int vt_disallocate(const char *name);
+int symlink_idempotent(const char *from, const char *to);
int symlink_atomic(const char *from, const char *to);
int mknod_atomic(const char *path, mode_t mode, dev_t dev);
@@ -628,11 +454,6 @@ int rlimit_from_string(const char *s) _pure_;
int ip_tos_to_string_alloc(int i, char **s);
int ip_tos_from_string(const char *s);
-const char *signal_to_string(int i) _const_;
-int signal_from_string(const char *s) _pure_;
-
-int signal_from_string_try_harder(const char *s);
-
extern int saved_argc;
extern char **saved_argv;
@@ -646,8 +467,6 @@ int fd_wait_for_event(int fd, int event, usec_t timeout);
void* memdup(const void *p, size_t l) _alloc_(2);
-int is_kernel_thread(pid_t pid);
-
int fd_inc_sndbuf(int fd, size_t n);
int fd_inc_rcvbuf(int fd, size_t n);
@@ -655,8 +474,6 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa
int setrlimit_closest(int resource, const struct rlimit *rlim);
-int getenv_for_pid(pid_t pid, const char *field, char **_value);
-
bool http_url_is_valid(const char *url) _pure_;
bool documentation_url_is_valid(const char *url) _pure_;
@@ -664,8 +481,6 @@ bool http_etag_is_valid(const char *etag);
bool in_initrd(void);
-void warn_melody(void);
-
int get_home_dir(char **ret);
int get_shell(char **_ret);
@@ -737,6 +552,8 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
int (*compar) (const void *, const void *, void *),
void *arg);
+#define _(String) gettext (String)
+void init_gettext(void);
bool is_locale_utf8(void);
typedef enum DrawSpecialChar {
@@ -931,19 +748,6 @@ int unlink_noerrno(const char *path);
_d_; \
})
-#define procfs_file_alloca(pid, field) \
- ({ \
- pid_t _pid_ = (pid); \
- const char *_r_; \
- if (_pid_ == 0) { \
- _r_ = ("/proc/self/" field); \
- } else { \
- _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
- sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \
- } \
- _r_; \
- })
-
bool id128_is_valid(const char *s) _pure_;
int split_pair(const char *s, const char *sep, char **l, char **r);
@@ -954,12 +758,27 @@ int shall_restore_state(void);
* Normal qsort requires base to be nonnull. Here were require
* that only if nmemb > 0.
*/
-static inline void qsort_safe(void *base, size_t nmemb, size_t size,
- int (*compar)(const void *, const void *)) {
- if (nmemb) {
- assert(base);
- qsort(base, nmemb, size, compar);
- }
+static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
+ if (nmemb <= 1)
+ return;
+
+ assert(base);
+ qsort(base, nmemb, size, compar);
+}
+
+/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */
+static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
+
+ if (needlelen <= 0)
+ return (void*) haystack;
+
+ if (haystacklen < needlelen)
+ return NULL;
+
+ assert(haystack);
+ assert(needle);
+
+ return memmem(haystack, haystacklen, needle, needlelen);
}
int proc_cmdline(char **ret);
@@ -971,9 +790,6 @@ int container_get_leader(const char *machine, pid_t *pid);
int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *root_fd);
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd);
-bool pid_is_alive(pid_t pid);
-bool pid_is_unwaited(pid_t pid);
-
int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
@@ -984,6 +800,13 @@ int open_tmpfile(const char *path, int flags);
int fd_warn_permissions(const char *path, int fd);
+#ifndef PERSONALITY_INVALID
+/* personality(7) documents that 0xffffffffUL is used for querying the
+ * current personality, hence let's use that here as error
+ * indicator. */
+#define PERSONALITY_INVALID 0xffffffffLU
+#endif
+
unsigned long personality_from_string(const char *p);
const char *personality_to_string(unsigned long);
@@ -1009,19 +832,21 @@ int tempfn_xxxxxx(const char *p, char **ret);
int tempfn_random(const char *p, char **ret);
int tempfn_random_child(const char *p, char **ret);
-bool is_localhost(const char *hostname);
-
int take_password_lock(const char *root);
int is_symlink(const char *path);
int is_dir(const char *path, bool follow);
+int is_device_node(const char *path);
-int unquote_first_word(const char **p, char **ret, bool relax);
-int unquote_many_words(const char **p, ...) _sentinel_;
+typedef enum UnquoteFlags {
+ UNQUOTE_RELAX = 1,
+ UNQUOTE_CUNESCAPE = 2,
+} UnquoteFlags;
-int free_and_strdup(char **p, const char *s);
+int unquote_first_word(const char **p, char **ret, UnquoteFlags flags);
+int unquote_many_words(const char **p, UnquoteFlags flags, ...) _sentinel_;
-int sethostname_idempotent(const char *s);
+int free_and_strdup(char **p, const char *s);
#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
@@ -1050,26 +875,12 @@ int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags);
int same_fd(int a, int b);
-int chattr_fd(int fd, bool b, unsigned mask);
-int chattr_path(const char *p, bool b, unsigned mask);
+int chattr_fd(int fd, unsigned value, unsigned mask);
+int chattr_path(const char *p, unsigned value, unsigned mask);
int read_attr_fd(int fd, unsigned *ret);
int read_attr_path(const char *p, unsigned *ret);
-typedef struct LockFile {
- char *path;
- int fd;
- int operation;
-} LockFile;
-
-int make_lock_file(const char *p, int operation, LockFile *ret);
-int make_lock_file_for(const char *p, int operation, LockFile *ret);
-void release_lock_file(LockFile *f);
-
-#define _cleanup_release_lock_file_ _cleanup_(release_lock_file)
-
-#define LOCK_FILE_INIT { .fd = -1, .path = NULL }
-
#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim })
ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
@@ -1078,3 +889,15 @@ void sigkill_wait(pid_t *pid);
#define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait)
int syslog_parse_priority(const char **p, int *priority, bool with_facility);
+
+void cmsg_close_all(struct msghdr *mh);
+
+int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
+
+char *shell_maybe_quote(const char *s);
+
+int parse_mode(const char *s, mode_t *ret);
+
+int mount_move_root(const char *path);
+
+int reset_uid_gid(void);
diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c
index bdb962af34..8f66df7718 100644
--- a/src/shared/utmp-wtmp.c
+++ b/src/shared/utmp-wtmp.c
@@ -21,7 +21,6 @@
#include <utmpx.h>
#include <errno.h>
-#include <assert.h>
#include <string.h>
#include <sys/utsname.h>
#include <fcntl.h>
@@ -30,6 +29,8 @@
#include "macro.h"
#include "path-util.h"
+#include "terminal-util.h"
+#include "hostname-util.h"
#include "utmp-wtmp.h"
int utmp_get_runlevel(int *runlevel, int *previous) {
@@ -347,8 +348,14 @@ static int write_to_terminal(const char *tty, const char *message) {
return 0;
}
-int utmp_wall(const char *message, const char *username, bool (*match_tty)(const char *tty)) {
- _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *tty = NULL;
+int utmp_wall(
+ const char *message,
+ const char *username,
+ const char *origin_tty,
+ bool (*match_tty)(const char *tty, void *userdata),
+ void *userdata) {
+
+ _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *stdin_tty = NULL;
char date[FORMAT_TIMESTAMP_MAX];
struct utmpx *u;
int r;
@@ -362,14 +369,17 @@ int utmp_wall(const char *message, const char *username, bool (*match_tty)(const
return -ENOMEM;
}
- getttyname_harder(STDIN_FILENO, &tty);
+ if (!origin_tty) {
+ getttyname_harder(STDIN_FILENO, &stdin_tty);
+ origin_tty = stdin_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 ?: username, hn,
- tty ? " on " : "", strempty(tty),
+ origin_tty ? " on " : "", strempty(origin_tty),
format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)),
message) < 0)
return -ENOMEM;
@@ -396,7 +406,7 @@ int utmp_wall(const char *message, const char *username, bool (*match_tty)(const
path = buf;
}
- if (!match_tty || match_tty(path)) {
+ if (!match_tty || match_tty(path, userdata)) {
q = write_to_terminal(path, text);
if (q < 0)
r = q;
diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h
index 87d004e615..5d26ba6fb1 100644
--- a/src/shared/utmp-wtmp.h
+++ b/src/shared/utmp-wtmp.h
@@ -33,7 +33,12 @@ 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, const char *username, bool (*match_tty)(const char *tty));
+int utmp_wall(
+ const char *message,
+ const char *username,
+ const char *origin_tty,
+ bool (*match_tty)(const char *tty, void *userdata),
+ void *userdata);
#else /* HAVE_UTMP */
@@ -55,8 +60,12 @@ static inline int utmp_put_dead_process(const char *id, pid_t pid, int code, int
static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
return 0;
}
-static inline int utmp_wall(const char *message, const char *username,
- bool (*match_tty)(const char *tty)) {
+static inline int utmp_wall(
+ const char *message,
+ const char *username,
+ const char *origin_tty,
+ bool (*match_tty)(const char *tty, void *userdata),
+ void *userdata) {
return 0;
}
diff --git a/src/shared/virt.c b/src/shared/virt.c
index 7c1381f4b8..1299a75ed5 100644
--- a/src/shared/virt.c
+++ b/src/shared/virt.c
@@ -24,6 +24,7 @@
#include <unistd.h>
#include "util.h"
+#include "process-util.h"
#include "virt.h"
#include "fileio.h"
@@ -102,15 +103,35 @@ static int detect_vm_cpuid(const char **_id) {
}
static int detect_vm_devicetree(const char **_id) {
-#if defined(__powerpc__) || defined(__powerpc64__)
+#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
_cleanup_free_ char *hvtype = NULL;
int r;
- r = read_one_line_file("/sys/firmware/devicetree/base/hypervisor/compatible", &hvtype);
+ r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
if (r >= 0) {
if (streq(hvtype, "linux,kvm")) {
*_id = "kvm";
return 1;
+ } else if (strstr(hvtype, "xen")) {
+ *_id = "xen";
+ return 1;
+ }
+ } else if (r == -ENOENT) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ struct dirent *dent;
+
+ dir = opendir("/proc/device-tree");
+ if (!dir) {
+ if (errno == ENOENT)
+ return 0;
+ return -errno;
+ }
+
+ FOREACH_DIRENT(dent, dir, return -errno) {
+ if (strstr(dent->d_name, "fw-cfg")) {
+ *_id = "qemu";
+ return 1;
+ }
}
}
#endif