Compare commits
15 Commits
e360d9b900
...
dd4f400953
Author | SHA1 | Date |
---|---|---|
Lennart Poettering | dd4f400953 | |
Luca Boccassi | 2e5b0412f9 | |
Yu Watanabe | 2b397d43ab | |
Yu Watanabe | 9ad294efd0 | |
Lennart Poettering | 4c9769353e | |
Lennart Poettering | b16c6b0c08 | |
Lennart Poettering | a48ae38ea3 | |
Lennart Poettering | 3722a71a87 | |
Lennart Poettering | 805495ade2 | |
Lennart Poettering | 03d8af8da4 | |
Lennart Poettering | 9ed47d39d9 | |
Lennart Poettering | 462baeb410 | |
Lennart Poettering | 420b74654d | |
Lennart Poettering | b56421e0dc | |
Lennart Poettering | 221d6e54c6 |
|
@ -0,0 +1,283 @@
|
||||||
|
---
|
||||||
|
title: OSC 300819: Hierarchial Context Signalling
|
||||||
|
category: Interfaces
|
||||||
|
layout: default
|
||||||
|
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
---
|
||||||
|
|
||||||
|
# OSC 300819: Hierarchial Context Signalling
|
||||||
|
|
||||||
|
A terminal connects a user with programs. Control of the program side of
|
||||||
|
terminals is typically passed around to various different components while the
|
||||||
|
user is active: a shell might pass control to a process it invokes. If that
|
||||||
|
process is `run0` then primary control is passed to the privileged session of
|
||||||
|
the target user. If `systemd-nspawn` is invoked to start a container primary
|
||||||
|
controls is passed to that container, and so on.
|
||||||
|
|
||||||
|
A terminal emulator might be interested to know which component is currently is
|
||||||
|
in primary control of the program side of a terminal. OSC 3000910 is a
|
||||||
|
mechanism to inform it about such contexts. Each component taking over control
|
||||||
|
can inform the terminal emulators that a new context begins now, and then use
|
||||||
|
the terminal or pass control down to further apps, which can introduce
|
||||||
|
contexts. Each context may carry various discriptive metadata fields.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
This OS is invented by systemd. Currently, no terminal application is known
|
||||||
|
that consumes these sequences.
|
||||||
|
|
||||||
|
## Usecases
|
||||||
|
|
||||||
|
Terminal emulators can use hierarchial context information:
|
||||||
|
|
||||||
|
1. To introduce markers/bookmarks in the output that the user can jump between.
|
||||||
|
|
||||||
|
2. To visually identify output from different contexts. For example the
|
||||||
|
background can be tinted in a reddish tone when privileges are acquired, and
|
||||||
|
similar.
|
||||||
|
|
||||||
|
3. Meta information on specific output can be shown in a tooltip or similar
|
||||||
|
|
||||||
|
4. Programs (and all subcontexts) can be killed by right-clicking on the output
|
||||||
|
they generate.
|
||||||
|
|
||||||
|
5. Failed commands or aborted sessions can be marked requesting use attention.
|
||||||
|
|
||||||
|
## Context Types
|
||||||
|
|
||||||
|
There are various types of contexts defined by this specification:
|
||||||
|
|
||||||
|
1. `boot` → a booted system initiates this context early at boot. (systemd's
|
||||||
|
PID 1 generates this on `/dev/console`.)
|
||||||
|
|
||||||
|
2. `container` → a container managed initialized an interactive connection to a
|
||||||
|
container. (`systemd-nspawn` generates this when interactively invoking a
|
||||||
|
container. `machinectl login`, `machinectl shell` do this too.)
|
||||||
|
|
||||||
|
3. `vm` → a VMM initialized a terminal connection to VM. (`systemd-vmspawn`
|
||||||
|
generates this when interactively invoking a VM, as one example.)
|
||||||
|
|
||||||
|
4. `elevate` → when the user interactively acquired higher privileges. (`run0`
|
||||||
|
initiates a context of this type whenever the user invokes it to acquire
|
||||||
|
root privileges.)
|
||||||
|
|
||||||
|
5. `chpriv` → similar, but when the user acquired *different* privileges, not
|
||||||
|
necessarily higher ones. (`run0` initiates a context of this type whenever
|
||||||
|
the user invokes it to acquire non-root privileges of another user.)
|
||||||
|
|
||||||
|
5. `subcontext` → similar, but the source and target privileges where
|
||||||
|
identical. (`run0` initiates a context of this type whenever the user
|
||||||
|
invokes it to acquire privileges of the user itself.)
|
||||||
|
|
||||||
|
6. `remote` → a user invoked a tool such as `ssh` to connect to a remote
|
||||||
|
system.
|
||||||
|
|
||||||
|
7. `shell` → an interactive terminal shell initiates this context
|
||||||
|
|
||||||
|
8. `command` → a shell interactively invokes a new program.
|
||||||
|
|
||||||
|
9. `app` → an interactive program may initiate this context.
|
||||||
|
|
||||||
|
10. `service` → the service manager invokes an interactive service on the terminal
|
||||||
|
|
||||||
|
11. `session` → a login session of the user is initialized.
|
||||||
|
|
||||||
|
## Semantics
|
||||||
|
|
||||||
|
Contexts in the sense of OSC 300819 are hierarchial, and describe a tree
|
||||||
|
structure: whenever a new context is opened it becomes the new active context,
|
||||||
|
and the previously active context becomes its parent (if there is one). Only
|
||||||
|
one context is currently active, but previously opened contexts remain valid in
|
||||||
|
the background. Any other data written or read should be considered associated
|
||||||
|
with the currently active context.
|
||||||
|
|
||||||
|
Each context carries an identifier, chosen by the component opening the
|
||||||
|
context. The identifier can chosen freely, but must not be longer than 64
|
||||||
|
characters. The characters may be in the 32…126 byte range. Identifiers should universally
|
||||||
|
unique, for example randomly generated. A freshly generated UUID would work
|
||||||
|
well for this, but this could also be something like the Linux boot ID combined
|
||||||
|
with the 64bit inode number of Linux pidfds, or something hashed from it.
|
||||||
|
|
||||||
|
Fundamentally, there are two OSC 300819 commands defined:
|
||||||
|
|
||||||
|
1. OSC "`300819;S`" (the *start sequence*) → this initiates, updates or indicates a return to a
|
||||||
|
context. It carries a context identifier, and typically some metadata. This
|
||||||
|
may be send to first initiate a context. If sent again for the a context ID
|
||||||
|
that was initiated already this indicates an update of the existing
|
||||||
|
context. In this case, any previously set metadata fields for the context
|
||||||
|
are flushed out, reset to their defaults, and then reinitialized from the
|
||||||
|
newly supplied data. Also, in this case any subcontects of the contexts are
|
||||||
|
implicitly terminated.
|
||||||
|
|
||||||
|
2. OSC "`300819;X`" (the *end sequence*)→ this terminates a context. It carries a context
|
||||||
|
identifier to close, initiated before with OSC `300819;S`. It may also carry
|
||||||
|
additional metadata.
|
||||||
|
|
||||||
|
## General Syntax
|
||||||
|
|
||||||
|
This builds on ECMA-48, and reuses the OSC and ST concepts introduced there.
|
||||||
|
|
||||||
|
For sequences following this specification it is recommended to encode OSC as
|
||||||
|
0x1B 0x5D, and ST as 0x1B 0x5C.
|
||||||
|
|
||||||
|
ECMA-48 only allows characters from the range 0x20…0x7e (i.e. 32…126) inside
|
||||||
|
OSC sequences. Hence, any fields that shall contain characters outside of this
|
||||||
|
range require escaping. All textual fields must be encoded in UTF-8, which
|
||||||
|
then must be escaped.
|
||||||
|
|
||||||
|
Escaping shall be applied by taking the byte values of the characters to
|
||||||
|
escape, and formatting them as lower-case hexadecimal prefixed with
|
||||||
|
`\x`. Example: `Schöpfgefäß` becomes `Sch\xc3\xb6pfgef\xc3\xa4\xc3\x9f`.
|
||||||
|
|
||||||
|
The start sequence begins with OSC, followed by the character `S`, followed by
|
||||||
|
the context ID. This is then followed by any number of metadata fields,
|
||||||
|
including none. Metadata fields begin with a semicolon (`;`) and end in a
|
||||||
|
character identifiying the type of field. The sequence ends in ST.
|
||||||
|
|
||||||
|
The end sequence begins with OSC, followed by the character `X`, followed by
|
||||||
|
the context ID, and a series of metadata fields in the the syntax as for the
|
||||||
|
start sequence.
|
||||||
|
|
||||||
|
## Metadata Fields
|
||||||
|
|
||||||
|
The following fields are currently defined:
|
||||||
|
|
||||||
|
| Suffix | Context Types | Description |
|
||||||
|
|--------|---------------|-------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `u` | *all* | UNIX user name the process issuing the sequence runs as |
|
||||||
|
| `h` | *all* | UNIX host name of the system the process issuing the sequence runs on |
|
||||||
|
| `m` | *all* | The machine ID (i.e. `/etc/machine-id`) of the system the process issuing the sequence runs on |
|
||||||
|
| `b` | *all* | The boot ID (i.e. `/proc/sys/kernel/random/boot_id`) of the system the process issuing the sequence runs on |
|
||||||
|
| `p` | *all* | The numeric PID of the process issuing the sequence, in decimal notation |
|
||||||
|
| `P` | *all* | The 64bit inode number of the pidfd of the process issuing the sequence, in decimal notation |
|
||||||
|
| `c` | *all* | The process name (i.e. `/proc/$PID/comm`, `PR_GET_NAME`) of the process issuing the sequence |
|
||||||
|
| `v` | `vm` | The name of the VM being invoked |
|
||||||
|
| `C` | `container` | The name of the container being invoked |
|
||||||
|
| `U` | `elevate`, `chpriv`, `vm`, `container`, `remote` | Target UNIX user name |
|
||||||
|
| `H` | `remote` | Target UNIX, DNS host name, or IP address |
|
||||||
|
|
||||||
|
All fields are optional, including the context type. However, it is generally
|
||||||
|
recommended to always include the first 7 fields listed above, to make it easy
|
||||||
|
to pinpoint the origin of a context in a race-free fashion without any
|
||||||
|
ambiguities.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
1. A new container `foobar` has been invoked by user `lennart` on host `zeta`:
|
||||||
|
`OSC "300819;Sbed86fab93af4328bbed0a1224af6d40;lennartu;zetah;3deb5353d3ba43d08201c136a47ead7bm;d4a3d0fdf2e24fdea6d971ce73f4fbf2b;1062862p;1063162P;foobarc;containert" ST`
|
||||||
|
|
||||||
|
2. A context ends: `OSC "300819;Xbed86fab93af4328bbed0a1224af6d40" ST`
|
||||||
|
|
||||||
|
## Syntax in ABNF
|
||||||
|
|
||||||
|
```abnf
|
||||||
|
OSC = %x1B %x5D
|
||||||
|
ST = %x1B %x5C
|
||||||
|
|
||||||
|
DECIMAL = "0"-"9"
|
||||||
|
HEX = "0"-"9" / "A"-"F" / "a-f"
|
||||||
|
ID128 = 32*36(HEX / "-")
|
||||||
|
UINT64 = 1*20DECIMAL
|
||||||
|
ESCAPED = "\x" HEX HEX
|
||||||
|
SAFE = %x20-3a / %x3c-5b / %x5d-7e / ESCAPED
|
||||||
|
|
||||||
|
CTXID = 1*64SAFE
|
||||||
|
|
||||||
|
USER = 1*255SAFE "u"
|
||||||
|
HOSTNAME = 1*255SAFE "h"
|
||||||
|
MACHINEID = 1D128 "m"
|
||||||
|
BOOTID = ID128 "b"
|
||||||
|
PID = UINT64 "p"
|
||||||
|
PIDFDID = UINT64 "P"
|
||||||
|
COMM = 1*255SAFE "c"
|
||||||
|
|
||||||
|
TYPE = ("service" / "session" / "shell" / "command" / "vm" / "container" / "elevate" / "chpriv" / "subcontext" / "remote" / "boot" / "app") "t"
|
||||||
|
|
||||||
|
SESSIONID = 1*255SAFE "s"
|
||||||
|
CWD = 1*255SAFE "d"
|
||||||
|
CMDLINE = *255SAFE "L"
|
||||||
|
VMNAME = 1*255SAFE "v"
|
||||||
|
CONTAINERNAME= 1*255SAFE "C"
|
||||||
|
TARGETUSER = 1*255SAFE "U"
|
||||||
|
TARGETHOST = 1*255SAFE "H"
|
||||||
|
APPID = 1*255SAFE "A"
|
||||||
|
|
||||||
|
STARTFIELD = (USER / HOSTNAME / MACHINEID / BOOTID / PID / PIDFDID / COMM / TYPE / SESSIONID / CWD / CMDLINE / VMNAME / CONTAINERNAME / TARGETUSER / TARGETHOST / APPID)
|
||||||
|
STARTSEQ = OSC "300819;" CTXID "S" *(";" STARTFIELD) ST
|
||||||
|
|
||||||
|
EXIT = "success" / "failure" / "crash" / "interrupt"
|
||||||
|
STATUS = UINT64
|
||||||
|
SIGNAL = "SIGBUS" / "SIGTRAP" / "SIGABRT" / "SIGSEGV" / …
|
||||||
|
|
||||||
|
ENDFIELD = (EXIT / STATUS / SIGNAL)
|
||||||
|
ENDSEQ = OSC "300819;" CTXID "X" *(";" ENDFIELD) ST
|
||||||
|
```
|
||||||
|
|
||||||
|
## Known OSC Prefixes
|
||||||
|
|
||||||
|
Here's a list of OSC prefixes used by the various sequences currently in public
|
||||||
|
use in various terminal emulators. It's not going to be complete, but I tried
|
||||||
|
to do some reasonably thorough research to avoid conflicts with the new OSC
|
||||||
|
sequence defined above.
|
||||||
|
|
||||||
|
| OSC Prefix | Purpose |
|
||||||
|
|----------------:|------------------------------------------------------------|
|
||||||
|
| `OSC "0;…"` | Icon name + window title |
|
||||||
|
| `OSC "1;…"` | Icon name |
|
||||||
|
| `OSC "2;…"` | Window title |
|
||||||
|
| `OSC "3;…"` | X11 property |
|
||||||
|
| `OSC "4;…"` | Palette |
|
||||||
|
| `OSC "5;…"` | Special palette |
|
||||||
|
| `OSC "6;…"` | Disable special color |
|
||||||
|
| `OSC "7;…"` | Report cwd |
|
||||||
|
| `OSC "8;…"` | Hyperlink |
|
||||||
|
| `OSC "9;…"` | Progress bar (conemu) [conflict: also growl notifications] |
|
||||||
|
| `OSC "10;…"` | Change colors |
|
||||||
|
| `OSC "11;…"` | " |
|
||||||
|
| `OSC "12;…"` | " |
|
||||||
|
| `OSC "13;…"` | " |
|
||||||
|
| `OSC "14;…"` | " |
|
||||||
|
| `OSC "15;…"` | " |
|
||||||
|
| `OSC "16;…"` | " |
|
||||||
|
| `OSC "17;…"` | " |
|
||||||
|
| `OSC "18;…"` | " |
|
||||||
|
| `OSC "19;…"` | " |
|
||||||
|
| `OSC "21;…"` | Query colors (kitty) |
|
||||||
|
| `OSC "22;…"` | Cursor shape |
|
||||||
|
| `OSC "46;…"` | Log file |
|
||||||
|
| `OSC "50;…"` | Set font |
|
||||||
|
| `OSC "51;…"` | Emacs shell |
|
||||||
|
| `OSC "52;…"` | Manipulate selection data (aka clipboard) |
|
||||||
|
| `OSC "60;…"` | Query allowed |
|
||||||
|
| `OSC "61;…"` | Query disallowed |
|
||||||
|
| `OSC "99;…"` | Notifications (kitty) |
|
||||||
|
| `OSC "104;…"` | Reset color |
|
||||||
|
| `OSC "105;…"` | Enable/disable special color |
|
||||||
|
| `OSC "110;…"` | Reset colors |
|
||||||
|
| `OSC "111;…"` | " |
|
||||||
|
| `OSC "112;…"` | " |
|
||||||
|
| `OSC "113;…"` | " |
|
||||||
|
| `OSC "114;…"` | " |
|
||||||
|
| `OSC "115;…"` | " |
|
||||||
|
| `OSC "116;…"` | " |
|
||||||
|
| `OSC "117;…"` | " |
|
||||||
|
| `OSC "118;…"` | " |
|
||||||
|
| `OSC "119;…"` | " |
|
||||||
|
| `OSC "133;…"` | Prompt/command begin/command end (finalterm/iterm2) |
|
||||||
|
| `OSC "440;…"` | Audio (mintty) |
|
||||||
|
| `OSC "633;…"` | vscode action (Windows Terminal) |
|
||||||
|
| `OSC "666;…"` | "termprop" (vte) |
|
||||||
|
| `OSC "701;…"` | Locale (mintty) |
|
||||||
|
| `OSC "777;…"` | Notification (rxvt) |
|
||||||
|
| `OSC "7704;…"` | ANSI colors (mintty) |
|
||||||
|
| `OSC "7750;…"` | Emoji style (mintty) |
|
||||||
|
| `OSC "7770;…"` | Font size (mintty) |
|
||||||
|
| `OSC "7771;…"` | Glyph coverage (mintty) |
|
||||||
|
| `OSC "7721:…"` | Copy window title (mintty) |
|
||||||
|
| `OSC "7777;…"` | Window size (mintty) |
|
||||||
|
| `OSC "9001;…"` | Action (Windows Terminal) |
|
||||||
|
| `OSC "1337;…"` | iterm2 multiplex seeuqnece |
|
||||||
|
| `OSC "5522;…"` | Clipboard (kitty) |
|
||||||
|
| `OSC "30001;…"` | Push color onto stack (kitty) |
|
||||||
|
| `OSC "30101;…"` | Pop color from stack (kitty) |
|
||||||
|
| `OSC "77119;…"` | Wide chars (mintty) |
|
|
@ -365,6 +365,8 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, XEscape
|
||||||
char *ans, *t, *prev, *prev2;
|
char *ans, *t, *prev, *prev2;
|
||||||
const char *f;
|
const char *f;
|
||||||
|
|
||||||
|
assert(s);
|
||||||
|
|
||||||
/* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be
|
/* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be
|
||||||
* reversed with cunescape(). If XESCAPE_8_BIT is specified, characters >= 127 are let through
|
* reversed with cunescape(). If XESCAPE_8_BIT is specified, characters >= 127 are let through
|
||||||
* unchanged. This corresponds to non-ASCII printable characters in pre-unicode encodings.
|
* unchanged. This corresponds to non-ASCII printable characters in pre-unicode encodings.
|
||||||
|
@ -397,7 +399,7 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, XEscape
|
||||||
|
|
||||||
if ((unsigned char) *f < ' ' ||
|
if ((unsigned char) *f < ' ' ||
|
||||||
(!FLAGS_SET(flags, XESCAPE_8_BIT) && (unsigned char) *f >= 127) ||
|
(!FLAGS_SET(flags, XESCAPE_8_BIT) && (unsigned char) *f >= 127) ||
|
||||||
*f == '\\' || strchr(bad, *f)) {
|
*f == '\\' || (bad && strchr(bad, *f))) {
|
||||||
if ((size_t) (t - ans) + 4 + 3 * force_ellipsis > console_width)
|
if ((size_t) (t - ans) + 4 + 3 * force_ellipsis > console_width)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -866,6 +866,9 @@ void hexdump(FILE *f, const void *p, size_t s) {
|
||||||
|
|
||||||
assert(b || s == 0);
|
assert(b || s == 0);
|
||||||
|
|
||||||
|
if (s == SIZE_MAX)
|
||||||
|
s = strlen(p);
|
||||||
|
|
||||||
if (!f)
|
if (!f)
|
||||||
f = stdout;
|
f = stdout;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <linux/magic.h>
|
||||||
#include <linux/oom.h>
|
#include <linux/oom.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <spawn.h>
|
#include <spawn.h>
|
||||||
|
@ -11,6 +12,9 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
#include <sys/personality.h>
|
#include <sys/personality.h>
|
||||||
|
#if HAVE_PIDFD_OPEN
|
||||||
|
#include <sys/pidfd.h>
|
||||||
|
#endif
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
@ -40,6 +44,7 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
#include "memory-util.h"
|
#include "memory-util.h"
|
||||||
|
#include "missing_magic.h"
|
||||||
#include "missing_sched.h"
|
#include "missing_sched.h"
|
||||||
#include "missing_syscall.h"
|
#include "missing_syscall.h"
|
||||||
#include "missing_threads.h"
|
#include "missing_threads.h"
|
||||||
|
@ -2289,3 +2294,42 @@ _noreturn_ void report_errno_and_exit(int errno_fd, int error) {
|
||||||
|
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getpidfdid_cached(uint64_t *ret) {
|
||||||
|
static uint64_t cached = 0;
|
||||||
|
static int initialized = 0;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
if (initialized > 0) {
|
||||||
|
*ret = cached;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (initialized < 0)
|
||||||
|
return initialized;
|
||||||
|
|
||||||
|
_cleanup_close_ int fd = pidfd_open(getpid_cached(), 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||||
|
return (initialized = -EOPNOTSUPP);
|
||||||
|
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = fd_is_fs_type(fd, PID_FS_MAGIC);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return (initialized = -EOPNOTSUPP);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fd, &st) < 0)
|
||||||
|
return -errno;
|
||||||
|
if (st.st_ino == 0)
|
||||||
|
return (initialized = -EOPNOTSUPP);
|
||||||
|
|
||||||
|
*ret = cached = st.st_ino;
|
||||||
|
initialized = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -275,3 +275,5 @@ int proc_dir_read(DIR *d, pid_t *ret);
|
||||||
int proc_dir_read_pidref(DIR *d, PidRef *ret);
|
int proc_dir_read_pidref(DIR *d, PidRef *ret);
|
||||||
|
|
||||||
_noreturn_ void report_errno_and_exit(int errno_fd, int error);
|
_noreturn_ void report_errno_and_exit(int errno_fd, int error);
|
||||||
|
|
||||||
|
int getpidfdid_cached(uint64_t *ret);
|
||||||
|
|
|
@ -42,6 +42,7 @@ static void fallback_random_bytes(void *p, size_t n) {
|
||||||
uint64_t call_id, block_id;
|
uint64_t call_id, block_id;
|
||||||
usec_t stamp_mono, stamp_real;
|
usec_t stamp_mono, stamp_real;
|
||||||
pid_t pid, tid;
|
pid_t pid, tid;
|
||||||
|
uint64_t pidfdid;
|
||||||
uint8_t auxval[16];
|
uint8_t auxval[16];
|
||||||
} state = {
|
} state = {
|
||||||
/* Arbitrary domain separation to prevent other usage of AT_RANDOM from clashing. */
|
/* Arbitrary domain separation to prevent other usage of AT_RANDOM from clashing. */
|
||||||
|
@ -57,6 +58,8 @@ static void fallback_random_bytes(void *p, size_t n) {
|
||||||
memcpy(state.auxval, ULONG_TO_PTR(getauxval(AT_RANDOM)), sizeof(state.auxval));
|
memcpy(state.auxval, ULONG_TO_PTR(getauxval(AT_RANDOM)), sizeof(state.auxval));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
(void) getpidfdid_cached(&state.pidfdid);
|
||||||
|
|
||||||
while (n > 0) {
|
while (n > 0) {
|
||||||
struct sha256_ctx ctx;
|
struct sha256_ctx ctx;
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include "efivars.h"
|
#include "efivars.h"
|
||||||
#include "emergency-action.h"
|
#include "emergency-action.h"
|
||||||
#include "env-util.h"
|
#include "env-util.h"
|
||||||
|
#include "escape.h"
|
||||||
#include "exit-status.h"
|
#include "exit-status.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fdset.h"
|
#include "fdset.h"
|
||||||
|
@ -57,6 +58,7 @@
|
||||||
#include "ima-setup.h"
|
#include "ima-setup.h"
|
||||||
#include "import-creds.h"
|
#include "import-creds.h"
|
||||||
#include "initrd-util.h"
|
#include "initrd-util.h"
|
||||||
|
#include "io-util.h"
|
||||||
#include "ipe-setup.h"
|
#include "ipe-setup.h"
|
||||||
#include "killall.h"
|
#include "killall.h"
|
||||||
#include "kmod-setup.h"
|
#include "kmod-setup.h"
|
||||||
|
@ -73,6 +75,7 @@
|
||||||
#include "mount-setup.h"
|
#include "mount-setup.h"
|
||||||
#include "mount-util.h"
|
#include "mount-util.h"
|
||||||
#include "os-util.h"
|
#include "os-util.h"
|
||||||
|
#include "osc-context.h"
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
#include "parse-argument.h"
|
#include "parse-argument.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
|
@ -2380,6 +2383,38 @@ static void log_execution_mode(bool *ret_first_boot) {
|
||||||
*ret_first_boot = first_boot;
|
*ret_first_boot = first_boot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int write_boot_or_shutdown_osc(bool boot) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (getenv_terminal_is_dumb())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const char *type = boot ? "boot" : "shutdown";
|
||||||
|
|
||||||
|
_cleanup_close_ int fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
|
||||||
|
if (fd < 0)
|
||||||
|
return log_debug_errno(fd, "Failed to open /dev/console to print %s OSC, ignoring: %m", type);
|
||||||
|
|
||||||
|
_cleanup_free_ char *seq = NULL;
|
||||||
|
if (boot)
|
||||||
|
r = osc_context_open_boot(&seq);
|
||||||
|
else
|
||||||
|
r = osc_context_close(SD_ID128_ALLF, &seq);
|
||||||
|
if (r < 0)
|
||||||
|
return log_debug_errno(r, "Failed to acquire %s OSC sequence, ignoring: %m", type);
|
||||||
|
|
||||||
|
r = loop_write(fd, seq, strlen(seq));
|
||||||
|
if (r < 0)
|
||||||
|
return log_debug_errno(r, "Failed to write %s OSC sequence, ignoring: %m", type);
|
||||||
|
|
||||||
|
if (DEBUG_LOGGING) {
|
||||||
|
_cleanup_free_ char *h = cescape(seq);
|
||||||
|
log_debug("OSC sequence for %s successfully written: %s", type, strna(h));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int initialize_runtime(
|
static int initialize_runtime(
|
||||||
bool skip_setup,
|
bool skip_setup,
|
||||||
bool first_boot,
|
bool first_boot,
|
||||||
|
@ -2438,6 +2473,8 @@ static int initialize_runtime(
|
||||||
|
|
||||||
write_container_id();
|
write_container_id();
|
||||||
|
|
||||||
|
(void) write_boot_or_shutdown_osc(/* boot= */ true);
|
||||||
|
|
||||||
/* Copy os-release to the propagate directory, so that we update it for services running
|
/* Copy os-release to the propagate directory, so that we update it for services running
|
||||||
* under RootDirectory=/RootImage= when we do a soft reboot. */
|
* under RootDirectory=/RootImage= when we do a soft reboot. */
|
||||||
r = setup_os_release(RUNTIME_SCOPE_SYSTEM);
|
r = setup_os_release(RUNTIME_SCOPE_SYSTEM);
|
||||||
|
@ -3427,6 +3464,8 @@ finish:
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
(void) write_boot_or_shutdown_osc(/* boot= */ false);
|
||||||
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
(void) sd_notifyf(/* unset_environment= */ false,
|
(void) sd_notifyf(/* unset_environment= */ false,
|
||||||
"ERRNO=%i", -r);
|
"ERRNO=%i", -r);
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
#include "mkdir.h"
|
#include "mkdir.h"
|
||||||
#include "nulstr-util.h"
|
#include "nulstr-util.h"
|
||||||
|
#include "osc-context.h"
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
#include "parse-argument.h"
|
#include "parse-argument.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
|
@ -1227,6 +1228,13 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, PT
|
||||||
log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
|
log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_cleanup_(osc_context_closep) sd_id128_t osc_context_id = SD_ID128_NULL;
|
||||||
|
if (!terminal_is_dumb()) {
|
||||||
|
r = osc_context_open_container(name, /* ret_seq= */ NULL, &osc_context_id);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
r = sd_event_set_signal_exit(event, true);
|
r = sd_event_set_signal_exit(event, true);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to enable SIGINT/SITERM handling: %m");
|
return log_error_errno(r, "Failed to enable SIGINT/SITERM handling: %m");
|
||||||
|
|
|
@ -1443,6 +1443,7 @@ int link_reconfigure_impl(Link *link, LinkReconfigurationFlag flags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct LinkReconfigurationData {
|
typedef struct LinkReconfigurationData {
|
||||||
|
Manager *manager;
|
||||||
Link *link;
|
Link *link;
|
||||||
LinkReconfigurationFlag flags;
|
LinkReconfigurationFlag flags;
|
||||||
sd_bus_message *message;
|
sd_bus_message *message;
|
||||||
|
@ -1473,6 +1474,12 @@ static void link_reconfiguration_data_destroy_callback(LinkReconfigurationData *
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data->counter || *data->counter <= 0) {
|
if (!data->counter || *data->counter <= 0) {
|
||||||
|
/* Update the state files before replying the bus method. Otherwise,
|
||||||
|
* systemd-networkd-wait-online following networkctl reload/reconfigure may read an
|
||||||
|
* outdated state file and wrongly handle an interface is already in the configured
|
||||||
|
* state. */
|
||||||
|
(void) manager_clean_all(data->manager);
|
||||||
|
|
||||||
r = sd_bus_reply_method_return(data->message, NULL);
|
r = sd_bus_reply_method_return(data->message, NULL);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
log_warning_errno(r, "Failed to reply for DBus method, ignoring: %m");
|
log_warning_errno(r, "Failed to reply for DBus method, ignoring: %m");
|
||||||
|
@ -1521,6 +1528,7 @@ int link_reconfigure_full(Link *link, LinkReconfigurationFlag flags, sd_bus_mess
|
||||||
}
|
}
|
||||||
|
|
||||||
*data = (LinkReconfigurationData) {
|
*data = (LinkReconfigurationData) {
|
||||||
|
.manager = link->manager,
|
||||||
.link = link_ref(link),
|
.link = link_ref(link),
|
||||||
.flags = flags,
|
.flags = flags,
|
||||||
.message = sd_bus_message_ref(message), /* message may be NULL, but _ref() works fine. */
|
.message = sd_bus_message_ref(message), /* message may be NULL, but _ref() works fine. */
|
||||||
|
|
|
@ -84,6 +84,7 @@
|
||||||
#include "nsresource.h"
|
#include "nsresource.h"
|
||||||
#include "nulstr-util.h"
|
#include "nulstr-util.h"
|
||||||
#include "os-util.h"
|
#include "os-util.h"
|
||||||
|
#include "osc-context.h"
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
#include "parse-argument.h"
|
#include "parse-argument.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
|
@ -5685,6 +5686,13 @@ static int run_container(
|
||||||
(void) expose_port_execute(rtnl, &expose_args->fw_ctx, arg_expose_ports, AF_INET6, &expose_args->address6);
|
(void) expose_port_execute(rtnl, &expose_args->fw_ctx, arg_expose_ports, AF_INET6, &expose_args->address6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_cleanup_(osc_context_closep) sd_id128_t osc_context_id = SD_ID128_NULL;
|
||||||
|
if (IN_SET(arg_console_mode, CONSOLE_INTERACTIVE, CONSOLE_READ_ONLY) && !terminal_is_dumb()) {
|
||||||
|
r = osc_context_open_container(arg_machine, /* ret_seq= */ NULL, &osc_context_id);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
if (arg_console_mode != CONSOLE_PIPE) {
|
if (arg_console_mode != CONSOLE_PIPE) {
|
||||||
_cleanup_close_ int fd = -EBADF;
|
_cleanup_close_ int fd = -EBADF;
|
||||||
PTYForwardFlags flags = 0;
|
PTYForwardFlags flags = 0;
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
#include "hostname-util.h"
|
#include "hostname-util.h"
|
||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
|
#include "osc-context.h"
|
||||||
#include "parse-argument.h"
|
#include "parse-argument.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
|
@ -2041,6 +2042,7 @@ static int start_transient_service(sd_bus *bus) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_cleanup_(osc_context_closep) sd_id128_t osc_context_id = SD_ID128_NULL;
|
||||||
if (arg_wait || arg_stdio != ARG_STDIO_NONE) {
|
if (arg_wait || arg_stdio != ARG_STDIO_NONE) {
|
||||||
_cleanup_(run_context_done) RunContext c = {
|
_cleanup_(run_context_done) RunContext c = {
|
||||||
.cpu_usage_nsec = NSEC_INFINITY,
|
.cpu_usage_nsec = NSEC_INFINITY,
|
||||||
|
@ -2067,6 +2069,12 @@ static int start_transient_service(sd_bus *bus) {
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
if (pty_fd >= 0) {
|
if (pty_fd >= 0) {
|
||||||
|
if (!terminal_is_dumb() && arg_exec_user) {
|
||||||
|
r = osc_context_open_chpriv(arg_exec_user, /* ret_seq= */ NULL, &osc_context_id);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
(void) sd_event_set_signal_exit(c.event, true);
|
(void) sd_event_set_signal_exit(c.event, true);
|
||||||
|
|
||||||
if (!arg_quiet)
|
if (!arg_quiet)
|
||||||
|
|
|
@ -132,6 +132,7 @@ shared_sources = files(
|
||||||
'open-file.c',
|
'open-file.c',
|
||||||
'openssl-util.c',
|
'openssl-util.c',
|
||||||
'output-mode.c',
|
'output-mode.c',
|
||||||
|
'osc-context.c',
|
||||||
'pager.c',
|
'pager.c',
|
||||||
'parse-argument.c',
|
'parse-argument.c',
|
||||||
'parse-helpers.c',
|
'parse-helpers.c',
|
||||||
|
|
|
@ -0,0 +1,275 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#if HAVE_SYS_AUXV_H
|
||||||
|
# include <sys/auxv.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "escape.h"
|
||||||
|
#include "hostname-util.h"
|
||||||
|
#include "osc-context.h"
|
||||||
|
#include "process-util.h"
|
||||||
|
#include "string-util.h"
|
||||||
|
#include "terminal-util.h"
|
||||||
|
#include "user-util.h"
|
||||||
|
|
||||||
|
/* This currently generates open sequences for OSC 300819 types "boot", "container", "vm", "elevate",
|
||||||
|
* "chpriv", "subcontext". */
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
*
|
||||||
|
* → "service" (from the service manager)
|
||||||
|
* → "session" (from pam_systemd?)
|
||||||
|
* → "shell", "command" (from a bash profile drop-in?)
|
||||||
|
*
|
||||||
|
* Not generated by systemd: "remote" (would have to be generated from the SSH client), "app".
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int strextend_escaped(char **s, const char *prefix, const char *value, const char *suffix) {
|
||||||
|
assert(s);
|
||||||
|
assert(value);
|
||||||
|
|
||||||
|
if (!strextend(s, prefix))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
_cleanup_free_ char *e = xescape(value, ";\\");
|
||||||
|
if (!e)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!strextend(s, e))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!strextend(s, suffix))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int osc_append_identity(char **s) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(s);
|
||||||
|
|
||||||
|
_cleanup_free_ char *u = getusername_malloc();
|
||||||
|
if (u) {
|
||||||
|
r = strextend_escaped(s, ";", u, "u");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleanup_free_ char *h = gethostname_malloc();
|
||||||
|
if (h) {
|
||||||
|
r = strextend_escaped(s, ";", h, "h");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_id128_t id;
|
||||||
|
if (sd_id128_get_machine(&id) >= 0) {
|
||||||
|
r = strextendf(s, ";" SD_ID128_FORMAT_STR "m", SD_ID128_FORMAT_VAL(id));
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sd_id128_get_boot(&id) >= 0) {
|
||||||
|
r = strextendf(s, ";" SD_ID128_FORMAT_STR "b", SD_ID128_FORMAT_VAL(id));
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = strextendf(s, ";" PID_FMT "p", getpid_cached());
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
uint64_t pidfdid;
|
||||||
|
r = getpidfdid_cached(&pidfdid);
|
||||||
|
if (r >= 0) {
|
||||||
|
r = strextendf(s, ";%" PRIu64 "P", pidfdid);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = strextend_escaped(s, ";", program_invocation_short_name, "c");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void osc_context_default_id(sd_id128_t *ret_id) {
|
||||||
|
|
||||||
|
/* Usually we only want one context ID per tool. Since we don't want to store the ID let's just hash
|
||||||
|
* one from process credentials */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint64_t pidfdid;
|
||||||
|
uint8_t auxval[16];
|
||||||
|
pid_t pid;
|
||||||
|
} data = {
|
||||||
|
.pid = getpid_cached(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(ret_id);
|
||||||
|
|
||||||
|
(void) getpidfdid_cached(&data.pidfdid);
|
||||||
|
|
||||||
|
memcpy(data.auxval, ULONG_TO_PTR(getauxval(AT_RANDOM)), sizeof(data.auxval));
|
||||||
|
|
||||||
|
ret_id->qwords[0] = siphash24(&data, sizeof(data), SD_ID128_MAKE(3f,8c,ee,e1,fd,35,41,ec,b8,b1,90,d4,59,e2,ae,5b).bytes);
|
||||||
|
ret_id->qwords[1] = siphash24(&data, sizeof(data), SD_ID128_MAKE(c6,41,ec,1b,d8,85,48,c0,8e,11,d7,e1,e1,fa,9e,03).bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int osc_context_intro(char **ret_seq, sd_id128_t *ret_context_id) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(ret_seq);
|
||||||
|
|
||||||
|
/* If the user passed us a buffer for the context ID generate a randomized one, since we have a place
|
||||||
|
* to store it. The user should pass the ID back to osc_context_close() later on. if the user did not
|
||||||
|
* pass us a buffer, we'll use a session ID hashed from process properties that remain stable as long
|
||||||
|
* our process exists. It hence also remains stable across reexec and similar. */
|
||||||
|
sd_id128_t id;
|
||||||
|
if (ret_context_id) {
|
||||||
|
r = sd_id128_randomize(&id);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
} else
|
||||||
|
osc_context_default_id(&id);
|
||||||
|
|
||||||
|
_cleanup_free_ char *seq = NULL;
|
||||||
|
if (asprintf(&seq, ANSI_OSC "300819;S" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id)) < 0)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = osc_append_identity(&seq);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (ret_context_id)
|
||||||
|
*ret_context_id = id;
|
||||||
|
|
||||||
|
*ret_seq = TAKE_PTR(seq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int osc_context_outro(char *_seq, sd_id128_t id, char **ret_seq, sd_id128_t *ret_context_id) {
|
||||||
|
_cleanup_free_ char *seq = TAKE_PTR(_seq); /* We take possession of the string no matter what */
|
||||||
|
|
||||||
|
if (ret_seq)
|
||||||
|
*ret_seq = TAKE_PTR(seq);
|
||||||
|
else {
|
||||||
|
fputs(seq, stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret_context_id)
|
||||||
|
*ret_context_id = id;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int osc_context_open_boot(char **ret_seq) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
_cleanup_free_ char *seq = NULL;
|
||||||
|
sd_id128_t id;
|
||||||
|
r = osc_context_intro(&seq, /* ret_context_id= */ NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (!strextend(&seq, ";" "boot" "t" ANSI_ST))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
return osc_context_outro(TAKE_PTR(seq), id, ret_seq, /* ret_context_id= */ NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int osc_context_open_container(const char *name, char **ret_seq, sd_id128_t *ret_context_id) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
_cleanup_free_ char *seq = NULL;
|
||||||
|
sd_id128_t id;
|
||||||
|
r = osc_context_intro(&seq, ret_context_id ?: &id);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
r = strextend_escaped(&seq, ";", name, "C");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strextend(&seq, ";" "container" "t" ANSI_ST))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
return osc_context_outro(TAKE_PTR(seq), id, ret_seq, ret_context_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
int osc_context_open_vm(const char *name, char **ret_seq, sd_id128_t *ret_context_id) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(name);
|
||||||
|
|
||||||
|
_cleanup_free_ char *seq = NULL;
|
||||||
|
sd_id128_t id;
|
||||||
|
r = osc_context_intro(&seq, ret_context_id ?: &id);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = strextend_escaped(&seq, ";", name, "v");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (!strextend(&seq, ";" "vm" "t" ANSI_ST))
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return osc_context_outro(TAKE_PTR(seq), id, ret_seq, ret_context_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
int osc_context_open_chpriv(const char *target_user, char **ret_seq, sd_id128_t *ret_context_id) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(target_user);
|
||||||
|
|
||||||
|
_cleanup_free_ char *seq = NULL;
|
||||||
|
sd_id128_t id;
|
||||||
|
r = osc_context_intro(&seq, ret_context_id ?: &id);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (STR_IN_SET(target_user, "root", "0")) {
|
||||||
|
if (!strextend(&seq, ";" "elevate" "t" ANSI_ST))
|
||||||
|
return -ENOMEM;
|
||||||
|
} else if (is_this_me(target_user) > 0) {
|
||||||
|
if (!strextend(&seq, ";" "subcontext" "t" ANSI_ST))
|
||||||
|
return -ENOMEM;
|
||||||
|
} else {
|
||||||
|
r = strextend_escaped(&seq, ";", target_user, "U");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (!strextend(&seq, ";" "chpriv" "t" ANSI_ST))
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return osc_context_outro(TAKE_PTR(seq), id, ret_seq, ret_context_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
int osc_context_close(sd_id128_t id, char **ret_seq) {
|
||||||
|
|
||||||
|
if (sd_id128_is_null(id)) /* nil uuid: no session opened */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (sd_id128_is_allf(id)) /* max uuid: default session opened */
|
||||||
|
osc_context_default_id(&id);
|
||||||
|
|
||||||
|
_cleanup_free_ char *seq = NULL;
|
||||||
|
if (asprintf(&seq, ANSI_OSC "300819;X" SD_ID128_FORMAT_STR ANSI_ST, SD_ID128_FORMAT_VAL(id)) < 0)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (ret_seq)
|
||||||
|
*ret_seq = TAKE_PTR(seq);
|
||||||
|
else {
|
||||||
|
fputs(seq, stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sd-id128.h"
|
||||||
|
|
||||||
|
int osc_context_open_boot(char **ret_seq);
|
||||||
|
int osc_context_open_container(const char *name, char **ret_seq, sd_id128_t *ret_context_id);
|
||||||
|
int osc_context_open_vm(const char *name, char **ret_seq, sd_id128_t *ret_context_id);
|
||||||
|
int osc_context_open_chpriv(const char *target_user, char **ret_seq, sd_id128_t *ret_context_id);
|
||||||
|
int osc_context_close(sd_id128_t id, char **ret_seq);
|
||||||
|
|
||||||
|
static inline void osc_context_closep(sd_id128_t *context_id) {
|
||||||
|
(void) osc_context_close(*context_id, NULL);
|
||||||
|
}
|
|
@ -137,6 +137,7 @@ simple_tests += files(
|
||||||
'test-open-file.c',
|
'test-open-file.c',
|
||||||
'test-ordered-set.c',
|
'test-ordered-set.c',
|
||||||
'test-os-util.c',
|
'test-os-util.c',
|
||||||
|
'test-osc-context.c',
|
||||||
'test-parse-argument.c',
|
'test-parse-argument.c',
|
||||||
'test-parse-helpers.c',
|
'test-parse-helpers.c',
|
||||||
'test-path-lookup.c',
|
'test-path-lookup.c',
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "hexdecoct.h"
|
||||||
|
#include "osc-context.h"
|
||||||
|
#include "tests.h"
|
||||||
|
|
||||||
|
#include "escape.h"
|
||||||
|
|
||||||
|
TEST(osc) {
|
||||||
|
_cleanup_free_ char *seq = NULL;
|
||||||
|
|
||||||
|
log_info("boot");
|
||||||
|
assert_se(osc_context_open_boot(&seq) >= 0);
|
||||||
|
hexdump(/* f = */ NULL, seq, SIZE_MAX);
|
||||||
|
seq = mfree(seq);
|
||||||
|
|
||||||
|
assert_se(osc_context_close(SD_ID128_ALLF, &seq) >= 0);
|
||||||
|
hexdump(/* f = */ NULL, seq, SIZE_MAX);
|
||||||
|
seq = mfree(seq);
|
||||||
|
|
||||||
|
log_info("container");
|
||||||
|
sd_id128_t id;
|
||||||
|
assert_se(osc_context_open_container("foobar", &seq, &id) >= 0);
|
||||||
|
hexdump(/* f = */ NULL, seq, SIZE_MAX);
|
||||||
|
seq = mfree(seq);
|
||||||
|
|
||||||
|
assert_se(osc_context_close(id, &seq) >= 0);
|
||||||
|
hexdump(/* f = */ NULL, seq, SIZE_MAX);
|
||||||
|
seq = mfree(seq);
|
||||||
|
|
||||||
|
log_info("vm");
|
||||||
|
assert_se(osc_context_open_vm("foobar", &seq, &id) >= 0);
|
||||||
|
hexdump(/* f = */ NULL, seq, SIZE_MAX);
|
||||||
|
seq = mfree(seq);
|
||||||
|
|
||||||
|
assert_se(osc_context_close(id, &seq) >= 0);
|
||||||
|
hexdump(/* f = */ NULL, seq, SIZE_MAX);
|
||||||
|
seq = mfree(seq);
|
||||||
|
|
||||||
|
printf("%s\n", xescape("Schöpfgefäß", NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intro(void) {
|
||||||
|
log_show_color(true);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);
|
|
@ -994,6 +994,21 @@ TEST(pid_get_start_time) {
|
||||||
ASSERT_GE(start_time2, start_time);
|
ASSERT_GE(start_time2, start_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(getpidfdid_cached) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
log_info("pid=" PID_FMT, getpid_cached());
|
||||||
|
|
||||||
|
uint64_t id;
|
||||||
|
r = getpidfdid_cached(&id);
|
||||||
|
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||||
|
log_info("pidfdid not supported");
|
||||||
|
else {
|
||||||
|
assert(r >= 0);
|
||||||
|
log_info("pidfdid=%" PRIu64, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int intro(void) {
|
static int intro(void) {
|
||||||
log_show_color(true);
|
log_show_color(true);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
#include "mkdir.h"
|
#include "mkdir.h"
|
||||||
#include "netif-util.h"
|
#include "netif-util.h"
|
||||||
|
#include "osc-context.h"
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
#include "parse-argument.h"
|
#include "parse-argument.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
|
@ -2189,8 +2190,15 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||||
/* Exit when the child exits */
|
/* Exit when the child exits */
|
||||||
(void) event_add_child_pidref(event, NULL, &child_pidref, WEXITED, on_child_exit, NULL);
|
(void) event_add_child_pidref(event, NULL, &child_pidref, WEXITED, on_child_exit, NULL);
|
||||||
|
|
||||||
|
_cleanup_(osc_context_closep) sd_id128_t osc_context_id = SD_ID128_NULL;
|
||||||
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
|
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
|
||||||
if (master >= 0) {
|
if (master >= 0) {
|
||||||
|
if (!terminal_is_dumb()) {
|
||||||
|
r = osc_context_open_vm(arg_machine, /* ret_seq= */ NULL, &osc_context_id);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
r = pty_forward_new(event, master, ptyfwd_flags, &forward);
|
r = pty_forward_new(event, master, ptyfwd_flags, &forward);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to create PTY forwarder: %m");
|
return log_error_errno(r, "Failed to create PTY forwarder: %m");
|
||||||
|
|
|
@ -6406,11 +6406,11 @@ class NetworkdRATests(unittest.TestCase, Utilities):
|
||||||
|
|
||||||
for i in [100, 200, 300, 512, 1024, 2048]:
|
for i in [100, 200, 300, 512, 1024, 2048]:
|
||||||
if i not in [metric_1, metric_2]:
|
if i not in [metric_1, metric_2]:
|
||||||
self.assertNotIn(f'{i}', output)
|
self.assertNotIn(f'metric {i} ', output)
|
||||||
|
|
||||||
for i in ['low', 'medium', 'high']:
|
for i in ['low', 'medium', 'high']:
|
||||||
if i not in [preference_1, preference_2]:
|
if i not in [preference_1, preference_2]:
|
||||||
self.assertNotIn(f'{i}', output)
|
self.assertNotIn(f'pref {i}', output)
|
||||||
|
|
||||||
def test_router_preference(self):
|
def test_router_preference(self):
|
||||||
copy_network_unit('25-veth-client.netdev',
|
copy_network_unit('25-veth-client.netdev',
|
||||||
|
|
Loading…
Reference in New Issue