8483bf
From 0b9f08931944c2e33c6ed012919157e429eb7be2 Mon Sep 17 00:00:00 2001
8483bf
From: Antony Deepak Thomas <antonydeepak@gmail.com>
8483bf
Date: Wed, 29 Sep 2021 12:47:49 +0900
8483bf
Subject: [PATCH 1/4] fileio: introduce read_virtual_file_fd()
8483bf
8483bf
---
8483bf
 src/basic/fileio.c | 24 ++++++++++++++++--------
8483bf
 src/basic/fileio.h |  1 +
8483bf
 2 files changed, 17 insertions(+), 8 deletions(-)
8483bf
8483bf
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
8483bf
index 466c6321c7..4a0d060105 100644
8483bf
--- a/src/basic/fileio.c
8483bf
+++ b/src/basic/fileio.c
8483bf
@@ -373,9 +373,8 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
8483bf
         return 1;
8483bf
 }
8483bf
 
8483bf
-int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
8483bf
+int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size) {
8483bf
         _cleanup_free_ char *buf = NULL;
8483bf
-        _cleanup_close_ int fd = -1;
8483bf
         size_t n, size;
8483bf
         int n_retries;
8483bf
         bool truncated = false;
8483bf
@@ -393,10 +392,7 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
8483bf
          * contents* may be returned. (Though the read is still done using one syscall.) Returns 0 on
8483bf
          * partial success, 1 if untruncated contents were read. */
8483bf
 
8483bf
-        fd = open(filename, O_RDONLY|O_CLOEXEC);
8483bf
-        if (fd < 0)
8483bf
-                return -errno;
8483bf
-
8483bf
+        assert(fd >= 0);
8483bf
         assert(max_size <= READ_VIRTUAL_BYTES_MAX || max_size == SIZE_MAX);
8483bf
 
8483bf
         /* Limit the number of attempts to read the number of bytes returned by fstat(). */
8483bf
@@ -432,8 +428,8 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
8483bf
 
8483bf
                         n_retries--;
8483bf
                 } else if (n_retries > 1) {
8483bf
-                        /* Files in /proc are generally smaller than the page size so let's start with a page size
8483bf
-                         * buffer from malloc and only use the max buffer on the final try. */
8483bf
+                        /* Files in /proc are generally smaller than the page size so let's start with
8483bf
+                         * a page size buffer from malloc and only use the max buffer on the final try. */
8483bf
                         size = MIN3(page_size() - 1, READ_VIRTUAL_BYTES_MAX, max_size);
8483bf
                         n_retries = 1;
8483bf
                 } else {
8483bf
@@ -517,6 +513,18 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
8483bf
         return !truncated;
8483bf
 }
8483bf
 
8483bf
+int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
8483bf
+        _cleanup_close_ int fd = -1;
8483bf
+
8483bf
+        assert(filename);
8483bf
+
8483bf
+        fd = open(filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
8483bf
+        if (fd < 0)
8483bf
+                return -errno;
8483bf
+
8483bf
+        return read_virtual_file_fd(fd, max_size, ret_contents, ret_size);
8483bf
+}
8483bf
+
8483bf
 int read_full_stream_full(
8483bf
                 FILE *f,
8483bf
                 const char *filename,
8483bf
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
8483bf
index 9bd2037f5b..82330840bf 100644
8483bf
--- a/src/basic/fileio.h
8483bf
+++ b/src/basic/fileio.h
8483bf
@@ -66,6 +66,7 @@ static inline int read_full_file(const char *filename, char **ret_contents, size
8483bf
         return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size);
8483bf
 }
8483bf
 
8483bf
+int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size);
8483bf
 int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
