From 6671501cfa2c49f201d3928d65dd538d3ef5c9cc Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Fri, 6 Mar 2020 12:03:50 +0100 Subject: [PATCH 1/2] unshare: fix help message indentation A few lines of the help message were unaligned in the output because of mixes use of tabs and space. This removes all tabs and replaces them with spaces. Signed-off-by: Adrian Reber --- sys-utils/unshare.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 8d33f2273..9b0a122a7 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -280,10 +280,10 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" --setgroups allow|deny control the setgroups syscall in user namespaces\n"), out); fputs(_(" --keep-caps retain capabilities granted in user namespaces\n"), out); fputs(USAGE_SEPARATOR, out); - fputs(_(" -R, --root= run the command with root directory set to \n"), out); - fputs(_(" -w, --wd= change working directory to \n"), out); - fputs(_(" -S, --setuid set uid in entered namespace\n"), out); - fputs(_(" -G, --setgid set gid in entered namespace\n"), out); + fputs(_(" -R, --root= run the command with root directory set to \n"), out); + fputs(_(" -w, --wd= change working directory to \n"), out); + fputs(_(" -S, --setuid set uid in entered namespace\n"), out); + fputs(_(" -G, --setgid set gid in entered namespace\n"), out); fputs(USAGE_SEPARATOR, out); printf(USAGE_HELP_OPTIONS(27)); From be7df01a62d1284debc6dbe8a77677795b6876ea Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Fri, 6 Mar 2020 12:05:00 +0100 Subject: [PATCH 2/2] unshare: support the time namespace 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 --- bash-completion/unshare | 3 +++ include/namespace.h | 3 +++ sys-utils/unshare.1 | 29 ++++++++++++++++++++++ sys-utils/unshare.c | 55 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/bash-completion/unshare b/bash-completion/unshare index 8c365a5fb..d421eb1a3 100644 --- a/bash-completion/unshare +++ b/bash-completion/unshare @@ -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) ) diff --git a/include/namespace.h b/include/namespace.h index c48602d70..2d0a56e02 100644 --- a/include/namespace.h +++ b/include/namespace.h @@ -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 diff --git a/sys-utils/unshare.1 b/sys-utils/unshare.1 index 597e6160e..4d70f3f29 100644 --- a/sys-utils/unshare.1 +++ b/sys-utils/unshare.1 @@ -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), diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 9b0a122a7..b67e023c7 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -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[=] unshare pid namespace\n"), out); fputs(_(" -U, --user[=] unshare user namespace\n"), out); fputs(_(" -C, --cgroup[=] unshare cgroup namespace\n"), out); + fputs(_(" -t, --time[=] unshare time namespace\n"), out); fputs(USAGE_SEPARATOR, out); fputs(_(" -f, --fork fork before launching \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= change working directory to \n"), out); fputs(_(" -S, --setuid set uid in entered namespace\n"), out); fputs(_(" -G, --setgid set gid in entered namespace\n"), out); + fputs(_(" --monotonic set clock monotonic offset (seconds) in time namespaces\n"), out); + fputs(_(" --boottime 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();