// see proc(5) manpage for more information #include #include #include #include #include #include #include #include // pid_t #include // 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; }