8483bf
 static inline int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) {
8483bf
         return read_virtual_file(filename, SIZE_MAX, ret_contents, ret_size);
8483bf
-- 
8483bf
2.31.1
8483bf
8483bf
8483bf
From bede594fa1ea4c32a886191b774134effcf71bef Mon Sep 17 00:00:00 2001
8483bf
From: Antony Deepak Thomas <antonydeepak@gmail.com>
8483bf
Date: Wed, 29 Sep 2021 12:57:30 +0900
8483bf
Subject: [PATCH 2/4] string-util: introduce streq_skip_trailing_chars()
8483bf
8483bf
---
8483bf
 src/basic/string-util.c     | 16 ++++++++++++++++
8483bf
 src/basic/string-util.h     |  2 ++
8483bf
 src/test/test-string-util.c | 28 ++++++++++++++++++++++++++++
8483bf
 3 files changed, 46 insertions(+)
8483bf
8483bf
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
8483bf
index a645958d38..6ceaeaf9df 100644
8483bf
--- a/src/basic/string-util.c
8483bf
+++ b/src/basic/string-util.c
8483bf
@@ -1146,3 +1146,19 @@ int string_contains_word_strv(const char *string, const char *separators, char *
8483bf
                 *ret_word = found;
8483bf
         return !!found;
8483bf
 }
8483bf
+
8483bf
+bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
8483bf
+        if (!s1 && !s2)
8483bf
+                return true;
8483bf
+        if (!s1 || !s2)
8483bf
+                return false;
8483bf
+
8483bf
+        if (!ok)
8483bf
+                ok = WHITESPACE;
8483bf
+
8483bf
+        for (; *s1 && *s2; s1++, s2++)
8483bf
+                if (*s1 != *s2)
8483bf
+                        break;
8483bf
+
8483bf
+        return in_charset(s1, ok) && in_charset(s2, ok);
8483bf
+}
8483bf
diff --git a/src/basic/string-util.h b/src/basic/string-util.h
8483bf
index 9155e50ba8..0bf215827e 100644
8483bf
--- a/src/basic/string-util.h
8483bf
+++ b/src/basic/string-util.h
8483bf
@@ -242,3 +242,5 @@ int string_contains_word_strv(const char *string, const char *separators, char *
8483bf
 static inline int string_contains_word(const char *string, const char *separators, const char *word) {
8483bf
         return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL);
8483bf
 }
8483bf
+
8483bf
+bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok);
8483bf
diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c
8483bf
index 4d9d0260c9..9a9c974332 100644
8483bf
--- a/src/test/test-string-util.c
8483bf
+++ b/src/test/test-string-util.c
8483bf
@@ -1000,6 +1000,33 @@ static void test_strextendf(void) {
8483bf
         assert_se(streq(p, "<77>,<99>,<                                                                              88>,<00001234>"));
8483bf
 }
8483bf
 
