summaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
authorMark Hindley <mark@hindley.org.uk>2018-11-12 09:10:28 +0000
committerMark Hindley <mark@hindley.org.uk>2018-11-12 09:21:21 +0000
commit2cc17d30309a1db16cccbf376a59ae40e47b6959 (patch)
treecd51ee95799631af348ebae8630a69219bee99cd /src/shared
parentae65e91a5439f395e0da0cd8ffda95d6289849e1 (diff)
parentd4a3f291e3955648ea1d29e674b0f8f9b1556257 (diff)
Merge remote-tracking branch 'upstream/v239-stable' into merge_upstream.
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/bus-util.c12
-rw-r--r--src/shared/bus-util.h4
-rw-r--r--src/shared/enable-mempool.c5
-rw-r--r--src/shared/meson.build11
-rw-r--r--src/shared/musl_missing.c17
-rw-r--r--src/shared/musl_missing.h3
-rw-r--r--src/shared/nsflags.h4
-rw-r--r--src/shared/qsort_r_missing.c497
-rw-r--r--src/shared/qsort_r_missing.h34
-rw-r--r--src/shared/sleep-config.c225
-rw-r--r--src/shared/sleep-config.h2
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);