diff options
38 files changed, 752 insertions, 299 deletions
diff --git a/.gitignore b/.gitignore index 5d18705531..f47de39f3d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ .config.args .gdb_history .deps/ +.mypy_cache/ +__pycache__/ /*.gcda /*.gcno /*.tar.bz2 @@ -34,4 +36,3 @@ /mkosi.builddir/ /mkosi.output/ /tags -__pycache__/ @@ -19,6 +19,14 @@ Janitorial Clean-ups: Features: +* add systemd.random_seed= on the kernel cmdline, taking some hex or base64 + encoded data. During earliest boot, credit it to entropy. This is not useful + for general purpose systems, but certainly for testing environments in VMs + and such, as it allows us to boot up instantly with fully initialized entropy + pool even if RNG pass-thru is not available. + +* Support ProtectProc= or so, using: https://patchwork.kernel.org/cover/11310197/ + * if /usr/bin/swapoff fails due to OOM, log a friendly explanatory message about it * build short web pages out of each catalog entry, build them along with man diff --git a/hwdb.d/60-keyboard.hwdb b/hwdb.d/60-keyboard.hwdb index 875bf66bd8..99fd78c364 100644 --- a/hwdb.d/60-keyboard.hwdb +++ b/hwdb.d/60-keyboard.hwdb @@ -529,6 +529,10 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pn*[sS][pP][eE][cC][tT][rR][eE]*x360Convert evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[pP][aA][vV][iI][lL][iI][oO][nN]*13*x360*:pvr* KEYBOARD_KEY_d7=unknown +# Spectre x360 13 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPSpectrex360Convertible13*:pvr* + KEYBOARD_KEY_82=f20 # Fn+F12; Microphone mute button, should be micmute + # Elitebook evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*Compaq*:pvr* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*EliteBook*:pvr* diff --git a/man/crypttab.xml b/man/crypttab.xml index 3170e5880f..2046911c78 100644 --- a/man/crypttab.xml +++ b/man/crypttab.xml @@ -178,6 +178,13 @@ </varlistentry> <varlistentry> + <term><option>bitlk</option></term> + + <listitem><para>Decrypt Bitlocker drive. Encryption parameters + are deduced by cryptsetup from Bitlocker header.</para></listitem> + </varlistentry> + + <varlistentry> <term><option>_netdev</option></term> <listitem><para>Marks this cryptsetup device as requiring network. It will be diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 7e59a1aaed..52939deec0 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -273,7 +273,8 @@ <term><varname>rd.udev.event_timeout=</varname></term> <term><varname>udev.timeout_signal=</varname></term> <term><varname>rd.udev.timeout_signal=</varname></term> - + <term><varname>udev.blockdev_read_only</varname></term> + <term><varname>rd.udev.blockdev_read_only</varname></term> <term><varname>net.ifnames=</varname></term> <term><varname>net.naming-scheme=</varname></term> diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml index 55edc17353..c6c1d9bcc6 100644 --- a/man/systemd-udevd.service.xml +++ b/man/systemd-udevd.service.xml @@ -77,7 +77,7 @@ </varlistentry> <varlistentry> - <term><option>-c=</option></term> + <term><option>-c</option></term> <term><option>--children-max=</option></term> <listitem> <para>Limit the number of events executed in parallel.</para> @@ -85,7 +85,7 @@ </varlistentry> <varlistentry> - <term><option>-e=</option></term> + <term><option>-e</option></term> <term><option>--exec-delay=</option></term> <listitem> <para>Delay the execution of <varname>RUN</varname> @@ -97,7 +97,7 @@ </varlistentry> <varlistentry> - <term><option>-t=</option></term> + <term><option>-t</option></term> <term><option>--event-timeout=</option></term> <listitem> <para>Set the number of seconds to wait for events to finish. After @@ -121,7 +121,7 @@ </varlistentry> <varlistentry> - <term><option>-N=</option></term> + <term><option>-N</option></term> <term><option>--resolve-names=</option></term> <listitem> <para>Specify when systemd-udevd should resolve names of users and groups. @@ -140,8 +140,8 @@ <refsect1><title>Kernel command line</title> <variablelist class='kernel-commandline-options'> - <para>Parameters starting with "rd." will be read when - <command>systemd-udevd</command> is used in an initrd.</para> + <para>Parameters prefixed with "rd." will be read when <command>systemd-udevd</command> is used in an + initrd, those without will be processed both in the initrd and on the host.</para> <varlistentry> <term><varname>udev.log_priority=</varname></term> <term><varname>rd.udev.log_priority=</varname></term> @@ -185,6 +185,22 @@ </listitem> </varlistentry> <varlistentry> + <term><varname>udev.blockdev_read_only</varname></term> + <term><varname>rd.udev.blockdev_read_only</varname></term> + <listitem> + <para>If specified, mark all physical block devices read-only as they appear. Synthetic block + devices (such as loopback block devices or device mapper devices) are left as they are. This is + useful to guarantee that the contents of physical block devices remains unmodified during runtime, + for example to implement fully stateless systems, for testing or for recovery situations where + corrupted file systems shall not be corrupted further through accidental modification.</para> + + <para>A block device may be marked writable again by issuing the <command>blockdev + --setrw</command> command, see <citerefentry + project='man-pages'><refentrytitle>blockdev</refentrytitle><manvolnum>8</manvolnum></citerefentry> + for details.</para> + </listitem> + </varlistentry> + <varlistentry> <term><varname>net.ifnames=</varname></term> <listitem> <para>Network interfaces are renamed to give them predictable names diff --git a/man/systemd.network.xml b/man/systemd.network.xml index a4ca67a27f..ae93a39eb4 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -673,6 +673,13 @@ </para></listitem> </varlistentry> <varlistentry> + <term><varname>IPv4AcceptLocal=</varname></term> + <listitem><para>Takes a boolean. Accept packets with local source addresses. In combination + with suitable routing, this can be used to direct packets between two local interfaces over + the wire and have them accepted properly. When unset, the kernel's default will be used. + </para></listitem> + </varlistentry> + <varlistentry> <term><varname>IPv4ProxyARP=</varname></term> <listitem><para>Takes a boolean. Configures proxy ARP for IPv4. Proxy ARP is the technique in which one host, usually a router, answers ARP requests intended for another machine. By "faking" its identity, @@ -3275,7 +3282,7 @@ <varlistentry> <term><varname>Weight=</varname></term> <listitem> - <para>Specifies the weight of the class. Takse an integer in the range 1..1023. Defaults to + <para>Specifies the weight of the class. Takes an integer in the range 1..1023. Defaults to unset in which case the kernel default is used.</para> </listitem> </varlistentry> diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index efbe95bb9e..3172865e3e 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -145,12 +145,7 @@ struct hashmap_debug_info { /* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */ static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list); static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER; - -#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug; - -#else /* !ENABLE_DEBUG_HASHMAP */ -#define HASHMAP_DEBUG_FIELDS -#endif /* ENABLE_DEBUG_HASHMAP */ +#endif enum HashmapType { HASHMAP_TYPE_PLAIN, @@ -212,7 +207,10 @@ struct HashmapBase { bool from_pool:1; /* whether was allocated from mempool */ bool dirty:1; /* whether dirtied since last iterated_cache_get() */ bool cached:1; /* whether this hashmap is being cached */ - HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */ + +#if ENABLE_DEBUG_HASHMAP + struct hashmap_debug_info debug; +#endif }; /* Specific hash types @@ -254,7 +252,7 @@ struct hashmap_type_info { unsigned n_direct_buckets; }; -static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { +static _used_ const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { [HASHMAP_TYPE_PLAIN] = { .head_size = sizeof(Hashmap), .entry_size = sizeof(struct plain_hashmap_entry), @@ -707,7 +705,7 @@ static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { : hashmap_iterate_in_internal_order(h, i); } -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { +bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { struct hashmap_base_entry *e; void *data; unsigned idx; @@ -733,7 +731,7 @@ bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const v } bool set_iterate(const Set *s, Iterator *i, void **value) { - return internal_hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL); + return _hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL); } #define HASHMAP_FOREACH_IDX(idx, h, i) \ @@ -741,7 +739,7 @@ bool set_iterate(const Set *s, Iterator *i, void **value) { (idx != IDX_NIL); \ (idx) = hashmap_iterate_entry((h), &(i))) -IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h) { +IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h) { IteratedCache *cache; assert(h); @@ -809,15 +807,15 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu return h; } -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +Hashmap *_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); } -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); } -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); } @@ -838,15 +836,15 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops return 0; } -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); } -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); } -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { +int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); } @@ -868,16 +866,16 @@ static void hashmap_free_no_clear(HashmapBase *h) { free(h); } -HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { +HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { if (h) { - internal_hashmap_clear(h, default_free_key, default_free_value); + _hashmap_clear(h, default_free_key, default_free_value); hashmap_free_no_clear(h); } return NULL; } -void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { +void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { free_func_t free_key, free_value; if (!h) return; @@ -891,11 +889,11 @@ void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_f * hash table, and only then call the destructor functions. If these destructors then try to unregister * themselves from our hash table a second time, the entry is already gone. */ - while (internal_hashmap_size(h) > 0) { + while (_hashmap_size(h) > 0) { void *k = NULL; void *v; - v = internal_hashmap_first_key_and_value(h, true, &k); + v = _hashmap_first_key_and_value(h, true, &k); if (free_key) free_key(k); @@ -1301,7 +1299,7 @@ int hashmap_update(Hashmap *h, const void *key, void *value) { return 0; } -void *internal_hashmap_get(HashmapBase *h, const void *key) { +void *_hashmap_get(HashmapBase *h, const void *key) { struct hashmap_base_entry *e; unsigned hash, idx; @@ -1336,7 +1334,7 @@ void *hashmap_get2(Hashmap *h, const void *key, void **key2) { return e->value; } -bool internal_hashmap_contains(HashmapBase *h, const void *key) { +bool _hashmap_contains(HashmapBase *h, const void *key) { unsigned hash; if (!h) @@ -1346,7 +1344,7 @@ bool internal_hashmap_contains(HashmapBase *h, const void *key) { return bucket_scan(h, hash, key) != IDX_NIL; } -void *internal_hashmap_remove(HashmapBase *h, const void *key) { +void *_hashmap_remove(HashmapBase *h, const void *key) { struct hashmap_base_entry *e; unsigned hash, idx; void *data; @@ -1484,7 +1482,7 @@ int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_ return 0; } -void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value) { +void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value) { struct hashmap_base_entry *e; unsigned hash, idx; @@ -1514,7 +1512,7 @@ static unsigned find_first_entry(HashmapBase *h) { return hashmap_iterate_entry(h, &i); } -void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { +void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { struct hashmap_base_entry *e; void *key, *data; unsigned idx; @@ -1539,21 +1537,21 @@ void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **r return data; } -unsigned internal_hashmap_size(HashmapBase *h) { +unsigned _hashmap_size(HashmapBase *h) { if (!h) return 0; return n_entries(h); } -unsigned internal_hashmap_buckets(HashmapBase *h) { +unsigned _hashmap_buckets(HashmapBase *h) { if (!h) return 0; return n_buckets(h); } -int internal_hashmap_merge(Hashmap *h, Hashmap *other) { +int _hashmap_merge(Hashmap *h, Hashmap *other) { Iterator i; unsigned idx; @@ -1589,7 +1587,7 @@ int set_merge(Set *s, Set *other) { return 0; } -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { +int _hashmap_reserve(HashmapBase *h, unsigned entries_add) { int r; assert(h); @@ -1607,7 +1605,7 @@ int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { * Returns: 0 on success. * -ENOMEM on alloc failure, in which case no move has been done. */ -int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { +int _hashmap_move(HashmapBase *h, HashmapBase *other) { struct swap_entries swap; struct hashmap_base_entry *e, *n; Iterator i; @@ -1652,7 +1650,7 @@ int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { return 0; } -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { +int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { struct swap_entries swap; unsigned h_hash, other_hash, idx; struct hashmap_base_entry *e, *n; @@ -1689,7 +1687,7 @@ int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *ke return 0; } -HashmapBase *internal_hashmap_copy(HashmapBase *h) { +HashmapBase *_hashmap_copy(HashmapBase *h) { HashmapBase *copy; int r; @@ -1712,14 +1710,14 @@ HashmapBase *internal_hashmap_copy(HashmapBase *h) { } if (r < 0) { - internal_hashmap_free(copy, false, false); + _hashmap_free(copy, false, false); return NULL; } return copy; } -char **internal_hashmap_get_strv(HashmapBase *h) { +char **_hashmap_get_strv(HashmapBase *h) { char **sv; Iterator i; unsigned idx, n; diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h index a3bc328142..230d322213 100644 --- a/src/basic/hashmap.h +++ b/src/basic/hashmap.h @@ -14,7 +14,7 @@ * will be treated as empty hashmap for all read operations. That way it is not * necessary to instantiate an object for each Hashmap use. * - * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap), + * If ENABLE_DEBUG_HASHMAP is defined (by configuring with -Ddebug-extra=hashmap), * the implementation will: * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py) * - perform extra checks for invalid use of iterators @@ -24,10 +24,9 @@ typedef void* (*hashmap_destroy_t)(void *p); -/* The base type for all hashmap and set types. Many functions in the - * implementation take (HashmapBase*) parameters and are run-time polymorphic, - * though the API is not meant to be polymorphic (do not call functions - * internal_*() directly). */ +/* The base type for all hashmap and set types. Many functions in the implementation take (HashmapBase*) + * parameters and are run-time polymorphic, though the API is not meant to be polymorphic (do not call + * underscore-prefixed functions directly). */ typedef struct HashmapBase HashmapBase; /* Specific hashmap/set types */ @@ -84,10 +83,10 @@ typedef struct { # define HASHMAP_DEBUG_PASS_ARGS #endif -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) +Hashmap *_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_new(ops) _hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_new(ops) _ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) #define hashmap_free_and_replace(a, b) \ ({ \ @@ -97,57 +96,57 @@ OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HA 0; \ }) -HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); +HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); static inline Hashmap *hashmap_free(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL); } static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL); } static inline Hashmap *hashmap_free_free(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free); } static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free); } static inline Hashmap *hashmap_free_free_key(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL); } static inline OrderedHashmap *ordered_hashmap_free_free_key(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL); } static inline Hashmap *hashmap_free_free_free(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, free); } static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, free); } IteratedCache *iterated_cache_free(IteratedCache *cache); int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries); -HashmapBase *internal_hashmap_copy(HashmapBase *h); +HashmapBase *_hashmap_copy(HashmapBase *h); static inline Hashmap *hashmap_copy(Hashmap *h) { - return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); + return (Hashmap*) _hashmap_copy(HASHMAP_BASE(h)); } static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { - return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); + return (OrderedHashmap*) _hashmap_copy(HASHMAP_BASE(h)); } -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h); +IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h); static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) { - return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); + return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); } static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) { - return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); + return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); } int hashmap_put(Hashmap *h, const void *key, void *value); @@ -167,12 +166,12 @@ static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, vo return hashmap_replace(PLAIN_HASHMAP(h), key, value); } -void *internal_hashmap_get(HashmapBase *h, const void *key); +void *_hashmap_get(HashmapBase *h, const void *key); static inline void *hashmap_get(Hashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); + return _hashmap_get(HASHMAP_BASE(h), key); } static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); + return _hashmap_get(HASHMAP_BASE(h), key); } void *hashmap_get2(Hashmap *h, const void *key, void **rkey); @@ -180,20 +179,20 @@ static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, voi return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); } -bool internal_hashmap_contains(HashmapBase *h, const void *key); +bool _hashmap_contains(HashmapBase *h, const void *key); static inline bool hashmap_contains(Hashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); + return _hashmap_contains(HASHMAP_BASE(h), key); } static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); + return _hashmap_contains(HASHMAP_BASE(h), key); } -void *internal_hashmap_remove(HashmapBase *h, const void *key); +void *_hashmap_remove(HashmapBase *h, const void *key); static inline void *hashmap_remove(Hashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); + return _hashmap_remove(HASHMAP_BASE(h), key); } static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); + return _hashmap_remove(HASHMAP_BASE(h), key); } void *hashmap_remove2(Hashmap *h, const void *key, void **rkey); @@ -201,9 +200,9 @@ static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); } -void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value); +void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value); static inline void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { - return internal_hashmap_remove_value(HASHMAP_BASE(h), key, value); + return _hashmap_remove_value(HASHMAP_BASE(h), key, value); } static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { @@ -222,41 +221,41 @@ static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const vo /* Since merging data from a OrderedHashmap into a Hashmap or vice-versa * should just work, allow this by having looser type-checking here. */ -int internal_hashmap_merge(Hashmap *h, Hashmap *other); -#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) +int _hashmap_merge(Hashmap *h, Hashmap *other); +#define hashmap_merge(h, other) _hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) #define ordered_hashmap_merge(h, other) hashmap_merge(h, other) -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add); +int _hashmap_reserve(HashmapBase *h, unsigned entries_add); static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); + return _hashmap_reserve(HASHMAP_BASE(h), entries_add); } static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); + return _hashmap_reserve(HASHMAP_BASE(h), entries_add); } -int internal_hashmap_move(HashmapBase *h, HashmapBase *other); +int _hashmap_move(HashmapBase *h, HashmapBase *other); /* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */ static inline int hashmap_move(Hashmap *h, Hashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); + return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); } static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); + return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); } -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); +int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); + return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); + return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } -unsigned internal_hashmap_size(HashmapBase *h) _pure_; +unsigned _hashmap_size(HashmapBase *h) _pure_; static inline unsigned hashmap_size(Hashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); + return _hashmap_size(HASHMAP_BASE(h)); } static inline unsigned ordered_hashmap_size(OrderedHashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); + return _hashmap_size(HASHMAP_BASE(h)); } static inline bool hashmap_isempty(Hashmap *h) { @@ -266,49 +265,49 @@ static inline bool ordered_hashmap_isempty(OrderedHashmap *h) { return ordered_hashmap_size(h) == 0; } -unsigned internal_hashmap_buckets(HashmapBase *h) _pure_; +unsigned _hashmap_buckets(HashmapBase *h) _pure_; static inline unsigned hashmap_buckets(Hashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); + return _hashmap_buckets(HASHMAP_BASE(h)); } static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); + return _hashmap_buckets(HASHMAP_BASE(h)); } -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); +bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); + return _hashmap_iterate(HASHMAP_BASE(h), i, value, key); } static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); + return _hashmap_iterate(HASHMAP_BASE(h), i, value, key); } -void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); +void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); static inline void hashmap_clear(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL); + _hashmap_clear(HASHMAP_BASE(h), NULL, NULL); } static inline void ordered_hashmap_clear(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL); + _hashmap_clear(HASHMAP_BASE(h), NULL, NULL); } static inline void hashmap_clear_free(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, free); + _hashmap_clear(HASHMAP_BASE(h), NULL, free); } static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, free); + _hashmap_clear(HASHMAP_BASE(h), NULL, free); } static inline void hashmap_clear_free_key(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, NULL); + _hashmap_clear(HASHMAP_BASE(h), free, NULL); } static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, NULL); + _hashmap_clear(HASHMAP_BASE(h), free, NULL); } static inline void hashmap_clear_free_free(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, free); + _hashmap_clear(HASHMAP_BASE(h), free, free); } static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, free); + _hashmap_clear(HASHMAP_BASE(h), free, free); } /* @@ -322,50 +321,50 @@ static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { * the first entry is O(1). */ -void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key); +void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key); static inline void *hashmap_steal_first_key_and_value(Hashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); } static inline void *ordered_hashmap_steal_first_key_and_value(OrderedHashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); } static inline void *hashmap_first_key_and_value(Hashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); } static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); } static inline void *hashmap_steal_first(Hashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); } static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); } static inline void *hashmap_first(Hashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); } static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); } -static inline void *internal_hashmap_first_key(HashmapBase *h, bool remove) { +static inline void *_hashmap_first_key(HashmapBase *h, bool remove) { void *key = NULL; - (void) internal_hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key); + (void) _hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key); return key; } static inline void *hashmap_steal_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), true); + return _hashmap_first_key(HASHMAP_BASE(h), true); } static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), true); + return _hashmap_first_key(HASHMAP_BASE(h), true); } static inline void *hashmap_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), false); + return _hashmap_first_key(HASHMAP_BASE(h), false); } static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), false); + return _hashmap_first_key(HASHMAP_BASE(h), false); } #define hashmap_clear_with_destructor(_s, _f) \ @@ -394,12 +393,12 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { /* no hashmap_next */ void *ordered_hashmap_next(OrderedHashmap *h, const void *key); -char **internal_hashmap_get_strv(HashmapBase *h); +char **_hashmap_get_strv(HashmapBase *h); static inline char **hashmap_get_strv(Hashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); + return _hashmap_get_strv(HASHMAP_BASE(h)); } static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); + return _hashmap_get_strv(HASHMAP_BASE(h)); } /* diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h index 383a729cab..a42a57eb49 100644 --- a/src/basic/ordered-set.h +++ b/src/basic/ordered-set.h @@ -59,7 +59,7 @@ static inline void* ordered_set_steal_first(OrderedSet *s) { } static inline char **ordered_set_get_strv(OrderedSet *s) { - return internal_hashmap_get_strv(HASHMAP_BASE((OrderedHashmap*) s)); + return _hashmap_get_strv(HASHMAP_BASE((OrderedHashmap*) s)); } int ordered_set_consume(OrderedSet *s, void *p); diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 59f8a31cec..44f0438cf4 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -70,26 +70,24 @@ int parse_pid(const char *s, pid_t* ret_pid) { } int parse_mode(const char *s, mode_t *ret) { - char *x; - long l; + unsigned m; + int r; assert(s); - assert(ret); - - s += strspn(s, WHITESPACE); - if (s[0] == '-') - return -ERANGE; - errno = 0; - l = strtol(s, &x, 8); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (l < 0 || l > 07777) + r = safe_atou_full(s, 8 | + SAFE_ATO_REFUSE_PLUS_MINUS, /* Leading '+' or even '-' char? that's just weird, + * refuse. User might have wanted to add mode flags or + * so, but this parser doesn't allow that, so let's + * better be safe. */ + &m); + if (r < 0) + return r; + if (m > 07777) return -ERANGE; - *ret = (mode_t) l; + if (ret) + *ret = m; return 0; } @@ -354,30 +352,73 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) { return 0; } +static const char *mangle_base(const char *s, unsigned *base) { + const char *k; + + assert(s); + assert(base); + + /* Base already explicitly specified, then don't do anything. */ + if (SAFE_ATO_MASK_FLAGS(*base) != 0) + return s; + + /* Support Python 3 style "0b" and 0x" prefixes, because they truly make sense, much more than C's "0" prefix for octal. */ + k = STARTSWITH_SET(s, "0b", "0B"); + if (k) { + *base = 2 | (*base & SAFE_ATO_ALL_FLAGS); + return k; + } + + k = STARTSWITH_SET(s, "0o", "0O"); + if (k) { + *base = 8 | (*base & SAFE_ATO_ALL_FLAGS); + return k; + } + + return s; +} + int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { char *x = NULL; unsigned long l; assert(s); - assert(base <= 16); + assert(SAFE_ATO_MASK_FLAGS(base) <= 16); - /* strtoul() is happy to parse negative values, and silently - * converts them to unsigned values without generating an - * error. We want a clean error, hence let's look for the "-" - * prefix on our own, and generate an error. But let's do so - * only after strtoul() validated that the string is clean - * otherwise, so that we return EINVAL preferably over - * ERANGE. */ + /* strtoul() is happy to parse negative values, and silently converts them to unsigned values without + * generating an error. We want a clean error, hence let's look for the "-" prefix on our own, and + * generate an error. But let's do so only after strtoul() validated that the string is clean + * otherwise, so that we return EINVAL preferably over ERANGE. */ + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && + strchr(WHITESPACE, s[0])) + return -EINVAL; s += strspn(s, WHITESPACE); + if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && + IN_SET(s[0], '+', '-')) + return -EINVAL; /* Note that we check the "-" prefix again a second time below, but return a + * different error. I.e. if the SAFE_ATO_REFUSE_PLUS_MINUS flag is set we + * blanket refuse +/- prefixed integers, while if it is missing we'll just + * return ERANGE, because the string actually parses correctly, but doesn't + * fit in the return type. */ + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && + s[0] == '0' && !streq(s, "0")) + return -EINVAL; /* This is particularly useful to avoid ambiguities between C's octal + * notation and assumed-to-be-decimal integers with a leading zero. */ + + s = mangle_base(s, &base); + errno = 0; - l = strtoul(s, &x, base); + l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base) /* Let's mask off the flags bits so that only the actual + * base is left */); if (errno > 0) return -errno; if (!x || x == s || *x != 0) return -EINVAL; - if (s[0] == '-') + if (l != 0 && s[0] == '-') return -ERANGE; if ((unsigned long) (unsigned) l != l) return -ERANGE; @@ -389,13 +430,17 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { } int safe_atoi(const char *s, int *ret_i) { + unsigned base = 0; char *x = NULL; long l; assert(s); + s += strspn(s, WHITESPACE); + s = mangle_base(s, &base); + errno = 0; - l = strtol(s, &x, 0); + l = strtol(s, &x, base); if (errno > 0) return -errno; if (!x || x == s || *x != 0) @@ -414,16 +459,31 @@ int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) unsigned long long l; assert(s); + assert(SAFE_ATO_MASK_FLAGS(base) <= 16); + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && + strchr(WHITESPACE, s[0])) + return -EINVAL; s += strspn(s, WHITESPACE); + if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && + IN_SET(s[0], '+', '-')) + return -EINVAL; + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && + s[0] == '0' && s[1] != 0) + return -EINVAL; + + s = mangle_base(s, &base); + errno = 0; - l = strtoull(s, &x, base); + l = strtoull(s, &x, SAFE_ATO_MASK_FLAGS(base)); if (errno > 0) return -errno; if (!x || x == s || *x != 0) return -EINVAL; - if (*s == '-') + if (l != 0 && s[0] == '-') return -ERANGE; if (ret_llu) @@ -433,13 +493,17 @@ int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) } int safe_atolli(const char *s, long long int *ret_lli) { + unsigned base = 0; char *x = NULL; long long l; assert(s); + s += strspn(s, WHITESPACE); + s = mangle_base(s, &base); + errno = 0; - l = strtoll(s, &x, 0); + l = strtoll(s, &x, base); if (errno > 0) return -errno; if (!x || x == s || *x != 0) @@ -452,20 +516,22 @@ int safe_atolli(const char *s, long long int *ret_lli) { } int safe_atou8(const char *s, uint8_t *ret) { - char *x = NULL; + unsigned base = 0; unsigned long l; + char *x = NULL; assert(s); s += strspn(s, WHITESPACE); + s = mangle_base(s, &base); errno = 0; - l = strtoul(s, &x, 0); + l = strtoul(s, &x, base); if (errno > 0) return -errno; if (!x || x == s || *x != 0) return -EINVAL; - if (s[0] == '-') + if (l != 0 && s[0] == '-') return -ERANGE; if ((unsigned long) (uint8_t) l != l) return -ERANGE; @@ -480,34 +546,53 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { unsigned long l; assert(s); - assert(ret); - assert(base <= 16); + assert(SAFE_ATO_MASK_FLAGS(base) <= 16); + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && + strchr(WHITESPACE, s[0])) + return -EINVAL; s += strspn(s, WHITESPACE); + if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && + IN_SET(s[0], '+', '-')) + return -EINVAL; + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && + s[0] == '0' && s[1] != 0) + return -EINVAL; + + s = mangle_base(s, &base); + errno = 0; - l = strtoul(s, &x, base); + l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base)); if (errno > 0) return -errno; if (!x || x == s || *x != 0) return -EINVAL; - if (s[0] == '-') + if (l != 0 && s[0] == '-') return -ERANGE; if ((unsigned long) (uint16_t) l != l) return -ERANGE; - *ret = (uint16_t) l; + if (ret) + *ret = (uint16_t) l; + return 0; } int safe_atoi16(const char *s, int16_t *ret) { + unsigned base = 0; char *x = NULL; long l; assert(s); + s += strspn(s, WHITESPACE); + s = mangle_base(s, &base); + errno = 0; - l = strtol(s, &x, 0); + l = strtol(s, &x, base); if (errno > 0) return -errno; if (!x || x == s || *x != 0) diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 970bdefbf0..9a516ce5f6 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -21,6 +21,12 @@ int parse_range(const char *t, unsigned *lower, unsigned *upper); int parse_errno(const char *t); int parse_syscall_and_errno(const char *in, char **name, int *error); +#define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30) +#define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29) +#define SAFE_ATO_REFUSE_LEADING_WHITESPACE (1U << 28) +#define SAFE_ATO_ALL_FLAGS (SAFE_ATO_REFUSE_PLUS_MINUS|SAFE_ATO_REFUSE_LEADING_ZERO|SAFE_ATO_REFUSE_LEADING_WHITESPACE) +#define SAFE_ATO_MASK_FLAGS(base) ((base) & ~SAFE_ATO_ALL_FLAGS) + int safe_atou_full(const char *s, unsigned base, unsigned *ret_u); static inline int safe_atou(const char *s, unsigned *ret_u) { diff --git a/src/basic/set.h b/src/basic/set.h index 8a95fec05f..621e83bf27 100644 --- a/src/basic/set.h +++ b/src/basic/set.h @@ -13,40 +13,40 @@ 0; \ }) -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) +Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS) static inline Set *set_free(Set *s) { - return (Set*) internal_hashmap_free(HASHMAP_BASE(s), NULL, NULL); + return (Set*) _hashmap_free(HASHMAP_BASE(s), NULL, NULL); } static inline Set *set_free_free(Set *s) { - return (Set*) internal_hashmap_free(HASHMAP_BASE(s), free, NULL); + return (Set*) _hashmap_free(HASHMAP_BASE(s), free, NULL); } /* no set_free_free_free */ static inline Set *set_copy(Set *s) { - return (Set*) internal_hashmap_copy(HASHMAP_BASE(s)); + return (Set*) _hashmap_copy(HASHMAP_BASE(s)); } -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) int set_put(Set *s, const void *key); /* no set_update */ /* no set_replace */ static inline void *set_get(const Set *s, void *key) { - return internal_hashmap_get(HASHMAP_BASE((Set *) s), key); + return _hashmap_get(HASHMAP_BASE((Set *) s), key); } /* no set_get2 */ static inline bool set_contains(const Set *s, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE((Set *) s), key); + return _hashmap_contains(HASHMAP_BASE((Set *) s), key); } static inline void *set_remove(Set *s, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(s), key); + return _hashmap_remove(HASHMAP_BASE(s), key); } /* no set_remove2 */ @@ -56,19 +56,19 @@ int set_remove_and_put(Set *s, const void *old_key, const void *new_key); int set_merge(Set *s, Set *other); static inline int set_reserve(Set *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); + return _hashmap_reserve(HASHMAP_BASE(h), entries_add); } static inline int set_move(Set *s, Set *other) { - return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); + return _hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); } static inline int set_move_one(Set *s, Set *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); + return _hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); } static inline unsigned set_size(const Set *s) { - return internal_hashmap_size(HASHMAP_BASE((Set *) s)); + return _hashmap_size(HASHMAP_BASE((Set *) s)); } static inline bool set_isempty(const Set *s) { @@ -76,23 +76,23 @@ static inline bool set_isempty(const Set *s) { } static inline unsigned set_buckets(const Set *s) { - return internal_hashmap_buckets(HASHMAP_BASE((Set *) s)); + return _hashmap_buckets(HASHMAP_BASE((Set *) s)); } bool set_iterate(const Set *s, Iterator *i, void **value); static inline void set_clear(Set *s) { - internal_hashmap_clear(HASHMAP_BASE(s), NULL, NULL); + _hashmap_clear(HASHMAP_BASE(s), NULL, NULL); } static inline void set_clear_free(Set *s) { - internal_hashmap_clear(HASHMAP_BASE(s), free, NULL); + _hashmap_clear(HASHMAP_BASE(s), free, NULL); } /* no set_clear_free_free */ static inline void *set_steal_first(Set *s) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); } #define set_clear_with_destructor(_s, _f) \ @@ -111,13 +111,13 @@ static inline void *set_steal_first(Set *s) { /* no set_first_key */ static inline void *set_first(const Set *s) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL); } /* no set_next */ static inline char **set_get_strv(Set *s) { - return internal_hashmap_get_strv(HASHMAP_BASE(s)); + return _hashmap_get_strv(HASHMAP_BASE(s)); } int set_consume(Set *s, void *value); diff --git a/src/basic/strv.c b/src/basic/strv.c index f1d2bb5190..858e1e62ec 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -946,20 +946,20 @@ static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const c return 1; } -int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) { +int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) { int r; - r = hashmap_ensure_allocated(h, &string_strv_hash_ops); + r = _hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; return string_strv_hashmap_put_internal(*h, key, value); } -int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) { +int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) { int r; - r = ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops); + r = _ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; diff --git a/src/basic/strv.h b/src/basic/strv.h index 0f81e34580..2ad927bce5 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -226,5 +226,7 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space); }) extern const struct hash_ops string_strv_hash_ops; -int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value); -int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value); +int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS); +int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS); +#define string_strv_hashmap_put(h, k, v) _string_strv_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS) +#define string_strv_ordered_hashmap_put(h, k, v) _string_strv_ordered_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS) diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 2db8ef6abf..7dd2f6664a 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -49,7 +49,15 @@ int parse_uid(const char *s, uid_t *ret) { assert(s); assert_cc(sizeof(uid_t) == sizeof(uint32_t)); - r = safe_atou32_full(s, 10, &uid); + + /* We are very strict when parsing UIDs, and prohibit +/- as prefix, leading zero as prefix, and + * whitespace. We do this, since this call is often used in a context where we parse things as UID + * first, and if that doesn't work we fall back to NSS. Thus we really want to make sure that UIDs + * are parsed as UIDs only if they really really look like UIDs. */ + r = safe_atou32_full(s, 10 + | SAFE_ATO_REFUSE_PLUS_MINUS + | SAFE_ATO_REFUSE_LEADING_ZERO + | SAFE_ATO_REFUSE_LEADING_WHITESPACE, &uid); if (r < 0) return r; @@ -66,22 +74,39 @@ int parse_uid(const char *s, uid_t *ret) { } int parse_uid_range(const char *s, uid_t *ret_lower, uid_t *ret_upper) { - uint32_t u, l; + _cleanup_free_ char *word = NULL; + uid_t l, u; int r; assert(s); assert(ret_lower); assert(ret_upper); - r = parse_range(s, &l, &u); + r = extract_first_word(&s, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + r = parse_uid(word, &l); if (r < 0) return r; - if (l > u) + /* Check for the upper bound and extract it if needed */ + if (!s) + /* Single number with no dash. */ + u = l; + else if (!*s) + /* Trailing dash is an error. */ return -EINVAL; + else { + r = parse_uid(s, &u); + if (r < 0) + return r; - if (!uid_is_valid(l) || !uid_is_valid(u)) - return -ENXIO; + if (l > u) + return -EINVAL; + } *ret_lower = l; *ret_upper = u; diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 5886f86db6..c05e2d1351 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -38,7 +38,7 @@ #define CRYPT_SECTOR_SIZE 512 #define CRYPT_MAX_SECTOR_SIZE 4096 -static const char *arg_type = NULL; /* ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT or CRYPT_PLAIN */ +static const char *arg_type = NULL; /* ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT, CRYPT_BITLK or CRYPT_PLAIN */ static char *arg_cipher = NULL; static unsigned arg_key_size = 0; static unsigned arg_sector_size = CRYPT_SECTOR_SIZE; @@ -220,6 +220,11 @@ static int parse_one_option(const char *option) { arg_submit_from_crypt_cpus = true; else if (streq(option, "luks")) arg_type = ANY_LUKS; +/* since cryptsetup 2.3.0 (Feb 2020) */ +#ifdef CRYPT_BITLK + else if (streq(option, "bitlk")) + arg_type = CRYPT_BITLK; +#endif else if (streq(option, "tcrypt")) arg_type = CRYPT_TCRYPT; else if (STR_IN_SET(option, "tcrypt-hidden", "tcrypthidden")) { @@ -545,7 +550,7 @@ static int attach_tcrypt( return 0; } -static int attach_luks_or_plain( +static int attach_luks_or_plain_or_bitlk( struct crypt_device *cd, const char *name, const char *key_file, @@ -950,6 +955,15 @@ static int run(int argc, char *argv[]) { } } +/* since cryptsetup 2.3.0 (Feb 2020) */ +#ifdef CRYPT_BITLK + if (!arg_type || STR_IN_SET(arg_type, ANY_LUKS, CRYPT_BITLK)) { + r = crypt_load(cd, CRYPT_BITLK, NULL); + if (r < 0) + return log_error_errno(r, "Failed to load Bitlocker superblock on device %s: %m", crypt_get_device_name(cd)); + } +#endif + for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) { _cleanup_strv_free_erase_ char **passwords = NULL; @@ -988,7 +1002,7 @@ static int run(int argc, char *argv[]) { if (streq_ptr(arg_type, CRYPT_TCRYPT)) r = attach_tcrypt(cd, argv[2], key_file, key_data, key_data_size, passwords, flags); else - r = attach_luks_or_plain(cd, argv[2], key_file, key_data, key_data_size, passwords, flags, until); + r = attach_luks_or_plain_or_bitlk(cd, argv[2], key_file, key_data, key_data_size, passwords, flags, until); if (r >= 0) break; if (r != -EAGAIN) diff --git a/src/home/homectl.c b/src/home/homectl.c index e9eb7c0ddb..f7237d22d8 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -2171,7 +2171,7 @@ static int help(int argc, char *argv[], void *userdata) { " --location=LOCATION Set location of user on earth\n" " --icon-name=NAME Icon name for user\n" " -d --home-dir=PATH Home directory\n" - " --uid=UID Numeric UID for user\n" + " -u --uid=UID Numeric UID for user\n" " -G --member-of=GROUP Add user to group\n" " --skel=PATH Skeleton directory to use\n" " --shell=PATH Shell for account\n" diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c index 1f28c5731f..d3606cf501 100644 --- a/src/libsystemd-network/sd-lldp.c +++ b/src/libsystemd-network/sd-lldp.c @@ -466,7 +466,7 @@ _public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) { _public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) { assert_return(lldp, -EINVAL); - assert_return(m <= 0, -EINVAL); + assert_return(m > 0, -EINVAL); lldp->neighbors_max = m; lldp_make_space(lldp, 0); diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index 7e03817f82..5195a5850f 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -168,6 +168,10 @@ _public_ int sd_network_link_get_address_state(int ifindex, char **state) { return network_link_get_string(ifindex, "ADDRESS_STATE", state); } +_public_ int sd_network_link_get_dhcp4_client_id_string(int ifindex, char **client_id) { + return network_link_get_string(ifindex, "DHCP4_CLIENT_ID", client_id); +} + _public_ int sd_network_link_get_required_for_online(int ifindex) { _cleanup_free_ char *s = NULL; int r; diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 59c2ed278b..dea190eb0d 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -1381,7 +1381,7 @@ static int link_status_one( _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **sip = NULL, **search_domains = NULL, **route_domains = NULL, **pop3_server = NULL, **smtp_server = NULL, **lpr_server = NULL; _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL; - _cleanup_free_ char *t = NULL, *network = NULL; + _cleanup_free_ char *t = NULL, *network = NULL, *client_id = NULL; const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL; const char *on_color_operational, *off_color_operational, *on_color_setup, *off_color_setup; @@ -2073,6 +2073,16 @@ static int link_status_one( return table_log_add_error(r); } + r = sd_network_link_get_dhcp4_client_id_string(info->ifindex, &client_id); + if (r >= 0) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "DHCP4 Client ID:", + TABLE_STRING, client_id); + if (r < 0) + return table_log_add_error(r); + } + r = dump_lldp_neighbors(table, "Connected To:", info->ifindex); if (r < 0) return r; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 805aff3ab1..20c5c1c4c8 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2532,6 +2532,22 @@ static int link_set_ipv6_mtu(Link *link) { return 0; } +static int link_set_ipv4_accept_local(Link *link) { + int r; + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (link->network->ipv4_accept_local < 0) + return 0; + + r = sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local); + if (r < 0) + log_link_warning_errno(link, r, "Cannot set IPv4 accept_local flag for interface: %m"); + + return 0; +} + static bool link_is_static_address_configured(Link *link, Address *address) { Address *net_address; @@ -2871,6 +2887,10 @@ static int link_configure(Link *link) { if (r < 0) return r; + r = link_set_ipv4_accept_local(link); + if (r < 0) + return r; + r = link_set_flags(link); if (r < 0) return r; @@ -4354,6 +4374,8 @@ int link_save(Link *link) { if (link->dhcp_lease) { struct in_addr address; const char *tz = NULL; + size_t client_id_len; + const void *client_id; assert(link->network); @@ -4368,6 +4390,15 @@ int link_save(Link *link) { fputc('\n', f); } + r = sd_dhcp_lease_get_client_id(link->dhcp_lease, &client_id, &client_id_len); + if (r >= 0) { + _cleanup_free_ char *id = NULL; + + r = sd_dhcp_client_id_to_string(client_id, client_id_len, &id); + if (r >= 0) + fprintf(f, "DHCP4_CLIENT_ID=%s\n", id); + } + r = dhcp_lease_save(link->dhcp_lease, link->lease_file); if (r < 0) goto fail; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 3918890664..5c2a4d36a1 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -96,6 +96,7 @@ Network.IPv6DuplicateAddressDetection, config_parse_int, Network.IPv6HopLimit, config_parse_int, 0, offsetof(Network, ipv6_hop_limit) Network.IPv6ProxyNDP, config_parse_tristate, 0, offsetof(Network, ipv6_proxy_ndp) Network.IPv6MTUBytes, config_parse_mtu, AF_INET6, offsetof(Network, ipv6_mtu) +Network.IPv4AcceptLocal, config_parse_tristate, 0, offsetof(Network, ipv4_accept_local) Network.ActiveSlave, config_parse_bool, 0, offsetof(Network, active_slave) Network.PrimarySlave, config_parse_bool, 0, offsetof(Network, primary_slave) Network.IPv4ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 124c570b0e..bbecd706ce 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -450,6 +450,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */ .link_local = _ADDRESS_FAMILY_INVALID, + .ipv4_accept_local = -1, + .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO, .ipv6_accept_ra = -1, .ipv6_dad_transmits = -1, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 934a33ac94..5901622862 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -237,6 +237,7 @@ struct Network { AddressFamily ip_forward; bool ip_masquerade; + int ipv4_accept_local; int ipv6_accept_ra; int ipv6_dad_transmits; diff --git a/src/resolve/resolv.conf b/src/resolve/resolv.conf index c3079aca1d..dc5ac05532 100644 --- a/src/resolve/resolv.conf +++ b/src/resolve/resolv.conf @@ -15,4 +15,4 @@ # operation for /etc/resolv.conf. nameserver 127.0.0.53 -options edns0 +options edns0 trust-ad diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 97aee7abc8..c6f48d6d88 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -307,7 +307,7 @@ static int write_stub_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet "# operation for /etc/resolv.conf.\n" "\n" "nameserver 127.0.0.53\n" - "options edns0\n", f); + "options edns0 trust-ad\n", f); if (!ordered_set_isempty(domains)) write_resolv_conf_search(domains, f); diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index 2637a0f8bb..876f010c95 100644 --- a/src/systemd/sd-network.h +++ b/src/systemd/sd-network.h @@ -188,6 +188,9 @@ int sd_network_link_get_carrier_bound_by(int ifindex, int **ifindexes); /* Get the timezone that was learnt on a specific link. */ int sd_network_link_get_timezone(int ifindex, char **timezone); +/* Get DHCPv4 client id for a given link. */ +int sd_network_link_get_dhcp4_client_id_string(int ifindex, char **client_id); + /* Monitor object */ typedef struct sd_network_monitor sd_network_monitor; diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c index 1627bc747d..3ca5e1e639 100644 --- a/src/test/test-parse-util.c +++ b/src/test/test-parse-util.c @@ -75,14 +75,22 @@ static void test_parse_mode(void) { mode_t m; assert_se(parse_mode("-1", &m) < 0); + assert_se(parse_mode("+1", &m) < 0); assert_se(parse_mode("", &m) < 0); assert_se(parse_mode("888", &m) < 0); assert_se(parse_mode("77777", &m) < 0); assert_se(parse_mode("544", &m) >= 0 && m == 0544); + assert_se(parse_mode("0544", &m) >= 0 && m == 0544); + assert_se(parse_mode("00544", &m) >= 0 && m == 0544); assert_se(parse_mode("777", &m) >= 0 && m == 0777); + assert_se(parse_mode("0777", &m) >= 0 && m == 0777); + assert_se(parse_mode("00777", &m) >= 0 && m == 0777); assert_se(parse_mode("7777", &m) >= 0 && m == 07777); + assert_se(parse_mode("07777", &m) >= 0 && m == 07777); + assert_se(parse_mode("007777", &m) >= 0 && m == 07777); assert_se(parse_mode("0", &m) >= 0 && m == 0); + assert_se(parse_mode(" 1", &m) >= 0 && m == 1); } static void test_parse_size(void) { @@ -358,6 +366,18 @@ static void test_safe_atolli(void) { assert_se(r == 0); assert_se(l == -12345); + r = safe_atolli("0x5", &l); + assert_se(r == 0); + assert_se(l == 5); + + r = safe_atolli("0o6", &l); + assert_se(r == 0); + assert_se(l == 6); + + r = safe_atolli("0B101", &l); + assert_se(r == 0); + assert_se(l == 5); + r = safe_atolli("12345678901234567890", &l); assert_se(r == -ERANGE); @@ -431,6 +451,14 @@ static void test_safe_atoi16(void) { assert_se(r == 0); assert_se(l == 32767); + r = safe_atoi16("0o11", &l); + assert_se(r == 0); + assert_se(l == 9); + + r = safe_atoi16("0B110", &l); + assert_se(r == 0); + assert_se(l == 6); + r = safe_atoi16("36536", &l); assert_se(r == -ERANGE); @@ -475,6 +503,13 @@ static void test_safe_atoux16(void) { r = safe_atoux16(" -1", &l); assert_se(r == -ERANGE); + r = safe_atoux16("0b1", &l); + assert_se(r == 0); + assert_se(l == 177); + + r = safe_atoux16("0o70", &l); + assert_se(r == -EINVAL); + r = safe_atoux16("junk", &l); assert_se(r == -EINVAL); @@ -500,6 +535,14 @@ static void test_safe_atou64(void) { assert_se(r == 0); assert_se(l == 12345); + r = safe_atou64("0o11", &l); + assert_se(r == 0); + assert_se(l == 9); + + r = safe_atou64("0b11", &l); + assert_se(r == 0); + assert_se(l == 3); + r = safe_atou64("18446744073709551617", &l); assert_se(r == -ERANGE); @@ -542,6 +585,14 @@ static void test_safe_atoi64(void) { assert_se(r == 0); assert_se(l == 32767); + r = safe_atoi64(" 0o20", &l); + assert_se(r == 0); + assert_se(l == 16); + + r = safe_atoi64(" 0b01010", &l); + assert_se(r == 0); + assert_se(l == 10); + r = safe_atoi64("9223372036854775813", &l); assert_se(r == -ERANGE); @@ -577,6 +628,13 @@ static void test_safe_atoux64(void) { assert_se(r == 0); assert_se(l == 0x12345); + r = safe_atoux64("0b11011", &l); + assert_se(r == 0); + assert_se(l == 11603985); + + r = safe_atoux64("0o11011", &l); + assert_se(r == -EINVAL); + r = safe_atoux64("18446744073709551617", &l); assert_se(r == -ERANGE); diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c index 3165232fef..c9bff941be 100644 --- a/src/test/test-user-util.c +++ b/src/test/test-user-util.c @@ -42,6 +42,22 @@ static void test_parse_uid(void) { log_info("/* %s */", __func__); + r = parse_uid("0", &uid); + assert_se(r == 0); + assert_se(uid == 0); + + r = parse_uid("1", &uid); + assert_se(r == 0); + assert_se(uid == 1); + + r = parse_uid("01", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 1); + + r = parse_uid("001", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 1); + r = parse_uid("100", &uid); assert_se(r == 0); assert_se(uid == 100); @@ -54,13 +70,57 @@ static void test_parse_uid(void) { assert_se(r == -EINVAL); assert_se(uid == 100); + r = parse_uid("0o1234", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); + + r = parse_uid("0b1234", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); + + r = parse_uid("+1234", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); + + r = parse_uid("-1234", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); + + r = parse_uid(" 1234", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); + r = parse_uid("01234", &uid); - assert_se(r == 0); - assert_se(uid == 1234); + assert_se(r == -EINVAL); + assert_se(uid == 100); + + r = parse_uid("001234", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); + + r = parse_uid("0001234", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); + + r = parse_uid("-0", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); + + r = parse_uid("+0", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); + + r = parse_uid("00", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); + + r = parse_uid("000", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); r = parse_uid("asdsdas", &uid); assert_se(r == -EINVAL); - assert_se(uid == 1234); + assert_se(uid == 100); } static void test_uid_ptr(void) { @@ -359,6 +419,39 @@ static void test_gid_lists_ops(void) { assert_se(gids); } +static void test_parse_uid_range(void) { + uid_t a = 4711, b = 4711; + + log_info("/* %s */", __func__); + + assert_se(parse_uid_range("", &a, &b) == -EINVAL && a == 4711 && b == 4711); + assert_se(parse_uid_range(" ", &a, &b) == -EINVAL && a == 4711 && b == 4711); + assert_se(parse_uid_range("x", &a, &b) == -EINVAL && a == 4711 && b == 4711); + + assert_se(parse_uid_range("0", &a, &b) >= 0 && a == 0 && b == 0); + assert_se(parse_uid_range("1", &a, &b) >= 0 && a == 1 && b == 1); + assert_se(parse_uid_range("2-2", &a, &b) >= 0 && a == 2 && b == 2); + assert_se(parse_uid_range("3-3", &a, &b) >= 0 && a == 3 && b == 3); + assert_se(parse_uid_range("4-5", &a, &b) >= 0 && a == 4 && b == 5); + + assert_se(parse_uid_range("7-6", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range("-1", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range("01", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range("001", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range("+1", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range("1--1", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range(" 1", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range(" 1-2", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range("1 -2", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range("1- 2", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range("1-2 ", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range("01-2", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range("1-02", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range("001-2", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range("1-002", &a, &b) == -EINVAL && a == 4 && b == 5); + assert_se(parse_uid_range(" 01", &a, &b) == -EINVAL && a == 4 && b == 5); +} + int main(int argc, char *argv[]) { test_uid_to_name_one(0, "root"); test_uid_to_name_one(UID_NOBODY, NOBODY_USER_NAME); @@ -396,5 +489,7 @@ int main(int argc, char *argv[]) { test_in_gid(); test_gid_lists_ops(); + test_parse_uid_range(); + return 0; } diff --git a/src/udev/udevd.c b/src/udev/udevd.c index a2b8c6162c..6e0ce72553 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -76,6 +76,7 @@ static unsigned arg_children_max = 0; static usec_t arg_exec_delay_usec = 0; static usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC; static int arg_timeout_signal = SIGKILL; +static bool arg_blockdev_read_only = false; typedef struct Manager { sd_event *event; @@ -383,6 +384,56 @@ static int worker_lock_block_device(sd_device *dev, int *ret_fd) { return 1; } +static int worker_mark_block_device_read_only(sd_device *dev) { + _cleanup_close_ int fd = -1; + const char *val; + int state = 1, r; + + assert(dev); + + if (!arg_blockdev_read_only) + return 0; + + /* Do this only once, when the block device is new. If the device is later retriggered let's not + * toggle the bit again, so that people can boot up with full read-only mode and then unset the bit + * for specific devices only. */ + if (!device_for_action(dev, DEVICE_ACTION_ADD)) + return 0; + + r = sd_device_get_subsystem(dev, &val); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get subsystem: %m"); + + if (!streq(val, "block")) + return 0; + + r = sd_device_get_sysname(dev, &val); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get sysname: %m"); + + /* Exclude synthetic devices for now, this is supposed to be a safety feature to avoid modification + * of physical devices, and what sits on top of those doesn't really matter if we don't allow the + * underlying block devices to recieve changes. */ + if (STARTSWITH_SET(val, "dm-", "md", "drbd", "loop", "nbd", "zram")) + return 0; + + r = sd_device_get_devname(dev, &val); + if (r == -ENOENT) + return 0; + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get devname: %m"); + + fd = open(val, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); + if (fd < 0) + return log_device_debug_errno(dev, errno, "Failed to open '%s', ignoring: %m", val); + + if (ioctl(fd, BLKROSET, &state) < 0) + return log_device_warning_errno(dev, errno, "Failed to mark block device '%s' read-only: %m", val); + + log_device_info(dev, "Successfully marked block device '%s' read-only.", val); + return 0; +} + static int worker_process_device(Manager *manager, sd_device *dev) { _cleanup_(udev_event_freep) UdevEvent *udev_event = NULL; _cleanup_close_ int fd_lock = -1; @@ -412,6 +463,8 @@ static int worker_process_device(Manager *manager, sd_device *dev) { if (r < 0) return r; + (void) worker_mark_block_device_read_only(dev); + /* apply rules, create node, symlinks */ r = udev_event_execute_rules(udev_event, arg_event_timeout_usec, arg_timeout_signal, manager->properties, manager->rules); if (r < 0) @@ -1417,15 +1470,13 @@ static int listen_fds(int *ret_ctrl, int *ret_netlink) { * udev.children_max=<number of workers> events are fully serialized if set to 1 * udev.exec_delay=<number of seconds> delay execution of every executed program * udev.event_timeout=<number of seconds> seconds to wait before terminating an event + * udev.blockdev_read_only<=bool> mark all block devices read-only when they appear */ static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { - int r = 0; + int r; assert(key); - if (!value) - return 0; - if (proc_cmdline_key_streq(key, "udev.log_priority")) { if (proc_cmdline_value_missing(key, value)) @@ -1457,14 +1508,37 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat r = parse_sec(value, &arg_exec_delay_usec); } else if (proc_cmdline_key_streq(key, "udev.timeout_signal")) { + if (proc_cmdline_value_missing(key, value)) return 0; r = signal_from_string(value); if (r > 0) arg_timeout_signal = r; - } else if (startswith(key, "udev.")) - log_warning("Unknown udev kernel command line option \"%s\", ignoring", key); + + } else if (proc_cmdline_key_streq(key, "udev.blockdev_read_only")) { + + if (!value) + arg_blockdev_read_only = true; + else { + r = parse_boolean(value); + if (r < 0) + log_warning_errno(r, "Failed to parse udev.blockdev-read-only argument, ignoring: %s", value); + else + arg_blockdev_read_only = r; + } + + if (arg_blockdev_read_only) + log_notice("All physical block devices will be marked read-only."); + + return 0; + + } else { + if (startswith(key, "udev.")) + log_warning("Unknown udev kernel command line option \"%s\", ignoring.", key); + + return 0; + } if (r < 0) log_warning_errno(r, "Failed to parse \"%s=%s\", ignoring: %m", key, value); diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 7cade0e9ed..478b574418 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -153,6 +153,7 @@ Address= IPv6ProxyNDPAddress= IPv6AcceptRA= IPv6AcceptRouterAdvertisements= +IPv4AcceptLocal= DNSSECNegativeTrustAnchors= MACVTAP= IPv6PrivacyExtensions= diff --git a/test/test-network/conf/25-sysctl.network b/test/test-network/conf/25-sysctl.network index 68be305477..dc1d6542c0 100644 --- a/test/test-network/conf/25-sysctl.network +++ b/test/test-network/conf/25-sysctl.network @@ -9,3 +9,4 @@ IPv6HopLimit=5 IPv4ProxyARP=true IPv6ProxyNDP=true IPv6AcceptRA=no +IPv4AcceptLocal=yes diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index da4063d51c..5d08a7584c 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -2203,6 +2203,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'proxy_ndp'), '1') self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'forwarding'),'1') self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'proxy_arp'), '1') + self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'accept_local'), '1') def test_sysctl_disable_ipv6(self): copy_unit_to_networkd_unit_path('25-sysctl-disable-ipv6.network', '12-dummy.netdev') @@ -2354,7 +2355,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): output = check_output('tc qdisc show dev test1') print(output) self.assertRegex(output, 'qdisc netem') - self.assertRegex(output, 'limit 100 delay 50.0ms 10.0ms loss 20%') + self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%') self.assertRegex(output, 'qdisc ingress') output = check_output('tc qdisc show dev dummy98') @@ -2365,9 +2366,9 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, r'default (0x30|30)') self.assertRegex(output, 'qdisc netem 30: parent 2:30') - self.assertRegex(output, 'limit 100 delay 50.0ms 10.0ms loss 20%') + self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%') self.assertRegex(output, 'qdisc fq_codel') - self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10.0ms ce_threshold 100.0ms interval 200.0ms memory_limit 64Mb ecn') + self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn') self.assertRegex(output, 'qdisc teql1 31: parent 2:31') @@ -2378,13 +2379,13 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'maxrate 1Mbit') self.assertRegex(output, 'qdisc codel 33: parent 2:33') - self.assertRegex(output, 'limit 2000p target 10.0ms ce_threshold 100.0ms interval 50.0ms ecn') + self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn') self.assertRegex(output, 'qdisc fq_codel 34: parent 2:34') - self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10.0ms ce_threshold 100.0ms interval 200.0ms memory_limit 64Mb ecn') + self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn') self.assertRegex(output, 'qdisc tbf 35: parent 2:35') - self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70.0ms') + self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms') self.assertRegex(output, 'qdisc sfq 36: parent 2:36') self.assertRegex(output, 'perturb 5sec') diff --git a/tools/gdb-sd_dump_hashmaps.py b/tools/gdb-sd_dump_hashmaps.py index 4e8593f320..66018a54fd 100644 --- a/tools/gdb-sd_dump_hashmaps.py +++ b/tools/gdb-sd_dump_hashmaps.py @@ -1,79 +1,77 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1+ -from __future__ import print_function - import gdb class sd_dump_hashmaps(gdb.Command): - "dump systemd's hashmaps" - - def __init__(self): - super(sd_dump_hashmaps, self).__init__("sd_dump_hashmaps", gdb.COMMAND_DATA, gdb.COMPLETE_NONE) - - def invoke(self, arg, from_tty): - d = gdb.parse_and_eval("hashmap_debug_list") - all_entry_sizes = gdb.parse_and_eval("all_entry_sizes") - all_direct_buckets = gdb.parse_and_eval("all_direct_buckets") - uchar_t = gdb.lookup_type("unsigned char") - ulong_t = gdb.lookup_type("unsigned long") - debug_offset = gdb.parse_and_eval("(unsigned long)&((HashmapBase*)0)->debug") - - print("type, hash, indirect, entries, max_entries, buckets, creator") - while d: - h = gdb.parse_and_eval("(HashmapBase*)((char*)%d - %d)" % (int(d.cast(ulong_t)), debug_offset)) - - if h["has_indirect"]: - storage_ptr = h["indirect"]["storage"].cast(uchar_t.pointer()) - n_entries = h["indirect"]["n_entries"] - n_buckets = h["indirect"]["n_buckets"] - else: - storage_ptr = h["direct"]["storage"].cast(uchar_t.pointer()) - n_entries = h["n_direct_entries"] - n_buckets = all_direct_buckets[int(h["type"])]; - - t = ["plain", "ordered", "set"][int(h["type"])] - - print("{}, {}, {}, {}, {}, {}, {} ({}:{})".format(t, h["hash_ops"], bool(h["has_indirect"]), n_entries, d["max_entries"], n_buckets, d["func"], d["file"], d["line"])) - - if arg != "" and n_entries > 0: - dib_raw_addr = storage_ptr + (all_entry_sizes[h["type"]] * n_buckets) - - histogram = {} - for i in xrange(0, n_buckets): - dib = int(dib_raw_addr[i]) - histogram[dib] = histogram.get(dib, 0) + 1 - - for dib in sorted(iter(histogram)): - if dib != 255: - print("{:>3} {:>8} {} of entries".format(dib, histogram[dib], 100.0*histogram[dib]/n_entries)) - else: - print("{:>3} {:>8} {} of slots".format(dib, histogram[dib], 100.0*histogram[dib]/n_buckets)) - print("mean DIB of entries: {}".format(sum([dib*histogram[dib] for dib in iter(histogram) if dib != 255])*1.0/n_entries)) - - blocks = [] - current_len = 1 - prev = int(dib_raw_addr[0]) - for i in xrange(1, n_buckets): - dib = int(dib_raw_addr[i]) - if (dib == 255) != (prev == 255): - if prev != 255: - blocks += [[i, current_len]] - current_len = 1 - else: - current_len += 1 - - prev = dib - if prev != 255: - blocks += [[i, current_len]] - # a block may be wrapped around - if len(blocks) > 1 and blocks[0][0] == blocks[0][1] and blocks[-1][0] == n_buckets - 1: - blocks[0][1] += blocks[-1][1] - blocks = blocks[0:-1] - print("max block: {}".format(max(blocks, key=lambda a: a[1]))) - print("sum block lens: {}".format(sum(b[1] for b in blocks))) - print("mean block len: {}".format((1.0 * sum(b[1] for b in blocks) / len(blocks)))) - - d = d["debug_list_next"] + "dump systemd's hashmaps" + + def __init__(self): + super().__init__("sd_dump_hashmaps", gdb.COMMAND_DATA, gdb.COMPLETE_NONE) + + def invoke(self, arg, from_tty): + d = gdb.parse_and_eval("hashmap_debug_list") + hashmap_type_info = gdb.parse_and_eval("hashmap_type_info") + uchar_t = gdb.lookup_type("unsigned char") + ulong_t = gdb.lookup_type("unsigned long") + debug_offset = gdb.parse_and_eval("(unsigned long)&((HashmapBase*)0)->debug") + + print("type, hash, indirect, entries, max_entries, buckets, creator") + while d: + h = gdb.parse_and_eval(f"(HashmapBase*)((char*){int(d.cast(ulong_t))} - {debug_offset})") + + if h["has_indirect"]: + storage_ptr = h["indirect"]["storage"].cast(uchar_t.pointer()) + n_entries = h["indirect"]["n_entries"] + n_buckets = h["indirect"]["n_buckets"] + else: + storage_ptr = h["direct"]["storage"].cast(uchar_t.pointer()) + n_entries = h["n_direct_entries"] + n_buckets = hashmap_type_info[h["type"]]["n_direct_buckets"] + + t = ["plain", "ordered", "set"][int(h["type"])] + + print(f'{t}, {h["hash_ops"]}, {bool(h["has_indirect"])}, {n_entries}, {d["max_entries"]}, {n_buckets}, {d["func"].string()}, {d["file"].string()}:{d["line"]}') + + if arg != "" and n_entries > 0: + dib_raw_addr = storage_ptr + hashmap_type_info[h["type"]]["entry_size"] * n_buckets + + histogram = {} + for i in range(0, n_buckets): + dib = int(dib_raw_addr[i]) + histogram[dib] = histogram.get(dib, 0) + 1 + + for dib in sorted(histogram): + if dib != 255: + print(f"{dib:>3} {histogram[dib]:>8} {float(histogram[dib]/n_entries):.0%} of entries") + else: + print(f"{dib:>3} {histogram[dib]:>8} {float(histogram[dib]/n_buckets):.0%} of slots") + s = sum(dib*count for (dib, count) in histogram.items() if dib != 255) / n_entries + print(f"mean DIB of entries: {s}") + + blocks = [] + current_len = 1 + prev = int(dib_raw_addr[0]) + for i in range(1, n_buckets): + dib = int(dib_raw_addr[i]) + if (dib == 255) != (prev == 255): + if prev != 255: + blocks += [[i, current_len]] + current_len = 1 + else: + current_len += 1 + + prev = dib + if prev != 255: + blocks += [[i, current_len]] + # a block may be wrapped around + if len(blocks) > 1 and blocks[0][0] == blocks[0][1] and blocks[-1][0] == n_buckets - 1: + blocks[0][1] += blocks[-1][1] + blocks = blocks[0:-1] + print("max block: {}".format(max(blocks, key=lambda a: a[1]))) + print("sum block lens: {}".format(sum(b[1] for b in blocks))) + print("mean block len: {}".format(sum(b[1] for b in blocks) / len(blocks))) + + d = d["debug_list_next"] sd_dump_hashmaps() diff --git a/units/initrd-udevadm-cleanup-db.service b/units/initrd-udevadm-cleanup-db.service index ad2f2a5b35..810cf5775e 100644 --- a/units/initrd-udevadm-cleanup-db.service +++ b/units/initrd-udevadm-cleanup-db.service @@ -8,7 +8,7 @@ # (at your option) any later version. [Unit] -Description=Cleanup udevd DB +Description=Cleanup udev Database DefaultDependencies=no ConditionPathExists=/etc/initrd-release Conflicts=systemd-udevd.service systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udev-trigger.service systemd-udev-settle.service diff --git a/units/systemd-udev-settle.service b/units/systemd-udev-settle.service index ed6a68b864..9352c6f598 100644 --- a/units/systemd-udev-settle.service +++ b/units/systemd-udev-settle.service @@ -12,7 +12,7 @@ # expect a populated /dev during bootup. [Unit] -Description=udev Wait for Complete Device Initialization +Description=Wait for udev To Complete Device Initialization Documentation=man:systemd-udev-settle.service(8) DefaultDependencies=no Wants=systemd-udevd.service diff --git a/units/systemd-udev-trigger.service b/units/systemd-udev-trigger.service index 8a625b6305..cfe8d61c2a 100644 --- a/units/systemd-udev-trigger.service +++ b/units/systemd-udev-trigger.service @@ -8,7 +8,7 @@ # (at your option) any later version. [Unit] -Description=udev Coldplug all Devices +Description=Coldplug All udev Devices Documentation=man:udev(7) man:systemd-udevd.service(8) DefaultDependencies=no Wants=systemd-udevd.service |