implement pgetppid

pgetppid will aid in detecting which terminal emulator a process is running under
This commit is contained in:
Emile Clark-Boman 2025-09-28 22:37:07 +10:00
parent edeec36ffa
commit 25f1180429
4 changed files with 105 additions and 2 deletions

View file

@ -27,7 +27,7 @@ all: tests
.PHONY: tests .PHONY: tests
tests: $(BIN) $(BIN)/ct_test tests: $(BIN) $(BIN)/ct_test
$(BIN)/ct_test: $(BUILD) $(addprefix $(BUILD)/, ct_test.o term.o termio.o) $(BIN)/ct_test: $(BUILD) $(addprefix $(BUILD)/, ct_test.o term.o termio.o pgetppid.o)
$(LD) $(LDFLAGS) -o $@ $(filter %.o, $^) $(LD) $(LDFLAGS) -o $@ $(filter %.o, $^)
$(BUILD)/%.o: $(SRC)/%.c $(BUILD)/%.o: $(SRC)/%.c

85
src/pgetppid.c Normal file
View file

@ -0,0 +1,85 @@
/* XXX: WARNING: TODO
* This file's implementation is Linux dependent!
* XXX: WARNING: TODO
*/
#include <stdio.h>
#include <unistd.h>
#include "pgetppid.h"
#include "ct_shared.h"
int pgetppid(const pid_t pid, pid_t *const ppid, char *const name) {
/* I'm making the assumption that each line in /proc/pid/status
* has a hard length limit of PAGE_SIZE.
*/
static int CT_K_PAGE_SIZE;
CT_K_PAGE_SIZE = getpagesize();
FILE *stream;
char line[CT_K_PAGE_SIZE];
/* The maximum PID value on a 64-Bit Linux kernel is defined 4194304,
* not INT_MAX even through pid_t is typedef'd to signed int.
* Hence "/proc/4194304/status\0" is the maximum path (length 21).
*/
#define PROCPATH_MAX (21)
char procname[PROCPATH_MAX]; // Holds /proc/4294967296/status\0
// XXX: NOTE: should this be %d not %u since pid_t is typedef'd to signed int?
snprintf(procname, PROCPATH_MAX, "/proc/%u/status", pid);
#undef PROCPATH_MAX
stream = fopen(procname, "r");
if (stream == NULL) {
return NERR;
}
/* /proc/pid/status format: ($ cat /proc/$$/status)
Name: bash
Umask: 0022
State: S (sleeping)
Tgid: 17248
Ngid: 0
Pid: 17248
PPid: 17200
*/
/* Name Line Format: "Name:___$NAME\n\0" where _ are spaces (5+3+len(NAME)+2 =
* 10+len(NAME)) `man proc_pid_status` claims $NAME will be truncated to
* TASK_COMM_LEN (including \0 terminating byte) aka 15+1 bytes long. However,
* They fail to account for work-queue-threads and kernel-threads which
* seem to not have this restriction.
* ie `rcu_exp_par_gp_kthread_worker/0` clearly exceeds this.
* We define NAMELINE_MAX to 9 + TASK_COMM_LEN (yes 9) because TASK_COMMON =
* len(NAME)+1 (for \0).
* NOTE: TASK_COMM_LEN isn't accessible in userspace so we define CT_PROC_NAME_MAX
*/
#define NAMELINE_MAX (9 + CT_PROC_NAME_MAX)
if (fgets(line, NAMELINE_MAX, stream) == NULL)
goto fail;
/* Fail if over max length, ie this is kernel related
* thread and we've recursed too deep. */
// if (line[NAMELINE_MAX - 2] != '\n')
// goto fail;
else if (sscanf(line, "Name: %s\n", name) == 0)
return 0;
#undef NAMELINE_MAX
/* Skip over the Umask, State, Tgid, Ngid, Pid lines
* The compiler can unroll this loop for us. */
for (int i = 0; i < 5; i++) {
if (fgets(line, CT_K_PAGE_SIZE, stream) == NULL)
goto fail;
}
if (fgets(line, CT_K_PAGE_SIZE, stream) == NULL)
goto fail;
else if (sscanf(line, "PPid: %d\n", ppid) == 0)
goto fail;
fclose(stream);
return OK;
fail:
fclose(stream);
return NERR;
}

15
src/pgetppid.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef CURSETREE_PGETPPID_H
#define CURSETREE_PGETPPID_H
#include <sys/types.h>
/* XXX: WARNING: TODO: Linux kernel specific
* TASK_COMM_LEN isn't accessible in userspace so we define CT_PROC_NAME_MAX
* instead, representing the maximum (in most instances) truncated length of
* a process' Name field in /proc/pid/status.
*/
#define CT_PROC_NAME_MAX 16
int pgetppid(const pid_t pid, pid_t *const ppid, char *const name);
#endif

View file

@ -1,5 +1,4 @@
#include <fcntl.h> #include <fcntl.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@ -51,3 +50,7 @@ void ct_resetterm(struct ct_term *const term) {
free(term); free(term);
} }
/* TODO: use pgetppid() and write a terminfo parser, otherwise default to TERM */
// static inline int ct_detectterm() {
// secure_getenv("TERM");
// }