]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
unshare: support the time namespace
authorAdrian Reber <areber@redhat.com>
Fri, 6 Mar 2020 11:05:00 +0000 (12:05 +0100)
committerAdrian Reber <areber@redhat.com>
Sun, 8 Mar 2020 18:02:00 +0000 (19:02 +0100)
This adds support to unshare for time namespaces. With the newly added
options '-t, --time' and '--monotonic' and '--boottime' it is now
possible to change CLOCK_MONOTONIC and CLOCK_BOOTTIME in a new time
namespace.

The time namespace has been merged in kernel version 5.6 and an easy way
to test it is using CLOCK_BOOTTIME and the uptime command:

 # uptime
 11:08:26 up 20:28,  1 user,  load average: 0.00, 0.00, 0.00
 # ./unshare --fork --time --boottime 100000000 uptime
 11:08:29 up 1158 days,  6:15,  1 user,  load average: 0.00, 0.00, 0.00

Signed-off-by: Adrian Reber <areber@redhat.com>
bash-completion/unshare
include/namespace.h
sys-utils/unshare.1
sys-utils/unshare.c

index 8c365a5fb1e212b9bb654b87b2e8dcf80d336072..d421eb1a3b46ad1df0f0489fa8de15aa48427bb9 100644 (file)
@@ -26,6 +26,7 @@ _unshare_module()
                                --pid
                                --user
                                --cgroup
+                               --time
                                --fork
                                --kill-child
                                --keep-caps
@@ -38,6 +39,8 @@ _unshare_module()
                                --version
                                --root
                                --wd
+                               --monotonic
+                               --boottime
                                --setuid
                                --setgid"
                        COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
index c48602d70c6119b41e62aca31db8007e54bfa5ab..2d0a56e0222ae607bb28652e053c6afde2f53b77 100644 (file)
@@ -31,6 +31,9 @@
 # ifndef CLONE_NEWPID
 #  define CLONE_NEWPID 0x20000000
 # endif
+# ifndef CLONE_NEWTIME
+#  define CLONE_NEWTIME 0x00000080
+# endif
 
 # if !defined(HAVE_UNSHARE) || !defined(HAVE_SETNS)
 #  include <sys/syscall.h>
index 597e6160eb1f689383836f3dcb144f0293fdd380..4d70f3f2904764ad043f659c6d6c95ca4c8d58c1 100644 (file)
@@ -100,6 +100,13 @@ and the discussion of the
 .B CLONE_NEWUSER
 flag in
 .BR clone (2).
+.TP
+.B time namespace
+The process can have a distinct view of
+.B CLOCK_MONOTONIC
+and/or
+.B CLOCK_BOOTTIME
+which can be changed using \fI/proc/self/timens_offsets\fP.
 .SH OPTIONS
 .TP
 .BR \-i , " \-\-ipc" [ =\fIfile ]
@@ -134,6 +141,12 @@ namespace is created by a bind mount.
 Unshare the cgroup namespace. If \fIfile\fP is specified then persistent namespace is created
 by bind mount.
 .TP
+.BR \-t , " \-\-time"[=\fIfile\fP]
+Unshare the time namespace. If \fIfile\fP is specified then a persistent
+namespace is created by a bind mount. The \fB\-\-monotonic\fP and
+\fB\-\-boottime\fP options can be used to specify the corresponding
+offset in the time namespace.
+.TP
 .BR \-f , " \-\-fork"
 Fork the specified \fIprogram\fR as a child process of \fBunshare\fR rather than
 running it directly.  This is useful when creating a new PID namespace.
@@ -208,6 +221,18 @@ Set the user ID which will be used in the entered namespace.
 Set the group ID which will be used in the entered namespace and drop
 supplementary groups.
 .TP
+.BR "\-\-monotonic \fIoffset"
+Set the offset of
+.B CLOCK_MONOTONIC
+which will be used in the entered time namespace. This option requires
+unsharing a time namespace with \fB\-\-time\fP.
+.TP
+.BR "\-\-boottime \fIoffset"
+Set the offset of
+.B CLOCK_BOOTTIME
+which will be used in the entered time namespace. This option requires
+unsharing a time namespace with \fB\-\-time\fP.
+.TP
 .BR \-V , " \-\-version"
 Display version information and exit.
 .TP
@@ -269,6 +294,10 @@ Reliable killing of subprocesses of the \fIprogram\fR.
 When \fBunshare\fR gets killed, everything below it gets killed as well.
 Without it, the children of \fIprogram\fR would have orphaned and
 been re-parented to PID 1.
+.TP
+.B # unshare \-\-fork \-\-time \-\-boottime 100000000 uptime
+.TQ
+ 10:58:48 up 1158 days,  6:05,  1 user,  load average: 0.00, 0.00, 0.00
 
 .SH SEE ALSO
 .BR clone (2),
index 9b0a122a7879d1dbf8822bbcb2afb3cfc32e47f2..b67e023c7d0d46306be4d495b4f1eb3f8d800028 100644 (file)
@@ -65,6 +65,7 @@ static struct namespace_file {
        { .type = CLONE_NEWNET,   .name = "ns/net"  },
        { .type = CLONE_NEWPID,   .name = "ns/pid"  },
        { .type = CLONE_NEWNS,    .name = "ns/mnt"  },
+       { .type = CLONE_NEWTIME,  .name = "ns/time"  },
        { .name = NULL }
 };
 
@@ -213,6 +214,23 @@ static ino_t get_mnt_ino(pid_t pid)
        return st.st_ino;
 }
 
