Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package rebootmgr for openSUSE:Factory 
checked in at 2026-10-30 18:03:37
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rebootmgr (Old)
 and      /work/SRC/openSUSE:Factory/.rebootmgr.new.1255 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rebootmgr"

Fri Oct 30 18:03:37 2026 rev:38 rq:1393609 version:4.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/rebootmgr/rebootmgr.changes      2026-10-09 
21:42:53.312031280 +0200
+++ /work/SRC/openSUSE:Factory/.rebootmgr.new.1255/rebootmgr.changes    
2026-10-30 18:04:06.934705840 +0100
@@ -1,0 +2,27 @@
+Thu Oct 29 11:24:18 UTC 2026 - Thorsten Kukuk <[email protected]>
+
+- Update to version 4.3:
+  * Release version 4.3
+  * Use logind varlink interface for reboot
+  * rebootmgrd: more error handler fixes
+
+-------------------------------------------------------------------
+Tue Oct 27 11:03:10 UTC 2026 - Thorsten Kukuk <[email protected]>
+
+- Update to version 4.2:
+  * Release version 4.2
+  * Fix error handling in case SELinux denies reboot [bsc#1287746]
+
+-------------------------------------------------------------------
+Fri Oct 23 13:08:26 UTC 2026 - Thorsten Kukuk <[email protected]>
+
+- Update to version 4.1:
+  * Release version 4.1
+  * Use varlink for reboot, else posix_spawn
+  * Update basics.h
+  * calendarspec: check for error when resetting TZ
+  * Fix URL of github project
+  * supporting time_zones
+  * initialize seed with random value
+
+-------------------------------------------------------------------

Old:
----
  rebootmgr-4.0+git20261008.76d8ee7.tar.xz

New:
----
  rebootmgr-4.3.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ rebootmgr.spec ++++++
--- /var/tmp/diff_new_pack.jeaDCW/_old  2026-10-30 18:04:09.868828703 +0100
+++ /var/tmp/diff_new_pack.jeaDCW/_new  2026-10-30 18:04:09.878829122 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           rebootmgr
-Version:        4.0+git20261008.76d8ee7
+Version:        4.3
 Release:        0
 Summary:        Automatic controlled reboot during a maintenance window
 License:        GPL-2.0-only AND LGPL-2.1-or-later

++++++ _service ++++++
--- /var/tmp/diff_new_pack.jeaDCW/_old  2026-10-30 18:04:10.015834859 +0100
+++ /var/tmp/diff_new_pack.jeaDCW/_new  2026-10-30 18:04:10.028835403 +0100
@@ -1,8 +1,8 @@
 <services>
   <service name="tar_scm" mode="manual">
     <param name="url">https://github.com/SUSE/rebootmgr.git</param>
-    <param name="versionformat">@PARENT_TAG@+git%cd.%h</param>
-    <param name="versionrewrite-pattern">v(.*?)(\+git0)?$</param>
+    <param name="versionformat">@PARENT_TAG@+git%cd.@TAG_OFFSET@</param>
+    <param name="versionrewrite-pattern">v(.*?)(\+git[0-9]{8}\.0)?$</param>
     <param name="revision">master</param>
     <param name="scm">git</param>
     <param name="changesgenerate">enable</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.jeaDCW/_old  2026-10-30 18:04:10.144840261 +0100
+++ /var/tmp/diff_new_pack.jeaDCW/_new  2026-10-30 18:04:10.155840721 +0100
@@ -1,7 +1,7 @@
 <servicedata>
   <service name="tar_scm">
     <param name="url">https://github.com/SUSE/rebootmgr.git</param>
- <param 
name="changesrevision">76d8ee7e7301b90403eed4b91ef15f08762d3dac</param></service>
+ <param 
name="changesrevision">1273fab3f26c32cbabde1c8fe0c6d6131bb286a8</param></service>
 </servicedata>
 (No newline at EOF)
 

++++++ rebootmgr-4.0+git20261008.76d8ee7.tar.xz -> rebootmgr-4.3.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rebootmgr-4.0+git20261008.76d8ee7/NEWS 
new/rebootmgr-4.3/NEWS
--- old/rebootmgr-4.0+git20261008.76d8ee7/NEWS  2026-10-08 09:48:28.000000000 
+0200
+++ new/rebootmgr-4.3/NEWS      2026-10-29 12:23:33.000000000 +0100
@@ -2,6 +2,17 @@
 
 Copyright (C) 2016-2026 Thorsten Kukuk
 
+Version 4.3
+* Use logind varlink interface for reboot
+
+Version 4.2
+* Fix error handling if SELinux denies reboot
+
+Version 4.1
+* Use varlink for reboot, else fallback to systemctl via posix_spwan
+* Support timezones for maintenance window
+* Bugfix: initialize seed with random value
+
 Version 4.0
 * Create symlink for varlink registry
 * Add metrics for systemd-report
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rebootmgr-4.0+git20261008.76d8ee7/lib/calendarspec/calendarspec.c 
new/rebootmgr-4.3/lib/calendarspec/calendarspec.c
--- old/rebootmgr-4.0+git20261008.76d8ee7/lib/calendarspec/calendarspec.c       
2026-10-08 09:48:28.000000000 +0200
+++ new/rebootmgr-4.3/lib/calendarspec/calendarspec.c   2026-10-29 
12:23:33.000000000 +0100
@@ -24,10 +24,8 @@
 #include <stdlib.h>
 #include <string.h>
 
-// #include "alloc-util.h"
+#include "basics.h"
 #include "calendarspec.h"
-// #include "fileio.h"
-// #include "string-util.h"
 
 #define assert_se assert
 
@@ -45,13 +43,6 @@
 }
 
 /* from basic/string-util.h */
