Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add time namespace support to unshare #974

Merged
merged 2 commits into from
Mar 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bash-completion/unshare
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ _unshare_module()
--pid
--user
--cgroup
--time
--fork
--kill-child
--keep-caps
Expand All @@ -38,6 +39,8 @@ _unshare_module()
--version
--root
--wd
--monotonic
--boottime
--setuid
--setgid"
COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
Expand Down
3 changes: 3 additions & 0 deletions include/namespace.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
29 changes: 29 additions & 0 deletions sys-utils/unshare.1
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down
63 changes: 58 additions & 5 deletions sys-utils/unshare.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
};

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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));
Expand All @@ -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' },
Expand All @@ -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 },
Expand All @@ -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 }
};

Expand All @@ -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;
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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();
Expand All @@ -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);

Expand Down Expand Up @@ -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();

Expand Down