.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 ]
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.
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
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),
{ .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 }
};
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;
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);
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));
OPT_SETGROUPS,
OPT_KILLCHILD,
OPT_KEEPCAPS,
+ OPT_MONOTONIC,
+ OPT_BOOTTIME,
};
static const struct option longopts[] = {
{ "help", no_argument, NULL, 'h' },
{ "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 },
{ "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 }
};
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;
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";
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();
}
}
+ 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);
bind_ns_files(getpid());
}
+ if (force_boottime)
+ settime(boottime, CLOCK_BOOTTIME);
+
+ if (force_monotonic)
+ settime(monotonic, CLOCK_MONOTONIC);
+
if (forkit) {
pid = fork();