203 lines
5.1 KiB
C
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;
|
|
}
|