+static void settime(time_t offset, clockid_t clk_id)
+{
+       char buf[sizeof(stringify_value(ULONG_MAX)) * 3];
+       int fd, len;
+
+       len = snprintf(buf, sizeof(buf), "%d %ld 0", clk_id, offset);
+
+       fd = open("/proc/self/timens_offsets", O_WRONLY);
+       if (fd < 0)
+               err(EXIT_FAILURE, _("failed to open /proc/self/timens_offsets"));
+
+       if (write(fd, buf, len) != len)
+               err(EXIT_FAILURE, _("failed to write to /proc/self/timens_offsets"));
+
+       close(fd);
+}
+
 static void bind_ns_files_from_child(pid_t *child, int fds[2])
 {
        char ch;
@@ -267,6 +285,7 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_(" -p, --pid[=<file>]        unshare pid namespace\n"), out);
        fputs(_(" -U, --user[=<file>]       unshare user namespace\n"), out);
        fputs(_(" -C, --cgroup[=<file>]     unshare cgroup namespace\n"), out);
+       fputs(_(" -t, --time[=<file>]       unshare time namespace\n"), out);
        fputs(USAGE_SEPARATOR, out);
        fputs(_(" -f, --fork                fork before launching <program>\n"), out);
        fputs(_(" -r, --map-root-user       map current user to root (implies --user)\n"), out);
@@ -284,6 +303,8 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_(" -w, --wd=<dir>            change working directory to <dir>\n"), out);
        fputs(_(" -S, --setuid <uid>        set uid in entered namespace\n"), out);
        fputs(_(" -G, --setgid <gid>        set gid in entered namespace\n"), out);
+       fputs(_(" --monotonic <offset>      set clock monotonic offset (seconds) in time namespaces\n"), out);
+       fputs(_(" --boottime <offset>       set clock boottime offset (seconds) in time namespaces\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
        printf(USAGE_HELP_OPTIONS(27));
@@ -300,6 +321,8 @@ int main(int argc, char *argv[])
                OPT_SETGROUPS,
                OPT_KILLCHILD,
                OPT_KEEPCAPS,
+               OPT_MONOTONIC,
+               OPT_BOOTTIME,
        };
        static const struct option longopts[] = {
                { "help",          no_argument,       NULL, 'h'             },
@@ -312,6 +335,7 @@ int main(int argc, char *argv[])
                { "pid",           optional_argument, NULL, 'p'             },
                { "user",          optional_argument, NULL, 'U'             },
                { "cgroup",        optional_argument, NULL, 'C'             },
+               { "time",          optional_argument, NULL, 't'             },
 
                { "fork",          no_argument,       NULL, 'f'             },
                { "kill-child",    optional_argument, NULL, OPT_KILLCHILD   },
@@ -325,6 +349,8 @@ int main(int argc, char *argv[])
                { "setgid",        required_argument, NULL, 'G'             },
                { "root",          required_argument, NULL, 'R'             },
                { "wd",            required_argument, NULL, 'w'             },
+               { "monotonic",     required_argument, NULL, OPT_MONOTONIC   },
+               { "boottime",      required_argument, NULL, OPT_BOOTTIME    },
                { NULL, 0, NULL, 0 }
        };
 
@@ -343,13 +369,17 @@ int main(int argc, char *argv[])
        uid_t uid = 0, real_euid = geteuid();
        gid_t gid = 0, real_egid = getegid();
        int keepcaps = 0;
+       time_t monotonic = 0;
+       time_t boottime = 0;
+       int force_monotonic = 0;
+       int force_boottime = 0;
 
        setlocale(LC_ALL, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
        close_stdout_atexit();
 
-       while ((c = getopt_long(argc, argv, "+fhVmuinpCUrR:w:S:G:c", longopts, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "+fhVmuinpCtUrR:w:S:G:c", longopts, NULL)) != -1) {
                switch (c) {
                case 'f':
                        forkit = 1;
@@ -389,6 +419,11 @@ int main(int argc, char *argv[])
                        if (optarg)
                                set_ns_target(CLONE_NEWCGROUP, optarg);
                        break;
+               case 't':
+                       unshare_flags |= CLONE_NEWTIME;
+                       if (optarg)
+                               set_ns_target(CLONE_NEWTIME, optarg);
+                       break;
                case OPT_MOUNTPROC:
                        unshare_flags |= CLONE_NEWNS;
                        procmnt = optarg ? optarg : "/proc";
@@ -443,6 +478,14 @@ int main(int argc, char *argv[])
                case 'w':
                        newdir = optarg;
                        break;
+                case OPT_MONOTONIC:
+                       monotonic = strtoul_or_err(optarg, _("failed to parse monotonic offset"));
+                       force_monotonic = 1;
+                       break;
+                case OPT_BOOTTIME:
+                       boottime = strtoul_or_err(optarg, _("failed to parse boottime offset"));
+                       force_boottime = 1;
+                       break;
 
                case 'h':
                        usage();
@@ -453,6 +496,10 @@ int main(int argc, char *argv[])
                }
        }
 
+       if ((force_monotonic || force_boottime) && !(unshare_flags & CLONE_NEWTIME))
+               errx(EXIT_FAILURE, _("options --monotonic and --boottime require "
+                       "unsharing of a time namespace (-t)"));
+
        if (npersists && (unshare_flags & CLONE_NEWNS))
                bind_ns_files_from_child(&pid, fds);
 
@@ -486,6 +533,12 @@ int main(int argc, char *argv[])
                        bind_ns_files(getpid());
        }
 
+       if (force_boottime)
+               settime(boottime, CLOCK_BOOTTIME);
+
+       if (force_monotonic)
+               settime(monotonic, CLOCK_MONOTONIC);
+
        if (forkit) {
                pid = fork();