7

I thought I could do something like:

sudo unshare -T bash -c 'date -s "$1" && foobar' sh "$(date -d -1day)"

so foobar would see a different system time from the rest of the system. However, it seems the change of system time is not contained. It changes the system time of the whole system.

This LWN article seems to suggest this namespace was meant for the use I tried to give it.

System calls that adjust the system time will, when called outside of the root time namespace, adjust the namespace-specific offsets instead.

Looking at strace date -s ..., I see among other output:

clock_settime(CLOCK_REALTIME, {tv_sec=1619044910, tv_nsec=0}) = 0

However, reading time_namespaces(7):

This affects various APIs that measure against these clocks, including: clock_gettime(2), clock_nanosleep(2), nanosleep(2), timer_settime(2), timerfd_settime(2), and /proc/uptime.

I see it doesn't mention clock_settime(2). The wording "including" tells me this is perhaps not the complete list, but maybe it is.

I also don't understand --boottime/--monotonic. Looking at clock_settime(2), I see:

CLOCK_MONOTONIC A nonsettable system-wide clock that represents monotonic time since—as described by POSIX—"some unspecified point in the past". On Linux, that point corresponds to the number of seconds that the system has been running since it was booted.

CLOCK_BOOTTIME (since Linux 2.6.39; Linux-specific) A nonsettable system-wide clock that is identical to CLOCK_MONOTONIC, except that it also includes any time that the system is suspended.

However, when trying them, they don't seem to change the uptime:

$ uptime -s
2021-04-10 10:30:45
$ sudo unshare -T --boottime 1000000000 uptime -s
2021-04-10 10:30:45
$ sudo unshare -T --monotonic 1000000000 uptime -s
2021-04-10 10:30:45
$ sudo unshare -T --boottime -100000 uptime -s
2021-04-10 10:30:45
$ sudo unshare -T --monotonic -100000 uptime -s
2021-04-10 10:30:45

I see from strace uptime that it reads /proc/uptime instead of calling clock_gettime(2), and /proc/uptime doesn't seem to be affected by the unshare calls and their offsets, despite the documentation at time_namespaces(7) saying that it affects /proc/uptime as I quoted above.

How is this namespace supposed to be used? I can't seem to find any command that would be affected by unshare --time.

1 Answer 1

7

I see three main points in your reasoning that need clarification:


The first one is that unsharing a time-namespace affects the children spawned from then on by the process that called the unshare(2). The calling process itself is unaffected. This is a bit like PID namespaces and unlike the other namespace types so far. However, the calling process may still enter that newly created time-namespace, it's just that if it wants to do so then it also has to setns(2) (i.e. nsenter(1) in CLI parlance) itself into it.

All this means that the unshare -T commands you've been running never really moved those commands into the newly made time-namespace. You can just add the -f option to unshare(1) to make it run the specified command as its child instead of execve(2) itself into it. That way the specified command will live into that time-namespace.

Naturally, as you've been doing correctly, you also want to specify the --boottime and/or --monotonic options to "warp" that time-namespace's vision of those clocks, otherwise the child time-namespace would simply have the same vision as its parent.

So, to cap it all, taking your attempt as an example, on my machine:

$ sudo unshare -T --boottime 1000000000 uptime -s
2021-04-23 11:07:10
$ sudo unshare -fT --boottime 1000000000 uptime -s
1989-08-15 09:20:30
$

Alternatively to using those convenient options, you may set the /proc/self/timens_offsets file manually as long as you do it prior to spawning any child. The "manual" equivalent of the above would be something like:

$ sudo unshare -T dash -c 'echo "boottime 1000000000 0" > /proc/self/timens_offsets; uptime -s'
1989-08-15 09:20:30

Here I'm using dash just to use a leaner shell that certainly does not spawn children for its own bootstrap and that also has a builtin echo (so as not to spawn a child for it) to go setting its own timens_offsets file. From then on, all subsequent commands run by dash will see a "warped" boottime.

But not if you rather do exec uptime -s, because that would replace dash with uptime hence still living in the parent time-namespace, unless you also nsenter(1) beforehand. Consider:

