scratch/misc/procread.c
2024-12-26 14:02:40 -03:00

203 lines
5.1 KiB
C

// see proc(5) manpage for more information
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <ctype.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h> // pid_t
#include <linux/kdev_t.h> // MAJOR() and MINOR() tty macros
#define __STR(n) #n
#define STR(n) __STR(n)
// Filename length of the process executable, truncated to 16 + nil terminator
// However, kernel processes have longer names than that.
#define _TASK_COMM_LEN 255
#define PROC_FOLDER "/proc"
#define INIT_PID 1
#define IS_KERNEL_PROC(p) (((p)->pid != INIT_PID && (p)->session_id == 0))
struct proc_t
{
pid_t pid;
pid_t parent_pid;
gid_t group_id;
id_t session_id;
id_t tty;
char *exe_name;
char state;
unsigned int flags;
};
void *xcalloc(size_t num, size_t size)
{
void *ptr = calloc(num, size);
if (ptr == NULL) {
perror("xcalloc");
//fcloseall();
abort();
}
return ptr;
}
char *build_proc_path(char *buffer, const char *proc_pid, const char *dir)
{
char *path = buffer; //xcalloc(256, sizeof(char));
strcpy(path, PROC_FOLDER"/");
strncat(path, proc_pid, 255 - 5); // always leave space for /stat
char proc_dir[255] = {0};
snprintf(proc_dir, sizeof(proc_dir), "/%s", dir);
strcat(path, proc_dir);
return path;
}
FILE *process_open(char *path)
{
FILE *proc_fp = fopen(path, "rb");
if (proc_fp == NULL) {
perror("fopen");
exit(EXIT_FAILURE);
}
return proc_fp;
}
int parse_proc(FILE *proc_fp, struct proc_t *p)
{
#define NMATCHES 7 // <- increment each time a format directive (that is bound to a reference) is added
p->exe_name = malloc(_TASK_COMM_LEN * sizeof(char));
int ret = fscanf(
proc_fp,
"%d %*[(]%"STR(_TASK_COMM_LEN)"[^)]%*[)] %c %d %d %d %u",
&p->pid, p->exe_name, &p->state, &p->parent_pid, &p->session_id, &p->tty,
&p->flags
);
if (ret != NMATCHES || (ret == EOF && ferror(proc_fp))) {
fprintf(stderr, "couldn't parse stat info\n");
return 1;
}
return 0;
}
int is_numeric_str(char *str)
{
while (*str)
if (!isdigit(*str++))
return 0;
return 1;
}
void list_procs_init(struct proc_t **proc_list, int proc_list_cnt, int offset)
{
for (int i = offset; i < proc_list_cnt; ++i) {
proc_list[i] = xcalloc(1, sizeof(struct proc_t));
}
}
struct proc_t **list_procs()
{
int proc_list_it = 0;
int proc_list_cnt = 255;
struct proc_t **proc_list = xcalloc(proc_list_cnt + 1, sizeof(**proc_list));
list_procs_init(proc_list, proc_list_cnt, 0);
char path_buf[255] = {0};
DIR *proc_dir = opendir(PROC_FOLDER);
if (proc_dir == NULL) {
perror("opendir");
exit(EXIT_FAILURE);
}
struct dirent *dir;
while ((dir = readdir(proc_dir)) != NULL) {
if (dir->d_type == DT_DIR && is_numeric_str(dir->d_name)) {
if ((proc_list_it + 1) > proc_list_cnt) {
proc_list_cnt += proc_list_cnt;
proc_list = realloc(proc_list, (proc_list_cnt + 1) * sizeof(**proc_list));
list_procs_init(proc_list, proc_list_cnt, proc_list_it);
}
FILE *pf = process_open(build_proc_path(path_buf, dir->d_name, "stat"));
parse_proc(pf, proc_list[proc_list_it++]);
fclose(pf);
}
}
closedir(proc_dir);
return proc_list;
}
void print_proc_info(struct proc_t *proc)
{
char *is_kernel_proc = IS_KERNEL_PROC(proc) ? "yes" : "no";
if (IS_KERNEL_PROC(proc)) // not init neither a child of it
printf("process %s (PID: %d) is a kernel process\n", proc->exe_name, proc->pid);
printf("PID: %d (parent: %d, session id: %d) Name: %s Status: %c TTY: %d"
" flags: 0x%08x\n",
proc->pid, proc->parent_pid, proc->session_id, proc->exe_name, proc->state, MAJOR(proc->tty), proc->flags);
}
void backtrace_to_init(struct proc_t *proc)
{
if (proc->parent_pid == 0)
return;
// pid == group pid for parent processes
while (proc->pid != INIT_PID && proc->parent_pid != 0) {
char pid_str[32];
char proc_path[255] = {0};
snprintf(pid_str, sizeof(pid_str), "%d", proc->parent_pid);
FILE *pf = process_open(build_proc_path(proc_path, pid_str, "stat"));
if (parse_proc(pf, proc))
break;
print_proc_info(proc);
fclose(pf);
free(proc->exe_name);
proc->exe_name = NULL;
}
}
int main(int argc, char **argv)
{
int c;
int list_procs_flag = 0, kernel_procs_flag = 0;
while ((c = getopt(argc, argv, "lkp:")) != -1) {
switch (c) {
case 'l': list_procs_flag = 1;
break;
case 'k': kernel_procs_flag = 1;
break;
case '?':
fprintf(stderr, "usage: ./procread [-lk][-p PID]\n");
return EXIT_FAILURE;
}
}
if (list_procs_flag) {
struct proc_t **proc, **proc_o;
proc = proc_o = list_procs();
while (*proc != NULL) {
if ((*proc)->pid && (!IS_KERNEL_PROC(*proc) || kernel_procs_flag)) {
print_proc_info(*proc);
}
free((*proc)->exe_name);
free(*proc++);
}
free(proc_o);
} else {
FILE *proc_fp;
char pid_str[24];
char proc_path[255] = {0};
if (optind >= argc) {
snprintf(pid_str, sizeof(pid_str), "%d", getpid());
proc_fp = process_open(build_proc_path(proc_path, pid_str, "stat"));
} else {
proc_fp = process_open(build_proc_path(proc_path, argv[optind], "stat"));
}
struct proc_t p = {};
if (parse_proc(proc_fp, &p))
return 1;
print_proc_info(&p);
free(p.exe_name);
fclose(proc_fp);
backtrace_to_init(&p);
}
return EXIT_SUCCESS;
}