diff options
Diffstat (limited to 'src/basic')
90 files changed, 2248 insertions, 666 deletions
diff --git a/src/basic/.gitignore b/src/basic/.gitignore deleted file mode 100644 index e22411e484..0000000000 --- a/src/basic/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -/cap-from-name.gperf -/cap-from-name.h -/cap-list.txt -/cap-to-name.h -/errno-from-name.gperf -/errno-from-name.h -/errno-list.txt -/errno-to-name.h -/af-from-name.gperf -/af-from-name.h -/af-list.txt -/af-to-name.h -/arphrd-from-name.gperf -/arphrd-from-name.h -/arphrd-list.txt -/arphrd-to-name.h diff --git a/src/basic/Makefile b/src/basic/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/basic/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/basic/alloc-util.c b/src/basic/alloc-util.c index b540dcddf5..948389f276 100644 --- a/src/basic/alloc-util.c +++ b/src/basic/alloc-util.c @@ -25,16 +25,31 @@ #include "util.h" void* memdup(const void *p, size_t l) { - void *r; + void *ret; - assert(p); + assert(l == 0 || p); + + ret = malloc(l); + if (!ret) + return NULL; + + memcpy(ret, p, l); + return ret; +} + +void* memdup_suffix0(const void*p, size_t l) { + void *ret; + + assert(l == 0 || p); + + /* The same as memdup() but place a safety NUL byte after the allocated memory */ - r = malloc(l); - if (!r) + ret = malloc(l + 1); + if (!ret) return NULL; - memcpy(r, p, l); - return r; + *((uint8_t*) mempcpy(ret, p, l)) = 0; + return ret; } void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) { diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h index a44dd473c1..0a89691bae 100644 --- a/src/basic/alloc-util.h +++ b/src/basic/alloc-util.h @@ -36,6 +36,8 @@ #define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) +#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n))) + #define malloc0(n) (calloc(1, (n))) static inline void *mfree(void *memory) { @@ -52,6 +54,7 @@ static inline void *mfree(void *memory) { }) void* memdup(const void *p, size_t l) _alloc_(2); +void* memdup_suffix0(const void*p, size_t l) _alloc_(2); static inline void freep(void *p) { free(*(void**) p); @@ -84,6 +87,13 @@ _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, si return memdup(p, size * need); } +_alloc_(2, 3) static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t need) { + if (size_multiply_overflow(size, need)) + return NULL; + + return memdup_suffix0(p, size * need); +} + void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size); void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); diff --git a/src/basic/audit-util.c b/src/basic/audit-util.c index d1c9695973..24a6c8a936 100644 --- a/src/basic/audit-util.c +++ b/src/basic/audit-util.c @@ -54,7 +54,7 @@ int audit_session_from_pid(pid_t pid, uint32_t *id) { if (r < 0) return r; - if (u == AUDIT_SESSION_INVALID || u <= 0) + if (!audit_session_is_valid(u)) return -ENODATA; *id = u; @@ -81,7 +81,7 @@ int audit_loginuid_from_pid(pid_t pid, uid_t *uid) { if (r < 0) return r; - *uid = (uid_t) u; + *uid = u; return 0; } diff --git a/src/basic/audit-util.h b/src/basic/audit-util.h index e048503991..3088951326 100644 --- a/src/basic/audit-util.h +++ b/src/basic/audit-util.h @@ -29,3 +29,7 @@ int audit_session_from_pid(pid_t pid, uint32_t *id); int audit_loginuid_from_pid(pid_t pid, uid_t *uid); bool use_audit(void); + +static inline bool audit_session_is_valid(uint32_t id) { + return id > 0 && id != AUDIT_SESSION_INVALID; +} diff --git a/src/basic/barrier.c b/src/basic/barrier.c index 2da633b311..0c44e47b12 100644 --- a/src/basic/barrier.c +++ b/src/basic/barrier.c @@ -171,7 +171,7 @@ void barrier_set_role(Barrier *b, unsigned int role) { int fd; assert(b); - assert(role == BARRIER_PARENT || role == BARRIER_CHILD); + assert(IN_SET(role, BARRIER_PARENT, BARRIER_CHILD)); /* make sure this is only called once */ assert(b->pipe[0] >= 0 && b->pipe[1] >= 0); diff --git a/src/basic/barrier.h b/src/basic/barrier.h index 6347fddc4d..fbc2d9d98d 100644 --- a/src/basic/barrier.h +++ b/src/basic/barrier.h @@ -70,11 +70,11 @@ bool barrier_sync_next(Barrier *b); bool barrier_sync(Barrier *b); static inline bool barrier_i_aborted(Barrier *b) { - return b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_WE_ABORTED; + return IN_SET(b->barriers, BARRIER_I_ABORTED, BARRIER_WE_ABORTED); } static inline bool barrier_they_aborted(Barrier *b) { - return b->barriers == BARRIER_THEY_ABORTED || b->barriers == BARRIER_WE_ABORTED; + return IN_SET(b->barriers, BARRIER_THEY_ABORTED, BARRIER_WE_ABORTED); } static inline bool barrier_we_aborted(Barrier *b) { @@ -82,7 +82,8 @@ static inline bool barrier_we_aborted(Barrier *b) { } static inline bool barrier_is_aborted(Barrier *b) { - return b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_THEY_ABORTED || b->barriers == BARRIER_WE_ABORTED; + return IN_SET(b->barriers, + BARRIER_I_ABORTED, BARRIER_THEY_ABORTED, BARRIER_WE_ABORTED); } static inline bool barrier_place_and_sync(Barrier *b) { diff --git a/src/basic/blkid-util.h b/src/basic/blkid-util.h index 1b9cace040..53340ec6f3 100644 --- a/src/basic/blkid-util.h +++ b/src/basic/blkid-util.h @@ -19,13 +19,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#ifdef HAVE_BLKID +#if HAVE_BLKID #include <blkid.h> #endif #include "util.h" -#ifdef HAVE_BLKID +#if HAVE_BLKID DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe); #define _cleanup_blkid_free_probe_ _cleanup_(blkid_free_probep) #endif diff --git a/src/basic/bpf-program.c b/src/basic/bpf-program.c new file mode 100644 index 0000000000..ce6f9e4409 --- /dev/null +++ b/src/basic/bpf-program.c @@ -0,0 +1,183 @@ +/*** + This file is part of systemd. + + Copyright 2016 Daniel Mack + + 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 <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "alloc-util.h" +#include "bpf-program.h" +#include "fd-util.h" +#include "log.h" +#include "missing.h" + +int bpf_program_new(uint32_t prog_type, BPFProgram **ret) { + _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL; + + p = new0(BPFProgram, 1); + if (!p) + return log_oom(); + + p->prog_type = prog_type; + p->kernel_fd = -1; + + *ret = p; + p = NULL; + return 0; +} + +BPFProgram *bpf_program_unref(BPFProgram *p) { + if (!p) + return NULL; + + safe_close(p->kernel_fd); + free(p->instructions); + + return mfree(p); +} + +int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *instructions, size_t count) { + + assert(p); + + if (!GREEDY_REALLOC(p->instructions, p->allocated, p->n_instructions + count)) + return -ENOMEM; + + memcpy(p->instructions + p->n_instructions, instructions, sizeof(struct bpf_insn) * count); + p->n_instructions += count; + + return 0; +} + +int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size) { + union bpf_attr attr; + + assert(p); + + if (p->kernel_fd >= 0) + return -EBUSY; + + attr = (union bpf_attr) { + .prog_type = p->prog_type, + .insns = PTR_TO_UINT64(p->instructions), + .insn_cnt = p->n_instructions, + .license = PTR_TO_UINT64("GPL"), + .log_buf = PTR_TO_UINT64(log_buf), + .log_level = !!log_buf, + .log_size = log_size, + }; + + p->kernel_fd = bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); + if (p->kernel_fd < 0) + return -errno; + + return 0; +} + +int bpf_program_cgroup_attach(BPFProgram *p, int type, const char *path, uint32_t flags) { + _cleanup_close_ int fd = -1; + union bpf_attr attr; + + assert(p); + assert(type >= 0); + assert(path); + + fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC); + if (fd < 0) + return -errno; + + attr = (union bpf_attr) { + .attach_type = type, + .target_fd = fd, + .attach_bpf_fd = p->kernel_fd, + .attach_flags = flags, + }; + + if (bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)) < 0) + return -errno; + + return 0; +} + +int bpf_program_cgroup_detach(int type, const char *path) { + _cleanup_close_ int fd = -1; + union bpf_attr attr; + + assert(path); + + fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC); + if (fd < 0) + return -errno; + + attr = (union bpf_attr) { + .attach_type = type, + .target_fd = fd, + }; + + if (bpf(BPF_PROG_DETACH, &attr, sizeof(attr)) < 0) + return -errno; + + return 0; +} + +int bpf_map_new(enum bpf_map_type type, size_t key_size, size_t value_size, size_t max_entries, uint32_t flags) { + union bpf_attr attr = { + .map_type = type, + .key_size = key_size, + .value_size = value_size, + .max_entries = max_entries, + .map_flags = flags, + }; + int fd; + + fd = bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); + if (fd < 0) + return -errno; + + return fd; +} + +int bpf_map_update_element(int fd, const void *key, void *value) { + + union bpf_attr attr = { + .map_fd = fd, + .key = PTR_TO_UINT64(key), + .value = PTR_TO_UINT64(value), + }; + + if (bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)) < 0) + return -errno; + + return 0; +} + +int bpf_map_lookup_element(int fd, const void *key, void *value) { + + union bpf_attr attr = { + .map_fd = fd, + .key = PTR_TO_UINT64(key), + .value = PTR_TO_UINT64(value), + }; + + if (bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)) < 0) + return -errno; + + return 0; +} diff --git a/src/basic/bpf-program.h b/src/basic/bpf-program.h new file mode 100644 index 0000000000..35a41ffc44 --- /dev/null +++ b/src/basic/bpf-program.h @@ -0,0 +1,55 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Daniel Mack + + 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/>. + + [Except for the stuff copy/pasted from the kernel sources, see below] +***/ + +#include <linux/bpf.h> +#include <stdint.h> +#include <sys/syscall.h> + +#include "list.h" +#include "macro.h" + +typedef struct BPFProgram BPFProgram; + +struct BPFProgram { + int kernel_fd; + uint32_t prog_type; + + size_t n_instructions; + size_t allocated; + struct bpf_insn *instructions; +}; + +int bpf_program_new(uint32_t prog_type, BPFProgram **ret); +BPFProgram *bpf_program_unref(BPFProgram *p); + +int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *insn, size_t count); +int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size); + +int bpf_program_cgroup_attach(BPFProgram *p, int type, const char *path, uint32_t flags); +int bpf_program_cgroup_detach(int type, const char *path); + +int bpf_map_new(enum bpf_map_type type, size_t key_size, size_t value_size, size_t max_entries, uint32_t flags); +int bpf_map_update_element(int fd, const void *key, void *value); +int bpf_map_lookup_element(int fd, const void *key, void *value); + +DEFINE_TRIVIAL_CLEANUP_FUNC(BPFProgram*, bpf_program_unref); diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index 5505499312..c2061addd1 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -32,7 +32,7 @@ #include <sys/sysmacros.h> #include <unistd.h> -#ifdef HAVE_LINUX_BTRFS_H +#if HAVE_LINUX_BTRFS_H #include <linux/btrfs.h> #endif diff --git a/src/basic/build.h b/src/basic/build.h index 3223915da6..9aaa6e3dae 100644 --- a/src/basic/build.h +++ b/src/basic/build.h @@ -19,121 +19,121 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#ifdef HAVE_PAM +#if HAVE_PAM #define _PAM_FEATURE_ "+PAM" #else #define _PAM_FEATURE_ "-PAM" #endif -#ifdef HAVE_AUDIT +#if HAVE_AUDIT #define _AUDIT_FEATURE_ "+AUDIT" #else #define _AUDIT_FEATURE_ "-AUDIT" #endif -#ifdef HAVE_SELINUX +#if HAVE_SELINUX #define _SELINUX_FEATURE_ "+SELINUX" #else #define _SELINUX_FEATURE_ "-SELINUX" #endif -#ifdef HAVE_APPARMOR +#if HAVE_APPARMOR #define _APPARMOR_FEATURE_ "+APPARMOR" #else #define _APPARMOR_FEATURE_ "-APPARMOR" #endif -#ifdef HAVE_IMA +#if ENABLE_IMA #define _IMA_FEATURE_ "+IMA" #else #define _IMA_FEATURE_ "-IMA" #endif -#ifdef HAVE_SMACK +#if ENABLE_SMACK #define _SMACK_FEATURE_ "+SMACK" #else #define _SMACK_FEATURE_ "-SMACK" #endif -#ifdef HAVE_SYSV_COMPAT +#if HAVE_SYSV_COMPAT #define _SYSVINIT_FEATURE_ "+SYSVINIT" #else #define _SYSVINIT_FEATURE_ "-SYSVINIT" #endif -#ifdef HAVE_UTMP +#if ENABLE_UTMP #define _UTMP_FEATURE_ "+UTMP" #else #define _UTMP_FEATURE_ "-UTMP" #endif -#ifdef HAVE_LIBCRYPTSETUP +#if HAVE_LIBCRYPTSETUP #define _LIBCRYPTSETUP_FEATURE_ "+LIBCRYPTSETUP" #else #define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP" #endif -#ifdef HAVE_GCRYPT +#if HAVE_GCRYPT #define _GCRYPT_FEATURE_ "+GCRYPT" #else #define _GCRYPT_FEATURE_ "-GCRYPT" #endif -#ifdef HAVE_GNUTLS +#if HAVE_GNUTLS #define _GNUTLS_FEATURE_ "+GNUTLS" #else #define _GNUTLS_FEATURE_ "-GNUTLS" #endif -#ifdef HAVE_ACL +#if HAVE_ACL #define _ACL_FEATURE_ "+ACL" #else #define _ACL_FEATURE_ "-ACL" #endif -#ifdef HAVE_XZ +#if HAVE_XZ #define _XZ_FEATURE_ "+XZ" #else #define _XZ_FEATURE_ "-XZ" #endif -#ifdef HAVE_LZ4 +#if HAVE_LZ4 #define _LZ4_FEATURE_ "+LZ4" #else #define _LZ4_FEATURE_ "-LZ4" #endif -#ifdef HAVE_SECCOMP +#if HAVE_SECCOMP #define _SECCOMP_FEATURE_ "+SECCOMP" #else #define _SECCOMP_FEATURE_ "-SECCOMP" #endif -#ifdef HAVE_BLKID +#if HAVE_BLKID #define _BLKID_FEATURE_ "+BLKID" #else #define _BLKID_FEATURE_ "-BLKID" #endif -#ifdef HAVE_ELFUTILS +#if HAVE_ELFUTILS #define _ELFUTILS_FEATURE_ "+ELFUTILS" #else #define _ELFUTILS_FEATURE_ "-ELFUTILS" #endif -#ifdef HAVE_KMOD +#if HAVE_KMOD #define _KMOD_FEATURE_ "+KMOD" #else #define _KMOD_FEATURE_ "-KMOD" #endif -#ifdef HAVE_LIBIDN2 +#if HAVE_LIBIDN2 #define _IDN2_FEATURE_ "+IDN2" #else #define _IDN2_FEATURE_ "-IDN2" #endif -#ifdef HAVE_LIBIDN +#if HAVE_LIBIDN #define _IDN_FEATURE_ "+IDN" #else #define _IDN_FEATURE_ "-IDN" diff --git a/src/basic/bus-label.h b/src/basic/bus-label.h index 62fb2c450c..600268b767 100644 --- a/src/basic/bus-label.h +++ b/src/basic/bus-label.h @@ -23,9 +23,11 @@ #include <stdlib.h> #include <string.h> +#include "string-util.h" + char *bus_label_escape(const char *s); 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); + return bus_label_unescape_n(f, strlen_ptr(f)); } diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index 204120ee0e..1fc9e9b154 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -25,6 +25,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/mman.h> #include <time.h> #include "alloc-util.h" @@ -33,6 +34,7 @@ #include "macro.h" #include "parse-util.h" #include "string-util.h" +#include "time-util.h" #define BITS_WEEKDAYS 127 #define MIN_YEAR 1970 @@ -59,6 +61,7 @@ void calendar_spec_free(CalendarSpec *c) { free_chain(c->hour); free_chain(c->minute); free_chain(c->microsecond); + free(c->timezone); free(c); } @@ -257,19 +260,19 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) { if (l < 0) { if (need_comma) - fputc(',', f); + fputc_unlocked(',', f); else need_comma = true; - fputs(days[x], f); + fputs_unlocked(days[x], f); l = x; } } else if (l >= 0) { if (x > l + 1) { - fputs(x > l + 2 ? ".." : ",", f); - fputs(days[x-1], f); + fputs_unlocked(x > l + 2 ? ".." : ",", f); + fputs_unlocked(days[x-1], f); } l = -1; @@ -277,8 +280,8 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) { } if (l >= 0 && x > l + 1) { - fputs(x > l + 2 ? ".." : ",", f); - fputs(days[x-1], f); + fputs_unlocked(x > l + 2 ? ".." : ",", f); + fputs_unlocked(days[x-1], f); } } @@ -288,12 +291,12 @@ static void format_chain(FILE *f, int space, const CalendarComponent *c, bool us assert(f); if (!c) { - fputc('*', f); + fputc_unlocked('*', f); return; } if (usec && c->start == 0 && c->repeat == USEC_PER_SEC && !c->next) { - fputc('*', f); + fputc_unlocked('*', f); return; } @@ -314,7 +317,7 @@ static void format_chain(FILE *f, int space, const CalendarComponent *c, bool us fprintf(f, ".%06i", c->repeat % d); if (c->next) { - fputc(',', f); + fputc_unlocked(',', f); format_chain(f, space, c->next, usec); } } @@ -334,32 +337,35 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) { if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) { format_weekdays(f, c); - fputc(' ', f); + fputc_unlocked(' ', f); } format_chain(f, 4, c->year, false); - fputc('-', f); + fputc_unlocked('-', f); format_chain(f, 2, c->month, false); - fputc(c->end_of_month ? '~' : '-', f); + fputc_unlocked(c->end_of_month ? '~' : '-', f); format_chain(f, 2, c->day, false); - fputc(' ', f); + fputc_unlocked(' ', f); format_chain(f, 2, c->hour, false); - fputc(':', f); + fputc_unlocked(':', f); format_chain(f, 2, c->minute, false); - fputc(':', f); + fputc_unlocked(':', f); format_chain(f, 2, c->microsecond, true); if (c->utc) - fputs(" UTC", f); - else if (IN_SET(c->dst, 0, 1)) { + fputs_unlocked(" UTC", f); + else if (c->timezone != NULL) { + fputc_unlocked(' ', f); + fputs_unlocked(c->timezone, f); + } else if (IN_SET(c->dst, 0, 1)) { /* If daylight saving is explicitly on or off, let's show the used timezone. */ tzset(); if (!isempty(tzname[c->dst])) { - fputc(' ', f); - fputs(tzname[c->dst], f); + fputc_unlocked(' ', f); + fputs_unlocked(tzname[c->dst], f); } } @@ -415,11 +421,7 @@ static int parse_weekdays(const char **p, CalendarSpec *c) { skip = strlen(day_nr[i].name); - if ((*p)[skip] != '-' && - (*p)[skip] != '.' && - (*p)[skip] != ',' && - (*p)[skip] != ' ' && - (*p)[skip] != 0) + if (!IN_SET((*p)[skip], 0, '-', '.', ',', ' ')) return -EINVAL; c->weekdays_bits |= 1 << day_nr[i].nr; @@ -478,7 +480,7 @@ static int parse_weekdays(const char **p, CalendarSpec *c) { } /* Allow a trailing comma but not an open range */ - if (**p == 0 || **p == ' ') { + if (IN_SET(**p, 0, ' ')) { *p += strspn(*p, " "); return l < 0 ? 0 : -EINVAL; } @@ -638,7 +640,7 @@ static int prepend_component(const char **p, bool usec, CalendarComponent **c) { return -ERANGE; } - if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != '~' && *e != ':') + if (!IN_SET(*e, 0, ' ', ',', '-', '~', ':')) return -EINVAL; cc = new0(CalendarComponent, 1); @@ -735,7 +737,7 @@ static int parse_date(const char **p, CalendarSpec *c) { return r; /* Already the end? A ':' as separator? In that case this was a time, not a date */ - if (*t == 0 || *t == ':') { + if (IN_SET(*t, 0, ':')) { free_chain(first); return 0; } @@ -755,7 +757,7 @@ static int parse_date(const char **p, CalendarSpec *c) { } /* Got two parts, hence it's month and day */ - if (*t == ' ' || *t == 0) { + if (IN_SET(*t, 0, ' ')) { *p = t + strspn(t, " "); c->month = first; c->day = second; @@ -783,7 +785,7 @@ static int parse_date(const char **p, CalendarSpec *c) { } /* Got three parts, hence it is year, month and day */ - if (*t == ' ' || *t == 0) { + if (IN_SET(*t, 0, ' ')) { *p = t + strspn(t, " "); c->year = first; c->month = second; @@ -888,6 +890,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { if (!c) return -ENOMEM; c->dst = -1; + c->timezone = NULL; utc = endswith_no_case(p, " UTC"); if (utc) { @@ -919,6 +922,19 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { if (IN_SET(j, 0, 1)) { p = strndupa(p, e - p - 1); c->dst = j; + } else { + const char *last_space; + + last_space = strrchr(p, ' '); + if (last_space != NULL && timezone_is_valid(last_space + 1)) { + c->timezone = strdup(last_space + 1); + if (!c->timezone) { + r = -ENOMEM; + goto fail; + } + + p = strndupa(p, last_space - p); + } } } @@ -1293,7 +1309,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { } } -int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) { +static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *next) { struct tm tm; time_t t; int r; @@ -1321,3 +1337,58 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) *next = (usec_t) t * USEC_PER_SEC + tm_usec; return 0; } + +typedef struct SpecNextResult { + usec_t next; + int return_value; +} SpecNextResult; + +int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) { + pid_t pid; + SpecNextResult *shared; + SpecNextResult tmp; + int r; + + if (isempty(spec->timezone)) + return calendar_spec_next_usec_impl(spec, usec, next); + + shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); + if (shared == MAP_FAILED) + return negative_errno(); + + pid = fork(); + + if (pid == -1) { + int fork_errno = errno; + (void) munmap(shared, sizeof *shared); + return -fork_errno; + } + + if (pid == 0) { + if (setenv("TZ", spec->timezone, 1) != 0) { + shared->return_value = negative_errno(); + _exit(EXIT_FAILURE); + } + + tzset(); + + shared->return_value = calendar_spec_next_usec_impl(spec, usec, &shared->next); + + _exit(EXIT_SUCCESS); + } + + r = wait_for_terminate(pid, NULL); + if (r < 0) { + (void) munmap(shared, sizeof *shared); + return r; + } + + tmp = *shared; + if (munmap(shared, sizeof *shared) != 0) + return negative_errno(); + + if (tmp.return_value == 0) + *next = tmp.next; + + return tmp.return_value; +} diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h index 3d8798de0b..8888251705 100644 --- a/src/basic/calendarspec.h +++ b/src/basic/calendarspec.h @@ -40,6 +40,7 @@ typedef struct CalendarSpec { bool end_of_month; bool utc; int dst; + char *timezone; CalendarComponent *year; CalendarComponent *month; diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c index d68cc78d05..2e9b2d9a55 100644 --- a/src/basic/cap-list.c +++ b/src/basic/cap-list.c @@ -20,7 +20,10 @@ #include <errno.h> #include <string.h> +#include "alloc-util.h" +#include "capability-util.h" #include "cap-list.h" +#include "extract-word.h" #include "macro.h" #include "missing.h" #include "parse-util.h" @@ -64,3 +67,67 @@ int capability_from_name(const char *name) { int capability_list_length(void) { return (int) ELEMENTSOF(capability_names); } + +int capability_set_to_string_alloc(uint64_t set, char **s) { + _cleanup_free_ char *str = NULL; + unsigned long i; + size_t allocated = 0, n = 0; + + assert(s); + + for (i = 0; i < cap_last_cap(); i++) + if (set & (UINT64_C(1) << i)) { + const char *p; + size_t add; + + p = capability_to_name(i); + if (!p) + return -EINVAL; + + add = strlen(p); + + if (!GREEDY_REALLOC(str, allocated, n + add + 2)) + return -ENOMEM; + + strcpy(mempcpy(str + n, p, add), " "); + n += add + 1; + } + + if (!GREEDY_REALLOC(str, allocated, n + 1)) + return -ENOMEM; + + str[n > 0 ? n - 1 : 0] = '\0'; /* truncate the last space, if it's there */ + + *s = str; + str = NULL; + + return 0; +} + +int capability_set_from_string(const char *s, uint64_t *set) { + uint64_t val = 0; + const char *p; + + assert(set); + + for (p = s;;) { + _cleanup_free_ char *word = NULL; + int r; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r == -ENOMEM) + return r; + if (r <= 0) + break; + + r = capability_from_name(word); + if (r < 0) + continue; + + val |= ((uint64_t) UINT64_C(1)) << (uint64_t) r; + } + + *set = val; + + return 0; +} diff --git a/src/basic/cap-list.h b/src/basic/cap-list.h index c1f6b94ad3..f9f6b70d80 100644 --- a/src/basic/cap-list.h +++ b/src/basic/cap-list.h @@ -22,3 +22,6 @@ const char *capability_to_name(int id); int capability_from_name(const char *name); int capability_list_length(void); + +int capability_set_to_string_alloc(uint64_t set, char **s); +int capability_set_from_string(const char *s, uint64_t *set); diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index c3de20a0e8..96c2e992bd 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -151,7 +151,7 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) { } int capability_bounding_set_drop(uint64_t keep, bool right_now) { - _cleanup_cap_free_ cap_t after_cap = NULL; + _cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL; cap_flag_value_t fv; unsigned long i; int r; @@ -161,71 +161,80 @@ int capability_bounding_set_drop(uint64_t keep, bool right_now) { * executing init!), so get it back temporarily so that we can * call PR_CAPBSET_DROP. */ - after_cap = cap_get_proc(); - if (!after_cap) + before_cap = cap_get_proc(); + if (!before_cap) return -errno; - if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) + if (cap_get_flag(before_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) return -errno; if (fv != CAP_SET) { _cleanup_cap_free_ cap_t temp_cap = NULL; static const cap_value_t v = CAP_SETPCAP; - temp_cap = cap_dup(after_cap); - if (!temp_cap) { - r = -errno; - goto finish; - } + temp_cap = cap_dup(before_cap); + if (!temp_cap) + return -errno; - if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) { - r = -errno; - goto finish; - } + if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) + return -errno; - if (cap_set_proc(temp_cap) < 0) { - r = -errno; - goto finish; - } + if (cap_set_proc(temp_cap) < 0) + log_debug_errno(errno, "Can't acquire effective CAP_SETPCAP bit, ignoring: %m"); + + /* If we didn't manage to acquire the CAP_SETPCAP bit, we continue anyway, after all this just means + * we'll fail later, when we actually intend to drop some capabilities. */ } + after_cap = cap_dup(before_cap); + if (!after_cap) + return -errno; + for (i = 0; i <= cap_last_cap(); i++) { + cap_value_t v; - if (!(keep & (UINT64_C(1) << i))) { - cap_value_t v; + if ((keep & (UINT64_C(1) << i))) + continue; - /* Drop it from the bounding set */ - if (prctl(PR_CAPBSET_DROP, i) < 0) { - r = -errno; + /* Drop it from the bounding set */ + if (prctl(PR_CAPBSET_DROP, i) < 0) { + r = -errno; + + /* If dropping the capability failed, let's see if we didn't have it in the first place. If so, + * continue anyway, as dropping a capability we didn't have in the first place doesn't really + * matter anyway. */ + if (prctl(PR_CAPBSET_READ, i) != 0) goto finish; - } - v = (cap_value_t) i; + } + v = (cap_value_t) i; + + /* Also drop it from the inheritable set, so + * that anything we exec() loses the + * capability for good. */ + if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) { + r = -errno; + goto finish; + } - /* Also drop it from the inheritable set, so - * that anything we exec() loses the - * capability for good. */ - if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) { + /* If we shall apply this right now drop it + * also from our own capability sets. */ + if (right_now) { + if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 || + cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) { r = -errno; goto finish; } - - /* If we shall apply this right now drop it - * also from our own capability sets. */ - if (right_now) { - if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 || - cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) { - r = -errno; - goto finish; - } - } } } r = 0; finish: - if (cap_set_proc(after_cap) < 0) - return -errno; + if (cap_set_proc(after_cap) < 0) { + /* If there are no actual changes anyway then let's ignore this error. */ + if (cap_compare(before_cap, after_cap) != 0) + r = -errno; + } return r; } @@ -361,3 +370,18 @@ int drop_capability(cap_value_t cv) { return 0; } + +bool ambient_capabilities_supported(void) { + static int cache = -1; + + if (cache >= 0) + return cache; + + /* If PR_CAP_AMBIENT returns something valid, or an unexpected error code we assume that ambient caps are + * available. */ + + cache = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_KILL, 0, 0) >= 0 || + !IN_SET(errno, EINVAL, EOPNOTSUPP, ENOSYS); + + return cache; +} diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h index 35a896e229..3dc9429153 100644 --- a/src/basic/capability-util.h +++ b/src/basic/capability-util.h @@ -55,3 +55,5 @@ static inline bool cap_test_all(uint64_t caps) { m = (UINT64_C(1) << (cap_last_cap() + 1)) - 1; return (caps & m) == m; } + +bool ambient_capabilities_supported(void); diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 6344372610..d51c3efd22 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -103,9 +103,12 @@ int cg_read_pid(FILE *f, pid_t *_pid) { return 1; } -int cg_read_event(const char *controller, const char *path, const char *event, - char **val) -{ +int cg_read_event( + const char *controller, + const char *path, + const char *event, + char **val) { + _cleanup_free_ char *events = NULL, *content = NULL; char *p, *line; int r; @@ -255,7 +258,7 @@ int cg_kill( return -ENOMEM; } - my_pid = getpid(); + my_pid = getpid_cached(); do { _cleanup_fclose_ FILE *f = NULL; @@ -371,7 +374,7 @@ int cg_kill_recursive( if (flags & CGROUP_REMOVE) { r = cg_rmdir(controller, path); - if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY) + if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY)) return r; } @@ -399,7 +402,7 @@ int cg_migrate( if (!s) return -ENOMEM; - my_pid = getpid(); + my_pid = getpid_cached(); do { _cleanup_fclose_ FILE *f = NULL; @@ -506,7 +509,7 @@ int cg_migrate_recursive( if (flags & CGROUP_REMOVE) { r = cg_rmdir(cfrom, pfrom); - if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY) + if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY)) return r; } @@ -825,7 +828,7 @@ int cg_attach(const char *controller, const char *path, pid_t pid) { return r; if (pid == 0) - pid = getpid(); + pid = getpid_cached(); xsprintf(c, PID_FMT "\n", pid); @@ -902,7 +905,7 @@ int cg_set_group_access( if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { r = cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, mode, uid, gid); if (r < 0) - log_warning_errno(r, "Failed to set group access on compat systemd cgroup %s: %m", path); + log_debug_errno(r, "Failed to set group access on compatibility systemd cgroup %s, ignoring: %m", path); } return 0; @@ -915,7 +918,7 @@ int cg_set_task_access( uid_t uid, gid_t gid) { - _cleanup_free_ char *fs = NULL, *procs = NULL; + _cleanup_free_ char *fs = NULL; int r; assert(path); @@ -926,6 +929,7 @@ int cg_set_task_access( if (mode != MODE_INVALID) mode &= 0666; + /* For both the legacy and unified hierarchies, "cgroup.procs" is the main entry point for PIDs */ r = cg_get_path(controller, path, "cgroup.procs", &fs); if (r < 0) return r; @@ -938,19 +942,48 @@ int cg_set_task_access( if (r < 0) return r; if (r == 0) { - /* Compatibility, Always keep values for "tasks" in sync with - * "cgroup.procs" */ - if (cg_get_path(controller, path, "tasks", &procs) >= 0) - (void) chmod_and_chown(procs, mode, uid, gid); + const char *fn; + + /* Compatibility: on cgroupsv1 always keep values for the legacy files "tasks" and + * "cgroup.clone_children" in sync with "cgroup.procs". Since this is legacy stuff, we don't care if + * this fails. */ + + FOREACH_STRING(fn, + "tasks", + "cgroup.clone_children") { + + fs = mfree(fs); + + r = cg_get_path(controller, path, fn, &fs); + if (r < 0) + log_debug_errno(r, "Failed to get path for %s of %s, ignoring: %m", fn, path); + + r = chmod_and_chown(fs, mode, uid, gid); + if (r < 0) + log_debug_errno(r, "Failed to to change ownership/access mode for %s of %s, ignoring: %m", fn, path); + } + } else { + /* On the unified controller, we want to permit subtree controllers too. */ + + fs = mfree(fs); + r = cg_get_path(controller, path, "cgroup.subtree_control", &fs); + if (r < 0) + return r; + + r = chmod_and_chown(fs, mode, uid, gid); + if (r < 0) + return r; } r = cg_hybrid_unified(); if (r < 0) return r; if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + /* Always propagate access mode from unified to legacy controller */ + r = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, mode, uid, gid); if (r < 0) - log_warning_errno(r, "Failed to set task access on compat systemd cgroup %s: %m", path); + log_debug_errno(r, "Failed to set task access on compatibility systemd cgroup %s, ignoring: %m", path); } return 0; @@ -1850,9 +1883,7 @@ char *cg_escape(const char *p) { /* The return value of this function (unlike cg_unescape()) * needs free()! */ - if (p[0] == 0 || - p[0] == '_' || - p[0] == '.' || + if (IN_SET(p[0], 0, '_', '.') || streq(p, "notify_on_release") || streq(p, "release_agent") || streq(p, "tasks") || @@ -1918,7 +1949,7 @@ bool cg_controller_is_valid(const char *p) { if (s) p = s; - if (*p == 0 || *p == '_') + if (IN_SET(*p, 0, '_')) return false; for (t = p; *t; t++) @@ -1970,7 +2001,7 @@ int cg_slice_to_path(const char *unit, char **ret) { char n[dash - p + sizeof(".slice")]; /* Don't allow trailing or double dashes */ - if (dash[1] == 0 || dash[1] == '-') + if (IN_SET(dash[1], 0, '-')) return -EINVAL; strcpy(stpncpy(n, p, dash - p), ".slice"); @@ -2326,7 +2357,6 @@ int cg_mask_supported(CGroupMask *ret) { int cg_kernel_controllers(Set *controllers) { _cleanup_fclose_ FILE *f = NULL; - char buf[LINE_MAX]; int r; assert(controllers); @@ -2344,7 +2374,7 @@ int cg_kernel_controllers(Set *controllers) { } /* Ignore the header line */ - (void) fgets(buf, sizeof(buf), f); + (void) read_line(f, (size_t) -1, NULL); for (;;) { char *controller; diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index b8f0f5d03d..907d1350fe 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -32,11 +32,12 @@ #include "macro.h" #include "missing.h" #include "path-util.h" +#include "stat-util.h" #include "string-util.h" #include "strv.h" #include "util.h" -static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) { +static int files_add(Hashmap *h, const char *suffix, const char *root, unsigned flags, const char *path) { _cleanup_closedir_ DIR *dir = NULL; const char *dirpath; struct dirent *de; @@ -56,8 +57,40 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char FOREACH_DIRENT(de, dir, return -errno) { char *p; - if (!dirent_is_file_with_suffix(de, suffix)) + if (!dirent_is_file_with_suffix(de, suffix)) { + log_debug("Ignoring %s/%s, because it's not a regular file with suffix %s.", dirpath, de->d_name, strna(suffix)); continue; + } + + if (flags & CONF_FILES_EXECUTABLE) { + struct stat st; + + /* As requested: check if the file is marked exectuable. Note that we don't check access(X_OK) + * here, as we care about whether the file is marked executable at all, and not whether it is + * executable for us, because if such errors are stuff we should log about. */ + + if (fstatat(dirfd(dir), de->d_name, &st, 0) < 0) { + log_debug_errno(errno, "Failed to stat %s/%s, ignoring: %m", dirpath, de->d_name); + continue; + } + + if (!null_or_empty(&st)) { + /* A mask is a symlink to /dev/null or an empty file. It does not even + * have to be executable. Other entries must be regular executable files + * or symlinks to them. */ + if (S_ISREG(st.st_mode)) { + if ((st.st_mode & 0111) == 0) { /* not executable */ + log_debug("Ignoring %s/%s, as it is not marked executable.", + dirpath, de->d_name); + continue; + } + } else { + log_debug("Ignoring %s/%s, as it is neither a regular file nor a mask.", + dirpath, de->d_name); + continue; + } + } + } p = strjoin(dirpath, "/", de->d_name); if (!p) @@ -87,7 +120,7 @@ static int base_cmp(const void *a, const void *b) { return strcmp(basename(s1), basename(s2)); } -static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) { +static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, unsigned flags, char **dirs) { _cleanup_hashmap_free_ Hashmap *fh = NULL; char **files, **p; int r; @@ -103,7 +136,7 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const return -ENOMEM; STRV_FOREACH(p, dirs) { - r = files_add(fh, root, *p, suffix); + r = files_add(fh, suffix, root, flags, *p); if (r == -ENOMEM) return r; if (r < 0) @@ -120,7 +153,7 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const return 0; } -int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs) { +int conf_files_list_strv(char ***strv, const char *suffix, const char *root, unsigned flags, const char* const* dirs) { _cleanup_strv_free_ char **copy = NULL; assert(strv); @@ -129,10 +162,10 @@ int conf_files_list_strv(char ***strv, const char *suffix, const char *root, con if (!copy) return -ENOMEM; - return conf_files_list_strv_internal(strv, suffix, root, copy); + return conf_files_list_strv_internal(strv, suffix, root, flags, copy); } -int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...) { +int conf_files_list(char ***strv, const char *suffix, const char *root, unsigned flags, const char *dir, ...) { _cleanup_strv_free_ char **dirs = NULL; va_list ap; @@ -145,10 +178,10 @@ int conf_files_list(char ***strv, const char *suffix, const char *root, const ch if (!dirs) return -ENOMEM; - return conf_files_list_strv_internal(strv, suffix, root, dirs); + return conf_files_list_strv_internal(strv, suffix, root, flags, dirs); } -int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *d) { +int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, unsigned flags, const char *d) { _cleanup_strv_free_ char **dirs = NULL; assert(strv); @@ -157,5 +190,5 @@ int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, c if (!dirs) return -ENOMEM; - return conf_files_list_strv_internal(strv, suffix, root, dirs); + return conf_files_list_strv_internal(strv, suffix, root, flags, dirs); } diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h index e00e0e81fb..20ecf6e5f1 100644 --- a/src/basic/conf-files.h +++ b/src/basic/conf-files.h @@ -20,6 +20,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -int conf_files_list(char ***ret, const char *suffix, const char *root, const char *dir, ...); -int conf_files_list_strv(char ***ret, const char *suffix, const char *root, const char* const* dirs); -int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, const char *dirs); +enum { + CONF_FILES_EXECUTABLE = 1, +}; + +int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir, ...); +int conf_files_list_strv(char ***ret, const char *suffix, const char *root, unsigned flags, const char* const* dirs); +int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dirs); diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c index 95ed6928ff..aec0edc3dc 100644 --- a/src/basic/cpu-set-util.c +++ b/src/basic/cpu-set-util.c @@ -112,3 +112,49 @@ int parse_cpu_set_and_warn( return (int) ncpus; } + +int parse_cpu_set( + const char *rvalue, + cpu_set_t **cpu_set) { + + _cleanup_cpu_free_ cpu_set_t *c = NULL; + unsigned ncpus = 0; + + assert(rvalue); + + for (;;) { + _cleanup_free_ char *word = NULL; + unsigned cpu, cpu_lower, cpu_upper; + int r; + + r = extract_first_word(&rvalue, &word, WHITESPACE ",", EXTRACT_QUOTES); + if (r == -ENOMEM) + return r; + if (r <= 0) + break; + + if (!c) { + c = cpu_set_malloc(&ncpus); + if (!c) + return -ENOMEM; + } + + r = parse_range(word, &cpu_lower, &cpu_upper); + if (r < 0) + return r; + if (cpu_lower >= ncpus || cpu_upper >= ncpus) + return -EINVAL; + + if (cpu_lower <= cpu_upper) + for (cpu = cpu_lower; cpu <= cpu_upper; cpu++) + CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c); + } + + /* On success, sets *cpu_set and returns ncpus for the system. */ + if (c) { + *cpu_set = c; + c = NULL; + } + + return (int) ncpus; +} diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h index 6f49d9afb0..9b08ba0a67 100644 --- a/src/basic/cpu-set-util.h +++ b/src/basic/cpu-set-util.h @@ -30,3 +30,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE); cpu_set_t* cpu_set_malloc(unsigned *ncpus); int parse_cpu_set_and_warn(const char *rvalue, cpu_set_t **cpu_set, const char *unit, const char *filename, unsigned line, const char *lvalue); +int parse_cpu_set(const char *rvalue, cpu_set_t **cpu_set); diff --git a/src/basic/def.h b/src/basic/def.h index b1a3bc190b..c04e58b57a 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -43,7 +43,7 @@ #define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT #define SIGNALS_IGNORE SIGPIPE -#ifdef HAVE_SPLIT_USR +#if HAVE_SPLIT_USR #define KBD_KEYMAP_DIRS \ "/usr/share/keymaps/\0" \ "/usr/share/kbd/keymaps/\0" \ @@ -57,10 +57,8 @@ #endif #define UNIX_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" -#define KERNEL_SYSTEM_BUS_ADDRESS "kernel:path=/sys/fs/kdbus/0-system/bus" -#define DEFAULT_SYSTEM_BUS_ADDRESS KERNEL_SYSTEM_BUS_ADDRESS ";" UNIX_SYSTEM_BUS_ADDRESS +#define DEFAULT_SYSTEM_BUS_ADDRESS UNIX_SYSTEM_BUS_ADDRESS #define UNIX_USER_BUS_ADDRESS_FMT "unix:path=%s/bus" -#define KERNEL_USER_BUS_ADDRESS_FMT "kernel:path=/sys/fs/kdbus/"UID_FMT"-user/bus" #define PLYMOUTH_SOCKET { \ .un.sun_family = AF_UNIX, \ @@ -70,7 +68,7 @@ #define NOTIFY_FD_MAX 768 #define NOTIFY_BUFFER_MAX PIPE_BUF -#ifdef HAVE_SPLIT_USR +#if HAVE_SPLIT_USR # define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0" #else # define _CONF_PATHS_SPLIT_USR(n) @@ -86,3 +84,5 @@ "/usr/local/lib/" n "\0" \ "/usr/lib/" n "\0" \ _CONF_PATHS_SPLIT_USR(n) + +#define LONG_LINE_MAX (1U*1024U*1024U) diff --git a/src/basic/env-util.c b/src/basic/env-util.c index 56e7b6fd8c..fa42edfa96 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -707,7 +707,7 @@ char **replace_env_argv(char **argv, char **env) { STRV_FOREACH(i, argv) { /* If $FOO appears as single word, replace it by the split up variable */ - if ((*i)[0] == '$' && (*i)[1] != '{' && (*i)[1] != '$') { + if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) { char *e; char **w, **m = NULL; unsigned q; @@ -769,6 +769,16 @@ int getenv_bool(const char *p) { return parse_boolean(e); } +int getenv_bool_secure(const char *p) { + const char *e; + + e = secure_getenv(p); + if (!e) + return -ENXIO; + + return parse_boolean(e); +} + int serialize_environment(FILE *f, char **environment) { char **e; diff --git a/src/basic/env-util.h b/src/basic/env-util.h index e88fa6aac0..d5da8cd67b 100644 --- a/src/basic/env-util.h +++ b/src/basic/env-util.h @@ -61,6 +61,7 @@ char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure char *strv_env_get(char **x, const char *n) _pure_; int getenv_bool(const char *p); +int getenv_bool_secure(const char *p); int serialize_environment(FILE *f, char **environment); int deserialize_environment(char ***environment, const char *line); diff --git a/src/basic/escape.c b/src/basic/escape.c index 85e4b5282e..0a6122c64a 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -314,7 +314,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi /* Undoes C style string escaping, and optionally prefixes it. */ - pl = prefix ? strlen(prefix) : 0; + pl = strlen_ptr(prefix); r = new(char, pl+length+1); if (!r) @@ -426,7 +426,7 @@ char *octescape(const char *s, size_t len) { for (f = s, t = r; f < s + len; f++) { - if (*f < ' ' || *f >= 127 || *f == '\\' || *f == '"') { + if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) { *(t++) = '\\'; *(t++) = '0' + (*f >> 6); *(t++) = '0' + ((*f >> 3) & 8); diff --git a/src/basic/exec-util.c b/src/basic/exec-util.c index aced9e8e3d..ade8511466 100644 --- a/src/basic/exec-util.c +++ b/src/basic/exec-util.c @@ -111,7 +111,7 @@ static int do_execute( assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - r = conf_files_list_strv(&paths, NULL, NULL, (const char* const*) directories); + r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE, (const char* const*) directories); if (r < 0) return r; diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c index 1e23c32c3f..7cfebbae72 100644 --- a/src/basic/exit-status.c +++ b/src/basic/exit-status.c @@ -140,9 +140,6 @@ const char* exit_status_to_string(int status, ExitStatusLevel level) { case EXIT_RUNTIME_DIRECTORY: return "RUNTIME_DIRECTORY"; - case EXIT_MAKE_STARTER: - return "MAKE_STARTER"; - case EXIT_CHOWN: return "CHOWN"; @@ -151,6 +148,18 @@ const char* exit_status_to_string(int status, ExitStatusLevel level) { case EXIT_KEYRING: return "KEYRING"; + + case EXIT_STATE_DIRECTORY: + return "STATE_DIRECTORY"; + + case EXIT_CACHE_DIRECTORY: + return "CACHE_DIRECTORY"; + + case EXIT_LOGS_DIRECTORY: + return "LOGS_DIRECTORY"; + + case EXIT_CONFIGURATION_DIRECTORY: + return "CONFIGURATION_DIRECTORY"; } } diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h index d22b2c00e4..195b3bc486 100644 --- a/src/basic/exit-status.h +++ b/src/basic/exit-status.h @@ -79,10 +79,14 @@ enum { EXIT_APPARMOR_PROFILE, EXIT_ADDRESS_FAMILIES, EXIT_RUNTIME_DIRECTORY, - EXIT_MAKE_STARTER, + _EXIT_RESERVED2, /* used to be used by kdbus, don't reuse */ EXIT_CHOWN, EXIT_SMACK_PROCESS_LABEL, EXIT_KEYRING, + EXIT_STATE_DIRECTORY, + EXIT_CACHE_DIRECTORY, + EXIT_LOGS_DIRECTORY, /* 240 */ + EXIT_CONFIGURATION_DIRECTORY, }; typedef enum ExitStatusLevel { diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c index 804f14c44c..f4ac526eb1 100644 --- a/src/basic/extract-word.c +++ b/src/basic/extract-word.c @@ -152,7 +152,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra for (;; (*p)++, c = **p) { if (c == 0) goto finish_force_terminate; - else if ((c == '\'' || c == '"') && (flags & EXTRACT_QUOTES)) { + else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_QUOTES)) { quote = c; break; } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 19ad20789b..9d61044c89 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -31,6 +31,7 @@ #include "missing.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "socket-util.h" #include "stdio-util.h" #include "util.h" @@ -282,7 +283,7 @@ int same_fd(int a, int b) { return true; /* Try to use kcmp() if we have it. */ - pid = getpid(); + pid = getpid_cached(); r = kcmp(pid, pid, KCMP_FILE, a, b); if (r == 0) return true; diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 9a185e3e60..9ee7a4af38 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -30,6 +30,7 @@ #include "alloc-util.h" #include "ctype.h" +#include "def.h" #include "env-util.h" #include "escape.h" #include "fd-util.h" @@ -41,6 +42,7 @@ #include "missing.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "random-util.h" #include "stdio-util.h" #include "string-util.h" @@ -51,13 +53,17 @@ #define READ_FULL_BYTES_MAX (4U*1024U*1024U) -int write_string_stream_ts(FILE *f, const char *line, bool enforce_newline, struct timespec *ts) { +int write_string_stream_ts( + FILE *f, + const char *line, + WriteStringFileFlags flags, + struct timespec *ts) { assert(f); assert(line); fputs(line, f); - if (enforce_newline && !endswith(line, "\n")) + if (!(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n")) fputc('\n', f); if (ts) { @@ -67,10 +73,18 @@ int write_string_stream_ts(FILE *f, const char *line, bool enforce_newline, stru return -errno; } - return fflush_and_check(f); + if (flags & WRITE_STRING_FILE_SYNC) + return fflush_sync_and_check(f); + else + return fflush_and_check(f); } -static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) { +static int write_string_file_atomic( + const char *fn, + const char *line, + WriteStringFileFlags flags, + struct timespec *ts) { + _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; int r; @@ -84,29 +98,41 @@ static int write_string_file_atomic(const char *fn, const char *line, bool enfor (void) fchmod_umask(fileno(f), 0644); - r = write_string_stream(f, line, enforce_newline); - if (r >= 0) { - if (rename(p, fn) < 0) - r = -errno; + r = write_string_stream_ts(f, line, flags, ts); + if (r < 0) + goto fail; + + if (rename(p, fn) < 0) { + r = -errno; + goto fail; } - if (r < 0) - (void) unlink(p); + return 0; +fail: + (void) unlink(p); return r; } -int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts) { +int write_string_file_ts( + const char *fn, + const char *line, + WriteStringFileFlags flags, + struct timespec *ts) { + _cleanup_fclose_ FILE *f = NULL; int q, r; assert(fn); assert(line); + /* We don't know how to verify whether the file contents was already on-disk. */ + assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC))); + if (flags & WRITE_STRING_FILE_ATOMIC) { assert(flags & WRITE_STRING_FILE_CREATE); - r = write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + r = write_string_file_atomic(fn, line, flags, ts); if (r < 0) goto fail; @@ -139,7 +165,7 @@ int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags } } - r = write_string_stream_ts(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE), ts); + r = write_string_stream_ts(f, line, flags, ts); if (r < 0) goto fail; @@ -163,7 +189,7 @@ fail: int read_one_line_file(const char *fn, char **line) { _cleanup_fclose_ FILE *f = NULL; - char t[LINE_MAX], *c; + int r; assert(fn); assert(line); @@ -172,21 +198,8 @@ int read_one_line_file(const char *fn, char **line) { if (!f) return -errno; - if (!fgets(t, sizeof(t), f)) { - - if (ferror(f)) - return errno > 0 ? -errno : -EIO; - - t[0] = 0; - } - - c = strdup(t); - if (!c) - return -ENOMEM; - truncate_nl(c); - - *line = c; - return 0; + r = read_line(f, LONG_LINE_MAX, line); + return r < 0 ? r : 0; } int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { @@ -245,11 +258,11 @@ int read_full_stream(FILE *f, char **contents, size_t *size) { if (st.st_size > READ_FULL_BYTES_MAX) return -E2BIG; - /* Start with the right file size, but be prepared for - * files from /proc which generally report a file size - * of 0 */ + /* Start with the right file size, but be prepared for files from /proc which generally report a file + * size of 0. Note that we increase the size to read here by one, so that the first read attempt + * already makes us notice the EOF. */ if (st.st_size > 0) - n = st.st_size; + n = st.st_size + 1; } l = 0; @@ -262,12 +275,13 @@ int read_full_stream(FILE *f, char **contents, size_t *size) { return -ENOMEM; buf = t; + errno = 0; k = fread(buf + l, 1, n - l, f); if (k > 0) l += k; if (ferror(f)) - return -errno; + return errno > 0 ? -errno : -EIO; if (feof(f)) break; @@ -823,29 +837,29 @@ static void write_env_var(FILE *f, const char *v) { p = strchr(v, '='); if (!p) { /* Fallback */ - fputs(v, f); - fputc('\n', f); + fputs_unlocked(v, f); + fputc_unlocked('\n', f); return; } p++; - fwrite(v, 1, p-v, f); + fwrite_unlocked(v, 1, p-v, f); if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) { - fputc('\"', f); + fputc_unlocked('\"', f); for (; *p; p++) { if (strchr(SHELL_NEED_ESCAPE, *p)) - fputc('\\', f); + fputc_unlocked('\\', f); - fputc(*p, f); + fputc_unlocked(*p, f); } - fputc('\"', f); + fputc_unlocked('\"', f); } else - fputs(p, f); + fputs_unlocked(p, f); - fputc('\n', f); + fputc_unlocked('\n', f); } int write_env_file(const char *fname, char **l) { @@ -1125,6 +1139,21 @@ int fflush_and_check(FILE *f) { return 0; } +int fflush_sync_and_check(FILE *f) { + int r; + + assert(f); + + r = fflush_and_check(f); + if (r < 0) + return r; + + if (fsync(fileno(f)) < 0) + return -errno; + + return 0; +} + /* This is much like mkostemp() but is subject to umask(). */ int mkostemp_safe(char *pattern) { _cleanup_umask_ mode_t u = 0; @@ -1399,7 +1428,7 @@ int open_serialization_fd(const char *ident) { if (fd < 0) { const char *path; - path = getpid() == 1 ? "/run/systemd" : "/tmp"; + path = getpid_cached() == 1 ? "/run/systemd" : "/tmp"; fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); if (fd < 0) return fd; @@ -1497,3 +1526,77 @@ int mkdtemp_malloc(const char *template, char **ret) { *ret = p; return 0; } + +static inline void funlockfilep(FILE **f) { + funlockfile(*f); +} + +int read_line(FILE *f, size_t limit, char **ret) { + _cleanup_free_ char *buffer = NULL; + size_t n = 0, allocated = 0, count = 0; + + assert(f); + + /* Something like a bounded version of getline(). + * + * Considers EOF, \n and \0 end of line delimiters, and does not include these delimiters in the string + * returned. + * + * Returns the number of bytes read from the files (i.e. including delimiters — this hence usually differs from + * the number of characters in the returned string). When EOF is hit, 0 is returned. + * + * The input parameter limit is the maximum numbers of characters in the returned string, i.e. excluding + * delimiters. If the limit is hit we fail and return -ENOBUFS. + * + * If a line shall be skipped ret may be initialized as NULL. */ + + if (ret) { + if (!GREEDY_REALLOC(buffer, allocated, 1)) + return -ENOMEM; + } + + { + _cleanup_(funlockfilep) FILE *flocked = f; + flockfile(f); + + for (;;) { + int c; + + if (n >= limit) + return -ENOBUFS; + + errno = 0; + c = fgetc_unlocked(f); + if (c == EOF) { + /* if we read an error, and have no data to return, then propagate the error */ + if (ferror_unlocked(f) && n == 0) + return errno > 0 ? -errno : -EIO; + + break; + } + + count++; + + if (IN_SET(c, '\n', 0)) /* Reached a delimiter */ + break; + + if (ret) { + if (!GREEDY_REALLOC(buffer, allocated, n + 2)) + return -ENOMEM; + + buffer[n] = (char) c; + } + + n++; + } + } + + if (ret) { + buffer[n] = 0; + + *ret = buffer; + buffer = NULL; + } + + return (int) count; +} diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 6098562265..eba05be2a9 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -29,15 +29,16 @@ #include "time-util.h" typedef enum { - WRITE_STRING_FILE_CREATE = 1, - WRITE_STRING_FILE_ATOMIC = 2, - WRITE_STRING_FILE_AVOID_NEWLINE = 4, - WRITE_STRING_FILE_VERIFY_ON_FAILURE = 8, + WRITE_STRING_FILE_CREATE = 1<<0, + WRITE_STRING_FILE_ATOMIC = 1<<1, + WRITE_STRING_FILE_AVOID_NEWLINE = 1<<2, + WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1<<3, + WRITE_STRING_FILE_SYNC = 1<<4, } WriteStringFileFlags; -int write_string_stream_ts(FILE *f, const char *line, bool enforce_newline, struct timespec *ts); -static inline int write_string_stream(FILE *f, const char *line, bool enforce_newline) { - return write_string_stream_ts(f, line, enforce_newline, NULL); +int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts); +static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) { + return write_string_stream_ts(f, line, flags, NULL); } int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts); static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { @@ -77,6 +78,7 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root } else int fflush_and_check(FILE *f); +int fflush_sync_and_check(FILE *f); int fopen_temporary(const char *path, FILE **_f, char **_temp_path); int mkostemp_safe(char *pattern); @@ -99,3 +101,5 @@ int link_tmpfile(int fd, const char *path, const char *target); int read_nul_string(FILE *f, char **ret); int mkdtemp_malloc(const char *template, char **ret); + +int read_line(FILE *f, size_t limit, char **ret); diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 8fe19ee4e4..b90f343ed3 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -23,6 +23,7 @@ #include <stdlib.h> #include <string.h> #include <sys/stat.h> +#include <linux/magic.h> #include <time.h> #include <unistd.h> @@ -307,7 +308,7 @@ int fd_warn_permissions(const char *path, int fd) { if (st.st_mode & 0002) log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path); - if (getpid() == 1 && (st.st_mode & 0044) != 0044) + if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044) log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path); return 0; @@ -323,7 +324,7 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi mkdir_parents(path, 0755); fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, - (mode == 0 || mode == MODE_INVALID) ? 0644 : mode); + IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode); if (fd < 0) return -errno; @@ -358,22 +359,25 @@ int touch(const char *path) { } int symlink_idempotent(const char *from, const char *to) { - _cleanup_free_ char *p = NULL; int r; assert(from); assert(to); if (symlink(from, to) < 0) { + _cleanup_free_ char *p = NULL; + if (errno != EEXIST) return -errno; r = readlink_malloc(to, &p); - if (r < 0) + if (r == -EINVAL) /* Not a symlink? In that case return the original error we encountered: -EEXIST */ + return -EEXIST; + if (r < 0) /* Any other error? In that case propagate it as is */ return r; - if (!streq(p, from)) - return -EINVAL; + if (!streq(p, from)) /* Not the symlink we want it to be? In that case, propagate the original -EEXIST */ + return -EEXIST; } return 0; @@ -721,6 +725,9 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(child, &st) < 0) return -errno; + if ((flags & CHASE_NO_AUTOFS) && + fd_check_fstype(child, AUTOFS_SUPER_MAGIC) > 0) + return -EREMOTE; if (S_ISLNK(st.st_mode)) { char *joined; diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 094acf1799..d3342d5cda 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -81,6 +81,7 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask); enum { CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */ CHASE_NONEXISTENT = 2, /* If set, it's OK if the path doesn't actually exist. */ + CHASE_NO_AUTOFS = 4, /* If set, return -EREMOTE if autofs mount point found */ }; int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret); diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 50fefb0b54..4bfaa864b4 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -34,7 +34,7 @@ #include "strv.h" #include "util.h" -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP #include <pthread.h> #include "list.h" #endif @@ -142,7 +142,7 @@ typedef uint8_t dib_raw_t; #define DIB_FREE UINT_MAX -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP struct hashmap_debug_info { LIST_FIELDS(struct hashmap_debug_info, debug_list); unsigned max_entries; /* high watermark of n_entries */ @@ -499,7 +499,7 @@ static void base_remove_entry(HashmapBase *h, unsigned idx) { dibs = dib_raw_ptr(h); assert(dibs[idx] != DIB_RAW_FREE); -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP h->debug.rem_count++; h->debug.last_rem_idx = idx; #endif @@ -508,7 +508,7 @@ static void base_remove_entry(HashmapBase *h, unsigned idx) { /* Find the stop bucket ("right"). It is either free or has DIB == 0. */ for (right = next_idx(h, left); ; right = next_idx(h, right)) { raw_dib = dibs[right]; - if (raw_dib == 0 || raw_dib == DIB_RAW_FREE) + if (IN_SET(raw_dib, 0, DIB_RAW_FREE)) break; /* The buckets are not supposed to be all occupied and with DIB > 0. @@ -578,7 +578,7 @@ static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator * assert(e->p.b.key == i->next_key); } -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP i->prev_idx = idx; #endif @@ -635,7 +635,7 @@ static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) { } idx = i->idx; -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP i->prev_idx = idx; #endif @@ -658,7 +658,7 @@ static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { return IDX_NIL; } -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP if (i->idx == IDX_FIRST) { i->put_count = h->debug.put_count; i->rem_count = h->debug.rem_count; @@ -750,7 +750,7 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu shared_hash_key_initialized= true; } -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP h->debug.func = func; h->debug.file = file; h->debug.line = line; @@ -807,7 +807,7 @@ static void hashmap_free_no_clear(HashmapBase *h) { assert(!h->has_indirect); assert(!h->n_direct_entries); -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug); assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); @@ -919,7 +919,7 @@ static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx, dib_raw_t raw_dib, *dibs; unsigned dib, distance; -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP h->debug.put_count++; #endif @@ -927,7 +927,7 @@ static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx, for (distance = 0; ; distance++) { raw_dib = dibs[idx]; - if (raw_dib == DIB_RAW_FREE || raw_dib == DIB_RAW_REHASH) { + if (IN_SET(raw_dib, DIB_RAW_FREE, DIB_RAW_REHASH)) { if (raw_dib == DIB_RAW_REHASH) bucket_move_entry(h, swap, idx, IDX_TMP); @@ -1012,7 +1012,7 @@ static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx, assert_se(hashmap_put_robin_hood(h, idx, swap) == false); n_entries_inc(h); -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h)); #endif @@ -1240,7 +1240,7 @@ int hashmap_replace(Hashmap *h, const void *key, void *value) { idx = bucket_scan(h, hash, key); if (idx != IDX_NIL) { e = plain_bucket_at(h, idx); -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP /* Although the key is equal, the key pointer may have changed, * and this would break our assumption for iterating. So count * this operation as incompatible with iteration. */ diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h index 6d1ae48b21..c1089652d3 100644 --- a/src/basic/hashmap.h +++ b/src/basic/hashmap.h @@ -58,7 +58,7 @@ typedef struct Set Set; /* Stores just keys */ typedef struct { unsigned idx; /* index of an entry to be iterated next */ const void *next_key; /* expected value of that entry's key pointer */ -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP unsigned put_count; /* hashmap's put_count recorded at start of iteration */ unsigned rem_count; /* hashmap's rem_count in previous iteration */ unsigned prev_idx; /* idx in previous iteration */ @@ -89,7 +89,7 @@ typedef struct { (Hashmap*)(h), \ (void)0) -#ifdef ENABLE_DEBUG_HASHMAP +#if ENABLE_DEBUG_HASHMAP # define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line # define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__ # define HASHMAP_DEBUG_PASS_ARGS , func, file, line diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index 2d6e377f0a..766770389c 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -25,6 +25,7 @@ #include "alloc-util.h" #include "hexdecoct.h" #include "macro.h" +#include "string-util.h" #include "util.h" char octchar(int x) { @@ -569,7 +570,7 @@ static int base64_append_width(char **prefix, int plen, lines = (len + width - 1) / width; - slen = sep ? strlen(sep) : 0; + slen = strlen_ptr(sep); t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines); if (!t) return -ENOMEM; diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index a94037b303..ea9e77087f 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -90,9 +90,7 @@ static bool hostname_valid_char(char c) { (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || - c == '-' || - c == '_' || - c == '.'; + IN_SET(c, '-', '_', '.'); } /** @@ -196,8 +194,11 @@ bool is_gateway_hostname(const char *hostname) { * synthetic "gateway" host. */ return - strcaseeq(hostname, "gateway") || - strcaseeq(hostname, "gateway."); + strcaseeq(hostname, "_gateway") || strcaseeq(hostname, "_gateway.") +#if ENABLE_COMPAT_GATEWAY_HOSTNAME + || strcaseeq(hostname, "gateway") || strcaseeq(hostname, "gateway.") +#endif + ; } int sethostname_idempotent(const char *s) { @@ -232,7 +233,7 @@ int read_hostname_config(const char *path, char **hostname) { /* may have comments, ignore them */ FOREACH_LINE(l, f, return -errno) { truncate_nl(l); - if (l[0] != '\0' && l[0] != '#') { + if (!IN_SET(l[0], '\0', '#')) { /* found line with value */ name = hostname_cleanup(l); name = strdup(name); diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index d52fdad3ac..e27faba75f 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -308,22 +308,22 @@ int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { return 0; } -int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) { +int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret) { int r; assert(s); r = in_addr_from_string(AF_INET, s, ret); if (r >= 0) { - if (family) - *family = AF_INET; + if (ret_family) + *ret_family = AF_INET; return 0; } r = in_addr_from_string(AF_INET6, s, ret); if (r >= 0) { - if (family) - *family = AF_INET6; + if (ret_family) + *ret_family = AF_INET6; return 0; } @@ -371,13 +371,13 @@ int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_u return r; } -unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) { +unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) { assert(addr); return 32 - u32ctz(be32toh(addr->s_addr)); } -struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { +struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { assert(addr); assert(prefixlen <= 32); @@ -390,7 +390,7 @@ struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char return addr; } -int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { +int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { uint8_t msb_octet = *(uint8_t*) addr; /* addr may not be aligned, so make sure we only access it byte-wise */ @@ -414,18 +414,18 @@ int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixl return 0; } -int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) { +int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) { unsigned char prefixlen; int r; assert(addr); assert(mask); - r = in_addr_default_prefixlen(addr, &prefixlen); + r = in4_addr_default_prefixlen(addr, &prefixlen); if (r < 0) return r; - in_addr_prefixlen_to_netmask(mask, prefixlen); + in4_addr_prefixlen_to_netmask(mask, prefixlen); return 0; } @@ -435,7 +435,7 @@ int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) if (family == AF_INET) { struct in_addr mask; - if (!in_addr_prefixlen_to_netmask(&mask, prefixlen)) + if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen)) return -EINVAL; addr->in.s_addr &= mask.s_addr; @@ -465,10 +465,57 @@ int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) return -EAFNOSUPPORT; } -int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, uint8_t *ret_prefixlen) { +int in_addr_prefix_covers(int family, + const union in_addr_union *prefix, + unsigned char prefixlen, + const union in_addr_union *address) { + + union in_addr_union masked_prefix, masked_address; + int r; + + assert(prefix); + assert(address); + + masked_prefix = *prefix; + r = in_addr_mask(family, &masked_prefix, prefixlen); + if (r < 0) + return r; + + masked_address = *address; + r = in_addr_mask(family, &masked_address, prefixlen); + if (r < 0) + return r; + + return in_addr_equal(family, &masked_prefix, &masked_address); +} + +int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) { + uint8_t u; + int r; + + if (!IN_SET(family, AF_INET, AF_INET6)) + return -EAFNOSUPPORT; + + r = safe_atou8(p, &u); + if (r < 0) + return r; + + if (u > FAMILY_ADDRESS_SIZE(family) * 8) + return -ERANGE; + + *ret = u; + return 0; +} + +int in_addr_prefix_from_string( + const char *p, + int family, + union in_addr_union *ret_prefix, + unsigned char *ret_prefixlen) { + union in_addr_union buffer; const char *e, *l; - uint8_t k; + unsigned char k; int r; assert(p); @@ -486,23 +533,58 @@ int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *r if (r < 0) return r; - k = FAMILY_ADDRESS_SIZE(family) * 8; - if (e) { - uint8_t n; - - r = safe_atou8(e + 1, &n); + r = in_addr_parse_prefixlen(family, e+1, &k); if (r < 0) return r; + } else + k = FAMILY_ADDRESS_SIZE(family) * 8; - if (n > k) - return -ERANGE; + if (ret_prefix) + *ret_prefix = buffer; + if (ret_prefixlen) + *ret_prefixlen = k; - k = n; - } + return 0; +} + +int in_addr_prefix_from_string_auto( + const char *p, + int *ret_family, + union in_addr_union *ret_prefix, + unsigned char *ret_prefixlen) { + + union in_addr_union buffer; + const char *e, *l; + unsigned char k; + int family, r; + + assert(p); + + e = strchr(p, '/'); + if (e) + l = strndupa(p, e - p); + else + l = p; - *ret_prefix = buffer; - *ret_prefixlen = k; + r = in_addr_from_string_auto(l, &family, &buffer); + if (r < 0) + return r; + + if (e) { + r = in_addr_parse_prefixlen(family, e+1, &k); + if (r < 0) + return r; + } else + k = FAMILY_ADDRESS_SIZE(family) * 8; + + if (ret_family) + *ret_family = family; + if (ret_prefix) + *ret_prefix = buffer; + if (ret_prefixlen) + *ret_prefixlen = k; return 0; + } diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 14e27246b5..59f8eb7edf 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -53,17 +53,20 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); int in_addr_from_string(int family, const char *s, union in_addr_union *ret); -int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret); +int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret); int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex); -unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr); -struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); -int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); -int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); +unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr); +struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); +int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); +int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen); -int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, uint8_t *ret_prefixlen); +int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address); +int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret); +int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); +int in_addr_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); static inline size_t FAMILY_ADDRESS_SIZE(int family) { - assert(family == AF_INET || family == AF_INET6); + assert(IN_SET(family, AF_INET, AF_INET6)); return family == AF_INET6 ? 16 : 4; } diff --git a/src/basic/io-util.h b/src/basic/io-util.h index 4684ed3bfc..d9b69adde9 100644 --- a/src/basic/io-util.h +++ b/src/basic/io-util.h @@ -40,14 +40,6 @@ int fd_wait_for_event(int fd, int event, usec_t timeout); ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); -#define IOVEC_SET_STRING(i, s) \ - do { \ - struct iovec *_i = &(i); \ - char *_s = (char *)(s); \ - _i->iov_base = _s; \ - _i->iov_len = strlen(_s); \ - } while (false) - static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) { unsigned j; size_t r = 0; @@ -93,3 +85,8 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) { return FILE_SIZE_VALID(l); } + +#define IOVEC_INIT(base, len) { .iov_base = (base), .iov_len = (len) } +#define IOVEC_MAKE(base, len) (struct iovec) IOVEC_INIT(base, len) +#define IOVEC_INIT_STRING(string) IOVEC_INIT((char*) string, strlen(string)) +#define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string) diff --git a/src/basic/journal-importer.c b/src/basic/journal-importer.c index 7d72effdea..e750101165 100644 --- a/src/basic/journal-importer.c +++ b/src/basic/journal-importer.c @@ -20,8 +20,9 @@ #include <unistd.h> #include "alloc-util.h" -#include "journal-importer.h" #include "fd-util.h" +#include "io-util.h" +#include "journal-importer.h" #include "parse-util.h" #include "string-util.h" #include "unaligned.h" @@ -38,7 +39,7 @@ static int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) { if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1)) return log_oom(); - iovw->iovec[iovw->count++] = (struct iovec) {data, len}; + iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len); return 0; } @@ -153,9 +154,7 @@ static int get_line(JournalImporter *imp, char **line, size_t *size) { static int fill_fixed_size(JournalImporter *imp, void **data, size_t size) { assert(imp); - assert(imp->state == IMPORTER_STATE_DATA_START || - imp->state == IMPORTER_STATE_DATA || - imp->state == IMPORTER_STATE_DATA_FINISH); + assert(IN_SET(imp->state, IMPORTER_STATE_DATA_START, IMPORTER_STATE_DATA, IMPORTER_STATE_DATA_FINISH)); assert(size <= DATA_SIZE_MAX); assert(imp->offset <= imp->filled); assert(imp->filled <= imp->size); diff --git a/src/basic/log.c b/src/basic/log.c index 3fd53800a0..4f0fe54579 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -74,6 +74,7 @@ static bool show_location = false; static bool upgrade_syslog_to_journal = false; static bool always_reopen_console = false; +static bool open_when_needed = false; /* Akin to glibc's __abort_msg; which is private and we hence cannot * use here. */ @@ -84,7 +85,7 @@ void log_close_console(void) { if (console_fd < 0) return; - if (getpid() == 1) { + if (getpid_cached() == 1) { if (console_fd >= 3) safe_close(console_fd); @@ -140,7 +141,7 @@ static int create_log_socket(int type) { /* We need a blocking fd here since we'd otherwise lose messages way too early. However, let's not hang forever in the unlikely case of a deadlock. */ - if (getpid() == 1) + if (getpid_cached() == 1) timeval_store(&tv, 10 * USEC_PER_MSEC); else timeval_store(&tv, 10 * USEC_PER_SEC); @@ -248,7 +249,7 @@ int log_open(void) { } if (!IN_SET(log_target, LOG_TARGET_AUTO, LOG_TARGET_SAFE) || - getpid() == 1 || + getpid_cached() == 1 || isatty(STDERR_FILENO) <= 0) { if (IN_SET(log_target, LOG_TARGET_AUTO, @@ -351,26 +352,26 @@ static int write_to_console( if (log_target == LOG_TARGET_CONSOLE_PREFIXED) { xsprintf(prefix, "<%i>", level); - IOVEC_SET_STRING(iovec[n++], prefix); + iovec[n++] = IOVEC_MAKE_STRING(prefix); } highlight = LOG_PRI(level) <= LOG_ERR && show_color; if (show_location) { snprintf(location, sizeof(location), "(%s:%i) ", file, line); - IOVEC_SET_STRING(iovec[n++], location); + iovec[n++] = IOVEC_MAKE_STRING(location); } if (highlight) - IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED); - IOVEC_SET_STRING(iovec[n++], buffer); + iovec[n++] = IOVEC_MAKE_STRING(ANSI_HIGHLIGHT_RED); + iovec[n++] = IOVEC_MAKE_STRING(buffer); if (highlight) - IOVEC_SET_STRING(iovec[n++], ANSI_NORMAL); - IOVEC_SET_STRING(iovec[n++], "\n"); + iovec[n++] = IOVEC_MAKE_STRING(ANSI_NORMAL); + iovec[n++] = IOVEC_MAKE_STRING("\n"); if (writev(console_fd, iovec, n) < 0) { - if (errno == EIO && getpid() == 1) { + if (errno == EIO && getpid_cached() == 1) { /* If somebody tried to kick us from our * console tty (via vhangup() or suchlike), @@ -423,13 +424,13 @@ static int write_to_syslog( if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) return -EINVAL; - xsprintf(header_pid, "["PID_FMT"]: ", getpid()); + xsprintf(header_pid, "["PID_FMT"]: ", getpid_cached()); - IOVEC_SET_STRING(iovec[0], header_priority); - IOVEC_SET_STRING(iovec[1], header_time); - IOVEC_SET_STRING(iovec[2], program_invocation_short_name); - IOVEC_SET_STRING(iovec[3], header_pid); - IOVEC_SET_STRING(iovec[4], buffer); + iovec[0] = IOVEC_MAKE_STRING(header_priority); + iovec[1] = IOVEC_MAKE_STRING(header_time); + iovec[2] = IOVEC_MAKE_STRING(program_invocation_short_name); + iovec[3] = IOVEC_MAKE_STRING(header_pid); + iovec[4] = IOVEC_MAKE_STRING(buffer); /* When using syslog via SOCK_STREAM separate the messages by NUL chars */ if (syslog_is_stream) @@ -468,13 +469,13 @@ static int write_to_kmsg( return 0; xsprintf(header_priority, "<%i>", level); - xsprintf(header_pid, "["PID_FMT"]: ", getpid()); + xsprintf(header_pid, "["PID_FMT"]: ", getpid_cached()); - IOVEC_SET_STRING(iovec[0], header_priority); - IOVEC_SET_STRING(iovec[1], program_invocation_short_name); - IOVEC_SET_STRING(iovec[2], header_pid); - IOVEC_SET_STRING(iovec[3], buffer); - IOVEC_SET_STRING(iovec[4], "\n"); + iovec[0] = IOVEC_MAKE_STRING(header_priority); + iovec[1] = IOVEC_MAKE_STRING(program_invocation_short_name); + iovec[2] = IOVEC_MAKE_STRING(header_pid); + iovec[3] = IOVEC_MAKE_STRING(buffer); + iovec[4] = IOVEC_MAKE_STRING("\n"); if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0) return -errno; @@ -547,10 +548,10 @@ static int write_to_journal( log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object, extra_field, extra); - IOVEC_SET_STRING(iovec[0], header); - IOVEC_SET_STRING(iovec[1], "MESSAGE="); - IOVEC_SET_STRING(iovec[2], buffer); - IOVEC_SET_STRING(iovec[3], "\n"); + iovec[0] = IOVEC_MAKE_STRING(header); + iovec[1] = IOVEC_MAKE_STRING("MESSAGE="); + iovec[2] = IOVEC_MAKE_STRING(buffer); + iovec[3] = IOVEC_MAKE_STRING("\n"); mh.msg_iov = iovec; mh.msg_iovlen = ELEMENTSOF(iovec); @@ -585,6 +586,9 @@ int log_dispatch_internal( if ((level & LOG_FACMASK) == 0) level = log_facility | LOG_PRI(level); + if (open_when_needed) + log_open(); + do { char *e; int k = 0; @@ -640,6 +644,9 @@ int log_dispatch_internal( buffer = e; } while (buffer); + if (open_when_needed) + log_close(); + return -error; } @@ -804,6 +811,7 @@ noreturn void log_assert_failed_realm( const char *file, int line, const char *func) { + log_open(); log_assert(LOG_REALM_PLUS_LEVEL(realm, LOG_CRIT), text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting."); abort(); @@ -815,6 +823,7 @@ noreturn void log_assert_failed_unreachable_realm( const char *file, int line, const char *func) { + log_open(); log_assert(LOG_REALM_PLUS_LEVEL(realm, LOG_CRIT), text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting."); abort(); @@ -832,9 +841,8 @@ void log_assert_failed_return_realm( } int log_oom_internal(LogRealm realm, const char *file, int line, const char *func) { - log_internal_realm(LOG_REALM_PLUS_LEVEL(realm, LOG_ERR), - ENOMEM, file, line, func, "Out of memory."); - return -ENOMEM; + return log_internal_realm(LOG_REALM_PLUS_LEVEL(realm, LOG_ERR), + ENOMEM, file, line, func, "Out of memory."); } int log_format_iovec( @@ -870,7 +878,7 @@ int log_format_iovec( * the next format string */ VA_FORMAT_ADVANCE(format, ap); - IOVEC_SET_STRING(iovec[(*n)++], m); + iovec[(*n)++] = IOVEC_MAKE_STRING(m); if (newline_separator) { iovec[*n].iov_base = (char*) &nl; @@ -891,9 +899,9 @@ int log_struct_internal( const char *func, const char *format, ...) { + LogRealm realm = LOG_REALM_REMOVE_LEVEL(level); char buf[LINE_MAX]; bool found = false; - LogRealm realm = LOG_REALM_REMOVE_LEVEL(level); PROTECT_ERRNO; va_list ap; @@ -909,38 +917,48 @@ int log_struct_internal( if ((level & LOG_FACMASK) == 0) level = log_facility | LOG_PRI(level); - if (IN_SET(log_target, LOG_TARGET_AUTO, - LOG_TARGET_JOURNAL_OR_KMSG, - LOG_TARGET_JOURNAL) && - journal_fd >= 0) { - char header[LINE_MAX]; - struct iovec iovec[17] = {}; - unsigned n = 0, i; - int r; - struct msghdr mh = { - .msg_iov = iovec, - }; - bool fallback = false; - - /* If the journal is available do structured logging */ - log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL); - IOVEC_SET_STRING(iovec[n++], header); + if (IN_SET(log_target, + LOG_TARGET_AUTO, + LOG_TARGET_JOURNAL_OR_KMSG, + LOG_TARGET_JOURNAL)) { + + if (open_when_needed) + log_open_journal(); + + if (journal_fd >= 0) { + char header[LINE_MAX]; + struct iovec iovec[17] = {}; + unsigned n = 0, i; + int r; + struct msghdr mh = { + .msg_iov = iovec, + }; + bool fallback = false; + + /* If the journal is available do structured logging */ + log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL); + iovec[n++] = IOVEC_MAKE_STRING(header); + + va_start(ap, format); + r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, true, error, format, ap); + if (r < 0) + fallback = true; + else { + mh.msg_iovlen = n; + (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL); + } - va_start(ap, format); - r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, true, error, format, ap); - if (r < 0) - fallback = true; - else { - mh.msg_iovlen = n; - (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL); - } + va_end(ap); + for (i = 1; i < n; i += 2) + free(iovec[i].iov_base); - va_end(ap); - for (i = 1; i < n; i += 2) - free(iovec[i].iov_base); + if (!fallback) { + if (open_when_needed) + log_close(); - if (!fallback) - return -error; + return -error; + } + } } /* Fallback if journal logging is not available or didn't work. */ @@ -967,12 +985,83 @@ int log_struct_internal( } va_end(ap); - if (!found) + if (!found) { + if (open_when_needed) + log_close(); + return -error; + } return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, buf + 8); } +int log_struct_iovec_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const struct iovec input_iovec[], + size_t n_input_iovec) { + + LogRealm realm = LOG_REALM_REMOVE_LEVEL(level); + PROTECT_ERRNO; + size_t i; + char *m; + + if (error < 0) + error = -error; + + if (_likely_(LOG_PRI(level) > log_max_level[realm])) + return -error; + + if (log_target == LOG_TARGET_NULL) + return -error; + + if ((level & LOG_FACMASK) == 0) + level = log_facility | LOG_PRI(level); + + if (IN_SET(log_target, LOG_TARGET_AUTO, + LOG_TARGET_JOURNAL_OR_KMSG, + LOG_TARGET_JOURNAL) && + journal_fd >= 0) { + + struct iovec iovec[1 + n_input_iovec*2]; + char header[LINE_MAX]; + struct msghdr mh = { + .msg_iov = iovec, + .msg_iovlen = 1 + n_input_iovec*2, + }; + + log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL); + iovec[0] = IOVEC_MAKE_STRING(header); + + for (i = 0; i < n_input_iovec; i++) { + iovec[1+i*2] = input_iovec[i]; + iovec[1+i*2+1] = IOVEC_MAKE_STRING("\n"); + } + + if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) >= 0) + return -error; + } + + for (i = 0; i < n_input_iovec; i++) { + if (input_iovec[i].iov_len < strlen("MESSAGE=")) + continue; + + if (memcmp(input_iovec[i].iov_base, "MESSAGE=", strlen("MESSAGE=")) == 0) + break; + } + + if (_unlikely_(i >= n_input_iovec)) /* Couldn't find MESSAGE=? */ + return -error; + + m = strndupa(input_iovec[i].iov_base + strlen("MESSAGE="), + input_iovec[i].iov_len - strlen("MESSAGE=")); + + return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, m); +} + int log_set_target_from_string(const char *e) { LogTarget t; @@ -1152,10 +1241,6 @@ 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, @@ -1189,7 +1274,7 @@ int log_syntax_internal( va_end(ap); if (unit) - unit_fmt = getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s"; + unit_fmt = getpid_cached() == 1 ? "UNIT=%s" : "USER_UNIT=%s"; return log_struct_internal( LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, level), @@ -1203,6 +1288,14 @@ int log_syntax_internal( NULL); } +void log_set_upgrade_syslog_to_journal(bool b) { + upgrade_syslog_to_journal = b; +} + void log_set_always_reopen_console(bool b) { always_reopen_console = b; } + +void log_set_open_when_needed(bool b) { + open_when_needed = b; +} diff --git a/src/basic/log.h b/src/basic/log.h index ff5d776b1d..10a6032788 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -30,6 +30,7 @@ #include "sd-id128.h" #include "macro.h" +#include "process-util.h" typedef enum LogRealm { LOG_REALM_SYSTEMD, @@ -186,6 +187,15 @@ int log_format_iovec( const char *format, va_list ap) _printf_(6, 0); +int log_struct_iovec_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const struct iovec input_iovec[], + size_t n_input_iovec); + /* This modifies the buffer passed! */ int log_dump_internal( int level, @@ -247,7 +257,7 @@ void log_assert_failed_return_realm( #define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) #define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) #define log_error(...) log_full(LOG_ERR, __VA_ARGS__) -#define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__) +#define log_emergency(...) log_full(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__) /* Logging triggered by an errno-like error */ #define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__) @@ -255,7 +265,7 @@ void log_assert_failed_return_realm( #define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__) #define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__) #define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) -#define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__) +#define log_emergency_errno(error, ...) log_full_errno(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__) #ifdef LOG_TRACE # define log_trace(...) log_debug(__VA_ARGS__) @@ -269,6 +279,11 @@ void log_assert_failed_return_realm( error, __FILE__, __LINE__, __func__, __VA_ARGS__) #define log_struct(level, ...) log_struct_errno(level, 0, __VA_ARGS__) +#define log_struct_iovec_errno(level, error, iovec, n_iovec) \ + log_struct_iovec_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ + error, __FILE__, __LINE__, __func__, iovec, n_iovec) +#define log_struct_iovec(level, iovec, n_iovec) log_struct_iovec_errno(level, 0, iovec, n_iovec) + /* This modifies the buffer passed! */ #define log_dump(level, buffer) \ log_dump_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ @@ -288,6 +303,7 @@ void log_received_signal(int level, const struct signalfd_siginfo *si); void log_set_upgrade_syslog_to_journal(bool b); void log_set_always_reopen_console(bool b); +void log_set_open_when_needed(bool b); int log_syntax_internal( const char *unit, diff --git a/src/basic/memfd-util.c b/src/basic/memfd-util.c index 8c8cc78ebf..8f4f0e3a24 100644 --- a/src/basic/memfd-util.c +++ b/src/basic/memfd-util.c @@ -21,7 +21,7 @@ #include <fcntl.h> #include <sys/stat.h> #include <unistd.h> -#ifdef HAVE_LINUX_MEMFD_H +#if HAVE_LINUX_MEMFD_H #include <linux/memfd.h> #endif #include <stdio.h> diff --git a/src/basic/meson.build b/src/basic/meson.build index 065f0ac4af..1ddefb7fbb 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -1,4 +1,6 @@ basic_sources_plain = files(''' + MurmurHash2.c + MurmurHash2.h af-list.c af-list.h alloc-util.c @@ -16,6 +18,8 @@ basic_sources_plain = files(''' bitmap.c bitmap.h blkid-util.h + bpf-program.c + bpf-program.h btrfs-ctree.h btrfs-util.c btrfs-util.h @@ -24,10 +28,10 @@ basic_sources_plain = files(''' bus-label.h calendarspec.c calendarspec.h - capability-util.c - capability-util.h cap-list.c cap-list.h + capability-util.c + capability-util.h cgroup-util.c cgroup-util.h chattr-util.c @@ -61,10 +65,10 @@ basic_sources_plain = files(''' extract-word.h fd-util.c fd-util.h - fileio.c - fileio.h fileio-label.c fileio-label.h + fileio.c + fileio.h format-util.h fs-util.c fs-util.h @@ -82,9 +86,9 @@ basic_sources_plain = files(''' hostname-util.h in-addr-util.c in-addr-util.h - ioprio.h io-util.c io-util.h + ioprio.h journal-importer.c journal-importer.h khash.c @@ -106,13 +110,11 @@ basic_sources_plain = files(''' mempool.c mempool.h missing_syscall.h + mkdir-label.c mkdir.c mkdir.h - mkdir-label.c mount-util.c mount-util.h - MurmurHash2.c - MurmurHash2.h nss-util.h ordered-set.c ordered-set.h @@ -138,9 +140,12 @@ basic_sources_plain = files(''' rlimit-util.h rm-rf.c rm-rf.h + securebits-util.c + securebits-util.h securebits.h selinux-util.c selinux-util.h + set.c set.h sigbus.c sigbus.h diff --git a/src/basic/missing.h b/src/basic/missing.h index 7830a4f415..352d2b024b 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -40,7 +40,7 @@ #include <uchar.h> #include <unistd.h> -#ifdef HAVE_AUDIT +#if HAVE_AUDIT #include <libaudit.h> #endif @@ -48,11 +48,11 @@ #include <asm/sgidefs.h> #endif -#ifdef HAVE_LINUX_BTRFS_H +#if HAVE_LINUX_BTRFS_H #include <linux/btrfs.h> #endif -#ifdef HAVE_LINUX_VM_SOCKETS_H +#if HAVE_LINUX_VM_SOCKETS_H #include <linux/vm_sockets.h> #else #define VMADDR_CID_ANY -1U @@ -204,7 +204,7 @@ struct sockaddr_vm { #define BTRFS_QGROUP_LEVEL_SHIFT 48 #endif -#ifndef HAVE_LINUX_BTRFS_H +#if ! HAVE_LINUX_BTRFS_H struct btrfs_ioctl_vol_args { int64_t fd; char name[BTRFS_PATH_NAME_MAX + 1]; @@ -546,8 +546,8 @@ struct btrfs_ioctl_quota_ctl_args { #define MAX_HANDLE_SZ 128 #endif -#ifndef HAVE_SECURE_GETENV -# ifdef HAVE___SECURE_GETENV +#if ! HAVE_SECURE_GETENV +# if HAVE___SECURE_GETENV # define secure_getenv __secure_getenv # else # error "neither secure_getenv nor __secure_getenv are available" @@ -606,15 +606,14 @@ struct input_mask { #else #define __O_TMPFILE 020000000 #endif +#endif /* a horrid kludge trying to make sure that this will fail on old kernels */ #ifndef O_TMPFILE #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #endif -#endif - -#if !HAVE_DECL_LO_FLAGS_PARTSCAN +#if !HAVE_LO_FLAGS_PARTSCAN #define LO_FLAGS_PARTSCAN 8 #endif @@ -626,7 +625,7 @@ struct input_mask { #define LOOP_CTL_GET_FREE 0x4C82 #endif -#if !HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE +#if !HAVE_IFLA_INET6_ADDR_GEN_MODE #define IFLA_INET6_UNSPEC 0 #define IFLA_INET6_FLAGS 1 #define IFLA_INET6_CONF 2 @@ -644,11 +643,11 @@ struct input_mask { #define IN6_ADDR_GEN_MODE_NONE 1 #endif -#if !HAVE_DECL_IN6_ADDR_GEN_MODE_STABLE_PRIVACY +#if !HAVE_IN6_ADDR_GEN_MODE_STABLE_PRIVACY #define IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2 #endif -#if !HAVE_DECL_IFLA_MACVLAN_FLAGS +#if !HAVE_IFLA_MACVLAN_FLAGS #define IFLA_MACVLAN_UNSPEC 0 #define IFLA_MACVLAN_MODE 1 #define IFLA_MACVLAN_FLAGS 2 @@ -657,7 +656,7 @@ struct input_mask { #define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1) #endif -#if !HAVE_DECL_IFLA_IPVLAN_MODE +#if !HAVE_IFLA_IPVLAN_MODE #define IFLA_IPVLAN_UNSPEC 0 #define IFLA_IPVLAN_MODE 1 #define __IFLA_IPVLAN_MAX 2 @@ -669,7 +668,7 @@ struct input_mask { #define IPVLAN_MAX 2 #endif -#if !HAVE_DECL_IFLA_VTI_REMOTE +#if !HAVE_IFLA_VTI_REMOTE #define IFLA_VTI_UNSPEC 0 #define IFLA_VTI_LINK 1 #define IFLA_VTI_IKEY 2 @@ -681,7 +680,7 @@ struct input_mask { #define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1) #endif -#if !HAVE_DECL_IFLA_PHYS_PORT_ID +#if !HAVE_IFLA_PHYS_PORT_ID #define IFLA_EXT_MASK 29 #undef IFLA_PROMISCUITY #define IFLA_PROMISCUITY 30 @@ -694,7 +693,7 @@ struct input_mask { #define IFLA_MAX (__IFLA_MAX - 1) #endif -#if !HAVE_DECL_IFLA_BOND_AD_INFO +#if !HAVE_IFLA_BOND_AD_INFO #define IFLA_BOND_UNSPEC 0 #define IFLA_BOND_MODE 1 #define IFLA_BOND_ACTIVE_SLAVE 2 @@ -724,7 +723,7 @@ struct input_mask { #define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1) #endif -#if !HAVE_DECL_IFLA_VLAN_PROTOCOL +#if !HAVE_IFLA_VLAN_PROTOCOL #define IFLA_VLAN_UNSPEC 0 #define IFLA_VLAN_ID 1 #define IFLA_VLAN_FLAGS 2 @@ -736,7 +735,7 @@ struct input_mask { #define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1) #endif -#if !HAVE_DECL_IFLA_VXLAN_GPE +#if !HAVE_IFLA_VXLAN_GPE #define IFLA_VXLAN_UNSPEC 0 #define IFLA_VXLAN_ID 1 #define IFLA_VXLAN_GROUP 2 @@ -771,7 +770,7 @@ struct input_mask { #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) #endif -#if !HAVE_DECL_IFLA_GENEVE_LABEL +#if !HAVE_IFLA_GENEVE_LABEL #define IFLA_GENEVE_UNSPEC 0 #define IFLA_GENEVE_ID 1 #define IFLA_GENEVE_REMOTE 2 @@ -790,7 +789,7 @@ struct input_mask { #define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1) #endif -#if !HAVE_DECL_IFLA_IPTUN_ENCAP_DPORT +#if !HAVE_IFLA_IPTUN_ENCAP_DPORT #define IFLA_IPTUN_UNSPEC 0 #define IFLA_IPTUN_LINK 1 #define IFLA_IPTUN_LOCAL 2 @@ -816,7 +815,7 @@ struct input_mask { #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) #endif -#if !HAVE_DECL_IFLA_GRE_ENCAP_DPORT +#if !HAVE_IFLA_GRE_ENCAP_DPORT #define IFLA_GRE_UNSPEC 0 #define IFLA_GRE_LINK 1 #define IFLA_GRE_IFLAGS 2 @@ -841,7 +840,7 @@ struct input_mask { #define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1) #endif -#if !HAVE_DECL_IFLA_BRIDGE_VLAN_INFO +#if !HAVE_IFLA_BRIDGE_VLAN_INFO #define IFLA_BRIDGE_FLAGS 0 #define IFLA_BRIDGE_MODE 1 #define IFLA_BRIDGE_VLAN_INFO 2 @@ -858,7 +857,7 @@ struct input_mask { #define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */ #endif -#if !HAVE_DECL_IFLA_BR_VLAN_DEFAULT_PVID +#if !HAVE_IFLA_BR_VLAN_DEFAULT_PVID #define IFLA_BR_UNSPEC 0 #define IFLA_BR_FORWARD_DELAY 1 #define IFLA_BR_HELLO_TIME 2 @@ -904,7 +903,7 @@ struct input_mask { #define IFLA_BR_MAX (__IFLA_BR_MAX - 1) #endif -#if !HAVE_DECL_IFLA_BRPORT_LEARNING_SYNC +#if !HAVE_IFLA_BRPORT_LEARNING_SYNC #define IFLA_BRPORT_UNSPEC 0 #define IFLA_BRPORT_STATE 1 #define IFLA_BRPORT_PRIORITY 2 @@ -921,15 +920,42 @@ struct input_mask { #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) #endif -#if !HAVE_DECL_IFLA_BRPORT_PROXYARP +#if !HAVE_FRA_UID_RANGE +#define FRA_UNSPEC 0 +#define FRA_DST 1 +#define FRA_SRC 2 +#define FRA_IIFNAME 3 +#define FRA_GOTO 4 +#define FRA_UNUSED2 5 +#define FRA_PRIORITY 6 +#define FRA_UNUSED3 7 +#define FRA_UNUSED4 8 +#define FRA_UNUSED5 9 +#define FRA_FWMARK 10 +#define FRA_FLOW 11 +#define FRA_TUN_ID 12 +#define FRA_SUPPRESS_IFGROUP 13 +#define FRA_SUPPRESS_PREFIXLEN 14 +#define FRA_TABLE 15 +#define FRA_FWMASK 16 +#define FRA_OIFNAME 17 +#define FRA_PAD 18 +#define FRA_L3MDEV 19 +#define FRA_UID_RANGE 20 +#define __FRA_MAX 12 + +#define FRA_MAX (__FRA_MAX - 1) +#endif + +#if !HAVE_IFLA_BRPORT_PROXYARP #define IFLA_BRPORT_PROXYARP 10 #endif -#if !HAVE_DECL_IFLA_VRF_TABLE +#if !HAVE_IFLA_VRF_TABLE #define IFLA_VRF_TABLE 1 #endif -#if !HAVE_DECL_NDA_IFINDEX +#if !HAVE_NDA_IFINDEX #define NDA_UNSPEC 0 #define NDA_DST 1 #define NDA_LLADDR 2 @@ -1013,7 +1039,7 @@ struct input_mask { #define LOOPBACK_IFINDEX 1 #endif -#if !HAVE_DECL_IFA_FLAGS +#if !HAVE_IFA_FLAGS #define IFA_FLAGS 8 #endif @@ -1082,7 +1108,7 @@ struct input_mask { #define KEY_ALS_TOGGLE 0x230 #endif -#ifndef HAVE_KEY_SERIAL_T +#if ! HAVE_KEY_SERIAL_T typedef int32_t key_serial_t; #endif @@ -1102,6 +1128,10 @@ typedef int32_t key_serial_t; #define KEYCTL_DESCRIBE 6 #endif +#ifndef KEYCTL_LINK +#define KEYCTL_LINK 8 +#endif + #ifndef KEYCTL_READ #define KEYCTL_READ 11 #endif @@ -1174,11 +1204,11 @@ typedef int32_t key_serial_t; #ifndef IF_OPER_UP #define IF_OPER_UP 6 -#ifndef HAVE_CHAR32_T +#if ! HAVE_CHAR32_T #define char32_t uint32_t #endif -#ifndef HAVE_CHAR16_T +#if ! HAVE_CHAR16_T #define char16_t uint16_t #endif @@ -1190,7 +1220,7 @@ typedef int32_t key_serial_t; #define IFA_F_MCAUTOJOIN 0x400 #endif -#ifndef HAVE_STRUCT_ETHTOOL_LINK_SETTINGS +#if ! HAVE_STRUCT_ETHTOOL_LINK_SETTINGS #define ETHTOOL_GLINKSETTINGS 0x0000004c /* Get ethtool_link_settings */ #define ETHTOOL_SLINKSETTINGS 0x0000004d /* Set ethtool_link_settings */ @@ -1217,6 +1247,15 @@ struct ethtool_link_settings { #endif +#if ! HAVE_STRUCT_FIB_RULE_UID_RANGE + +struct fib_rule_uid_range { + __u32 start; + __u32 end; +}; + +#endif + #endif #ifndef SOL_ALG diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h index 898116c7b3..3322e4e4d6 100644 --- a/src/basic/missing_syscall.h +++ b/src/basic/missing_syscall.h @@ -22,7 +22,9 @@ /* Missing glibc definitions to access certain kernel APIs */ -#if !HAVE_DECL_PIVOT_ROOT +#include <sys/types.h> + +#if !HAVE_PIVOT_ROOT static inline int pivot_root(const char *new_root, const char *put_old) { return syscall(SYS_pivot_root, new_root, put_old); } @@ -30,7 +32,7 @@ static inline int pivot_root(const char *new_root, const char *put_old) { /* ======================================================================= */ -#if !HAVE_DECL_MEMFD_CREATE +#if !HAVE_MEMFD_CREATE # ifndef __NR_memfd_create # if defined __x86_64__ # define __NR_memfd_create 319 @@ -71,7 +73,7 @@ static inline int memfd_create(const char *name, unsigned int flags) { /* ======================================================================= */ -#if !HAVE_DECL_GETRANDOM +#if !HAVE_GETRANDOM # ifndef __NR_getrandom # if defined __x86_64__ # define __NR_getrandom 318 @@ -118,7 +120,7 @@ static inline int getrandom(void *buffer, size_t count, unsigned flags) { /* ======================================================================= */ -#if !HAVE_DECL_GETTID +#if !HAVE_GETTID static inline pid_t gettid(void) { return (pid_t) syscall(SYS_gettid); } @@ -126,7 +128,7 @@ static inline pid_t gettid(void) { /* ======================================================================= */ -#if !HAVE_DECL_NAME_TO_HANDLE_AT +#if !HAVE_NAME_TO_HANDLE_AT # ifndef __NR_name_to_handle_at # if defined(__x86_64__) # define __NR_name_to_handle_at 303 @@ -161,7 +163,7 @@ static inline int name_to_handle_at(int fd, const char *name, struct file_handle /* ======================================================================= */ -#if !HAVE_DECL_SETNS +#if !HAVE_SETNS # ifndef __NR_setns # if defined(__x86_64__) # define __NR_setns 308 @@ -196,7 +198,7 @@ static inline pid_t raw_getpid(void) { /* ======================================================================= */ -#if !HAVE_DECL_RENAMEAT2 +#if !HAVE_RENAMEAT2 # ifndef __NR_renameat2 # if defined __x86_64__ # define __NR_renameat2 316 @@ -239,7 +241,7 @@ static inline int renameat2(int oldfd, const char *oldname, int newfd, const cha /* ======================================================================= */ -#if !HAVE_DECL_KCMP +#if !HAVE_KCMP static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) { # ifdef __NR_kcmp return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2); @@ -252,7 +254,7 @@ static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, uns /* ======================================================================= */ -#if !HAVE_DECL_KEYCTL +#if !HAVE_KEYCTL static inline long keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) { # ifdef __NR_keyctl return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5); @@ -283,7 +285,7 @@ static inline key_serial_t request_key(const char *type, const char *description /* ======================================================================= */ -#if !HAVE_DECL_COPY_FILE_RANGE +#if !HAVE_COPY_FILE_RANGE # ifndef __NR_copy_file_range # if defined(__x86_64__) # define __NR_copy_file_range 326 @@ -316,3 +318,33 @@ static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, # endif } #endif + +#if !HAVE_BPF +# ifndef __NR_bpf +# if defined __i386__ +# define __NR_bpf 357 +# elif defined __x86_64__ +# define __NR_bpf 321 +# elif defined __aarch64__ +# define __NR_bpf 280 +# elif defined __sparc__ +# define __NR_bpf 349 +# elif defined __s390__ +# define __NR_bpf 351 +# else +# warning "__NR_bpf not defined for your architecture" +# endif +# endif + +union bpf_attr; + +static inline int bpf(int cmd, union bpf_attr *attr, size_t size) { +#ifdef __NR_bpf + return (int) syscall(__NR_bpf, cmd, attr, size); +#else + errno = ENOSYS; + return -1; +#endif +} + +#endif diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index 6b1a98402c..7db09fc6a1 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -31,10 +31,13 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir) { struct stat st; + int r; - if (_mkdir(path, mode) >= 0) - if (chmod_and_chown(path, mode, uid, gid) < 0) - return -errno; + if (_mkdir(path, mode) >= 0) { + r = chmod_and_chown(path, mode, uid, gid); + if (r < 0) + return r; + } if (lstat(path, &st) < 0) return -errno; diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index 7b9400b47c..c4d451db73 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -472,14 +472,14 @@ int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **bl while ((x = set_steal_first(todo))) { r = set_consume(done, x); - if (r == -EEXIST || r == 0) + if (IN_SET(r, 0, -EEXIST)) continue; if (r < 0) return r; /* Deal with mount points that are obstructed by a later mount */ r = path_is_mount_point(x, NULL, 0); - if (r == -ENOENT || r == 0) + if (IN_SET(r, 0, -ENOENT)) continue; if (r < 0) return r; @@ -526,30 +526,67 @@ int mount_move_root(const char *path) { } bool fstype_is_network(const char *fstype) { - static const char table[] = - "afs\0" - "cifs\0" - "smbfs\0" - "sshfs\0" - "ncpfs\0" - "ncp\0" - "nfs\0" - "nfs4\0" - "gfs\0" - "gfs2\0" - "glusterfs\0" - "pvfs2\0" /* OrangeFS */ - "ocfs2\0" - "lustre\0" - ; - const char *x; x = startswith(fstype, "fuse."); if (x) fstype = x; - return nulstr_contains(table, fstype); + return STR_IN_SET(fstype, + "afs", + "cifs", + "smbfs", + "sshfs", + "ncpfs", + "ncp", + "nfs", + "nfs4", + "gfs", + "gfs2", + "glusterfs", + "pvfs2", /* OrangeFS */ + "ocfs2", + "lustre"); +} + +bool fstype_is_api_vfs(const char *fstype) { + return STR_IN_SET(fstype, + "autofs", + "bpf", + "cgroup", + "cgroup2", + "configfs", + "cpuset", + "debugfs", + "devpts", + "devtmpfs", + "efivarfs", + "fusectl", + "hugetlbfs", + "mqueue", + "proc", + "pstore", + "ramfs", + "securityfs", + "sysfs", + "tmpfs", + "tracefs"); +} + +bool fstype_is_ro(const char *fstype) { + /* All Linux file systems that are necessarily read-only */ + return STR_IN_SET(fstype, + "DM_verity_hash", + "iso9660", + "squashfs"); +} + +bool fstype_can_discard(const char *fstype) { + return STR_IN_SET(fstype, + "btrfs", + "ext4", + "vfat", + "xfs"); } int repeat_unmount(const char *path, int flags) { diff --git a/src/basic/mount-util.h b/src/basic/mount-util.h index 2e24a184c5..1e066d8886 100644 --- a/src/basic/mount-util.h +++ b/src/basic/mount-util.h @@ -44,6 +44,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent); #define _cleanup_endmntent_ _cleanup_(endmntentp) bool fstype_is_network(const char *fstype); +bool fstype_is_api_vfs(const char *fstype); +bool fstype_is_ro(const char *fsype); +bool fstype_can_discard(const char *fstype); union file_handle_union { struct file_handle handle; diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 4532f222c8..4ae07b0a8e 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -59,7 +59,7 @@ int parse_pid(const char *s, pid_t* ret_pid) { if ((unsigned long) pid != ul) return -ERANGE; - if (pid <= 0) + if (!pid_is_valid(pid)) return -ERANGE; *ret_pid = pid; @@ -152,7 +152,7 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) { unsigned n_entries, start_pos = 0; assert(t); - assert(base == 1000 || base == 1024); + assert(IN_SET(base, 1000, 1024)); assert(size); if (base == 1000) { diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 80fdda170f..6c06bd2acb 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -131,8 +131,7 @@ int path_make_relative(const char *from_dir, const char *to_path, char **_r) { /* Skip the common part. */ for (;;) { - size_t a; - size_t b; + size_t a, b; from_dir += strspn(from_dir, "/"); to_path += strspn(to_path, "/"); @@ -144,7 +143,6 @@ int path_make_relative(const char *from_dir, const char *to_path, char **_r) { else /* from_dir is a parent directory of to_path. */ r = strdup(to_path); - if (!r) return -ENOMEM; @@ -175,21 +173,32 @@ int path_make_relative(const char *from_dir, const char *to_path, char **_r) { /* Count the number of necessary ".." elements. */ for (n_parents = 0;;) { + size_t w; + from_dir += strspn(from_dir, "/"); if (!*from_dir) break; - from_dir += strcspn(from_dir, "/"); - n_parents++; + w = strcspn(from_dir, "/"); + + /* If this includes ".." we can't do a simple series of "..", refuse */ + if (w == 2 && from_dir[0] == '.' && from_dir[1] == '.') + return -EINVAL; + + /* Count number of elements, except if they are "." */ + if (w != 1 || from_dir[0] != '.') + n_parents++; + + from_dir += w; } - r = malloc(n_parents * 3 + strlen(to_path) + 1); + r = new(char, n_parents * 3 + strlen(to_path) + 1); if (!r) return -ENOMEM; - for (p = r; n_parents > 0; n_parents--, p += 3) - memcpy(p, "../", 3); + for (p = r; n_parents > 0; n_parents--) + p = mempcpy(p, "../", 3); strcpy(p, to_path); path_kill_slashes(r); diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 26f165fc38..546246595c 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -30,7 +30,7 @@ #define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" #define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin" -#ifdef HAVE_SPLIT_USR +#if HAVE_SPLIT_USR # define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR #else # define DEFAULT_PATH DEFAULT_PATH_NORMAL @@ -143,3 +143,13 @@ bool is_deviceallow_pattern(const char *path); int systemd_installation_has_version(const char *root, unsigned minimal_version); bool dot_or_dot_dot(const char *path); + +static inline const char *skip_dev_prefix(const char *p) { + const char *e; + + /* Drop any /dev prefix if there is any */ + + e = path_startswith(p, "/dev/"); + + return e ?: p; +} diff --git a/src/basic/process-util.c b/src/basic/process-util.c index b80cacaa42..99b0946a03 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -34,7 +34,7 @@ #include <sys/wait.h> #include <syslog.h> #include <unistd.h> -#ifdef HAVE_VALGRIND_VALGRIND_H +#if HAVE_VALGRIND_VALGRIND_H #include <valgrind/valgrind.h> #endif @@ -312,19 +312,18 @@ int rename_process(const char name[]) { /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at * the end. This is the best option for changing /proc/self/cmdline. */ - if (mm_size < l+1) { + + /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the + * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is + * present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if + * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but + * mmap() is not. */ + if (geteuid() != 0) + log_debug("Skipping PR_SET_MM, as we don't have privileges."); + else if (mm_size < l+1) { size_t nn_size; char *nn; - /* Let's not bother with this if we don't have euid == 0. Strictly speaking if people do weird stuff - * with capabilities this could work even for euid != 0, but our own code generally doesn't do that, - * hence let's use this as quick bypass check, to avoid calling mmap() if PR_SET_MM_ARG_START fails - * with EPERM later on anyway. After all geteuid() is dead cheap to call, but mmap() is not. */ - if (geteuid() != 0) { - log_debug("Skipping PR_SET_MM_ARG_START, as we don't have privileges."); - goto use_saved_argv; - } - nn_size = PAGE_ALIGN(l+1); nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); if (nn == MAP_FAILED) { @@ -351,9 +350,14 @@ int rename_process(const char name[]) { mm = nn; mm_size = nn_size; - } else + } else { strncpy(mm, name, mm_size); + /* Update the end pointer, continuing regardless of any failure. */ + if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0) + log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); + } + use_saved_argv: /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if * it still looks here */ @@ -388,7 +392,7 @@ int is_kernel_thread(pid_t pid) { bool eof; FILE *f; - if (pid == 0 || pid == 1) /* pid 1, and we ourselves certainly aren't a kernel thread */ + if (IN_SET(pid, 0, 1) || pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */ return 0; assert(pid > 1); @@ -471,6 +475,9 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) { assert(field); assert(uid); + if (pid < 0) + return -EINVAL; + p = procfs_file_alloca(pid, "status"); f = fopen(p, "re"); if (!f) { @@ -498,10 +505,22 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) { } int get_process_uid(pid_t pid, uid_t *uid) { + + if (pid == 0 || pid == getpid_cached()) { + *uid = getuid(); + return 0; + } + return get_process_id(pid, "Uid:", uid); } int get_process_gid(pid_t pid, gid_t *gid) { + + if (pid == 0 || pid == getpid_cached()) { + *gid = getgid(); + return 0; + } + assert_cc(sizeof(uid_t) == sizeof(gid_t)); return get_process_id(pid, "Gid:", gid); } @@ -577,7 +596,7 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) { assert(pid >= 0); assert(_ppid); - if (pid == 0) { + if (pid == 0 || pid == getpid_cached()) { *_ppid = getppid(); return 0; } @@ -669,8 +688,7 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_cod log_debug("%s succeeded.", name); return status.si_status; - } else if (status.si_code == CLD_KILLED || - status.si_code == CLD_DUMPED) { + } else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) { log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status)); return -EPROTO; @@ -775,6 +793,9 @@ bool pid_is_unwaited(pid_t pid) { if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */ return true; + if (pid == getpid_cached()) + return true; + if (kill(pid, 0) >= 0) return true; @@ -792,8 +813,11 @@ bool pid_is_alive(pid_t pid) { if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */ return true; + if (pid == getpid_cached()) + return true; + r = get_process_state(pid); - if (r == -ESRCH || r == 'Z') + if (IN_SET(r, -ESRCH, 'Z')) return false; return true; @@ -803,7 +827,10 @@ int pid_from_same_root_fs(pid_t pid) { const char *root; if (pid < 0) - return 0; + return false; + + if (pid == 0 || pid == getpid_cached()) + return true; root = procfs_file_alloca(pid, "root"); @@ -814,7 +841,7 @@ bool is_main_thread(void) { static thread_local int cached = 0; if (_unlikely_(cached == 0)) - cached = getpid() == gettid() ? 1 : -1; + cached = getpid_cached() == gettid() ? 1 : -1; return cached > 0; } @@ -876,9 +903,50 @@ const char* personality_to_string(unsigned long p) { return architecture_to_string(architecture); } +int safe_personality(unsigned long p) { + int ret; + + /* So here's the deal, personality() is weirdly defined by glibc. In some cases it returns a failure via errno, + * and in others as negative return value containing an errno-like value. Let's work around this: this is a + * wrapper that uses errno if it is set, and uses the return value otherwise. And then it sets both errno and + * the return value indicating the same issue, so that we are definitely on the safe side. + * + * See https://github.com/systemd/systemd/issues/6737 */ + + errno = 0; + ret = personality(p); + if (ret < 0) { + if (errno != 0) + return -errno; + + errno = -ret; + } + + return ret; +} + +int opinionated_personality(unsigned long *ret) { + int current; + + /* Returns the current personality, or PERSONALITY_INVALID if we can't determine it. This function is a bit + * opinionated though, and ignores all the finer-grained bits and exotic personalities, only distinguishing the + * two most relevant personalities: PER_LINUX and PER_LINUX32. */ + + current = safe_personality(PERSONALITY_INVALID); + if (current < 0) + return current; + + if (((unsigned long) current & 0xffff) == PER_LINUX32) + *ret = PER_LINUX32; + else + *ret = PER_LINUX; + + return 0; +} + void valgrind_summary_hack(void) { -#ifdef HAVE_VALGRIND_VALGRIND_H - if (getpid() == 1 && RUNNING_ON_VALGRIND) { +#if HAVE_VALGRIND_VALGRIND_H + if (getpid_cached() == 1 && RUNNING_ON_VALGRIND) { pid_t pid; pid = raw_clone(SIGCHLD); if (pid < 0) @@ -922,6 +990,68 @@ int ioprio_parse_priority(const char *s, int *ret) { return 0; } +/* The cached PID, possible values: + * + * == UNSET [0] → cache not initialized yet + * == BUSY [-1] → some thread is initializing it at the moment + * any other → the cached PID + */ + +#define CACHED_PID_UNSET ((pid_t) 0) +#define CACHED_PID_BUSY ((pid_t) -1) + +static pid_t cached_pid = CACHED_PID_UNSET; + +static void reset_cached_pid(void) { + /* Invoked in the child after a fork(), i.e. at the first moment the PID changed */ + cached_pid = CACHED_PID_UNSET; +} + +/* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc + * headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against + * libpthread, as it is part of glibc anyway. */ +extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void * __dso_handle); +extern void* __dso_handle __attribute__ ((__weak__)); + +pid_t getpid_cached(void) { + pid_t current_value; + + /* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a + * system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally + * cached. Starting with 2.24 getpid() started to become prohibitively expensive when used for detecting when + * objects were used across fork()s. With this caching the old behaviour is somewhat restored. + * + * https://bugzilla.redhat.com/show_bug.cgi?id=1443976 + * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e + */ + + current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY); + + switch (current_value) { + + case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */ + pid_t new_pid; + + new_pid = getpid(); + + if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) { + /* OOM? Let's try again later */ + cached_pid = CACHED_PID_UNSET; + return new_pid; + } + + cached_pid = new_pid; + return new_pid; + } + + case CACHED_PID_BUSY: /* Somebody else is currently initializing */ + return getpid(); + + default: /* Properly initialized */ + return current_value; + } +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 28d8d7499a..82af2f9181 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -20,6 +20,7 @@ ***/ #include <alloca.h> +#include <sched.h> #include <signal.h> #include <stdbool.h> #include <stddef.h> @@ -90,6 +91,9 @@ bool oom_score_adjust_is_valid(int oa); unsigned long personality_from_string(const char *p); const char *personality_to_string(unsigned long); +int safe_personality(unsigned long p); +int opinionated_personality(unsigned long *ret); + int ioprio_class_to_string_alloc(int i, char **s); int ioprio_class_from_string(const char *s); @@ -110,6 +114,14 @@ static inline bool nice_is_valid(int n) { return n >= PRIO_MIN && n < PRIO_MAX; } +static inline bool sched_policy_is_valid(int i) { + return IN_SET(i, SCHED_OTHER, SCHED_BATCH, SCHED_IDLE, SCHED_FIFO, SCHED_RR); +} + +static inline bool sched_priority_is_valid(int i) { + return i >= 0 && i <= sched_get_priority_max(SCHED_RR); +} + static inline bool ioprio_class_is_valid(int i) { return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE); } @@ -118,4 +130,10 @@ static inline bool ioprio_priority_is_valid(int i) { return i >= 0 && i < IOPRIO_BE_NR; } +static inline bool pid_is_valid(pid_t p) { + return p > 0; +} + int ioprio_parse_priority(const char *s, int *ret); + +pid_t getpid_cached(void); diff --git a/src/basic/random-util.c b/src/basic/random-util.c index 810eeab4d5..146c8f55ed 100644 --- a/src/basic/random-util.c +++ b/src/basic/random-util.c @@ -26,11 +26,11 @@ #include <linux/random.h> #include <stdint.h> -#ifdef HAVE_SYS_AUXV_H +#if HAVE_SYS_AUXV_H # include <sys/auxv.h> #endif -#ifdef USE_SYS_RANDOM_H +#if USE_SYS_RANDOM_H # include <sys/random.h> #else # include <linux/random.h> @@ -64,7 +64,7 @@ int acquire_random_bytes(void *p, size_t n, bool high_quality_required) { if ((size_t) r == n) return 0; if (!high_quality_required) { - /* Fill in the remaing bytes using pseudorandom values */ + /* Fill in the remaining bytes using pseudorandom values */ pseudorandom_bytes((uint8_t*) p + r, n - r); return 0; } @@ -100,14 +100,14 @@ int acquire_random_bytes(void *p, size_t n, bool high_quality_required) { void initialize_srand(void) { static bool srand_called = false; unsigned x; -#ifdef HAVE_SYS_AUXV_H +#if HAVE_SYS_AUXV_H void *auxv; #endif if (srand_called) return; -#ifdef HAVE_SYS_AUXV_H +#if HAVE_SYS_AUXV_H /* The kernel provides us with 16 bytes of entropy in auxv, so let's * try to make use of that to seed the pseudo-random generator. It's * better than nothing... */ diff --git a/src/basic/replace-var.c b/src/basic/replace-var.c index 0d21423a9c..d2642812e7 100644 --- a/src/basic/replace-var.c +++ b/src/basic/replace-var.c @@ -54,7 +54,7 @@ static int get_variable(const char *b, char **r) { return 1; } -char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata) { +char *replace_var(const char *text, char *(*lookup)(const char *variable, void *userdata), void *userdata) { char *r, *t; const char *f; size_t l; diff --git a/src/basic/replace-var.h b/src/basic/replace-var.h index 78412910b2..31eb057803 100644 --- a/src/basic/replace-var.h +++ b/src/basic/replace-var.h @@ -19,4 +19,4 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata); +char *replace_var(const char *text, char *(*lookup)(const char *variable, void *userdata), void *userdata); diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c index ca834df621..5c41429f01 100644 --- a/src/basic/rlimit-util.c +++ b/src/basic/rlimit-util.c @@ -42,7 +42,8 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) { /* So we failed to set the desired setrlimit, then let's try * to get as close as we can */ - assert_se(getrlimit(resource, &highest) == 0); + if (getrlimit(resource, &highest) < 0) + return -errno; fixed.rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max); fixed.rlim_max = MIN(rlim->rlim_max, highest.rlim_max); diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c index 3f80ed263a..0bbafb4cd7 100644 --- a/src/basic/rm-rf.c +++ b/src/basic/rm-rf.c @@ -132,7 +132,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); if (r < 0) { - if (r != -ENOTTY && r != -EINVAL) { + if (!IN_SET(r, -ENOTTY, -EINVAL)) { if (ret == 0) ret = r; @@ -193,7 +193,7 @@ int rm_rf(const char *path, RemoveFlags flags) { if (r >= 0) return r; - if (r != -ENOTTY && r != -EINVAL && r != -ENOTDIR) + if (!IN_SET(r, -ENOTTY, -EINVAL, -ENOTDIR)) return r; /* Not btrfs or not a subvolume */ @@ -202,7 +202,7 @@ int rm_rf(const char *path, RemoveFlags flags) { fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); if (fd < 0) { - if (errno != ENOTDIR && errno != ELOOP) + if (!IN_SET(errno, ENOTDIR, ELOOP)) return -errno; if (!(flags & REMOVE_PHYSICAL)) { diff --git a/src/basic/securebits-util.c b/src/basic/securebits-util.c new file mode 100644 index 0000000000..011ec36af4 --- /dev/null +++ b/src/basic/securebits-util.c @@ -0,0 +1,84 @@ +/*** + This file is part of systemd. + + Copyright 2017 Yu Watanabe + + 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 <errno.h> + +#include "alloc-util.h" +#include "extract-word.h" +#include "securebits.h" +#include "securebits-util.h" +#include "string-util.h" + +int secure_bits_to_string_alloc(int i, char **s) { + _cleanup_free_ char *str = NULL; + size_t len; + int r; + + assert(s); + + r = asprintf(&str, "%s%s%s%s%s%s", + (i & (1 << SECURE_KEEP_CAPS)) ? "keep-caps " : "", + (i & (1 << SECURE_KEEP_CAPS_LOCKED)) ? "keep-caps-locked " : "", + (i & (1 << SECURE_NO_SETUID_FIXUP)) ? "no-setuid-fixup " : "", + (i & (1 << SECURE_NO_SETUID_FIXUP_LOCKED)) ? "no-setuid-fixup-locked " : "", + (i & (1 << SECURE_NOROOT)) ? "noroot " : "", + (i & (1 << SECURE_NOROOT_LOCKED)) ? "noroot-locked " : ""); + if (r < 0) + return -ENOMEM; + + len = strlen(str); + if (len != 0) + str[len - 1] = '\0'; + + *s = str; + str = NULL; + + return 0; +} + +int secure_bits_from_string(const char *s) { + int secure_bits = 0; + const char *p; + int r; + + for (p = s;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r == -ENOMEM) + return r; + if (r <= 0) + break; + + if (streq(word, "keep-caps")) + secure_bits |= 1 << SECURE_KEEP_CAPS; + else if (streq(word, "keep-caps-locked")) + secure_bits |= 1 << SECURE_KEEP_CAPS_LOCKED; + else if (streq(word, "no-setuid-fixup")) + secure_bits |= 1 << SECURE_NO_SETUID_FIXUP; + else if (streq(word, "no-setuid-fixup-locked")) + secure_bits |= 1 << SECURE_NO_SETUID_FIXUP_LOCKED; + else if (streq(word, "noroot")) + secure_bits |= 1 << SECURE_NOROOT; + else if (streq(word, "noroot-locked")) + secure_bits |= 1 << SECURE_NOROOT_LOCKED; + } + + return secure_bits; +} diff --git a/src/basic/securebits-util.h b/src/basic/securebits-util.h new file mode 100644 index 0000000000..b4d970c366 --- /dev/null +++ b/src/basic/securebits-util.h @@ -0,0 +1,28 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2017 Yu Watanabe + + 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 "securebits.h" + +int secure_bits_to_string_alloc(int i, char **s); +int secure_bits_from_string(const char *s); +static inline bool secure_bits_is_valid(int i) { + return ((SECURE_ALL_BITS | SECURE_ALL_LOCKS) & i) == i; +} diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c index 139e6e21e3..93bcdb2160 100644 --- a/src/basic/selinux-util.c +++ b/src/basic/selinux-util.c @@ -26,7 +26,7 @@ #include <sys/un.h> #include <syslog.h> -#ifdef HAVE_SELINUX +#if HAVE_SELINUX #include <selinux/context.h> #include <selinux/label.h> #include <selinux/selinux.h> @@ -40,7 +40,7 @@ #include "time-util.h" #include "util.h" -#ifdef HAVE_SELINUX +#if HAVE_SELINUX DEFINE_TRIVIAL_CLEANUP_FUNC(char*, freecon); DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free); @@ -54,7 +54,7 @@ static struct selabel_handle *label_hnd = NULL; #endif bool mac_selinux_use(void) { -#ifdef HAVE_SELINUX +#if HAVE_SELINUX if (cached_use < 0) cached_use = is_selinux_enabled() > 0; @@ -65,7 +65,7 @@ bool mac_selinux_use(void) { } void mac_selinux_retest(void) { -#ifdef HAVE_SELINUX +#if HAVE_SELINUX cached_use = -1; #endif } @@ -73,7 +73,7 @@ void mac_selinux_retest(void) { int mac_selinux_init(void) { int r = 0; -#ifdef HAVE_SELINUX +#if HAVE_SELINUX usec_t before_timestamp, after_timestamp; struct mallinfo before_mallinfo, after_mallinfo; @@ -110,7 +110,7 @@ int mac_selinux_init(void) { void mac_selinux_finish(void) { -#ifdef HAVE_SELINUX +#if HAVE_SELINUX if (!label_hnd) return; @@ -121,7 +121,7 @@ void mac_selinux_finish(void) { int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { -#ifdef HAVE_SELINUX +#if HAVE_SELINUX struct stat st; int r; @@ -169,7 +169,7 @@ int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { int mac_selinux_apply(const char *path, const char *label) { -#ifdef HAVE_SELINUX +#if HAVE_SELINUX if (!mac_selinux_use()) return 0; @@ -188,7 +188,7 @@ int mac_selinux_apply(const char *path, const char *label) { int mac_selinux_get_create_label_from_exe(const char *exe, char **label) { int r = -EOPNOTSUPP; -#ifdef HAVE_SELINUX +#if HAVE_SELINUX _cleanup_freecon_ char *mycon = NULL, *fcon = NULL; security_class_t sclass; @@ -220,7 +220,7 @@ int mac_selinux_get_our_label(char **label) { assert(label); -#ifdef HAVE_SELINUX +#if HAVE_SELINUX if (!mac_selinux_use()) return -EOPNOTSUPP; @@ -235,7 +235,7 @@ int mac_selinux_get_our_label(char **label) { int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) { int r = -EOPNOTSUPP; -#ifdef HAVE_SELINUX +#if HAVE_SELINUX _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL; _cleanup_context_free_ context_t pcon = NULL, bcon = NULL; security_class_t sclass; @@ -296,7 +296,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * char* mac_selinux_free(char *label) { -#ifdef HAVE_SELINUX +#if HAVE_SELINUX if (!label) return NULL; @@ -312,7 +312,7 @@ char* mac_selinux_free(char *label) { int mac_selinux_create_file_prepare(const char *path, mode_t mode) { -#ifdef HAVE_SELINUX +#if HAVE_SELINUX _cleanup_freecon_ char *filecon = NULL; int r; @@ -355,7 +355,7 @@ int mac_selinux_create_file_prepare(const char *path, mode_t mode) { void mac_selinux_create_file_clear(void) { -#ifdef HAVE_SELINUX +#if HAVE_SELINUX PROTECT_ERRNO; if (!mac_selinux_use()) @@ -367,7 +367,7 @@ void mac_selinux_create_file_clear(void) { int mac_selinux_create_socket_prepare(const char *label) { -#ifdef HAVE_SELINUX +#if HAVE_SELINUX if (!mac_selinux_use()) return 0; @@ -386,7 +386,7 @@ int mac_selinux_create_socket_prepare(const char *label) { void mac_selinux_create_socket_clear(void) { -#ifdef HAVE_SELINUX +#if HAVE_SELINUX PROTECT_ERRNO; if (!mac_selinux_use()) @@ -400,7 +400,7 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { /* Binds a socket and label its file system object according to the SELinux policy */ -#ifdef HAVE_SELINUX +#if HAVE_SELINUX _cleanup_freecon_ char *fcon = NULL; const struct sockaddr_un *un; bool context_changed = false; diff --git a/src/basic/set.c b/src/basic/set.c new file mode 100644 index 0000000000..fd398b8212 --- /dev/null +++ b/src/basic/set.c @@ -0,0 +1,61 @@ +/*** + This file is part of systemd. + + Copyright 2017 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 "set.h" + +int set_make(Set **ret, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS, void *add, ...) { + _cleanup_set_free_ Set *s = NULL; + int r; + + assert(ret); + + s = set_new(hash_ops HASHMAP_DEBUG_PASS_ARGS); + if (!s) + return -ENOMEM; + + if (add) { + va_list ap; + + r = set_put(s, add); + if (r < 0) + return r; + + va_start(ap, add); + + for(;;) { + void *arg = va_arg(ap, void*); + + if (!arg) + break; + + r = set_put(s, arg); + if (r < 0) { + va_end(ap); + return r; + } + } + + va_end(ap); + } + + *ret = s; + s = NULL; + + return 0; +} diff --git a/src/basic/set.h b/src/basic/set.h index a5f8beb0c4..12d0fda1ca 100644 --- a/src/basic/set.h +++ b/src/basic/set.h @@ -136,3 +136,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); #define _cleanup_set_free_ _cleanup_(set_freep) #define _cleanup_set_free_free_ _cleanup_(set_free_freep) + +int set_make(Set **ret, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS, void *add, ...); diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c index 280b5c3251..df6b742fde 100644 --- a/src/basic/signal-util.c +++ b/src/basic/signal-util.c @@ -38,7 +38,7 @@ int reset_all_signal_handlers(void) { for (sig = 1; sig < _NSIG; sig++) { /* These two cannot be caught... */ - if (sig == SIGKILL || sig == SIGSTOP) + if (IN_SET(sig, SIGKILL, SIGSTOP)) continue; /* On Linux the first two RT signals are reserved by diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c index 3a3df987df..3dcf150c59 100644 --- a/src/basic/smack-util.c +++ b/src/basic/smack-util.c @@ -35,7 +35,7 @@ #include "string-table.h" #include "xattr-util.h" -#ifdef HAVE_SMACK +#if ENABLE_SMACK bool mac_smack_use(void) { static int cached_use = -1; diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c index 6d1dc83874..6e7cdaac63 100644 --- a/src/basic/socket-label.c +++ b/src/basic/socket-label.c @@ -83,7 +83,7 @@ int socket_address_listen( return -errno; } - if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) { + if (IN_SET(socket_address_family(a), AF_INET, AF_INET6)) { if (bind_to_device) if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0) return -errno; diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 016e64aa03..29c779552d 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -48,7 +48,7 @@ #include "utf8.h" #include "util.h" -#ifdef ENABLE_IDN +#if ENABLE_IDN # define IDN_FLAGS (NI_IDN|NI_IDN_USE_STD3_ASCII_RULES) #else # define IDN_FLAGS 0 @@ -268,7 +268,7 @@ int socket_address_verify(const SocketAddress *a) { if (a->sockaddr.in.sin_port == 0) return -EINVAL; - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM) + if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) return -EINVAL; return 0; @@ -280,7 +280,7 @@ int socket_address_verify(const SocketAddress *a) { if (a->sockaddr.in6.sin6_port == 0) return -EINVAL; - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM) + if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) return -EINVAL; return 0; @@ -304,7 +304,7 @@ int socket_address_verify(const SocketAddress *a) { } } - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET) + if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET)) return -EINVAL; return 0; @@ -314,7 +314,7 @@ int socket_address_verify(const SocketAddress *a) { if (a->size != sizeof(struct sockaddr_nl)) return -EINVAL; - if (a->type != SOCK_RAW && a->type != SOCK_DGRAM) + if (!IN_SET(a->type, SOCK_RAW, SOCK_DGRAM)) return -EINVAL; return 0; @@ -323,7 +323,7 @@ int socket_address_verify(const SocketAddress *a) { if (a->size != sizeof(struct sockaddr_vm)) return -EINVAL; - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM) + if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) return -EINVAL; return 0; @@ -364,8 +364,7 @@ bool socket_address_can_accept(const SocketAddress *a) { assert(a); return - a->type == SOCK_STREAM || - a->type == SOCK_SEQPACKET; + IN_SET(a->type, SOCK_STREAM, SOCK_SEQPACKET); } bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { @@ -793,7 +792,8 @@ static const char* const netlink_family_table[] = { [NETLINK_KOBJECT_UEVENT] = "kobject-uevent", [NETLINK_GENERIC] = "generic", [NETLINK_SCSITRANSPORT] = "scsitransport", - [NETLINK_ECRYPTFS] = "ecryptfs" + [NETLINK_ECRYPTFS] = "ecryptfs", + [NETLINK_RDMA] = "rdma", }; DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX); @@ -892,7 +892,7 @@ bool ifname_valid(const char *p) { if ((unsigned char) *p <= 32U) return false; - if (*p == ':' || *p == '/') + if (IN_SET(*p, ':', '/')) return false; numeric = numeric && (*p >= '0' && *p <= '9'); @@ -1080,7 +1080,7 @@ ssize_t next_datagram_size_fd(int fd) { l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC); if (l < 0) { - if (errno == EOPNOTSUPP || errno == EFAULT) + if (IN_SET(errno, EOPNOTSUPP, EFAULT)) goto fallback; return -errno; diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 73c3a339fc..43edc05c63 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -27,6 +27,7 @@ #include <sys/types.h> #include <sys/un.h> #include <linux/netlink.h> +#include <linux/if_infiniband.h> #include <linux/if_packet.h> #include "macro.h" @@ -42,6 +43,8 @@ union sockaddr_union { struct sockaddr_storage storage; struct sockaddr_ll ll; struct sockaddr_vm vm; + /* Ensure there is enough space to store Infiniband addresses */ + uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)]; }; typedef struct SocketAddress { @@ -147,6 +150,23 @@ int flush_accept(int fd); struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length); +/* + * Certain hardware address types (e.g Infiniband) do not fit into sll_addr + * (8 bytes) and run over the structure. This macro returns the correct size that + * must be passed to kernel. + */ +#define SOCKADDR_LL_LEN(sa) \ + ({ \ + const struct sockaddr_ll *_sa = &(sa); \ + size_t _mac_len = sizeof(_sa->sll_addr); \ + assert(_sa->sll_family == AF_PACKET); \ + if (be16toh(_sa->sll_hatype) == ARPHRD_ETHER) \ + _mac_len = MAX(_mac_len, (size_t) ETH_ALEN); \ + if (be16toh(_sa->sll_hatype) == ARPHRD_INFINIBAND) \ + _mac_len = MAX(_mac_len, (size_t) INFINIBAND_ALEN); \ + offsetof(struct sockaddr_ll, sll_addr) + _mac_len; \ + }) + /* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */ #define SOCKADDR_UN_LEN(sa) \ ({ \ diff --git a/src/basic/special.h b/src/basic/special.h index feb8e5fe21..ddd4e84019 100644 --- a/src/basic/special.h +++ b/src/basic/special.h @@ -46,7 +46,6 @@ /* Early boot targets */ #define SPECIAL_SYSINIT_TARGET "sysinit.target" #define SPECIAL_SOCKETS_TARGET "sockets.target" -#define SPECIAL_BUSNAMES_TARGET "busnames.target" #define SPECIAL_TIMERS_TARGET "timers.target" #define SPECIAL_PATHS_TARGET "paths.target" #define SPECIAL_LOCAL_FS_TARGET "local-fs.target" diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 9d2f4bc8f9..3179fba3ba 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -215,7 +215,7 @@ char *strnappend(const char *s, const char *suffix, size_t b) { } char *strappend(const char *s, const char *suffix) { - return strnappend(s, suffix, suffix ? strlen(suffix) : 0); + return strnappend(s, suffix, strlen_ptr(suffix)); } char *strjoin_real(const char *x, ...) { @@ -542,7 +542,7 @@ char *ellipsize(const char *s, size_t length, unsigned percent) { return ellipsize_mem(s, strlen(s), length, percent); } -bool nulstr_contains(const char*nulstr, const char *needle) { +bool nulstr_contains(const char *nulstr, const char *needle) { const char *i; if (!nulstr) @@ -558,7 +558,7 @@ bool nulstr_contains(const char*nulstr, const char *needle) { char* strshorten(char *s, size_t l) { assert(s); - if (l < strlen(s)) + if (strnlen(s, l+1) > l) s[l] = 0; return s; @@ -635,6 +635,11 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { if (!f) return NULL; + /* Note we use the _unlocked() stdio variants on f for performance + * reasons. It's safe to do so since we created f here and it + * doesn't leave our scope. + */ + for (i = *ibuf; i < *ibuf + isz + 1; i++) { switch (state) { @@ -645,21 +650,21 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { else if (*i == '\x1B') state = STATE_ESCAPE; else if (*i == '\t') - fputs(" ", f); + fputs_unlocked(" ", f); else - fputc(*i, f); + fputc_unlocked(*i, f); break; case STATE_ESCAPE: if (i >= *ibuf + isz) { /* EOT */ - fputc('\x1B', f); + fputc_unlocked('\x1B', f); break; } else if (*i == '[') { state = STATE_BRACKET; begin = i + 1; } else { - fputc('\x1B', f); - fputc(*i, f); + fputc_unlocked('\x1B', f); + fputc_unlocked(*i, f); state = STATE_OTHER; } @@ -668,9 +673,9 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { case STATE_BRACKET: if (i >= *ibuf + isz || /* EOT */ - (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) { - fputc('\x1B', f); - fputc('[', f); + (!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) { + fputc_unlocked('\x1B', f); + fputc_unlocked('[', f); state = STATE_OTHER; i = begin-1; } else if (*i == 'm') @@ -702,7 +707,7 @@ char *strextend(char **x, ...) { assert(x); - l = f = *x ? strlen(*x) : 0; + l = f = strlen_ptr(*x); va_start(ap, x); for (;;) { @@ -821,7 +826,7 @@ int free_and_strdup(char **p, const char *s) { return 1; } -#if !HAVE_DECL_EXPLICIT_BZERO +#if !HAVE_EXPLICIT_BZERO /* * Pointer to memset is volatile so that compiler must de-reference * the pointer and can't assume that it points to any function in diff --git a/src/basic/string-util.h b/src/basic/string-util.h index be44dedff4..4c94b182c1 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -120,7 +120,7 @@ char *strjoin_real(const char *x, ...) _sentinel_; ({ \ const char *_appendees_[] = { a, __VA_ARGS__ }; \ char *_d_, *_p_; \ - int _len_ = 0; \ + size_t _len_ = 0; \ unsigned _i_; \ for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ _len_ += strlen(_appendees_[_i_]); \ @@ -158,7 +158,7 @@ bool string_has_cc(const char *p, const char *ok) _pure_; char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent); char *ellipsize(const char *s, size_t length, unsigned percent); -bool nulstr_contains(const char*nulstr, const char *needle); +bool nulstr_contains(const char *nulstr, const char *needle); char* strshorten(char *s, size_t l); @@ -189,7 +189,7 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const return memmem(haystack, haystacklen, needle, needlelen); } -#if !HAVE_DECL_EXPLICIT_BZERO +#if !HAVE_EXPLICIT_BZERO void explicit_bzero(void *p, size_t l); #endif @@ -200,3 +200,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase); #define _cleanup_string_free_erase_ _cleanup_(string_free_erasep) bool string_is_safe(const char *p) _pure_; + +static inline size_t strlen_ptr(const char *s) { + if (!s) + return 0; + + return strlen(s); +} diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 9a8ef825c5..28c8c35fe0 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -55,6 +55,7 @@ #include "terminal-util.h" #include "time-util.h" #include "util.h" +#include "path-util.h" static volatile unsigned cached_columns = 0; static volatile unsigned cached_lines = 0; @@ -244,6 +245,7 @@ int ask_string(char **ret, const char *text, ...) { int reset_terminal_fd(int fd, bool switch_to_text) { struct termios termios; + _cleanup_free_ char *utf8 = NULL; int r = 0; /* Set terminal to some sane defaults */ @@ -261,8 +263,8 @@ int reset_terminal_fd(int fd, bool switch_to_text) { if (switch_to_text) (void) ioctl(fd, KDSETMODE, KD_TEXT); - /* Enable console unicode mode */ - (void) ioctl(fd, KDSKBMODE, K_UNICODE); + /* Set default keyboard mode */ + (void) vt_reset_keyboard(fd); if (tcgetattr(fd, &termios) < 0) { r = -errno; @@ -474,7 +476,7 @@ int acquire_terminal( l = read(notify, &buffer, sizeof(buffer)); if (l < 0) { - if (errno == EINTR || errno == EAGAIN) + if (IN_SET(errno, EINTR, EAGAIN)) continue; r = -errno; @@ -556,6 +558,7 @@ int terminal_vhangup(const char *name) { int vt_disallocate(const char *name) { _cleanup_close_ int fd = -1; + const char *e, *n; unsigned u; int r; @@ -563,7 +566,8 @@ int vt_disallocate(const char *name) { * (i.e. because it is the active one), at least clear it * entirely (including the scrollback buffer) */ - if (!startswith(name, "/dev/")) + e = path_startswith(name, "/dev/"); + if (!e) return -EINVAL; if (!tty_is_vc(name)) { @@ -582,10 +586,11 @@ int vt_disallocate(const char *name) { return 0; } - if (!startswith(name, "/dev/tty")) + n = startswith(e, "tty"); + if (!n) return -EINVAL; - r = safe_atou(name+8, &u); + r = safe_atou(n, &u); if (r < 0) return r; @@ -649,10 +654,7 @@ bool tty_is_vc(const char *tty) { bool tty_is_console(const char *tty) { assert(tty); - if (startswith(tty, "/dev/")) - tty += 5; - - return streq(tty, "console"); + return streq(skip_dev_prefix(tty), "console"); } int vtnr_from_tty(const char *tty) { @@ -660,8 +662,7 @@ int vtnr_from_tty(const char *tty) { assert(tty); - if (startswith(tty, "/dev/")) - tty += 5; + tty = skip_dev_prefix(tty); if (!startswith(tty, "tty") ) return -EINVAL; @@ -775,8 +776,7 @@ bool tty_is_vc_resolve(const char *tty) { assert(tty); - if (startswith(tty, "/dev/")) - tty += 5; + tty = skip_dev_prefix(tty); if (streq(tty, "console")) { tty = resolve_dev_console(&active); @@ -918,11 +918,9 @@ int getttyname_malloc(int fd, char **ret) { r = ttyname_r(fd, path, sizeof(path)); if (r == 0) { - const char *p; char *c; - p = startswith(path, "/dev/"); - c = strdup(p ?: path); + c = strdup(skip_dev_prefix(path)); if (!c) return -ENOMEM; @@ -1220,7 +1218,7 @@ bool colors_enabled(void) { val = getenv_bool("SYSTEMD_COLORS"); if (val >= 0) enabled = val; - else if (getpid() == 1) + else if (getpid_cached() == 1) /* PID1 outputs to the console without holding it open all the time */ enabled = !getenv_terminal_is_dumb(); else @@ -1229,3 +1227,44 @@ bool colors_enabled(void) { return enabled; } + +bool underline_enabled(void) { + static int enabled = -1; + + if (enabled < 0) { + + /* The Linux console doesn't support underlining, turn it off, but only there. */ + + if (!colors_enabled()) + enabled = false; + else + enabled = !streq_ptr(getenv("TERM"), "linux"); + } + + return enabled; +} + +int vt_default_utf8(void) { + _cleanup_free_ char *b = NULL; + int r; + + /* Read the default VT UTF8 setting from the kernel */ + + r = read_one_line_file("/sys/module/vt/parameters/default_utf8", &b); + if (r < 0) + return r; + + return parse_boolean(b); +} + +int vt_reset_keyboard(int fd) { + int kb; + + /* If we can't read the default, then default to unicode. It's 2017 after all. */ + kb = vt_default_utf8() != 0 ? K_UNICODE : K_XLATE; + + if (ioctl(fd, KDSKBMODE, kb) < 0) + return -errno; + + return 0; +} diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index b862bfaf05..c3045eb09d 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -86,6 +86,7 @@ void columns_lines_cache_reset(int _unused_ signum); bool on_tty(void); bool terminal_is_dumb(void); bool colors_enabled(void); +bool underline_enabled(void); #define DEFINE_ANSI_FUNC(name, NAME) \ static inline const char *ansi_##name(void) { \ @@ -93,19 +94,28 @@ bool colors_enabled(void); } \ struct __useless_struct_to_allow_trailing_semicolon__ -DEFINE_ANSI_FUNC(underline, UNDERLINE); +#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME, REPLACEMENT) \ + static inline const char *ansi_##name(void) { \ + return underline_enabled() ? ANSI_##NAME : \ + colors_enabled() ? ANSI_##REPLACEMENT : ""; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + + DEFINE_ANSI_FUNC(highlight, HIGHLIGHT); -DEFINE_ANSI_FUNC(highlight_underline, HIGHLIGHT_UNDERLINE); DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED); DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN); DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW); DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE); -DEFINE_ANSI_FUNC(highlight_red_underline, HIGHLIGHT_RED_UNDERLINE); -DEFINE_ANSI_FUNC(highlight_green_underline, HIGHLIGHT_GREEN_UNDERLINE); -DEFINE_ANSI_FUNC(highlight_yellow_underline, HIGHLIGHT_YELLOW_UNDERLINE); -DEFINE_ANSI_FUNC(highlight_blue_underline, HIGHLIGHT_BLUE_UNDERLINE); DEFINE_ANSI_FUNC(normal, NORMAL); +DEFINE_ANSI_FUNC_UNDERLINE(underline, UNDERLINE, NORMAL); +DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT_UNDERLINE, HIGHLIGHT); +DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED_UNDERLINE, HIGHLIGHT_RED); +DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN_UNDERLINE, HIGHLIGHT_GREEN); +DEFINE_ANSI_FUNC_UNDERLINE(highlight_yellow_underline, HIGHLIGHT_YELLOW_UNDERLINE, HIGHLIGHT_YELLOW); +DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue_underline, HIGHLIGHT_BLUE_UNDERLINE, HIGHLIGHT_BLUE); + int get_ctty_devnr(pid_t pid, dev_t *d); int get_ctty(pid_t, dev_t *_devnr, char **r); @@ -117,3 +127,6 @@ int ptsname_namespace(int pty, char **ret); int openpt_in_namespace(pid_t pid, int flags); int open_terminal_in_namespace(pid_t pid, const char *name, int mode); + +int vt_default_utf8(void); +int vt_reset_keyboard(int fd); diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 68ba86f6a5..f7f5e614f2 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -21,6 +21,7 @@ #include <limits.h> #include <stdlib.h> #include <string.h> +#include <sys/mman.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/timerfd.h> @@ -596,7 +597,7 @@ int timestamp_deserialize(const char *value, usec_t *timestamp) { return r; } -int parse_timestamp(const char *t, usec_t *usec) { +static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { static const struct { const char *name; const int nr; @@ -617,7 +618,7 @@ int parse_timestamp(const char *t, usec_t *usec) { { "Sat", 6 }, }; - const char *k, *utc, *tzn = NULL; + const char *k, *utc = NULL, *tzn = NULL; struct tm tm, copy; time_t x; usec_t x_usec, plus = 0, minus = 0, ret; @@ -645,84 +646,86 @@ int parse_timestamp(const char *t, usec_t *usec) { assert(t); assert(usec); - if (t[0] == '@') + if (t[0] == '@' && !with_tz) return parse_sec(t + 1, usec); ret = now(CLOCK_REALTIME); - if (streq(t, "now")) - goto finish; + if (!with_tz) { + if (streq(t, "now")) + goto finish; - else if (t[0] == '+') { - r = parse_sec(t+1, &plus); - if (r < 0) - return r; + else if (t[0] == '+') { + r = parse_sec(t+1, &plus); + if (r < 0) + return r; - goto finish; + goto finish; - } else if (t[0] == '-') { - r = parse_sec(t+1, &minus); - if (r < 0) - return r; + } else if (t[0] == '-') { + r = parse_sec(t+1, &minus); + if (r < 0) + return r; - goto finish; + goto finish; - } else if ((k = endswith(t, " ago"))) { - t = strndupa(t, k - t); + } else if ((k = endswith(t, " ago"))) { + t = strndupa(t, k - t); - r = parse_sec(t, &minus); - if (r < 0) - return r; + r = parse_sec(t, &minus); + if (r < 0) + return r; - goto finish; + goto finish; - } else if ((k = endswith(t, " left"))) { - t = strndupa(t, k - t); + } else if ((k = endswith(t, " left"))) { + t = strndupa(t, k - t); - r = parse_sec(t, &plus); - if (r < 0) - return r; + r = parse_sec(t, &plus); + if (r < 0) + return r; - goto finish; - } + goto finish; + } - /* See if the timestamp is suffixed with UTC */ - utc = endswith_no_case(t, " UTC"); - if (utc) - t = strndupa(t, utc - t); - else { - const char *e = NULL; - int j; + /* See if the timestamp is suffixed with UTC */ + utc = endswith_no_case(t, " UTC"); + if (utc) + t = strndupa(t, utc - t); + else { + const char *e = NULL; + int j; - tzset(); + tzset(); - /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only - * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because - * there are no nice APIs available to cover this. By accepting the local time zone strings, we make - * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't - * support arbitrary timezone specifications. */ + /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only + * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because + * there are no nice APIs available to cover this. By accepting the local time zone strings, we make + * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't + * support arbitrary timezone specifications. */ - for (j = 0; j <= 1; j++) { + for (j = 0; j <= 1; j++) { - if (isempty(tzname[j])) - continue; + if (isempty(tzname[j])) + continue; - e = endswith_no_case(t, tzname[j]); - if (!e) - continue; - if (e == t) - continue; - if (e[-1] != ' ') - continue; + e = endswith_no_case(t, tzname[j]); + if (!e) + continue; + if (e == t) + continue; + if (e[-1] != ' ') + continue; - break; - } + break; + } - if (IN_SET(j, 0, 1)) { - /* Found one of the two timezones specified. */ - t = strndupa(t, e - t - 1); - dst = j; - tzn = tzname[j]; + if (IN_SET(j, 0, 1)) { + /* Found one of the two timezones specified. */ + t = strndupa(t, e - t - 1); + dst = j; + tzn = tzname[j]; + } } } @@ -733,7 +736,7 @@ int parse_timestamp(const char *t, usec_t *usec) { return -EINVAL; tm.tm_isdst = dst; - if (tzn) + if (!with_tz && tzn) tm.tm_zone = tzn; if (streq(t, "today")) { @@ -846,11 +849,11 @@ parse_usec: } from_tm: - x = mktime_or_timegm(&tm, utc); - if (x < 0) + if (weekday >= 0 && tm.tm_wday != weekday) return -EINVAL; - if (weekday >= 0 && tm.tm_wday != weekday) + x = mktime_or_timegm(&tm, utc); + if (x < 0) return -EINVAL; ret = (usec_t) x * USEC_PER_SEC + x_usec; @@ -874,6 +877,75 @@ finish: return 0; } +typedef struct ParseTimestampResult { + usec_t usec; + int return_value; +} ParseTimestampResult; + +int parse_timestamp(const char *t, usec_t *usec) { + char *last_space, *tz = NULL; + ParseTimestampResult *shared, tmp; + int r; + pid_t pid; + + last_space = strrchr(t, ' '); + if (last_space != NULL && timezone_is_valid(last_space + 1)) + tz = last_space + 1; + + if (tz == NULL || endswith_no_case(t, " UTC")) + return parse_timestamp_impl(t, usec, false); + + shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); + if (shared == MAP_FAILED) + return negative_errno(); + + pid = fork(); + + if (pid == -1) { + int fork_errno = errno; + (void) munmap(shared, sizeof *shared); + return -fork_errno; + } + + if (pid == 0) { + bool with_tz = true; + + if (setenv("TZ", tz, 1) != 0) { + shared->return_value = negative_errno(); + _exit(EXIT_FAILURE); + } + + tzset(); + + /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation. + * Otherwise just cut it off */ + with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]); + + /*cut off the timezone if we dont need it*/ + if (with_tz) + t = strndupa(t, last_space - t); + + shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz); + + _exit(EXIT_SUCCESS); + } + + r = wait_for_terminate(pid, NULL); + if (r < 0) { + (void) munmap(shared, sizeof *shared); + return r; + } + + tmp = *shared; + if (munmap(shared, sizeof *shared) != 0) + return negative_errno(); + + if (tmp.return_value == 0) + *usec = tmp.usec; + + return tmp.return_value; +} + static char* extract_multiplier(char *p, usec_t *multiplier) { static const struct { const char *suffix; @@ -1010,7 +1082,11 @@ int parse_sec(const char *t, usec_t *usec) { } int parse_sec_fix_0(const char *t, usec_t *usec) { + assert(t); + assert(usec); + t += strspn(t, WHITESPACE); + if (streq(t, "0")) { *usec = USEC_INFINITY; return 0; @@ -1236,7 +1312,7 @@ bool timezone_is_valid(const char *name) { if (!(*p >= '0' && *p <= '9') && !(*p >= 'a' && *p <= 'z') && !(*p >= 'A' && *p <= 'Z') && - !(*p == '-' || *p == '_' || *p == '+' || *p == '/')) + !IN_SET(*p, '-', '_', '+', '/')) return false; if (*p == '/') { diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index 920ca0d9f5..f9c034c94b 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -305,7 +305,7 @@ static char *do_escape(const char *f, char *t) { for (; *f; f++) { if (*f == '/') *(t++) = '-'; - else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f)) + else if (IN_SET(*f, '-', '\\') || !strchr(VALID_CHARS, *f)) t = do_escape_char(*f, t); else *(t++) = *f; @@ -608,7 +608,6 @@ const char* unit_dbus_interface_from_type(UnitType t) { static const char *const table[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = "org.freedesktop.systemd1.Service", [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket", - [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName", [UNIT_TARGET] = "org.freedesktop.systemd1.Target", [UNIT_DEVICE] = "org.freedesktop.systemd1.Device", [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount", @@ -839,7 +838,6 @@ bool slice_name_is_valid(const char *name) { static const char* const unit_type_table[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = "service", [UNIT_SOCKET] = "socket", - [UNIT_BUSNAME] = "busname", [UNIT_TARGET] = "target", [UNIT_DEVICE] = "device", [UNIT_MOUNT] = "mount", @@ -884,19 +882,6 @@ static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState); -static const char* const busname_state_table[_BUSNAME_STATE_MAX] = { - [BUSNAME_DEAD] = "dead", - [BUSNAME_MAKING] = "making", - [BUSNAME_REGISTERED] = "registered", - [BUSNAME_LISTENING] = "listening", - [BUSNAME_RUNNING] = "running", - [BUSNAME_SIGTERM] = "sigterm", - [BUSNAME_SIGKILL] = "sigkill", - [BUSNAME_FAILED] = "failed", -}; - -DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState); - static const char* const device_state_table[_DEVICE_STATE_MAX] = { [DEVICE_DEAD] = "dead", [DEVICE_TENTATIVE] = "tentative", @@ -912,8 +897,6 @@ static const char* const mount_state_table[_MOUNT_STATE_MAX] = { [MOUNT_MOUNTED] = "mounted", [MOUNT_REMOUNTING] = "remounting", [MOUNT_UNMOUNTING] = "unmounting", - [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm", - [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill", [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm", [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill", [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm", @@ -995,8 +978,6 @@ static const char* const swap_state_table[_SWAP_STATE_MAX] = { [SWAP_ACTIVATING_DONE] = "activating-done", [SWAP_ACTIVE] = "active", [SWAP_DEACTIVATING] = "deactivating", - [SWAP_ACTIVATING_SIGTERM] = "activating-sigterm", - [SWAP_ACTIVATING_SIGKILL] = "activating-sigkill", [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm", [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill", [SWAP_FAILED] = "failed" diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h index 0f164a6aa9..15558b4fbd 100644 --- a/src/basic/unit-name.h +++ b/src/basic/unit-name.h @@ -28,7 +28,6 @@ typedef enum UnitType { UNIT_SERVICE = 0, UNIT_SOCKET, - UNIT_BUSNAME, UNIT_TARGET, UNIT_DEVICE, UNIT_MOUNT, @@ -73,19 +72,6 @@ typedef enum AutomountState { _AUTOMOUNT_STATE_INVALID = -1 } AutomountState; -typedef enum BusNameState { - BUSNAME_DEAD, - BUSNAME_MAKING, - BUSNAME_REGISTERED, - BUSNAME_LISTENING, - BUSNAME_RUNNING, - BUSNAME_SIGTERM, - BUSNAME_SIGKILL, - BUSNAME_FAILED, - _BUSNAME_STATE_MAX, - _BUSNAME_STATE_INVALID = -1 -} BusNameState; - /* We simply watch devices, we cannot plug/unplug them. That * simplifies the state engine greatly */ typedef enum DeviceState { @@ -103,8 +89,6 @@ typedef enum MountState { MOUNT_MOUNTED, MOUNT_REMOUNTING, MOUNT_UNMOUNTING, - MOUNT_MOUNTING_SIGTERM, - MOUNT_MOUNTING_SIGKILL, MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL, MOUNT_UNMOUNTING_SIGTERM, @@ -186,8 +170,6 @@ typedef enum SwapState { SWAP_ACTIVATING_DONE, /* /sbin/swapon is running, and the swap is done. */ SWAP_ACTIVE, SWAP_DEACTIVATING, - SWAP_ACTIVATING_SIGTERM, - SWAP_ACTIVATING_SIGKILL, SWAP_DEACTIVATING_SIGTERM, SWAP_DEACTIVATING_SIGKILL, SWAP_FAILED, @@ -339,9 +321,6 @@ UnitActiveState unit_active_state_from_string(const char *s) _pure_; const char* automount_state_to_string(AutomountState i) _const_; AutomountState automount_state_from_string(const char *s) _pure_; -const char* busname_state_to_string(BusNameState i) _const_; -BusNameState busname_state_from_string(const char *s) _pure_; - const char* device_state_to_string(DeviceState i) _const_; DeviceState device_state_from_string(const char *s) _pure_; diff --git a/src/basic/user-util.c b/src/basic/user-util.c index c619dad527..a691a0d3fc 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -543,8 +543,7 @@ bool valid_user_group_name(const char *u) { if (!(*i >= 'a' && *i <= 'z') && !(*i >= 'A' && *i <= 'Z') && !(*i >= '0' && *i <= '9') && - *i != '_' && - *i != '-') + !IN_SET(*i, '_', '-')) return false; } diff --git a/src/basic/utf8.c b/src/basic/utf8.c index 6eae2b983d..7a52fac621 100644 --- a/src/basic/utf8.c +++ b/src/basic/utf8.c @@ -73,7 +73,7 @@ static bool unichar_is_control(char32_t ch) { '\t' is in C0 range, but more or less harmless and commonly used. */ - return (ch < ' ' && ch != '\t' && ch != '\n') || + return (ch < ' ' && !IN_SET(ch, '\t', '\n')) || (0x7F <= ch && ch <= 0x9F); } diff --git a/src/basic/util.c b/src/basic/util.c index b52a5db31b..687de40993 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -34,6 +34,7 @@ #include <unistd.h> #include "alloc-util.h" +#include "btrfs-util.h" #include "build.h" #include "cgroup-util.h" #include "def.h" @@ -219,7 +220,7 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa /* Spawns a temporary TTY agent, making sure it goes away when * we go away */ - parent_pid = getpid(); + parent_pid = getpid_cached(); /* First we temporarily block all signals, so that the new * child has them blocked initially. This way, we can be sure @@ -377,7 +378,7 @@ int on_ac_power(void) { device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); if (device < 0) { - if (errno == ENOENT || errno == ENOTDIR) + if (IN_SET(errno, ENOENT, ENOTDIR)) continue; return -errno; @@ -719,3 +720,133 @@ int version(void) { SYSTEMD_FEATURES); return 0; } + +int get_block_device(const char *path, dev_t *dev) { + struct stat st; + struct statfs sfs; + + assert(path); + assert(dev); + + /* Get's the block device directly backing a file system. If + * the block device is encrypted, returns the device mapper + * block device. */ + + if (lstat(path, &st)) + return -errno; + + if (major(st.st_dev) != 0) { + *dev = st.st_dev; + return 1; + } + + if (statfs(path, &sfs) < 0) + return -errno; + + if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) + return btrfs_get_block_device(path, dev); + + return 0; +} + +int get_block_device_harder(const char *path, dev_t *dev) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_free_ char *p = NULL, *t = NULL; + struct dirent *de, *found = NULL; + const char *q; + unsigned maj, min; + dev_t dt; + int r; + + assert(path); + assert(dev); + + /* Gets the backing block device for a file system, and + * handles LUKS encrypted file systems, looking for its + * immediate parent, if there is one. */ + + r = get_block_device(path, &dt); + if (r <= 0) + return r; + + if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0) + return -ENOMEM; + + d = opendir(p); + if (!d) { + if (errno == ENOENT) + goto fallback; + + return -errno; + } + + FOREACH_DIRENT_ALL(de, d, return -errno) { + + if (dot_or_dot_dot(de->d_name)) + continue; + + if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) + continue; + + if (found) { + _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL; + + /* We found a device backed by multiple other devices. We don't really support automatic + * discovery on such setups, with the exception of dm-verity partitions. In this case there are + * two backing devices: the data partition and the hash partition. We are fine with such + * setups, however, only if both partitions are on the same physical device. Hence, let's + * verify this. */ + + u = strjoin(p, "/", de->d_name, "/../dev"); + if (!u) + return -ENOMEM; + + v = strjoin(p, "/", found->d_name, "/../dev"); + if (!v) + return -ENOMEM; + + r = read_one_line_file(u, &a); + if (r < 0) { + log_debug_errno(r, "Failed to read %s: %m", u); + goto fallback; + } + + r = read_one_line_file(v, &b); + if (r < 0) { + log_debug_errno(r, "Failed to read %s: %m", v); + goto fallback; + } + + /* Check if the parent device is the same. If not, then the two backing devices are on + * different physical devices, and we don't support that. */ + if (!streq(a, b)) + goto fallback; + } + + found = de; + } + + if (!found) + goto fallback; + + q = strjoina(p, "/", found->d_name, "/dev"); + + r = read_one_line_file(q, &t); + if (r == -ENOENT) + goto fallback; + if (r < 0) + return r; + + if (sscanf(t, "%u:%u", &maj, &min) != 2) + return -EINVAL; + + if (maj == 0) + goto fallback; + + *dev = makedev(maj, min); + return 1; + +fallback: + *dev = dt; + return 1; +} diff --git a/src/basic/util.h b/src/basic/util.h index c7da6c39bf..b31dfd1c92 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -192,3 +192,6 @@ uint64_t system_tasks_max_scale(uint64_t v, uint64_t max); int update_reboot_parameter_and_warn(const char *param); int version(void); + +int get_block_device(const char *path, dev_t *dev); +int get_block_device_harder(const char *path, dev_t *dev); diff --git a/src/basic/virt.c b/src/basic/virt.c index 6011744523..d8eeb54dbf 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -46,6 +46,7 @@ static int detect_vm_cpuid(void) { } cpuid_vendor_table[] = { { "XenVMMXenVMM", VIRTUALIZATION_XEN }, { "KVMKVMKVM", VIRTUALIZATION_KVM }, + { "TCGTCGTCGTCG", VIRTUALIZATION_QEMU }, /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ { "VMwareVMware", VIRTUALIZATION_VMWARE }, /* https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs */ @@ -421,7 +422,7 @@ int detect_container(void) { goto finish; } - if (getpid() == 1) { + if (getpid_cached() == 1) { /* If we are PID 1 we can just check our own environment variable, and that's authoritative. */ e = getenv("container"); diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c index 8256899eda..e086376d7c 100644 --- a/src/basic/xattr-util.c +++ b/src/basic/xattr-util.c @@ -129,7 +129,7 @@ static int parse_crtime(le64_t le, usec_t *usec) { assert(usec); u = le64toh(le); - if (u == 0 || u == (uint64_t) -1) + if (IN_SET(u, 0, (uint64_t) -1)) return -EIO; *usec = (usec_t) u; diff --git a/src/basic/xml.c b/src/basic/xml.c index 1dbeac7324..a4337f4865 100644 --- a/src/basic/xml.c +++ b/src/basic/xml.c @@ -208,7 +208,7 @@ int xml_tokenize(const char **p, char **name, void **state, unsigned *line) { if (*c == '=') { c++; - if (*c == '\'' || *c == '\"') { + if (IN_SET(*c, '\'', '\"')) { /* Tag with a quoted value */ e = strchr(c+1, *c); |