summaryrefslogtreecommitdiffstats
path: root/src/shell.h
blob: 18c8f6743943b510b72bbde1f4f30f2f265aa4b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#ifndef LLSH_SHELL_H
#define LLSH_SHELL_H

#include "util.h"
#include <pthread.h>
#include <stdbool.h>

/* An interactive user shell running on a private PTY. llsh forwards raw
 * terminal input to the PTY and streams the PTY's output back to the real
 * terminal, so the shell's own line editor (Tab completion, history, …) is
 * used while in shell mode. Mirrors the Rust `InteractiveShell` in main.rs.
 * POSIX-only (forkpty/raw mode). */
typedef struct interactive_shell {
    int pid;            /* shell PID; 0 once reaped */
    int master;         /* PTY master fd; -1 once closed */
    int reader_fd;      /* dup of master, owned by the reader thread */
    pthread_t reader_th;
    pthread_mutex_t done_mu;  /* guards reader_done */
    bool reader_done;   /* set by the reader thread on EOF */
    xbuf outbuf;        /* owned; output capture (shared with reader thread) */
    pthread_mutex_t out_mu; /* guards outbuf */
    unsigned long capture_cap; /* max bytes to capture (bounds memory) */
    char *last_cwd;     /* owned; last observed cwd (fallback after reap) */
} interactive_shell;

/* Fork `$SHELL` (else `bash`) with `-i` on a private PTY and start a reader
 * thread that streams the shell's output to stdout (CRLF collapsed to LF).
 * Returns NULL on failure. */
interactive_shell *shell_spawn(void);
/* Forward raw input bytes to the shell. 0 on success, -1 on error. */
int shell_write(const interactive_shell *s, const void *buf, size_t len);
/* Resize the PTY to match the current terminal size. */
void shell_update_winsize(const interactive_shell *s);
/* True once the shell has exited, its output is drained, and it has been
 * reaped. Cheap to call each loop iteration while in shell mode. */
bool shell_reap_if_dead(interactive_shell *s);
/* The directory the shell ended in: a live `/proc/<pid>/cwd` read while it
 * is still alive, falling back to the last observed directory once reaped.
 * Returns a freshly allocated string, or NULL when unknown. */
char *shell_final_cwd(const interactive_shell *s);
/* Everything the shell has printed so far (owned copy), and reset the
 * capture. Thread-safe against the reader thread; returns an empty string
 * when nothing was captured. */
char *shell_captured(interactive_shell *s);
/* Kill the shell (whole process group), close the PTY, and join the reader
 * thread. Safe to call on a fully-reaped shell. */
void shell_free(interactive_shell *s);

#endif