-#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
-
-static inline bool isempty(const char *p) {
-        return !p || !p[0];
-}
-
-/* from basic/string-util.h */
 static inline const char *startswith_no_case(const char *s, const char 
*prefix) {
         size_t l;
 
@@ -106,6 +97,8 @@
         if (!c)
                 return;
 
+        free(c->timezone);
+
         free_chain(c->year);
         free_chain(c->month);
         free_chain(c->day);
@@ -346,6 +339,10 @@
 
         if (c->utc)
                 fputs(" UTC", f);
+        else if (c->timezone) {
+                fputc(' ', f);
+                fputs(c->timezone, f);
+        }
 
         r = fflush_and_check(f);
         if (r < 0) {
@@ -730,6 +727,19 @@
         if (utc) {
                 c->utc = true;
                 p = strndupa(p, utc - p);
+        } else {
+                const char *last_space;
+
+                last_space = strrchr(p, ' ');
+                if (last_space && timezone_is_valid(last_space + 1)) {
+                        c->timezone = strdup(last_space + 1);
+                        if (!c->timezone) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        p = strndupa(p, last_space - p);
+                }
         }
 
         if (strcaseeq(p, "minutely")) {
@@ -1061,21 +1071,44 @@
         struct tm tm;
         time_t t;
         int r;
+        const char *old_tz = NULL;
 
         assert(spec);
         assert(next);
 
+        if (spec->timezone) {
+                old_tz = getenv("TZ");
+                if (old_tz)
+                        old_tz = strdupa(old_tz);
+
+                if (setenv("TZ", spec->timezone, 1) < 0)
+                        return -errno;
+                tzset();
+        }
+
         t = (time_t) (usec / USEC_PER_SEC) + 1;
         assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
 
         r = find_next(spec, &tm);
         if (r < 0)
-                return r;
+                goto finish;
 
         t = mktime_or_timegm(&tm, spec->utc);
-        if (t == (time_t) -1)
-                return -EINVAL;
+        if (t == (time_t) -1) {
+                r = -EINVAL;
+                goto finish;
+        }
 
         *next = (usec_t) t * USEC_PER_SEC;
-        return 0;
+        r = 0;
+
+finish:
+        if (spec->timezone) {
+                int rr = old_tz ? setenv("TZ", old_tz, 1) : unsetenv("TZ");
+                if (rr < 0 && r == 0)
+                        r = -errno;
+                tzset();
+        }
+
+        return r;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rebootmgr-4.0+git20261008.76d8ee7/lib/calendarspec/calendarspec.h 
new/rebootmgr-4.3/lib/calendarspec/calendarspec.h
--- old/rebootmgr-4.0+git20261008.76d8ee7/lib/calendarspec/calendarspec.h       
2026-10-08 09:48:28.000000000 +0200
+++ new/rebootmgr-4.3/lib/calendarspec/calendarspec.h   2026-10-29 
12:23:33.000000000 +0100
@@ -38,6 +38,7 @@
 typedef struct CalendarSpec {
         int weekdays_bits;
         bool utc;
+        char *timezone;
 
         CalendarComponent *year;
         CalendarComponent *month;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rebootmgr-4.0+git20261008.76d8ee7/lib/calendarspec/meson.build 
new/rebootmgr-4.3/lib/calendarspec/meson.build
--- old/rebootmgr-4.0+git20261008.76d8ee7/lib/calendarspec/meson.build  
2026-10-08 09:48:28.000000000 +0200
+++ new/rebootmgr-4.3/lib/calendarspec/meson.build      2026-10-29 
12:23:33.000000000 +0100
@@ -3,4 +3,5 @@
 libcalendarspec_a = static_library(
   'libcalendarspec',
   libcalendarspec_c,
+  include_directories : inc,
   install : false)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rebootmgr-4.0+git20261008.76d8ee7/lib/calendarspec/time-util.c 
new/rebootmgr-4.3/lib/calendarspec/time-util.c
--- old/rebootmgr-4.0+git20261008.76d8ee7/lib/calendarspec/time-util.c  
2026-10-08 09:48:28.000000000 +0200
+++ new/rebootmgr-4.3/lib/calendarspec/time-util.c      2026-10-29 
12:23:33.000000000 +0100
@@ -20,6 +20,9 @@
 ***/
 
 #include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
 #define assert_se assert
 
 #include "time-util.h"
@@ -48,6 +51,47 @@
 }
 
 
+bool timezone_is_valid(const char *name) {
+        bool slash = false;
+        const char *p;
+        char *t;
+        struct stat st;
+        bool ret;
+
+        if (!name || !name[0])
+                return false;
+
+        if (name[0] == '/')
+                return false;
+
+        for (p = name; *p; p++) {
+                if (!(*p >= '0' && *p <= '9') &&
+                    !(*p >= 'a' && *p <= 'z') &&
+                    !(*p >= 'A' && *p <= 'Z') &&
+                    !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
+                        return false;
+
+                if (*p == '/') {
+                        if (slash)
+                                return false;
+
+                        slash = true;
+                } else
+                        slash = false;
+        }
+
+        if (slash)
+                return false;
+
+        if (asprintf(&t, "/usr/share/zoneinfo/%s", name) < 0)
+                return false;
+
+        ret = stat(t, &st) >= 0 && S_ISREG(st.st_mode);
+        free(t);
+
+        return ret;
+}
+
 time_t mktime_or_timegm(struct tm *tm, bool utc) {
         return utc ? timegm(tm) : mktime(tm);
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rebootmgr-4.0+git20261008.76d8ee7/lib/calendarspec/time-util.h 
new/rebootmgr-4.3/lib/calendarspec/time-util.h
--- old/rebootmgr-4.0+git20261008.76d8ee7/lib/calendarspec/time-util.h  
2026-10-08 09:48:28.000000000 +0200
+++ new/rebootmgr-4.3/lib/calendarspec/time-util.h      2026-10-29 
12:23:33.000000000 +0100
@@ -27,14 +27,14 @@
 #include <time.h>
 #include <sys/time.h>
 
+#include "basics.h"
+
 typedef uint64_t usec_t;
 typedef uint64_t nsec_t;
 
 #define NSEC_FMT "%" PRIu64
 #define USEC_FMT "%" PRIu64
 
-//#include "macro.h"
-#define _pure_ __attribute__ ((pure))
 #define ELEMENTSOF(x)                                                    \
         __extension__ (__builtin_choose_expr(                            \
                 !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rebootmgr-4.0+git20261008.76d8ee7/lib/common/basics.h 
new/rebootmgr-4.3/lib/common/basics.h
--- old/rebootmgr-4.0+git20261008.76d8ee7/lib/common/basics.h   2026-10-08 
09:48:28.000000000 +0200
+++ new/rebootmgr-4.3/lib/common/basics.h       2026-10-29 12:23:33.000000000 
+0100
@@ -1,25 +1,32 @@
-//SPDX-License-Identifier: GPL-2.0-or-later
-
-/* Copyright (c) 2024 Thorsten Kukuk
-   Author: Thorsten Kukuk <[email protected]>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
-
-   This program 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 General Public License for more details.
-
-   You should have received a copy of the GNU General Public License along
-   with this program; if not, see <http://www.gnu.org/licenses/>. */
+//SPDX-License-Identifier: LGPL-2.1-or-later
 
 #pragma once
 
-#define _cleanup_(x) __attribute__((__cleanup__(x)))
-#define _unused_(x) x __attribute__((unused))
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define _unused_ __attribute__((unused))
+#define _pure_ __attribute__((__pure__))
+#define _const_ __attribute__((__const__))
+#define _sentinel_ __attribute__((__sentinel__))
+
+/* Takes inspiration from Rust's Option::take() method: reads and returns a 
pointer, but at the same time
+ * resets it to NULL. See: 
https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
+#define TAKE_GENERIC(var, type, nullvalue)                       \
+        ({                                                       \
+                type *_pvar_ = &(var);                           \
+                type _var_ = *_pvar_;                            \
+                type _nullvalue_ = nullvalue;                    \
+                *_pvar_ = _nullvalue_;                           \
+                _var_;                                           \
+        })
+#define TAKE_PTR_TYPE(ptr, type) TAKE_GENERIC(ptr, type, NULL)
+#define TAKE_PTR(ptr) TAKE_PTR_TYPE(ptr, typeof(ptr))
+#define TAKE_FD(fd) TAKE_GENERIC(fd, int, -EBADF)
 
 #define mfree(memory)                           \
         ({                                      \
@@ -31,3 +38,48 @@
         *(void**)p = mfree(*(void**) p);
 }
 
+static inline void closep(int *fd) {
+  if (*fd >= 0)
+        close(*fd);
+  *fd = -EBADF;
+}
+
+static inline void fclosep(FILE **f) {
+  if (*f)
+        fclose(*f);
+  *f = NULL;
+}
+
+#define _cleanup_(x) __attribute__((__cleanup__(x)))
+#define _cleanup_close_ _cleanup_(closep)
+#define _cleanup_fclose_ _cleanup_(fclosep)
+#define _cleanup_free_ _cleanup_(freep)
+
+
+/* from string-util-fundamental.h */
+
+#define WHITESPACE          " \t\n\r"
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
+#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
+#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
+
+static inline const char *strempty(const char *s) {
+        return s ?:"";
+}
+
+static inline const char *strna(const char *s) {
+        return s ?: "n/a";
+}
+
+static inline const char *stroom(const char *s) {
+        return s ?: "Out of memory";
+}
+
+extern char *startswith(const char *s, const char *prefix) _pure_;
+extern char *endswith(const char *s, const char *suffix) _pure_;
+
+static inline bool isempty(const char *a) {
+        return !a || a[0] == '\0';
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rebootmgr-4.0+git20261008.76d8ee7/lib/common/exec_cmd.c 
new/rebootmgr-4.3/lib/common/exec_cmd.c
--- old/rebootmgr-4.0+git20261008.76d8ee7/lib/common/exec_cmd.c 1970-01-01 
01:00:00.000000000 +0100
+++ new/rebootmgr-4.3/lib/common/exec_cmd.c     2026-10-29 12:23:33.000000000 
+0100
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <spawn.h>
+#include <sys/wait.h>
+
+#include "basics.h"
+#include "exec_cmd.h"
+
+extern char **environ;
+
+/* Executes a command using posix_spawnp and waits for it to finish.
+   If "output" is not NULL, the command's stdout is captured and
+   returned as a malloc'ed, NUL-terminated string in *output, which
+   the caller has to free. On failure, *output is set to NULL.
+   Returns 0 on success, -errno on failure, > 0 exit code of the
+   called command. */
+int
+exec_cmd(char **output, char *const *extra_env, const char *cmd, ...)
+{
+  _cleanup_free_ char **argv = NULL;
+  _cleanup_free_ char **envp_buf = NULL;
+  char **envp = environ;
+  va_list args, args_copy;
+  int argc;
+  int r;
+  int pipefd[2] = { -1, -1 };
+  posix_spawn_file_actions_t file_actions;
+  posix_spawn_file_actions_t *file_actionsp = NULL;
+
+  if (output)
+    *output = NULL;
+
+  argc = 0;
+  va_start(args, cmd);
+  va_copy(args_copy, args);
+  while (va_arg(args_copy, const char *) != NULL)
+    argc++;
+  va_end(args_copy);
+
+  argv = malloc((argc + 1) * sizeof(char *));
+  if (!argv)
+    {
+      va_end(args);
+      return -ENOMEM;
+    }
+
+  for (int i = 0; i < argc; i++)
+    argv[i] = (char *)va_arg(args, const char *);
+  argv[argc] = NULL;
+  va_end(args);
+
+  if (extra_env && extra_env[0])
+    {
+      size_t env_count = 0;
+      while (environ[env_count])
+       env_count++;
+      size_t extra_count = 0;
+      while (extra_env[extra_count])
+       extra_count++;
+
+      envp_buf = malloc((env_count + extra_count + 1) * sizeof(char *));
+      if (!envp_buf)
+       return -ENOMEM;
+
+      memcpy(envp_buf, environ, env_count * sizeof(char *));
+      memcpy(envp_buf + env_count, extra_env, extra_count * sizeof(char *));
+      envp_buf[env_count + extra_count] = NULL;
+      envp = envp_buf;
+    }
+
+  if (output)
+    {
+      if (pipe(pipefd) == -1)
+       return -errno;
+
+      r = posix_spawn_file_actions_init(&file_actions);
+      if (r != 0)
+       {
+         close(pipefd[0]);
+         close(pipefd[1]);
+         return -r;
+       }
+      file_actionsp = &file_actions;
+
+      posix_spawn_file_actions_adddup2(&file_actions, pipefd[1], 
STDOUT_FILENO);
+      posix_spawn_file_actions_addclose(&file_actions, pipefd[0]);
+      posix_spawn_file_actions_addclose(&file_actions, pipefd[1]);
+    }
+
+  pid_t pid;
+
+  r = posix_spawnp(&pid, cmd, file_actionsp, NULL, argv, envp);
+
+  if (file_actionsp)
+    posix_spawn_file_actions_destroy(file_actionsp);
+
+  if (r != 0)
+    {
+      if (output)
+       {
+         close(pipefd[0]);
+         close(pipefd[1]);
+       }
+      return -r;
+    }
+
+  _cleanup_free_ char *buf = NULL;
+
+  if (output)
+    {
+      close(pipefd[1]);
+
+      size_t size = 0;
+      size_t capacity = 0;
+      ssize_t n;
+      char chunk[4096];
+
+      while ((n = read(pipefd[0], chunk, sizeof(chunk))) > 0)
+       {
+         if (size + (size_t)n + 1 > capacity)
+           {
+             capacity = size + (size_t)n + 1;
+             char *tmp = realloc(buf, capacity);
+             if (!tmp)
+               {
+                 close(pipefd[0]);
+                 waitpid(pid, NULL, 0);
+                 return -ENOMEM;
+               }
+             buf = tmp;
+           }
+         memcpy(buf + size, chunk, (size_t)n);
+         size += (size_t)n;
+         buf[size] = '\0';
+       }
+
+      close(pipefd[0]);
+
+      if (!buf)
+       {
+         buf = strdup("");
+         if (!buf)
+           {
+             waitpid(pid, NULL, 0);
+             return -ENOMEM;
+           }
+       }
+    }
+
+  // Wait for the child process to complete
+  int wait_status;
+  while (waitpid(pid, &wait_status, 0) == -1)
+    {
+      if (errno == EINTR)
+        continue;
+      return -errno;
+    }
+
+  // Evaluate the exit status
+  if (WIFEXITED(wait_status))
+    {
+      int exit_code = WEXITSTATUS(wait_status);
+      if (exit_code == 0 && output)
+       *output = TAKE_PTR(buf);
+      if (exit_code != 0)
+       return exit_code;
+      return 0;
+    }
+  else if (WIFSIGNALED(wait_status))
+    {
+      int sig = WTERMSIG(wait_status);
+      // Conventionally, shells return 128 + signal number for fatal signals
+      return 128 + sig;
+    }
+
+  // fprintf(stderr, "Command '%s' terminated in an unknown state.\n", cmd);
+  return -EIO;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rebootmgr-4.0+git20261008.76d8ee7/lib/common/exec_cmd.h 
new/rebootmgr-4.3/lib/common/exec_cmd.h
--- old/rebootmgr-4.0+git20261008.76d8ee7/lib/common/exec_cmd.h 1970-01-01 
01:00:00.000000000 +0100
+++ new/rebootmgr-4.3/lib/common/exec_cmd.h     2026-10-29 12:23:33.000000000 
+0100
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "basics.h"
+
+/* The variadic argument list must be NULL-terminated.
+   "extra_env" is an optional NULL-terminated array of "NAME=VALUE"
+   strings which are added to the environment of the executed command;
+   it may be NULL if no extra environment variables are needed. */
+extern int exec_cmd(char **output, char *const *extra_env, const char *cmd, 
...) _sentinel_;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rebootmgr-4.0+git20261008.76d8ee7/lib/common/meson.build 
new/rebootmgr-4.3/lib/common/meson.build
--- old/rebootmgr-4.0+git20261008.76d8ee7/lib/common/meson.build        
2026-10-08 09:48:28.000000000 +0200
+++ new/rebootmgr-4.3/lib/common/meson.build    2026-10-29 12:23:33.000000000 
+0100
@@ -1,5 +1,5 @@
 libcommon_c = ['load_config.c', 'save_config.c', 'mkdir_p.c', 'log_msg.c',
-  'util.c']
+               'util.c', 'exec_cmd.c']
 
 libcommon_a = static_library(
   'libcommon',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rebootmgr-4.0+git20261008.76d8ee7/man/rebootmgr.conf.5.xml 
new/rebootmgr-4.3/man/rebootmgr.conf.5.xml
--- old/rebootmgr-4.0+git20261008.76d8ee7/man/rebootmgr.conf.5.xml      
2026-10-08 09:48:28.000000000 +0200
+++ new/rebootmgr-4.3/man/rebootmgr.conf.5.xml  2026-10-29 12:23:33.000000000 
+0100
@@ -52,7 +52,7 @@
          <para>
            The format of <varname>window-start</varname> is the same as
            described in  <citerefentry
-           
project='systemd'><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
 Except that timezones are not supported.
+           
project='systemd'><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
         </para>
        </listitem>
       </varlistentry>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rebootmgr-4.0+git20261008.76d8ee7/meson.build 
new/rebootmgr-4.3/meson.build
--- old/rebootmgr-4.0+git20261008.76d8ee7/meson.build   2026-10-08 
09:48:28.000000000 +0200
+++ new/rebootmgr-4.3/meson.build       2026-10-29 12:23:33.000000000 +0100
@@ -13,7 +13,7 @@
                   'c_std=gnu17',
                  'warning_level=2',],
   license : ['GPL-2.0-only', 'LGPL-2.1-or-later'],
-  version : '4.0',
+  version : '4.3',
 )
 
 fs = import('fs')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rebootmgr-4.0+git20261008.76d8ee7/src/rebootmgrctl.c 
new/rebootmgr-4.3/src/rebootmgrctl.c
--- old/rebootmgr-4.0+git20261008.76d8ee7/src/rebootmgrctl.c    2026-10-08 
09:48:28.000000000 +0200
+++ new/rebootmgr-4.3/src/rebootmgrctl.c        2026-10-29 12:23:33.000000000 
+0100
@@ -19,19 +19,6 @@
 #define _(String) gettext(String)
 #endif
 
-/* Takes inspiration from Rust's Option::take() method: reads and returns a 
pointer, but at the same time
- * resets it to NULL. See: 
https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
-#define TAKE_GENERIC(var, type, nullvalue)                       \
-        ({                                                       \
-                type *_pvar_ = &(var);                           \
-                type _var_ = *_pvar_;                            \
-                type _nullvalue_ = nullvalue;                    \
-                *_pvar_ = _nullvalue_;                           \
-                _var_;                                           \
-        })
-#define TAKE_PTR_TYPE(ptr, type) TAKE_GENERIC(ptr, type, NULL)
-#define TAKE_PTR(ptr) TAKE_PTR_TYPE(ptr, typeof(ptr))
-
 static int
 connect_to_rebootmgr(sd_varlink **ret)
 {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rebootmgr-4.0+git20261008.76d8ee7/src/rebootmgrd.c 
new/rebootmgr-4.3/src/rebootmgrd.c
--- old/rebootmgr-4.0+git20261008.76d8ee7/src/rebootmgrd.c      2026-10-08 
09:48:28.000000000 +0200
+++ new/rebootmgr-4.3/src/rebootmgrd.c  2026-10-29 12:23:33.000000000 +0100
@@ -6,17 +6,23 @@
 #include <stdlib.h>
 #include <stdbool.h>
 #include <libintl.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/random.h>
 #include <systemd/sd-daemon.h>
 #include <systemd/sd-varlink.h>
 
 #include "basics.h"
 #include "common.h"
 #include "parse-duration.h"
+#include "exec_cmd.h"
 
 #include "varlink-org.openSUSE.rebootmgr.h"
 #include "varlink-io.systemd.Metrics.h"
 #include "rebootmgrd-varlink-metrics.h"
 
+#define VARLINK_REBOOT_SOCKET "/run/systemd/io.systemd.Login"
+
 static int verbose_flag = 0;
 
 static int
@@ -298,10 +304,93 @@
   ctx->timer = sd_event_source_unref(ctx->timer);
 }
 
+struct system_error
+{
+  char *origin;
+  char *errno_name;
+  int errno_val;
+};
+
+static void
+system_error_free(struct system_error *e)
+{
+  e->origin = mfree(e->origin);
+  e->errno_name = mfree(e->errno_name);
+}
+
+static int
+varlink_reboot(const char *method)
+{
+  _cleanup_(sd_varlink_unrefp) sd_varlink *link = NULL;
+  _cleanup_(sd_json_variant_unrefp) sd_json_variant *reply = NULL;
+  const char *error_id = NULL;
+  int r;
+
+  r = sd_varlink_connect_address(&link, VARLINK_REBOOT_SOCKET);
+  if (r < 0)
+    {
+      log_msg(LOG_DEBUG, "Connecting to %s failed: %s", VARLINK_REBOOT_SOCKET, 
strerror(-r));
+      return r;
+    }
+
+  r = sd_varlink_call(link, method, NULL, &reply, &error_id);
+  if (r < 0)
+    {
+      log_msg(LOG_ERR, "varlink_reboot(%s) failed: %s", method, strerror(-r));
+      return r;
+    }
+  if (!isempty(error_id))
+    {
+      if (streq(error_id, "io.systemd.Shutdown.AlreadyInProgress"))
+       {
+         log_msg(LOG_NOTICE, "Reboot already in progress (systemd-logind)!");
+         return 0;
+       }
+
+      /* The common SD_VARLINK_ERROR_SYSTEM error wraps a plain Linux
+        errno; the useful detail is in its parameters, not in the
+        error ID itself. */
+      if (streq(error_id, SD_VARLINK_ERROR_SYSTEM))
+       {
+         _cleanup_(system_error_free) struct system_error e = { .errno_val = 0 
};
+         static const sd_json_dispatch_field dispatch_table[] = {
+           { "origin",    SD_JSON_VARIANT_STRING,  sd_json_dispatch_string, 
offsetof(struct system_error, origin),     0 },
+           { "errnoName", SD_JSON_VARIANT_STRING,  sd_json_dispatch_string, 
offsetof(struct system_error, errno_name), 0 },
+           { "errno",     SD_JSON_VARIANT_INTEGER, sd_json_dispatch_int,    
offsetof(struct system_error, errno_val),  0 },
+           {}
+         };
+
+         r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, 
&e);
+         if (r < 0)
+           {
+             log_msg(LOG_ERR, "sd_json_dispatch failed: %s", strerror(-r));
+             if (debug_flag)
+               sd_json_variant_dump(reply, SD_JSON_FORMAT_NEWLINE, stdout, 
NULL);
+             log_msg(LOG_ERR, "varlink_reboot(%s) failed: %s", method, 
error_id);
+           }
+         else
+           {
+             if (!isempty(e.origin) && streq(e.origin, "linux"))
+               log_msg(LOG_ERR, "varlink_reboot(%s) failed: %s:%s", method, 
strna(e.errno_name), strerror(e.errno_val));
+             else
+               log_msg(LOG_ERR, "varlink_reboot(%s) failed: %s:%s", method, 
strna(e.origin), strna(e.errno_name));
+             return e.errno_val?-e.errno_val:-EACCES;
+           }
+       }
+      else
+       log_msg(LOG_ERR, "varlink_reboot(%s) failed: %s", method, error_id);
+
+      return -EACCES;
+    }
+
+  return 0;
+}
+
 static int
 time_handler(sd_event_source _unused_(*s), uint64_t _unused_(usec), void 
*userdata)
 {
   RM_CTX *ctx = userdata;
+  int r;
 
   if (debug_flag)
     log_msg(LOG_DEBUG, "Time handler for reboot called");
@@ -328,15 +417,16 @@
          return -EINVAL;
        }
 
+      // Don't do a real reboot in debug mode
       if (debug_flag)
        {
          switch (ctx->reboot_method)
            {
            case RM_REBOOTMETHOD_HARD:
-             log_msg(LOG_DEBUG, "systemctl reboot called!");
+             log_msg(LOG_DEBUG, "DEBUG: reboot requested!");
              break;
            case RM_REBOOTMETHOD_SOFT:
-             log_msg(LOG_DEBUG, "systemctl soft-reboot called!");
+             log_msg(LOG_DEBUG, "DEBUG: soft-reboot requested!");
              break;
            default:
              /* cannot happen */
@@ -345,45 +435,55 @@
        }
       else
         {
-          pid_t pid = fork();
-
-          if (pid < 0)
-            {
-              log_msg(LOG_ERR, "Calling /usr/bin/systemctl failed: %m");
-            }
-          else if (pid == 0)
-            {
-             int r;
+         // Try varlink first without fork.
+         // Ignore return value, function will only return if it
+         // fails
+         switch (ctx->reboot_method)
+           {
+           case RM_REBOOTMETHOD_HARD:
+             r = varlink_reboot("io.systemd.Shutdown.Reboot");
+             break;
+           case RM_REBOOTMETHOD_SOFT:
+             r = varlink_reboot("io.systemd.Shutdown.SoftReboot");
+             break;
+           default:
+             /* cannot happen */
+             r = -EINVAL;
+             break;
+           }
+         if (r < 0)
+           {
+             log_msg(LOG_ERR, "varlink_reboot failed, falling back to 
systemctl");
 
+             // varlink failed, use systemctl
              switch (ctx->reboot_method)
                {
                case RM_REBOOTMETHOD_HARD:
-                  char envar1[] = "SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT=1";
-                  char *env[] = {envar1, NULL};
-
-                  r = execle("/usr/bin/systemctl", "systemctl", "reboot",
-                            NULL, env);
-
+                 {
+                   char *env[] = {"SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT=1", NULL};
+                   r = exec_cmd(NULL, env, "/usr/bin/systemctl", "systemctl",
+                                "reboot", NULL);
+                 }
                  break;
                case RM_REBOOTMETHOD_SOFT:
-                  r = execl("/usr/bin/systemctl", "systemctl", "soft-reboot",
-                            NULL);
+                 r = exec_cmd(NULL, NULL, "/usr/bin/systemctl", "systemctl",
+                              "soft-reboot", NULL);
                  break;
                default:
                  /* cannot happen */
+                 r = -EINVAL;
                  break;
                }
              if (r < 0)
-               {
-                 log_msg(LOG_ERR, "Calling /usr/bin/systemctl %s failed: %m",
-                         (ctx->reboot_method == 
RM_REBOOTMETHOD_HARD)?"reboot":"soft-reboot");
-                 exit(1);
-                }
-              exit(0);
-            }
-        }
-
-      reset_timer(ctx);
+               log_msg(LOG_ERR, "Calling /usr/bin/systemctl %s failed: %s",
+                       (ctx->reboot_method == 
RM_REBOOTMETHOD_HARD)?"reboot":"soft-reboot",
+                       strerror(-r));
+             else if (r > 0)
+               log_msg(LOG_ERR, "Calling /usr/bin/systemctl %s failed with 
exit code %i",
+                       (ctx->reboot_method == 
RM_REBOOTMETHOD_HARD)?"reboot":"soft-reboot", r);
+           }
+         reset_timer(ctx);
+       }
     }
 
   return 0;
@@ -873,7 +973,7 @@
   r = sd_varlink_server_set_info(varlink_server, "openSUSE",
                                 PACKAGE" (rebootmgrd)",
                                 VERSION,
-                                "https://github.com/thkukuk/rebootmgr3";);
+                                "https://github.com/SUSE/rebootmgr";);
   if (r < 0)
     return r;
 
@@ -1008,6 +1108,14 @@
 
   log_init();
 
+  /* Seed the PRNG used for the random reboot delay with real entropy.
+     Without this, every freshly started daemon uses the same libc
+     default seed and thus picks the exact same "random" delay. */
+  unsigned int seed;
+  if (getrandom(&seed, sizeof(seed), 0) != sizeof(seed))
+    seed = (unsigned int)time(NULL) ^ (unsigned int)getpid();
+  srand(seed);
+
   while (1)
     {
       int c;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rebootmgr-4.0+git20261008.76d8ee7/tests/test-calendarspec.c 
new/rebootmgr-4.3/tests/test-calendarspec.c
--- old/rebootmgr-4.0+git20261008.76d8ee7/tests/test-calendarspec.c     
2026-10-08 09:48:28.000000000 +0200
+++ new/rebootmgr-4.3/tests/test-calendarspec.c 2026-10-29 12:23:33.000000000 
+0100
@@ -137,6 +137,8 @@
         test_one("annually", "*-01-01 00:00:00");
         test_one("*:2/3", "*-*-* *:02/3:00");
         test_one("2015-10-25 01:00:00 uTc", "2015-10-25 01:00:00 UTC");
+        test_one("2015-10-25 01:00:00 Europe/Berlin", "2015-10-25 01:00:00 
Europe/Berlin");
+        test_one("*-*-* 04:04:00 America/New_York", "*-*-* 04:04:00 
America/New_York");
 
         test_next("2016-03-27 03:17:00", "", 12345, 1459048620000000);
         test_next("2016-03-27 03:17:00", "CET", 12345, 1459041420000000);
@@ -146,10 +148,15 @@
         test_next("2016-03-27 03:17:00 UTC", "CET", 12345, 1459048620000000);
         test_next("2016-03-27 03:17:00 UTC", "EET", 12345, 1459048620000000);
 
+        /* Explicit per-spec timezone, independent of the process-wide TZ */
+        test_next("2016-03-27 03:17:00 Europe/Berlin", "EET", 12345, 
1459041420000000);
+        test_next("2016-03-27 03:17:00 America/New_York", "UTC", 12345, 
1459063020000000);
+
         assert_se(calendar_spec_from_string("test", &c) < 0);
         assert_se(calendar_spec_from_string("", &c) < 0);
         assert_se(calendar_spec_from_string("7", &c) < 0);
         assert_se(calendar_spec_from_string("121212:1:2", &c) < 0);
+        assert_se(calendar_spec_from_string("04:04:00 Bogus/Zone", &c) < 0);
 
         return 0;
 }

Reply via email to