8483bf
+static void test_streq_skip_trailing_chars(void) {
8483bf
+        log_info("/* %s */", __func__);
8483bf
+
8483bf
+        /* NULL is WHITESPACE by default*/
8483bf
+        assert_se(streq_skip_trailing_chars("foo bar", "foo bar", NULL));
8483bf
+        assert_se(streq_skip_trailing_chars("foo", "foo", NULL));
8483bf
+        assert_se(streq_skip_trailing_chars("foo bar      ", "foo bar", NULL));
8483bf
+        assert_se(streq_skip_trailing_chars("foo bar", "foo bar\t\t", NULL));
8483bf
+        assert_se(streq_skip_trailing_chars("foo bar  ", "foo bar\t\t", NULL));
8483bf
+        assert_se(streq_skip_trailing_chars("foo\nbar", "foo\nbar", NULL));
8483bf
+        assert_se(streq_skip_trailing_chars("\t\tfoo bar", "\t\tfoo bar", NULL));
8483bf
+        assert_se(streq_skip_trailing_chars(" foo bar\t", " foo bar\n", NULL));
8483bf
+
8483bf
+        assert_se(!streq_skip_trailing_chars("foobar", "foo bar", NULL));
8483bf
+        assert_se(!streq_skip_trailing_chars("foo\nbar", "foo\tbar", NULL));
8483bf
+        assert_se(!streq_skip_trailing_chars("\t\nfoo bar", "\t foo bar", NULL));
8483bf
+
8483bf
+        assert_se(streq_skip_trailing_chars("foo bar      ", "foo bar", WHITESPACE));
8483bf
+        assert_se(!streq_skip_trailing_chars("foo bar      ", "foo bar", NEWLINE));
8483bf
+
8483bf
+        assert_se(streq_skip_trailing_chars(NULL, NULL, NULL));
8483bf
+        assert_se(streq_skip_trailing_chars("", "", NULL));
8483bf
+        assert_se(!streq_skip_trailing_chars(NULL, "foo bar", NULL));
8483bf
+        assert_se(!streq_skip_trailing_chars("foo", NULL, NULL));
8483bf
+        assert_se(!streq_skip_trailing_chars("", "f", NULL));
8483bf
+}
8483bf
+
8483bf
 int main(int argc, char *argv[]) {
8483bf
         test_setup_logging(LOG_DEBUG);
8483bf
 
8483bf
@@ -1039,6 +1066,7 @@ int main(int argc, char *argv[]) {
8483bf
         test_string_contains_word();
8483bf
         test_strverscmp_improved();
8483bf
         test_strextendf();
8483bf
+        test_streq_skip_trailing_chars();
8483bf
 
8483bf
         return 0;
8483bf
 }
8483bf
-- 
8483bf
2.31.1
8483bf
8483bf
8483bf
From a2552e17829d0090db3ff5f2e6f2d772d0fca3e9 Mon Sep 17 00:00:00 2001
8483bf
From: Antony Deepak Thomas <antonydeepak@gmail.com>
8483bf
Date: Wed, 29 Sep 2021 13:06:25 +0900
8483bf
Subject: [PATCH 3/4] fileio: introduce new mode to suppress writing the same
8483bf
 value
8483bf
8483bf
---
8483bf
 src/basic/fileio.c | 29 +++++++++++++++++++++++++++--
8483bf
 src/basic/fileio.h | 23 ++++++++++++-----------
8483bf
 2 files changed, 39 insertions(+), 13 deletions(-)
8483bf
8483bf
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
8483bf
index 4a0d060105..729789ce47 100644
8483bf
--- a/src/basic/fileio.c
8483bf
+++ b/src/basic/fileio.c
8483bf
@@ -146,6 +146,30 @@ int write_string_stream_ts(
8483bf
                         return -EBADF;
8483bf
         }
8483bf
 
8483bf
+        if (flags & WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) {
8483bf
+                _cleanup_free_ char *t = NULL;
8483bf
+
8483bf
+                /* If value to be written is same as that of the existing value, then suppress the write. */
8483bf
+
8483bf
+                if (fd < 0) {
8483bf
+                        fd = fileno(f);
8483bf
+                        if (fd < 0)
8483bf
+                                return -EBADF;
8483bf
+                }
8483bf
+
8483bf
+                /* Read an additional byte to detect cases where the prefix matches but the rest
8483bf
+                 * doesn't. Also, 0 returned by read_virtual_file_fd() means the read was truncated and
8483bf
+                 * it won't be equal to the new value. */
8483bf
+                if (read_virtual_file_fd(fd, strlen(line)+1, &t, NULL) > 0 &&
8483bf
+                    streq_skip_trailing_chars(line, t, NEWLINE)) {
8483bf
+                        log_debug("No change in value '%s', supressing write", line);
8483bf
+                        return 0;
8483bf
+                }
8483bf
+
8483bf
+                if (lseek(fd, 0, SEEK_SET) < 0)
8483bf
+                        return -errno;
8483bf
+        }
8483bf
+
8483bf
         needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
8483bf
 
8483bf
         if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
8483bf
@@ -261,10 +285,11 @@ int write_string_file_ts(
8483bf
                 assert(!ts);
8483bf
 
8483bf
         /* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
8483bf
-        fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY |
8483bf
+        fd = open(fn, O_CLOEXEC|O_NOCTTY |
8483bf
                   (FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
8483bf
                   (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
8483bf
-                  (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0),
8483bf
+                  (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
8483bf
+                  (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
8483bf
                   (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
8483bf
         if (fd < 0) {
8483bf
                 r = -errno;
8483bf
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
8483bf
index 82330840bf..a72b2f3881 100644
8483bf
--- a/src/basic/fileio.h
8483bf
+++ b/src/basic/fileio.h
8483bf
@@ -15,17 +15,18 @@
8483bf
 #define LONG_LINE_MAX (1U*1024U*1024U)
8483bf
 
8483bf
 typedef enum {
8483bf
-        WRITE_STRING_FILE_CREATE                = 1 << 0,
8483bf
-        WRITE_STRING_FILE_TRUNCATE              = 1 << 1,
8483bf
-        WRITE_STRING_FILE_ATOMIC                = 1 << 2,
8483bf
-        WRITE_STRING_FILE_AVOID_NEWLINE         = 1 << 3,
8483bf
-        WRITE_STRING_FILE_VERIFY_ON_FAILURE     = 1 << 4,
8483bf
-        WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE = 1 << 5,
8483bf
-        WRITE_STRING_FILE_SYNC                  = 1 << 6,
8483bf
-        WRITE_STRING_FILE_DISABLE_BUFFER        = 1 << 7,
8483bf
-        WRITE_STRING_FILE_NOFOLLOW              = 1 << 8,
8483bf
-        WRITE_STRING_FILE_MKDIR_0755            = 1 << 9,
8483bf
-        WRITE_STRING_FILE_MODE_0600             = 1 << 10,
8483bf
+        WRITE_STRING_FILE_CREATE                     = 1 << 0,
8483bf
+        WRITE_STRING_FILE_TRUNCATE                   = 1 << 1,
8483bf
+        WRITE_STRING_FILE_ATOMIC                     = 1 << 2,
8483bf
+        WRITE_STRING_FILE_AVOID_NEWLINE              = 1 << 3,
8483bf
+        WRITE_STRING_FILE_VERIFY_ON_FAILURE          = 1 << 4,
8483bf
+        WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE      = 1 << 5,
8483bf
+        WRITE_STRING_FILE_SYNC                       = 1 << 6,
8483bf
+        WRITE_STRING_FILE_DISABLE_BUFFER             = 1 << 7,
8483bf
+        WRITE_STRING_FILE_NOFOLLOW                   = 1 << 8,
8483bf
+        WRITE_STRING_FILE_MKDIR_0755                 = 1 << 9,
8483bf
+        WRITE_STRING_FILE_MODE_0600                  = 1 << 10,
8483bf
+        WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 11,
8483bf
 
8483bf
         /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
8483bf
            more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
8483bf
-- 
8483bf
2.31.1
8483bf
8483bf
8483bf
From 41d86b627331f432454280714dd5b17d255367ba Mon Sep 17 00:00:00 2001
8483bf
From: Antony Deepak Thomas <antonydeepak@gmail.com>
8483bf
Date: Wed, 29 Sep 2021 13:07:42 +0900
8483bf
Subject: [PATCH 4/4] sysctl-util: minimize side-effects when running
8483bf
 `systemd-sysctl`
8483bf
8483bf
Currently `systemd-sysctl` binary is used in `systemd-sysctl.service`
8483bf
which is mostly configured as `oneshot`. There are situations where one
8483bf
would like to use systemd to maintain Sysctl configurations on a host,
8483bf
using a configuration managers such as Chef or Puppet, by apply
8483bf
configurations every X duration.
8483bf
The problem with using `systemd-sysctl` is that it writes all the Sysctl
8483bf
settings, even if the values for those settings have not changed. From
8483bf
experience, we have observed that some Sysctl settings cause actions in
8483bf
the kernel upon writing(like dropping caches) which in turn cause
8483bf
undesired side effects.
8483bf
This patch tries to minimize such side effects by comparing values
8483bf
before writing.
8483bf
---
8483bf
 src/basic/sysctl-util.c | 19 +++++--------------
8483bf
 1 file changed, 5 insertions(+), 14 deletions(-)
8483bf
8483bf
diff --git a/src/basic/sysctl-util.c b/src/basic/sysctl-util.c
8483bf
index 8913e6ff85..4da3eaf5f7 100644
8483bf
--- a/src/basic/sysctl-util.c
8483bf
+++ b/src/basic/sysctl-util.c
8483bf
@@ -44,25 +44,16 @@ char *sysctl_normalize(char *s) {
8483bf
 
8483bf
 int sysctl_write(const char *property, const char *value) {
8483bf
         char *p;
8483bf
-        _cleanup_close_ int fd = -1;
8483bf
-
8483bf
         assert(property);
8483bf
         assert(value);
8483bf
-
8483bf
-        log_debug("Setting '%s' to '%.*s'.", property, (int) strcspn(value, NEWLINE), value);
8483bf
-
8483bf
         p = strjoina("/proc/sys/", property);
8483bf
-        fd = open(p, O_WRONLY|O_CLOEXEC);
8483bf
-        if (fd < 0)
8483bf
-                return -errno;
8483bf
+        path_simplify(p);
8483bf
+        if (!path_is_normalized(p))
8483bf
+                return -EINVAL;
8483bf
 
8483bf
-        if (!endswith(value, "\n"))
8483bf
-                value = strjoina(value, "\n");
8483bf
-
8483bf
-        if (write(fd, value, strlen(value)) < 0)
8483bf
-                return -errno;
8483bf
+        log_debug("Setting '%s' to '%s'", p, value);
8483bf
 
8483bf
-        return 0;
8483bf
+        return write_string_file(p, value, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL);
8483bf
 }
8483bf
 
8483bf
 int sysctl_writef(const char *property, const char *format, ...) {
8483bf
-- 
8483bf
2.31.1
8483bf