* 'time-namespace' of https://github.com/adrianreber/util-linux:
  unshare: support the time namespace
  unshare: fix help message indentation
This commit is contained in:
Karel Zak 2020-03-09 10:31:45 +01:00
commit ef0341c9be
4 changed files with 93 additions and 5 deletions

View 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) )

View 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>

View 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),

View 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);
@ -280,10 +299,12 @@ 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=<dir> run the command with root directory set to <dir>\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(_(" -R, --root=<dir> run the command with root directory set to <dir>\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));
@ -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();