aboutsummaryrefslogtreecommitdiffstats
path: root/src/server.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-17 13:38:15 +0200
committeruser <user@clank>2026-08-17 13:38:15 +0200
commitea39e09a52240ce1d5166e9137e39b7caad499fd (patch)
tree8f841f2ff3f14abce054136ac9a4cf3fe47162fe /src/server.c
parentsl0ppty: a skill for driving it, and the two variables that made one possible (diff)
downloadslosh-ea39e09a52240ce1d5166e9137e39b7caad499fd.tar.gz
slosh-ea39e09a52240ce1d5166e9137e39b7caad499fd.tar.xz
slosh-ea39e09a52240ce1d5166e9137e39b7caad499fd.zip
sl0ppty: a --script pane is told no session rather than somebody else's
`server.c` set `SL0PPTY_SESSION` for its panes and carried a comment saying the value was "deliberately unset under `--script`". Nothing unset it. A value never set is a value inherited, so a script driver started from inside a pane handed its own panes the *outer* session's name -- and the recipe the skill documents, `-s ${SL0PPTY_SESSION:-main} cmd`, then sent its commands to a session it was not in. Not a fallback to a guess: a confident wrong answer, because the name it read was a real session that really answers. Both variables now go through one `session_env()`, called by the server with its name and by the headless driver with NULL, because the two modes have to agree about what a pane is told and a comment in one of them is not agreement. `SL0PPTY_BIN` is set there too rather than inherited, so it names the binary that actually made the pane instead of one that made some pane somewhere -- which is the whole reason that variable exists. Found while explaining a stale pane title, from a check in `test_skill.py` that failed under `make test` and passed on its own. It was not flaky: the suite runs from inside a pane, where these are set, and the tool I ran it from by hand had an environment where they were not. A test whose answer depends on where you run it looks exactly like a race. So it now passes the lie in deliberately: the session under test is started with `SL0PPTY_SESSION=somebody-elses-session`, which makes the difference between "never set" and "cleared" visible from any shell. The mirror check is next to it -- a real session tells its panes its *own* name, even when started from inside another one -- so the obvious wrong fix, unsetting it in both modes, fails. Both were verified by making both wrong versions and watching each test bite. The two title checks now wait for the line instead of sampling after 120ms. That was not the bug (a shell prints in about 12ms here, even under disk churn) and it is one assumption fewer.
Diffstat (limited to 'src/server.c')
-rw-r--r--src/server.c52
1 files changed, 34 insertions, 18 deletions
diff --git a/src/server.c b/src/server.c
index f552f30..f728036 100644
--- a/src/server.c
+++ b/src/server.c
@@ -237,6 +237,39 @@ static bool watch_hit(const watchset_t *w, const char *name) {
return false;
}
+/* What every pane is told about the session around it. Panes inherit this
+ * process's environment, so this is how a program *inside* one learns which
+ * session it is in -- and therefore which socket to talk to. `SL0PPTY=1`
+ * (pty.c) says "you are in a pane"; this says which one of possibly several,
+ * which is the part a program cannot work out for itself: `ls` lists them all
+ * and none of them is labelled "the one you are in".
+ *
+ * NULL for a mode with no socket, and that case is why this is a function both
+ * modes call rather than two setenvs in the mode that has one. `--script` has
+ * always been *documented* as leaving `SL0PPTY_SESSION` unset -- but a value it
+ * never set was a value it inherited, and a script driver started from inside a
+ * pane handed its own panes the *outer* session's name. Anything following the
+ * documented recipe (`-s ${SL0PPTY_SESSION:-main} cmd`) then sent its commands
+ * to a session it was not in, confidently, because the name it read was real.
+ *
+ * `SL0PPTY_BIN` is set in both modes for the same reason it is set in either: a
+ * program in a pane that finds `sl0ppty` missing from its PATH -- a session
+ * started from a build tree, an agent with a trimmed environment -- can
+ * otherwise see the session it is in and have no way to reach it. Read from
+ * /proc rather than argv[0], which is whatever the caller felt like passing,
+ * and set rather than inherited so it names the binary that actually made this
+ * pane, not one that made some pane somewhere. */
+void session_env(const char *name) {
+ if (name) setenv("SL0PPTY_SESSION", name, 1);
+ else unsetenv("SL0PPTY_SESSION");
+ char self[512];
+ ssize_t sn = readlink("/proc/self/exe", self, sizeof self - 1);
+ if (sn > 0) {
+ self[sn] = 0;
+ setenv("SL0PPTY_BIN", self, 1);
+ }
+}
+
int server_run(const char *name, const char *const argv[], uint16_t cols,
uint16_t rows, const char *layout, bool watch) {
char path[512];
@@ -247,24 +280,7 @@ int server_run(const char *name, const char *const argv[], uint16_t cols,
return 1;
}
- /* Every pane inherits this process's environment, so setting it here is how a
- * program *inside* a pane learns which session it is in -- and therefore which
- * socket to talk to. `SL0PPTY=1` (pty.c) says "you are in a pane"; this says
- * which one of possibly several sessions, which is the part a program cannot
- * work out for itself: `ls` lists them all and none of them is labelled "the
- * one you are in". Deliberately unset under `--script`, which has no socket. */
- setenv("SL0PPTY_SESSION", name, 1);
- /* ...and which binary to talk to it with. A program in a pane that finds
- * `sl0ppty` missing from its PATH -- a session started from a build tree, an
- * agent with a trimmed environment -- can otherwise see the session it is in
- * and have no way to reach it. Read from /proc rather than argv[0], which is
- * whatever the caller felt like passing. */
- char self[512];
- ssize_t sn = readlink("/proc/self/exe", self, sizeof self - 1);
- if (sn > 0) {
- self[sn] = 0;
- setenv("SL0PPTY_BIN", self, 1);
- }
+ session_env(name);
struct sigaction sa = {.sa_handler = on_sig};
sigaction(SIGTERM, &sa, NULL);