diff options
author | Mark Hindley <mark@hindley.org.uk> | 2018-11-12 09:10:28 +0000 |
---|---|---|
committer | Mark Hindley <mark@hindley.org.uk> | 2018-11-12 09:21:21 +0000 |
commit | 2cc17d30309a1db16cccbf376a59ae40e47b6959 (patch) | |
tree | cd51ee95799631af348ebae8630a69219bee99cd /src/shared | |
parent | ae65e91a5439f395e0da0cd8ffda95d6289849e1 (diff) | |
parent | d4a3f291e3955648ea1d29e674b0f8f9b1556257 (diff) |
Merge remote-tracking branch 'upstream/v239-stable' into merge_upstream.
Diffstat (limited to 'src/shared')
-rw-r--r-- | src/shared/bus-util.c | 12 | ||||
-rw-r--r-- | src/shared/bus-util.h | 4 | ||||
-rw-r--r-- | src/shared/enable-mempool.c | 5 | ||||
-rw-r--r-- | src/shared/meson.build | 11 | ||||
-rw-r--r-- | src/shared/musl_missing.c | 17 | ||||
-rw-r--r-- | src/shared/musl_missing.h | 3 | ||||
-rw-r--r-- | src/shared/nsflags.h | 4 | ||||
-rw-r--r-- | src/shared/qsort_r_missing.c | 497 | ||||
-rw-r--r-- | src/shared/qsort_r_missing.h | 34 | ||||
-rw-r--r-- | src/shared/sleep-config.c | 225 | ||||
-rw-r--r-- | src/shared/sleep-config.h | 2 |
11 files changed, 758 insertions, 56 deletions
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index c3be78769..c03fe2b46 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -941,7 +941,7 @@ int bus_message_print_all_properties( return log_oom(); r = set_put(*found_properties, name); - if (r < 0 && r != EEXIST) + if (r < 0 && r != -EEXIST) return log_oom(); } @@ -1299,10 +1299,10 @@ int bus_connect_transport(BusTransport transport, const char *host, bool user, s switch (transport) { case BUS_TRANSPORT_LOCAL: -#if 0 /// elogind does not support a user bus if (user) r = sd_bus_default_user(&bus); else { +#if 0 /// elogind is never used with systemd. Avoid useless check. if (sd_booted() <= 0) { /* Print a friendly message when the local system is actually not running systemd as PID 1. */ log_error("System has not been booted with systemd as init system (PID 1). Can't operate."); @@ -1311,9 +1311,7 @@ int bus_connect_transport(BusTransport transport, const char *host, bool user, s } #endif // 0 r = sd_bus_default_system(&bus); -#if 0 /// No closing bracket with elogind... Ain't we simple? ;-) } -#endif // 0 break; case BUS_TRANSPORT_REMOTE: @@ -1356,12 +1354,14 @@ int bus_connect_transport_systemd(BusTransport transport, const char *host, bool if (user) r = bus_connect_user_systemd(bus); else { +#if 0 /// elogind is never used with systemd. Avoid useless check. if (sd_booted() <= 0) { /* Print a friendly message when the local system is actually not running systemd as PID 1. */ log_error("System has not been booted with systemd as init system (PID 1). Can't operate."); return -EHOSTDOWN; } +#endif // 0 r = bus_connect_system_systemd(bus); } break; @@ -1484,12 +1484,10 @@ int bus_log_parse_error(int r) { return log_error_errno(r, "Failed to parse bus message: %m"); } -#if 0 /// UNNEEDED by elogind int bus_log_create_error(int r) { return log_error_errno(r, "Failed to create bus message: %m"); } -#endif // 0 #if 0 /// UNNEEDED by elogind /** * bus_path_encode_unique() - encode unique object path @@ -1758,7 +1756,7 @@ static void request_name_destroy_callback(void *userdata) { assert(data); assert(data->n_ref > 0); - log_info("%s n_ref=%u", __func__, data->n_ref); + log_debug("%s n_ref=%u", __func__, data->n_ref); data->n_ref--; if (data->n_ref == 0) diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index e7231bea9..153ea1240 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -37,10 +37,10 @@ enum { int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata); -#if 0 /// UNNEEDED by elogind -#endif // 0 int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata); +#if 0 /// UNNEEDED by elogind int bus_message_map_properties_changed(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata); +#endif // 0 int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, sd_bus_message **reply, void *userdata); diff --git a/src/shared/enable-mempool.c b/src/shared/enable-mempool.c new file mode 100644 index 000000000..a571b43f5 --- /dev/null +++ b/src/shared/enable-mempool.c @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "mempool.h" + +const bool mempool_use_allowed = true; diff --git a/src/shared/meson.build b/src/shared/meson.build index b215e267d..944909c67 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -37,6 +37,7 @@ # dropin.h # efivars.c # efivars.h +# enable-mempool.c # fdset.c # fdset.h # firewall-util.h @@ -64,6 +65,7 @@ # machine-image.h # machine-pool.c # machine-pool.h +# module-util.h # nsflags.c # nsflags.h # output-mode.c @@ -112,12 +114,15 @@ shared_sources = ''' clean-ipc.h conf-parser.c conf-parser.h + enable-mempool.c musl_missing.c musl_missing.h nsflags.c nsflags.h output-mode.c output-mode.h + qsort_r_missing.c + qsort_r_missing.h sleep-config.c sleep-config.h spawn-polkit-agent.c @@ -148,6 +153,11 @@ endif #endif // 0 libshared_name = 'elogind-shared-@0@'.format(meson.project_version()) +#if 0 /// elogind does not play around with kernel modules. Not its job. +# if conf.get('HAVE_KMOD') == 1 +# shared_sources += files('module-util.c') +# endif +#endif // 0 #if 0 /// elogind doesn't need all this # libshared_deps = [threads, @@ -157,6 +167,7 @@ libshared_name = 'elogind-shared-@0@'.format(meson.project_version()) # libcryptsetup, # libgcrypt, # libiptc, +# libkmod, # libseccomp, # libselinux, # libidn, diff --git a/src/shared/musl_missing.c b/src/shared/musl_missing.c index 0f8b4d1ad..a34e4eaec 100644 --- a/src/shared/musl_missing.c +++ b/src/shared/musl_missing.c @@ -26,17 +26,17 @@ #if HAVE_PROGRAM_INVOCATION_NAME == 0 char *program_invocation_name = NULL; char *program_invocation_short_name = NULL; -#endif // libc does not provide these variables -const char *program_arg_name = NULL; +const char *program_arg_name = NULL; /* Helper */ +#endif // libc does not provide program_invocation_[short_]name #include "musl_missing.h" +#if HAVE_PROGRAM_INVOCATION_NAME == 0 static void elogind_free_program_name(void) { - - if (program_invocation_name && (program_invocation_name != program_arg_name) && strlen(program_invocation_name)) + if (program_invocation_name) program_invocation_name = mfree(program_invocation_name); - if (program_invocation_short_name && (program_invocation_short_name != program_arg_name) && strlen(program_invocation_short_name)) + if (program_invocation_short_name) program_invocation_short_name = mfree(program_invocation_short_name); } @@ -55,8 +55,11 @@ void elogind_set_program_name(const char* pcall) { program_invocation_name = strdup(program_arg_name); if (NULL == program_invocation_short_name) program_invocation_short_name = strdup(basename(program_arg_name)); -#if HAVE_PROGRAM_INVOCATION_NAME == 0 atexit(elogind_free_program_name); -#endif // libc does not provide these variables } +#else +void elogind_set_program_name(const char* pcall) { + assert(pcall && pcall[0]); +} +#endif // libc does not provide program_invocation_[short_]name diff --git a/src/shared/musl_missing.h b/src/shared/musl_missing.h index cf5a01481..6f1e25b6d 100644 --- a/src/shared/musl_missing.h +++ b/src/shared/musl_missing.h @@ -19,8 +19,9 @@ void elogind_set_program_name(const char* pcall); +#include "qsort_r_missing.h" + #if !defined(__GLIBC__) -#include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> /* for pthread_atfork */ diff --git a/src/shared/nsflags.h b/src/shared/nsflags.h index 4fe5b55cf..ffb81bffe 100644 --- a/src/shared/nsflags.h +++ b/src/shared/nsflags.h @@ -18,11 +18,11 @@ CLONE_NEWUSER| \ CLONE_NEWUTS)) -#if 0 /// UNNEEDED by elogind -#endif // 0 #define NAMESPACE_FLAGS_INITIAL ((unsigned long) -1) +#if 0 /// UNNEEDED by elogind int namespace_flags_from_string(const char *name, unsigned long *ret); +#endif // 0 int namespace_flags_to_string(unsigned long flags, char **ret); struct namespace_flag_map { diff --git a/src/shared/qsort_r_missing.c b/src/shared/qsort_r_missing.c new file mode 100644 index 000000000..8cef9e736 --- /dev/null +++ b/src/shared/qsort_r_missing.c @@ -0,0 +1,497 @@ +/*** + This file is part of elogind. + + Copyright 2017-2018 Sven Eden + + elogind 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. + + elogind 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 elogind; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "qsort_r_missing.h" + +#if HAVE_QSORT_R == 0 + +#include <alloca.h> +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> + +/*** + Original disclaimer of glibc-2.28 msort.c concerning qsort_r() follows: +***/ + +/* An alternative to qsort, with an identical interface. + This file is part of the GNU C Library. + Copyright (C) 1992-2018 Free Software Foundation, Inc. + Written by Mike Haertel, September 1988. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + + +/* qsort_r() calls internal _quicksort() function from qsort.c. Disclaimer is as above. */ +static void quicksort ( void* const pbase, size_t total_elems, size_t size, compare_fn_t cmp, void* arg ); + +struct msort_param { + size_t s; + size_t var; + compare_fn_t cmp; + void* arg; + char* t; +}; + +static void msort_with_tmp ( const struct msort_param* p, void* b, size_t n ) { + char* b1, *b2; + size_t n1, n2; + + if ( n <= 1 ) + return; + + n1 = n / 2; + n2 = n - n1; + b1 = b; + b2 = ( char* ) b + ( n1 * p->s ); + + msort_with_tmp ( p, b1, n1 ); + msort_with_tmp ( p, b2, n2 ); + + char* tmp = p->t; + const size_t s = p->s; + compare_fn_t cmp = p->cmp; + void* arg = p->arg; + switch ( p->var ) { + case 0: + while ( n1 > 0 && n2 > 0 ) { + if ( ( *cmp ) ( b1, b2, arg ) <= 0 ) { + *( uint32_t* ) tmp = *( uint32_t* ) b1; + b1 += sizeof ( uint32_t ); + --n1; + } else { + *( uint32_t* ) tmp = *( uint32_t* ) b2; + b2 += sizeof ( uint32_t ); + --n2; + } + tmp += sizeof ( uint32_t ); + } + break; + case 1: + while ( n1 > 0 && n2 > 0 ) { + if ( ( *cmp ) ( b1, b2, arg ) <= 0 ) { + *( uint64_t* ) tmp = *( uint64_t* ) b1; + b1 += sizeof ( uint64_t ); + --n1; + } else { + *( uint64_t* ) tmp = *( uint64_t* ) b2; + b2 += sizeof ( uint64_t ); + --n2; + } + tmp += sizeof ( uint64_t ); + } + break; + case 2: + while ( n1 > 0 && n2 > 0 ) { + unsigned long* tmpl = ( unsigned long* ) tmp; + unsigned long* bl; + + tmp += s; + if ( ( *cmp ) ( b1, b2, arg ) <= 0 ) { + bl = ( unsigned long* ) b1; + b1 += s; + --n1; + } else { + bl = ( unsigned long* ) b2; + b2 += s; + --n2; + } + while ( tmpl < ( unsigned long* ) tmp ) + *tmpl++ = *bl++; + } + break; + case 3: + while ( n1 > 0 && n2 > 0 ) { + if ( ( *cmp ) ( *( const void** ) b1, *( const void** ) b2, arg ) <= 0 ) { + *( void** ) tmp = *( void** ) b1; + b1 += sizeof ( void* ); + --n1; + } else { + *( void** ) tmp = *( void** ) b2; + b2 += sizeof ( void* ); + --n2; + } + tmp += sizeof ( void* ); + } + break; + default: + while ( n1 > 0 && n2 > 0 ) { + if ( ( *cmp ) ( b1, b2, arg ) <= 0 ) { + tmp = ( char* ) memcpy ( tmp, b1, s ); + b1 += s; + --n1; + } else { + tmp = ( char* ) memcpy ( tmp, b2, s ); + b2 += s; + --n2; + } + } + break; + } + + if ( n1 > 0 ) + memcpy ( tmp, b1, n1 * s ); + memcpy ( b, p->t, ( n - n2 ) * s ); +} + +void qsort_r ( void* b, size_t n, size_t s, compare_fn_t cmp, void* arg ) { + size_t size = n * s; + char* tmp = NULL; + struct msort_param p; + + /* For large object sizes use indirect sorting. */ + if ( s > 32 ) + size = 2 * n * sizeof ( void* ) + s; + + if ( size < 1024 ) + /* The temporary array is small, so put it on the stack. */ + p.t = alloca ( size ); + else { + /* We should avoid allocating too much memory since this might + have to be backed up by swap space. */ + static long int phys_pages; + static int pagesize; + + if ( pagesize == 0 ) { + phys_pages = sysconf ( _SC_PHYS_PAGES ); + + if ( phys_pages == -1 ) + /* Error while determining the memory size. So let's + assume there is enough memory. Otherwise the + implementer should provide a complete implementation of + the `sysconf' function. */ + phys_pages = ( long int ) ( ~0ul >> 1 ); + + /* The following determines that we will never use more than + a quarter of the physical memory. */ + phys_pages /= 4; + + /* Make sure phys_pages is written to memory. */ + __asm ( "" ::: "memory" ); /*atomic_write_barrier () */ + + pagesize = sysconf ( _SC_PAGESIZE ); + } + + /* Just a comment here. We cannot compute + phys_pages * pagesize + and compare the needed amount of memory against this value. + The problem is that some systems might have more physical + memory then can be represented with a `size_t' value (when + measured in bytes. */ + + /* If the memory requirements are too high don't allocate memory. */ + if ( size / pagesize > ( size_t ) phys_pages ) { + quicksort ( b, n, s, cmp, arg ); + return; + } + + /* It's somewhat large, so malloc it. */ + int save = errno; + tmp = malloc ( size ); + errno = ( save ); + if ( tmp == NULL ) { + /* Couldn't get space, so use the slower algorithm + that doesn't need a temporary array. */ + quicksort ( b, n, s, cmp, arg ); + return; + } + p.t = tmp; + } + + p.s = s; + p.var = 4; + p.cmp = cmp; + p.arg = arg; + + if ( s > 32 ) { + /* Indirect sorting. */ + char* ip = ( char* ) b; + void** tp = ( void** ) ( p.t + n * sizeof ( void* ) ); + void** t = tp; + void* tmp_storage = ( void* ) ( tp + n ); + + while ( ( void* ) t < tmp_storage ) { + *t++ = ip; + ip += s; + } + p.s = sizeof ( void* ); + p.var = 3; + msort_with_tmp ( &p, p.t + n * sizeof ( void* ), n ); + + /* tp[0] .. tp[n - 1] is now sorted, copy around entries of + the original array. Knuth vol. 3 (2nd ed.) exercise 5.2-10. */ + char* kp; + size_t i; + for ( i = 0, ip = ( char* ) b; i < n; i++, ip += s ) + if ( ( kp = tp[i] ) != ip ) { + size_t j = i; + char* jp = ip; + memcpy ( tmp_storage, ip, s ); + + do { + size_t k = ( kp - ( char* ) b ) / s; + tp[j] = jp; + memcpy ( jp, kp, s ); + j = k; + jp = kp; + kp = tp[k]; + } while ( kp != ip ); + + tp[j] = jp; + memcpy ( jp, tmp_storage, s ); + } + } else { + if ( ( s & ( sizeof ( uint32_t ) - 1 ) ) == 0 + && ( ( char* ) b - ( char* ) 0 ) % __alignof__ ( uint32_t ) == 0 ) { + if ( s == sizeof ( uint32_t ) ) + p.var = 0; + else if ( s == sizeof ( uint64_t ) + && ( ( char* ) b - ( char* ) 0 ) % __alignof__ ( uint64_t ) == 0 ) + p.var = 1; + else if ( ( s & ( sizeof ( unsigned long ) - 1 ) ) == 0 + && ( ( char* ) b - ( char* ) 0 ) + % __alignof__ ( unsigned long ) == 0 ) + p.var = 2; + } + msort_with_tmp ( &p, b, n ); + } + free ( tmp ); +} + +/**** quicksort from qsort.c follows ****/ + +/* Byte-wise swap two items of size SIZE. */ +#define SWAP(a, b, size) \ + do \ + { \ + size_t size_ = (size); \ + char *a_ = (a), *b_ = (b); \ + do \ + { \ + char tmp_ = *a_; \ + *a_++ = *b_; \ + *b_++ = tmp_; \ + } while (--size_ > 0); \ + } while (0) + +/* Discontinue quicksort algorithm when partition gets below this size. + This particular magic number was chosen to work best on a Sun 4/260. */ +#define MAX_THRESH 4 + +/* Stack node declarations used to store unfulfilled partition obligations. */ +typedef struct { + char* lo; + char* hi; +} stack_node; + +/* The next 4 #defines implement a very fast in-line stack abstraction. */ +/* The stack needs log (total_elements) entries (we could even subtract + log(MAX_THRESH)). Since total_elements has type size_t, we get as + upper bound for log (total_elements): + bits per byte (CHAR_BIT) * sizeof(size_t). */ +#define STACK_SIZE (CHAR_BIT * sizeof(size_t)) +#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top)) +#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi))) +#define STACK_NOT_EMPTY (stack < top) + +/* Order size using quicksort. This implementation incorporates + four optimizations discussed in Sedgewick: + + 1. Non-recursive, using an explicit stack of pointer that store the + next array partition to sort. To save time, this maximum amount + of space required to store an array of SIZE_MAX is allocated on the + stack. Assuming a 32-bit (64 bit) integer for size_t, this needs + only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes). + Pretty cheap, actually. + + 2. Chose the pivot element using a median-of-three decision tree. + This reduces the probability of selecting a bad pivot value and + eliminates certain extraneous comparisons. + + 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving + insertion sort to order the MAX_THRESH items within each partition. + This is a big win, since insertion sort is faster for small, mostly + sorted array segments. + + 4. The larger of the two sub-partitions is always pushed onto the + stack first, with the algorithm then concentrating on the + smaller partition. This *guarantees* no more than log (total_elems) + stack size is needed (actually O(1) in this case)! */ + +static void quicksort ( void* const pbase, size_t total_elems, size_t size, compare_fn_t cmp, void* arg ) { + char* base_ptr = ( char* ) pbase; + + const size_t max_thresh = MAX_THRESH * size; + + if ( total_elems == 0 ) + /* Avoid lossage with unsigned arithmetic below. */ + return; + + if ( total_elems > MAX_THRESH ) { + char* lo = base_ptr; + char* hi = &lo[size * ( total_elems - 1 )]; + stack_node stack[STACK_SIZE]; + stack_node* top = stack; + + PUSH ( NULL, NULL ); + + while ( STACK_NOT_EMPTY ) { + char* left_ptr; + char* right_ptr; + + /* Select median value from among LO, MID, and HI. Rearrange + LO and HI so the three values are sorted. This lowers the + probability of picking a pathological pivot value and + skips a comparison for both the LEFT_PTR and RIGHT_PTR in + the while loops. */ + + char* mid = lo + size * ( ( hi - lo ) / size >> 1 ); + + if ( ( *cmp ) ( ( void* ) mid, ( void* ) lo, arg ) < 0 ) + SWAP ( mid, lo, size ); + if ( ( *cmp ) ( ( void* ) hi, ( void* ) mid, arg ) < 0 ) + SWAP ( mid, hi, size ); + else + goto jump_over; + if ( ( *cmp ) ( ( void* ) mid, ( void* ) lo, arg ) < 0 ) + SWAP ( mid, lo, size ); + jump_over: + ; + + left_ptr = lo + size; + right_ptr = hi - size; + + /* Here's the famous ``collapse the walls'' section of quicksort. + Gotta like those tight inner loops! They are the main reason + that this algorithm runs much faster than others. */ + do { + while ( ( *cmp ) ( ( void* ) left_ptr, ( void* ) mid, arg ) < 0 ) + left_ptr += size; + + while ( ( *cmp ) ( ( void* ) mid, ( void* ) right_ptr, arg ) < 0 ) + right_ptr -= size; + + if ( left_ptr < right_ptr ) { + SWAP ( left_ptr, right_ptr, size ); + if ( mid == left_ptr ) + mid = right_ptr; + else if ( mid == right_ptr ) + mid = left_ptr; + left_ptr += size; + right_ptr -= size; + } else if ( left_ptr == right_ptr ) { + left_ptr += size; + right_ptr -= size; + break; + } + } while ( left_ptr <= right_ptr ); + + /* Set up pointers for next iteration. First determine whether + left and right partitions are below the threshold size. If so, + ignore one or both. Otherwise, push the larger partition's + bounds on the stack and continue sorting the smaller one. */ + + if ( ( size_t ) ( right_ptr - lo ) <= max_thresh ) { + if ( ( size_t ) ( hi - left_ptr ) <= max_thresh ) + /* Ignore both small partitions. */ + POP ( lo, hi ); + else + /* Ignore small left partition. */ + lo = left_ptr; + } else if ( ( size_t ) ( hi - left_ptr ) <= max_thresh ) + /* Ignore small right partition. */ + hi = right_ptr; + else if ( ( right_ptr - lo ) > ( hi - left_ptr ) ) { + /* Push larger left partition indices. */ + PUSH ( lo, right_ptr ); + lo = left_ptr; + } else { + /* Push larger right partition indices. */ + PUSH ( left_ptr, hi ); + hi = right_ptr; + } + } + } + + /* Once the BASE_PTR array is partially sorted by quicksort the rest + is completely sorted using insertion sort, since this is efficient + for partitions below MAX_THRESH size. BASE_PTR points to the beginning + of the array to sort, and END_PTR points at the very last element in + the array (*not* one beyond it!). */ + +#define min(x, y) ((x) < (y) ? (x) : (y)) + + { + char* const end_ptr = &base_ptr[size * ( total_elems - 1 )]; + char* tmp_ptr = base_ptr; + char* thresh = min( end_ptr, base_ptr + max_thresh ); + char* run_ptr; + + /* Find smallest element in first threshold and place it at the + array's beginning. This is the smallest array element, + and the operation speeds up insertion sort's inner loop. */ + + for ( run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size ) + if ( ( *cmp ) ( ( void* ) run_ptr, ( void* ) tmp_ptr, arg ) < 0 ) + tmp_ptr = run_ptr; + + if ( tmp_ptr != base_ptr ) + SWAP ( tmp_ptr, base_ptr, size ); + + /* Insertion sort, running from left-hand-side up to right-hand-side. */ + + run_ptr = base_ptr + size; + while ( ( run_ptr += size ) <= end_ptr ) { + tmp_ptr = run_ptr - size; + while ( ( *cmp ) ( ( void* ) run_ptr, ( void* ) tmp_ptr, arg ) < 0 ) + tmp_ptr -= size; + + tmp_ptr += size; + if ( tmp_ptr != run_ptr ) { + char* trav; + + trav = run_ptr + size; + while ( --trav >= run_ptr ) { + char c = *trav; + char* hi, *lo; + + for ( hi = lo = trav; ( lo -= size ) >= tmp_ptr; hi = lo ) + * hi = *lo; + *hi = c; + } + } + } + } +} + +#endif // HAVE_QSORT_R diff --git a/src/shared/qsort_r_missing.h b/src/shared/qsort_r_missing.h new file mode 100644 index 000000000..32c036c0d --- /dev/null +++ b/src/shared/qsort_r_missing.h @@ -0,0 +1,34 @@ +#pragma once +#ifndef ELOGIND_SRC_SHARED_QSORT_R_MISSING_H_INCLUDED +#define ELOGIND_SRC_SHARED_QSORT_R_MISSING_H_INCLUDED + +/*** + This file is part of elogind. + + Copyright 2017-2018 Sven Eden + + elogind 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. + + elogind 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 elogind; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdlib.h> + +#if HAVE_QSORT_R == 0 + +typedef int (*compare_fn_t) (const void *, const void *, void *); + +void qsort_r (void *base_, size_t nmemb_, size_t size_, compare_fn_t comp_, void *arg_); + +#endif // qsort_r() missing + +#endif // ELOGIND_SRC_SHARED_QSORT_R_MISSING_H_INCLUDED diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 77b380ca3..804581666 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -13,6 +13,7 @@ //#include <unistd.h> #include "alloc-util.h" +//#include "bootspec.h" //#include "conf-parser.h" //#include "def.h" //#include "env-util.h" @@ -21,13 +22,17 @@ //#include "log.h" //#include "macro.h" #include "parse-util.h" +#include "path-util.h" +#include "proc-cmdline.h" #include "sleep-config.h" #include "string-util.h" #include "strv.h" #if 0 /// UNNEEDED by elogind -int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t *_delay) { - +int parse_sleep_config(const char *verb, bool *ret_allow, char ***ret_modes, char ***ret_states, usec_t *ret_delay) { + int allow_suspend = -1, allow_hibernate = -1, + allow_s2h = -1, allow_hybrid_sleep = -1; + bool allow; _cleanup_strv_free_ char **suspend_mode = NULL, **suspend_state = NULL, **hibernate_mode = NULL, **hibernate_state = NULL, @@ -36,13 +41,19 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t usec_t delay = 180 * USEC_PER_MINUTE; const ConfigTableItem items[] = { - { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode }, - { "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state }, - { "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode }, - { "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state }, - { "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode }, - { "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state }, - { "Sleep", "HibernateDelaySec", config_parse_sec, 0, &delay}, + { "Sleep", "AllowSuspend", config_parse_tristate, 0, &allow_suspend }, + { "Sleep", "AllowHibernation", config_parse_tristate, 0, &allow_hibernate }, + { "Sleep", "AllowSuspendThenHibernate", config_parse_tristate, 0, &allow_s2h }, + { "Sleep", "AllowHybridSleep", config_parse_tristate, 0, &allow_hybrid_sleep }, + + { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode }, + { "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state }, + { "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode }, + { "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state }, + { "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode }, + { "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state }, + + { "Sleep", "HibernateDelaySec", config_parse_sec, 0, &delay}, {} }; @@ -52,6 +63,8 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t CONFIG_PARSE_WARN, NULL); if (streq(verb, "suspend")) { + allow = allow_suspend != 0; + /* empty by default */ modes = TAKE_PTR(suspend_mode); @@ -61,6 +74,8 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t states = strv_new("mem", "standby", "freeze", NULL); } else if (streq(verb, "hibernate")) { + allow = allow_hibernate != 0; + if (hibernate_mode) modes = TAKE_PTR(hibernate_mode); else @@ -72,6 +87,9 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t states = strv_new("disk", NULL); } else if (streq(verb, "hybrid-sleep")) { + allow = allow_hybrid_sleep > 0 || + (allow_suspend != 0 && allow_hibernate != 0); + if (hybrid_mode) modes = TAKE_PTR(hybrid_mode); else @@ -82,21 +100,26 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t else states = strv_new("disk", NULL); - } else if (streq(verb, "suspend-then-hibernate")) + } else if (streq(verb, "suspend-then-hibernate")) { + allow = allow_s2h > 0 || + (allow_suspend != 0 && allow_hibernate != 0); + modes = states = NULL; - else + } else assert_not_reached("what verb"); if ((!modes && STR_IN_SET(verb, "hibernate", "hybrid-sleep")) || (!states && !streq(verb, "suspend-then-hibernate"))) return log_oom(); - if (_modes) - *_modes = TAKE_PTR(modes); - if (_states) - *_states = TAKE_PTR(states); - if (_delay) - *_delay = delay; + if (ret_allow) + *ret_allow = allow; + if (ret_modes) + *ret_modes = TAKE_PTR(modes); + if (ret_states) + *ret_states = TAKE_PTR(states); + if (ret_delay) + *ret_delay = delay; return 0; } @@ -201,18 +224,30 @@ int find_hibernate_location(char **device, char **type, size_t *size, size_t *us "%zu " /* used */ "%*i\n", /* priority */ &dev_field, &type_field, &size_field, &used_field); + if (k == EOF) + break; if (k != 4) { - if (k == EOF) - break; - log_warning("Failed to parse /proc/swaps:%u", i); continue; } - if (streq(type_field, "partition") && endswith(dev_field, "\\040(deleted)")) { - log_warning("Ignoring deleted swapfile '%s'.", dev_field); - continue; + if (streq(type_field, "file")) { + + if (endswith(dev_field, "\\040(deleted)")) { + log_warning("Ignoring deleted swap file '%s'.", dev_field); + continue; + } + + } else if (streq(type_field, "partition")) { + const char *fn; + + fn = path_startswith(dev_field, "/dev/"); + if (fn && startswith(fn, "zram")) { + log_debug("Ignoring compressed RAM swap device '%s'.", dev_field); + continue; + } } + if (device) *device = TAKE_PTR(dev_field); if (type) @@ -257,12 +292,96 @@ static bool enough_swap_for_hibernation(void) { } r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD; - log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%", - r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD); + log_debug("%s swap for hibernation, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%", + r ? "Enough" : "Not enough", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD); return r; } +#if 0 /// elogind is not init and can not check boot devices and partitions. +static int check_resume_keys(const char *key, const char *value, void *data) { + assert_se(key); + assert_se(data); + + int *resume = data; + + if (*resume == 0) + /* Exit if we already know we can't resume. */ + return 0; + + if (streq(key, "noresume")) { + log_debug("Found \"noresume\" on the kernel command line, hibernation is disabled."); + *resume = 0; + + } else if (streq(key, "resume")) { + log_debug("Found resume= option on the kernel command line, hibernation is possible."); + *resume = 1; + } + + return 0; +} + +static int resume_configured_in_options(const char *options) { + int resume = -1, r; + + /* We don't use PROC_CMDLINE_STRIP_RD_PREFIX here, so rd.resume is *not* supported. */ + r = proc_cmdline_parse_given(options, check_resume_keys, &resume, 0); + if (r < 0) + return r; + + if (resume < 0) + log_debug("Couldn't find resume= option, hibernation is disabled."); + return resume > 0; +} + +static int resume_configured(void) { + _cleanup_(boot_config_free) BootConfig config = {}; + const BootEntry *e; + int r; + + /* Check whether a valid resume= option is present. If possible, we query the boot options + * for the default kernel. If the system is not using sd-boot, fall back to checking the + * current kernel command line. This is not perfect, but should suffice for most cases. */ + + r = find_default_boot_entry(NULL, NULL, &config, &e); + if (r == -ENOKEY) + log_debug_errno(r, "Cannot find the ESP partition mount point, falling back to other checks."); + else if (r < 0) + return log_debug_errno(r, "Cannot read boot configuration from ESP, assuming hibernation is not possible."); + else { + _cleanup_free_ char *options = NULL; + + options = strv_join(e->options, " "); + if (!options) + return log_oom(); + + r = resume_configured_in_options(options); + if (r < 0) + return log_error_errno(r, "Failed to parse kernel options in \"%s\": %m", + strnull(e->path)); + return r; + } + + /* If we can't figure out the default boot entry, let's fall back to current kernel cmdline */ + _cleanup_free_ char *line = NULL; + r = proc_cmdline(&line); + if (IN_SET(r, -EPERM, -EACCES, -ENOENT)) + log_debug_errno(r, "Cannot access /proc/cmdline: %m"); + else if (r < 0) + return log_error_errno(r, "Failed to query /proc/cmdline: %m"); + else { + r = resume_configured_in_options(line); + if (r < 0) + return log_error_errno(r, "Failed to parse kernel proc cmdline: %m"); + + return r; + } + + log_debug("Couldn't detect any resume mechanism, hibernation is disabled."); + return false; +} +#endif // 0 + int read_fiemap(int fd, struct fiemap **ret) { _cleanup_free_ struct fiemap *fiemap = NULL, *result_fiemap = NULL; struct stat statinfo; @@ -348,6 +467,12 @@ int read_fiemap(int fd, struct fiemap **ret) { } #if 0 /// elogind has to ask the manager for some stuff +static int can_sleep_internal(const char *verb, bool check_allowed); +#else +static int can_sleep_internal(Manager *m, const char *verb, bool check_allowed); +#endif // 0 + +#if 0 /// elogind has to ask the manager for some stuff static bool can_s2h(void) { #else static bool can_s2h(Manager *m) { @@ -364,11 +489,11 @@ static bool can_s2h(Manager *m) { FOREACH_STRING(p, "suspend", "hibernate") { #if 0 /// elogind must transport a pointer to its managers instance - r = can_sleep(p); + r = can_sleep_internal(p, false); #else r = can_sleep(m, p); #endif // 0 - if (IN_SET(r, 0, -ENOSPC)) { + if (IN_SET(r, 0, -ENOSPC, -EADV)) { log_debug("Unable to %s system.", p); return false; } @@ -381,11 +506,15 @@ static bool can_s2h(Manager *m) { #if 0 /// elogind has to ask the manager for some stuff -int can_sleep(const char *verb) { +static int can_sleep_internal(const char *verb, bool check_allowed) { + bool allow; _cleanup_strv_free_ char **modes = NULL, **states = NULL; int r; + + + assert(STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate")); #else -int can_sleep(Manager *m, const char *verb) { +static int can_sleep_internal(Manager *m, const char *verb, bool check_allowed) { assert(m); char **modes = streq(verb, "suspend") ? m->suspend_mode : @@ -396,7 +525,20 @@ int can_sleep(Manager *m, const char *verb) { m->hybrid_sleep_state; #endif // 0 - assert(STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate")); + +#if 0 /// already parsed by elogind config + r = parse_sleep_config(verb, &allow, &modes, &states, NULL); + if (r < 0) + return false; + + if (check_allowed && !allow) { +#else + if (check_allowed && !STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate")) { +#endif // 0 + + log_debug("Sleep mode \"%s\" is disabled by configuration.", verb); + return false; + } if (streq(verb, "suspend-then-hibernate")) #if 0 /// elogind must transport a pointer to its managers instance @@ -405,12 +547,6 @@ int can_sleep(Manager *m, const char *verb) { return can_s2h(m); #endif // 0 -#if 0 /// already parsed by elogind config - r = parse_sleep_config(verb, &modes, &states, NULL); - if (r < 0) - return false; -#endif // 0 - if (!can_sleep_state(states) || !can_sleep_disk(modes)) return false; @@ -420,5 +556,22 @@ int can_sleep(Manager *m, const char *verb) { if (!enough_swap_for_hibernation()) return -ENOSPC; +#if 0 /// elogind is not init and can not check this + r = resume_configured(); + if (r <= 0) + /* We squash all errors (e.g. EPERM) into a single value for reporting. */ + return -EADV; +#endif // 0 + return true; } + +#if 0 /// elogind has to ask the manager for some stuff +int can_sleep(const char *verb) { + return can_sleep_internal(verb, true); +} +#else +int can_sleep(Manager *m, const char *verb) { + return can_sleep_internal(m, verb, true); +} +#endif // 0 diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h index b29165143..19bbaa491 100644 --- a/src/shared/sleep-config.h +++ b/src/shared/sleep-config.h @@ -6,7 +6,7 @@ int read_fiemap(int fd, struct fiemap **ret); #if 0 /// UNNEEDED by elogind -int parse_sleep_config(const char *verb, char ***modes, char ***states, usec_t *delay); +int parse_sleep_config(const char *verb, bool *ret_allow, char ***ret_modes, char ***ret_states, usec_t *ret_delay); #endif // 0 int find_hibernate_location(char **device, char **type, size_t *size, size_t *used); |