$ sudo unshare -T dash -c 'echo "boottime 1000000000 0" > /proc/self/timens_offsets; exec uptime -s'
2021-04-23 11:07:10
$ sudo unshare -T dash -c 'echo "boottime 1000000000 0" > /proc/self/timens_offsets; exec nsenter --time=/proc/self/ns/time_for_children uptime -s'
1989-08-15 09:20:30
$

The second point I see that needs clarification is regarding the date command specifically.

Note that as of kernel v5.11 (as well as current latest v5.12-rc8) only CLOCK_BOOTIME and CLOCK_MONOTONIC can be "warped" in a new time-namespace, as it is also stated in the NOTES of time_namespaces(7):

Note that time namespaces do not virtualize the CLOCK_REALTIME clock. Virtualization of this clock was avoided for reasons of complexity and overhead within the kernel.

However, date specifically acts on the CLOCK_REALTIME clock as you can notice from your strace date -s ... command. This means that, at the time I'm writing this, the date command is still quite unaffected by the time-namespace it lives in.

A quick example of command that is indeed affected by the time-namespace because it refers to CLOCK_MONOTONIC is dmesg. Try something like:

$ sudo unshare -fT --monotonic 1000000000 dmesg -T

The third point seems to be about this quote:

System calls that adjust the system time will, when called outside of the root time namespace, adjust the namespace-specific offsets instead.

Admittedly that is a bit misleading because (currently) it is clearly not possible to arbitrarily change a time-namespace's vision of time afterwards, simply because both CLOCK_MONOTONIC and CLOCK_BOOTTIME are in fact unchangeable. Those two clocks are meant to be unchangeable.

The only operation allowed is thus to "bootstrap" them to some offset (related to the initial time namespace) before any process has joined that time-namespace, so that no process will ever experience jumps (not even forwards) of those two clocks.

This is why the unshare(2) does not move the calling process into the newly created time-namespace: that way it (or some other process) has the opportunity to specify the "bootstrapping" offsets, which in fact cannot be changed after any one process entered the time-namespace. Computing the correct offset is obviously a delicate operation, and that is a job for a "namespace manager".

6
  • "The calling process itself is unaffected." -- For a moment, I thought you meant for all namespaces in general, but I see the time namespace is relatively special in this regard. Having the time namespace work like this makes sense when I re-read this sentence from a comment in the LWN article I linked: "If you're not careful, a migrated process has a high probability of seeing CLOCK_MONOTONIC going backward, for example, which is going to create confusion." I guess this is to avoid that.
    – JoL
    Commented Apr 23, 2021 at 15:49
  • @JoL Sort of, although that quote seems to be more related to the very existence of time-namespaces: migrating processes to other machines would definitely make them see different values of monotonic clocks, which is unwanted. See edited answer.
    – LL3
    Commented Apr 23, 2021 at 17:36
  • 1
    To be thorough, on architectures where vdso(7) is available to provide the time (like amd64) , the clock_gettime(2) syscall doesn't happen, but the vDSO clock_gettime() library call can still be seen with ltrace instead of strace, and one can indeed see its first arg is 0 <=> CLOCK_REALTIME.
    – A.B
    Commented Apr 23, 2021 at 22:04
  • "Note that time namespaces do not virtualize the CLOCK_REALTIME clock." - disappointing. Other than migrating VMs to different hosts, what use are time namespaces as currently implemented?
    – Wodin
    Commented Jan 7, 2022 at 9:41
  • @Wodin Note that Linux Namespaces are used to implement containers, not full VMs. In fact, VMs even have their own CLOCK_REALTIME as emulated by the virtualizer (Qemu/KVM, Xen Hypervisor, etc.). I'm just conjecturing here but one use of time-namespaces may be for migrating, or simply stopping and restarting, containers. For instance, (re-)starting a container by priming its new time-namespace with the correct offset in relation to the container's previous time-namespace. Don't know whether orchestration engines such as Kubernetes actually allow that though.
    – LL3
    Commented Jan 7, 2022 at 13:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .