split process into procstat and process source files, since other OSes like BSD demand different procfs treatment, and process hijacking is not part of parsing it
This commit is contained in:
parent
226d306bfa
commit
db6a7cdde8
6 changed files with 298 additions and 285 deletions
|
|
@ -4,9 +4,9 @@ CFLAGS = -Wall -I$(INC_DIR)
|
||||||
ASFLAGS =
|
ASFLAGS =
|
||||||
LDFLAGS = -z noexecstack -lcap
|
LDFLAGS = -z noexecstack -lcap
|
||||||
|
|
||||||
SRC = main.c process.c
|
SRC = main.c procstat.c process.c
|
||||||
OBJ := $(addprefix $(OBJ_DIR)/,$(patsubst %.s,%.o,$(patsubst %.c,%.o,$(SRC))))
|
OBJ := $(addprefix $(OBJ_DIR)/,$(patsubst %.s,%.o,$(patsubst %.c,%.o,$(SRC))))
|
||||||
DEPS = process.h process_debug.h
|
DEPS = procstat.h process.h process_debug.h
|
||||||
|
|
||||||
$(OBJ_DIR)/%.o: %.c
|
$(OBJ_DIR)/%.o: %.c
|
||||||
@mkdir -p $(OBJ_DIR)
|
@mkdir -p $(OBJ_DIR)
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "process.h"
|
#include "blackjack/procstat.h"
|
||||||
|
#include "blackjack/process.h"
|
||||||
|
|
||||||
void print_process(process_status_t* proc)
|
void print_status(procstat_status_t* proc)
|
||||||
{
|
{
|
||||||
puts("Process:");
|
puts("Process:");
|
||||||
printf("name: %s\n", proc->name);
|
printf("name: %s\n", proc->name);
|
||||||
|
|
@ -24,39 +25,39 @@ void print_process(process_status_t* proc)
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
process_status_t* list = NULL;
|
procstat_status_t* list = NULL;
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
|
|
||||||
// find process
|
// find process
|
||||||
process_by_name("dummy_target", &list, &count);
|
procstat_by_name("dummy_target", &list, &count);
|
||||||
// get real parent
|
// get real parent
|
||||||
process_status_t* parent;
|
procstat_status_t* parent;
|
||||||
if (process_determine_parent(list, count, &parent))
|
if (procstat_determine_parent(list, count, &parent))
|
||||||
{
|
{
|
||||||
fputs("unable to determine parent process. exiting\n", stderr);
|
fputs("unable to determine parent process. exiting\n", stderr);
|
||||||
free(list);
|
free(list);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_process(parent);
|
print_status(parent);
|
||||||
|
|
||||||
// find active thread
|
// find active thread
|
||||||
puts("Looking for active thread..");
|
puts("Looking for active thread..");
|
||||||
|
|
||||||
process_status_t* threads = NULL;
|
procstat_status_t* threads = NULL;
|
||||||
size_t thread_count = 0;
|
size_t thread_count = 0;
|
||||||
|
|
||||||
process_status_t* active;
|
procstat_status_t* active;
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
if (process_get_threads(parent->pid, &threads, &thread_count))
|
if (procstat_get_threads(parent->pid, &threads, &thread_count))
|
||||||
{
|
{
|
||||||
fputs("failed to obtain process threads\n", stderr);
|
fputs("failed to obtain process threads\n", stderr);
|
||||||
free(list);
|
free(list);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process_find_active(threads, thread_count, &active))
|
if (procstat_find_active(threads, thread_count, &active))
|
||||||
{
|
{
|
||||||
// no active threads - free list and continue
|
// no active threads - free list and continue
|
||||||
free(threads);
|
free(threads);
|
||||||
|
|
@ -70,7 +71,7 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
puts("Active thread:");
|
puts("Active thread:");
|
||||||
print_process(active);
|
print_status(active);
|
||||||
|
|
||||||
if (!process_ptrace_permissions())
|
if (!process_ptrace_permissions())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,223 +1,14 @@
|
||||||
#define _DEFAULT_SOURCE
|
#define _DEFAULT_SOURCE
|
||||||
#include "process.h"
|
#include "blackjack/process.h"
|
||||||
#include "debug.h"
|
#include "blackjack/debug.h"
|
||||||
#include "process_debug.h"
|
#include "blackjack/process_debug.h"
|
||||||
#include <sys/capability.h>
|
#include <sys/capability.h>
|
||||||
#include <sys/ptrace.h>
|
#include <sys/ptrace.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
#include <linux/elf.h>
|
#include <linux/elf.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
int process_parse_status(pid_t pid, process_status_t* status)
|
|
||||||
{
|
|
||||||
char statusPath[256] = {0};
|
|
||||||
snprintf(statusPath, sizeof(statusPath), "/proc/%d/status", pid);
|
|
||||||
|
|
||||||
int statusFd = open(statusPath, O_RDONLY);
|
|
||||||
if (statusFd < 0) return 1;
|
|
||||||
|
|
||||||
char buffer[4096] = {0};
|
|
||||||
int rd = read(statusFd, buffer, sizeof(buffer));
|
|
||||||
close(statusFd);
|
|
||||||
|
|
||||||
if (rd < 1) return 1;
|
|
||||||
|
|
||||||
char* lineptr = NULL, *line = strtok_r(buffer, "\n", &lineptr);
|
|
||||||
while (line != NULL)
|
|
||||||
{
|
|
||||||
char* fieldptr = NULL;
|
|
||||||
const char* key = (const char*)strtok_r(line, ":\t", &fieldptr);
|
|
||||||
const char* value = (const char*)strtok_r(NULL, ":\t", &fieldptr);
|
|
||||||
|
|
||||||
if (!strcmp(key, "Name"))
|
|
||||||
strncpy(status->name, value, MAX_PROCESS_NAME);
|
|
||||||
else if (!strcmp(key, "Umask"))
|
|
||||||
status->umask = (unsigned int)strtoul(value, NULL, 8);
|
|
||||||
else if (!strcmp(key, "State"))
|
|
||||||
status->state = (process_state_t)value[0];
|
|
||||||
else if (!strcmp(key, "Tgid"))
|
|
||||||
status->tgid = atoi(value);
|
|
||||||
else if (!strcmp(key, "Ngid"))
|
|
||||||
status->ngid = atoi(value);
|
|
||||||
else if (!strcmp(key, "Pid"))
|
|
||||||
status->pid = atoi(value);
|
|
||||||
else if (!strcmp(key, "PPid"))
|
|
||||||
status->ppid = atoi(value);
|
|
||||||
else if (!strcmp(key, "TracerPid"))
|
|
||||||
status->tracer_pid = atoi(value);
|
|
||||||
else if (!strcmp(key, "Uid"))
|
|
||||||
status->uid = atoi(value);
|
|
||||||
else if (!strcmp(key, "Gid"))
|
|
||||||
status->gid = atoi(value);
|
|
||||||
|
|
||||||
line = strtok_r(NULL, "\n", &lineptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_numeric(const char* str)
|
|
||||||
{
|
|
||||||
for (const char* p = str; *p; p++)
|
|
||||||
if (!isdigit(*p)) return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int process_by_name(const char* name, process_status_t** list, size_t* count)
|
|
||||||
{
|
|
||||||
*list = NULL;
|
|
||||||
*count = 0;
|
|
||||||
|
|
||||||
DIR* proc = opendir("/proc");
|
|
||||||
if (!proc) return 1;
|
|
||||||
|
|
||||||
struct dirent* entry;
|
|
||||||
while ((entry = readdir(proc)))
|
|
||||||
{
|
|
||||||
if (entry->d_type != DT_DIR) continue;
|
|
||||||
if (!is_numeric(entry->d_name)) continue;
|
|
||||||
|
|
||||||
pid_t pid = strtod(entry->d_name, NULL);
|
|
||||||
process_status_t status = {};
|
|
||||||
if (process_parse_status(pid, &status))
|
|
||||||
{
|
|
||||||
TRACE("process parse status failed for %d\n", pid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcasecmp(status.name, name))
|
|
||||||
{
|
|
||||||
// we have match! lets add it to list
|
|
||||||
size_t _count = *count;
|
|
||||||
process_status_t* _list = *list;
|
|
||||||
|
|
||||||
if (_count) _list = (process_status_t*)realloc(_list, ++_count * sizeof(process_status_t));
|
|
||||||
else _list = (process_status_t*)malloc(++_count * sizeof(process_status_t));
|
|
||||||
|
|
||||||
if (!_list)
|
|
||||||
{
|
|
||||||
TRACE("out of memory for process status!\n");
|
|
||||||
closedir(proc);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy process status to list
|
|
||||||
memcpy(&_list[_count - 1], &status, sizeof(process_status_t));
|
|
||||||
|
|
||||||
// update pointers
|
|
||||||
*list = _list;
|
|
||||||
*count = _count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir(proc);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static process_status_t* process_by_pid_in_list(pid_t pid, process_status_t* list, size_t count)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < count; i++)
|
|
||||||
if (list[i].pid == pid) return &list[i];
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int process_determine_parent(process_status_t* list, size_t count, process_status_t** parent)
|
|
||||||
{
|
|
||||||
// we're gonna find any process that doesnt have parent in this list,
|
|
||||||
// that means we hit real parent, not descendant
|
|
||||||
for (size_t i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
if (!process_by_pid_in_list(list[i].ppid, list, count))
|
|
||||||
{
|
|
||||||
// that's real parent
|
|
||||||
*parent = &list[i];
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int process_get_threads(pid_t pid, process_status_t** list, size_t* count)
|
|
||||||
{
|
|
||||||
*list = NULL;
|
|
||||||
*count = 0;
|
|
||||||
|
|
||||||
char taskPath[256] = {0};
|
|
||||||
snprintf(taskPath, sizeof(taskPath), "/proc/%d/task", pid);
|
|
||||||
|
|
||||||
DIR* taskDir = opendir(taskPath);
|
|
||||||
if (!taskDir) return 1;
|
|
||||||
|
|
||||||
struct dirent* entry;
|
|
||||||
while ((entry = readdir(taskDir)))
|
|
||||||
{
|
|
||||||
if (entry->d_type != DT_DIR) continue;
|
|
||||||
if (!is_numeric(entry->d_name)) continue;
|
|
||||||
|
|
||||||
pid_t pid = strtod(entry->d_name, NULL);
|
|
||||||
process_status_t status = {};
|
|
||||||
if (process_parse_status(pid, &status))
|
|
||||||
{
|
|
||||||
// if we can't some threads, we should fail completely
|
|
||||||
// failure is better than incomplete info
|
|
||||||
TRACE("failed to parse %d task status\n", pid);
|
|
||||||
closedir(taskDir);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add thread to list
|
|
||||||
process_status_t* _list = *list;
|
|
||||||
size_t _count = *count;
|
|
||||||
|
|
||||||
if (_count) _list = (process_status_t*)realloc(_list, ++_count * sizeof(process_status_t));
|
|
||||||
else _list = (process_status_t*)malloc(++_count * sizeof(process_status_t));
|
|
||||||
|
|
||||||
if (!_list)
|
|
||||||
{
|
|
||||||
TRACE("out of memory for process status!\n");
|
|
||||||
closedir(taskDir);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy thread to list
|
|
||||||
memcpy(&_list[_count - 1], &status, sizeof(process_status_t));
|
|
||||||
|
|
||||||
*list = _list;
|
|
||||||
*count = _count;
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir(taskDir);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int process_is_considered_active(process_state_t state)
|
|
||||||
{
|
|
||||||
return state == INTERRUPTIBLE_SLEEP || state == RUNNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
int process_find_active(process_status_t* list, size_t count, process_status_t** thread)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
TRACE("task %d state %d\n", list[i].pid, list[i].state);
|
|
||||||
if (process_is_considered_active(list[i].state))
|
|
||||||
{
|
|
||||||
*thread = &list[i];
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int process_ptrace_permissions()
|
int process_ptrace_permissions()
|
||||||
{
|
{
|
||||||
|
|
@ -242,7 +33,7 @@ int process_ptrace_permissions()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int process_attach_all(process_status_t* threads, size_t thread_count)
|
int process_attach_all(procstat_status_t* threads, size_t thread_count)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < thread_count; i++)
|
for (size_t i = 0; i < thread_count; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -278,7 +69,7 @@ int process_attach_all(process_status_t* threads, size_t thread_count)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_detach_all(process_status_t* threads, size_t thread_count)
|
void process_detach_all(procstat_status_t* threads, size_t thread_count)
|
||||||
{
|
{
|
||||||
while (thread_count--) ptrace(PTRACE_DETACH, threads[thread_count].pid, NULL, NULL);
|
while (thread_count--) ptrace(PTRACE_DETACH, threads[thread_count].pid, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
@ -286,12 +77,12 @@ void process_detach_all(process_status_t* threads, size_t thread_count)
|
||||||
// hardcoded syscall instruction size
|
// hardcoded syscall instruction size
|
||||||
#define BJ_PTRACE_CONT_OFFSET 2
|
#define BJ_PTRACE_CONT_OFFSET 2
|
||||||
|
|
||||||
uintptr_t process_calculate_ip(process_status_t* thread, uintptr_t addr)
|
uintptr_t process_calculate_ip(procstat_status_t* thread, uintptr_t addr)
|
||||||
{
|
{
|
||||||
return addr + BJ_PTRACE_CONT_OFFSET;
|
return addr + BJ_PTRACE_CONT_OFFSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
int process_read_registers(process_status_t* thread, struct user_regs_struct* regs)
|
int process_read_registers(procstat_status_t* thread, struct user_regs_struct* regs)
|
||||||
{
|
{
|
||||||
struct iovec data = {
|
struct iovec data = {
|
||||||
.iov_base = regs,
|
.iov_base = regs,
|
||||||
|
|
@ -304,7 +95,7 @@ int process_read_registers(process_status_t* thread, struct user_regs_struct* re
|
||||||
return ret < 0;
|
return ret < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int process_write_registers(process_status_t* thread, const struct user_regs_struct* regs)
|
int process_write_registers(procstat_status_t* thread, const struct user_regs_struct* regs)
|
||||||
{
|
{
|
||||||
struct iovec data = {
|
struct iovec data = {
|
||||||
.iov_base = (void*)regs,
|
.iov_base = (void*)regs,
|
||||||
|
|
|
||||||
|
|
@ -1,75 +1,27 @@
|
||||||
#ifndef __PROCESS_H
|
#ifndef __PROCESS_H
|
||||||
#define __PROCESS_H
|
#define __PROCESS_H
|
||||||
|
|
||||||
|
#include "blackjack/procstat.h"
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/user.h>
|
#include <sys/user.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
UNINTERRUPTABLE_SLEEP = 'D',
|
|
||||||
IDLE_KERNEL_THREAD = 'I',
|
|
||||||
RUNNING = 'R',
|
|
||||||
INTERRUPTIBLE_SLEEP = 'S',
|
|
||||||
STOPPED = 'T',
|
|
||||||
STOPPED_BY_DEBUGGER = 't',
|
|
||||||
DEAD = 'X',
|
|
||||||
ZOMBIE = 'Z'
|
|
||||||
} process_state_t;
|
|
||||||
|
|
||||||
#define MAX_PROCESS_NAME 256
|
|
||||||
typedef struct {
|
|
||||||
char name[MAX_PROCESS_NAME];
|
|
||||||
mode_t umask;
|
|
||||||
process_state_t state;
|
|
||||||
pid_t tgid;
|
|
||||||
pid_t ngid;
|
|
||||||
pid_t pid;
|
|
||||||
pid_t ppid;
|
|
||||||
pid_t tracer_pid;
|
|
||||||
uid_t uid;
|
|
||||||
gid_t gid;
|
|
||||||
} process_status_t;
|
|
||||||
|
|
||||||
// parse process status from procfs. returns 0 no errors and 1 on any kind of error
|
|
||||||
// error information obtain from errno
|
|
||||||
int process_parse_status(pid_t pid, process_status_t* status);
|
|
||||||
|
|
||||||
// find any process that matches name, case insensitive.
|
|
||||||
// list pointer must point to NULL-initialized pointer, and count pointer must pount to initialized 0
|
|
||||||
// will skip any process which status couldn't be parsed
|
|
||||||
// deallocate list with free later
|
|
||||||
int process_by_name(const char* name, process_status_t** list, size_t* count);
|
|
||||||
|
|
||||||
// determine parent process amongst children and set parent pointer to element in list
|
|
||||||
// process list must consist of parent and children processes,
|
|
||||||
// obtained from processes_by_name call. of course parent pointer shouldn't be NULL
|
|
||||||
int process_determine_parent(process_status_t* list, size_t count, process_status_t** parent);
|
|
||||||
|
|
||||||
// get all process threads. for list and count same rules applies as for processes_by_name
|
|
||||||
int process_get_threads(pid_t pid, process_status_t** list, size_t* count);
|
|
||||||
|
|
||||||
// returns 1 if state considered active for a process/thread
|
|
||||||
int process_is_considered_active(process_state_t state);
|
|
||||||
|
|
||||||
// find any active (running) thread and returns 0 and success, otherwise non zero
|
|
||||||
int process_find_active(process_status_t* list, size_t count, process_status_t** thread);
|
|
||||||
|
|
||||||
// check if this process has any capability or is ran as root to be able to ptrace attach
|
// check if this process has any capability or is ran as root to be able to ptrace attach
|
||||||
int process_ptrace_permissions();
|
int process_ptrace_permissions();
|
||||||
|
|
||||||
// attach to all threads of the process. on error returns 1 and detaches from already attached
|
// attach to all threads of the process. on error returns 1 and detaches from already attached
|
||||||
int process_attach_all(process_status_t* threads, size_t thread_count);
|
int process_attach_all(procstat_status_t* threads, size_t thread_count);
|
||||||
|
|
||||||
// detaches from all threads
|
// detaches from all threads
|
||||||
void process_detach_all(process_status_t* threads, size_t thread_count);
|
void process_detach_all(procstat_status_t* threads, size_t thread_count);
|
||||||
|
|
||||||
// calculate correct instruction pointer address for hijacking
|
// calculate correct instruction pointer address for hijacking
|
||||||
uintptr_t process_calculate_ip(process_status_t* thread, uintptr_t addr);
|
uintptr_t process_calculate_ip(procstat_status_t* thread, uintptr_t addr);
|
||||||
|
|
||||||
// read registers of thread. returns 0 on success, 1 on error
|
// read registers of thread. returns 0 on success, 1 on error
|
||||||
int process_read_registers(process_status_t* thread, struct user_regs_struct* regs);
|
int process_read_registers(procstat_status_t* thread, struct user_regs_struct* regs);
|
||||||
|
|
||||||
// write registers for thread. for return value same rules apply as read registers function
|
// write registers for thread. for return value same rules apply as read registers function
|
||||||
int process_write_registers(process_status_t* thread, const struct user_regs_struct* regs);
|
int process_write_registers(procstat_status_t* thread, const struct user_regs_struct* regs);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
214
src/blackjack/procstat.c
Normal file
214
src/blackjack/procstat.c
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
#define _DEFAULT_SOURCE
|
||||||
|
#include "blackjack/procstat.h"
|
||||||
|
#include "blackjack/debug.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
int procstat_parse_status(pid_t pid, procstat_status_t* status)
|
||||||
|
{
|
||||||
|
char statusPath[256] = {0};
|
||||||
|
snprintf(statusPath, sizeof(statusPath), "/proc/%d/status", pid);
|
||||||
|
|
||||||
|
int statusFd = open(statusPath, O_RDONLY);
|
||||||
|
if (statusFd < 0) return 1;
|
||||||
|
|
||||||
|
char buffer[4096] = {0};
|
||||||
|
int rd = read(statusFd, buffer, sizeof(buffer));
|
||||||
|
close(statusFd);
|
||||||
|
|
||||||
|
if (rd < 1) return 1;
|
||||||
|
|
||||||
|
char* lineptr = NULL, *line = strtok_r(buffer, "\n", &lineptr);
|
||||||
|
while (line != NULL)
|
||||||
|
{
|
||||||
|
char* fieldptr = NULL;
|
||||||
|
const char* key = (const char*)strtok_r(line, ":\t", &fieldptr);
|
||||||
|
const char* value = (const char*)strtok_r(NULL, ":\t", &fieldptr);
|
||||||
|
|
||||||
|
if (!strcmp(key, "Name"))
|
||||||
|
strncpy(status->name, value, MAX_PROCESS_NAME);
|
||||||
|
else if (!strcmp(key, "Umask"))
|
||||||
|
status->umask = (unsigned int)strtoul(value, NULL, 8);
|
||||||
|
else if (!strcmp(key, "State"))
|
||||||
|
status->state = (procstat_state_t)value[0];
|
||||||
|
else if (!strcmp(key, "Tgid"))
|
||||||
|
status->tgid = atoi(value);
|
||||||
|
else if (!strcmp(key, "Ngid"))
|
||||||
|
status->ngid = atoi(value);
|
||||||
|
else if (!strcmp(key, "Pid"))
|
||||||
|
status->pid = atoi(value);
|
||||||
|
else if (!strcmp(key, "PPid"))
|
||||||
|
status->ppid = atoi(value);
|
||||||
|
else if (!strcmp(key, "TracerPid"))
|
||||||
|
status->tracer_pid = atoi(value);
|
||||||
|
else if (!strcmp(key, "Uid"))
|
||||||
|
status->uid = atoi(value);
|
||||||
|
else if (!strcmp(key, "Gid"))
|
||||||
|
status->gid = atoi(value);
|
||||||
|
|
||||||
|
line = strtok_r(NULL, "\n", &lineptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_numeric(const char* str)
|
||||||
|
{
|
||||||
|
for (const char* p = str; *p; p++)
|
||||||
|
if (!isdigit(*p)) return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int procstat_by_name(const char* name, procstat_status_t** list, size_t* count)
|
||||||
|
{
|
||||||
|
*list = NULL;
|
||||||
|
*count = 0;
|
||||||
|
|
||||||
|
DIR* proc = opendir("/proc");
|
||||||
|
if (!proc) return 1;
|
||||||
|
|
||||||
|
struct dirent* entry;
|
||||||
|
while ((entry = readdir(proc)))
|
||||||
|
{
|
||||||
|
if (entry->d_type != DT_DIR) continue;
|
||||||
|
if (!is_numeric(entry->d_name)) continue;
|
||||||
|
|
||||||
|
pid_t pid = strtod(entry->d_name, NULL);
|
||||||
|
procstat_status_t status = {};
|
||||||
|
if (procstat_parse_status(pid, &status))
|
||||||
|
{
|
||||||
|
TRACE("process parse status failed for %d\n", pid);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcasecmp(status.name, name))
|
||||||
|
{
|
||||||
|
// we have match! lets add it to list
|
||||||
|
size_t _count = *count;
|
||||||
|
procstat_status_t* _list = *list;
|
||||||
|
|
||||||
|
if (_count) _list = (procstat_status_t*)realloc(_list, ++_count * sizeof(procstat_status_t));
|
||||||
|
else _list = (procstat_status_t*)malloc(++_count * sizeof(procstat_status_t));
|
||||||
|
|
||||||
|
if (!_list)
|
||||||
|
{
|
||||||
|
TRACE("out of memory for process status!\n");
|
||||||
|
closedir(proc);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy process status to list
|
||||||
|
memcpy(&_list[_count - 1], &status, sizeof(procstat_status_t));
|
||||||
|
|
||||||
|
// update pointers
|
||||||
|
*list = _list;
|
||||||
|
*count = _count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(proc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static procstat_status_t* procstat_by_pid_in_list(pid_t pid, procstat_status_t* list, size_t count)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < count; i++)
|
||||||
|
if (list[i].pid == pid) return &list[i];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int procstat_determine_parent(procstat_status_t* list, size_t count, procstat_status_t** parent)
|
||||||
|
{
|
||||||
|
// we're gonna find any process that doesnt have parent in this list,
|
||||||
|
// that means we hit real parent, not descendant
|
||||||
|
for (size_t i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (!procstat_by_pid_in_list(list[i].ppid, list, count))
|
||||||
|
{
|
||||||
|
// that's real parent
|
||||||
|
*parent = &list[i];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int procstat_get_threads(pid_t pid, procstat_status_t** list, size_t* count)
|
||||||
|
{
|
||||||
|
*list = NULL;
|
||||||
|
*count = 0;
|
||||||
|
|
||||||
|
char taskPath[256] = {0};
|
||||||
|
snprintf(taskPath, sizeof(taskPath), "/proc/%d/task", pid);
|
||||||
|
|
||||||
|
DIR* taskDir = opendir(taskPath);
|
||||||
|
if (!taskDir) return 1;
|
||||||
|
|
||||||
|
struct dirent* entry;
|
||||||
|
while ((entry = readdir(taskDir)))
|
||||||
|
{
|
||||||
|
if (entry->d_type != DT_DIR) continue;
|
||||||
|
if (!is_numeric(entry->d_name)) continue;
|
||||||
|
|
||||||
|
pid_t pid = strtod(entry->d_name, NULL);
|
||||||
|
procstat_status_t status = {};
|
||||||
|
if (procstat_parse_status(pid, &status))
|
||||||
|
{
|
||||||
|
// if we can't some threads, we should fail completely
|
||||||
|
// failure is better than incomplete info
|
||||||
|
TRACE("failed to parse %d task status\n", pid);
|
||||||
|
closedir(taskDir);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add thread to list
|
||||||
|
procstat_status_t* _list = *list;
|
||||||
|
size_t _count = *count;
|
||||||
|
|
||||||
|
if (_count) _list = (procstat_status_t*)realloc(_list, ++_count * sizeof(procstat_status_t));
|
||||||
|
else _list = (procstat_status_t*)malloc(++_count * sizeof(procstat_status_t));
|
||||||
|
|
||||||
|
if (!_list)
|
||||||
|
{
|
||||||
|
TRACE("out of memory for process status!\n");
|
||||||
|
closedir(taskDir);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy thread to list
|
||||||
|
memcpy(&_list[_count - 1], &status, sizeof(procstat_status_t));
|
||||||
|
|
||||||
|
*list = _list;
|
||||||
|
*count = _count;
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(taskDir);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int procstat_is_considered_active(procstat_state_t state)
|
||||||
|
{
|
||||||
|
return state == INTERRUPTIBLE_SLEEP || state == RUNNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
int procstat_find_active(procstat_status_t* list, size_t count, procstat_status_t** thread)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
TRACE("task %d state %d\n", list[i].pid, list[i].state);
|
||||||
|
if (procstat_is_considered_active(list[i].state))
|
||||||
|
{
|
||||||
|
*thread = &list[i];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
55
src/blackjack/procstat.h
Normal file
55
src/blackjack/procstat.h
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
#ifndef __PROCSTAT_H
|
||||||
|
#define __PROCSTAT_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
UNINTERRUPTABLE_SLEEP = 'D',
|
||||||
|
IDLE_KERNEL_THREAD = 'I',
|
||||||
|
RUNNING = 'R',
|
||||||
|
INTERRUPTIBLE_SLEEP = 'S',
|
||||||
|
STOPPED = 'T',
|
||||||
|
STOPPED_BY_DEBUGGER = 't',
|
||||||
|
DEAD = 'X',
|
||||||
|
ZOMBIE = 'Z'
|
||||||
|
} procstat_state_t;
|
||||||
|
|
||||||
|
#define MAX_PROCESS_NAME 256
|
||||||
|
typedef struct {
|
||||||
|
char name[MAX_PROCESS_NAME];
|
||||||
|
mode_t umask;
|
||||||
|
procstat_state_t state;
|
||||||
|
pid_t tgid;
|
||||||
|
pid_t ngid;
|
||||||
|
pid_t pid;
|
||||||
|
pid_t ppid;
|
||||||
|
pid_t tracer_pid;
|
||||||
|
uid_t uid;
|
||||||
|
gid_t gid;
|
||||||
|
} procstat_status_t;
|
||||||
|
|
||||||
|
// parse process status from procfs. returns 0 no errors and 1 on any kind of error
|
||||||
|
// error information obtain from errno
|
||||||
|
int procstat_parse_status(pid_t pid, procstat_status_t* status);
|
||||||
|
|
||||||
|
// find any process that matches name, case insensitive.
|
||||||
|
// list pointer must point to NULL-initialized pointer, and count pointer must pount to initialized 0
|
||||||
|
// will skip any process which status couldn't be parsed
|
||||||
|
// deallocate list with free later
|
||||||
|
int procstat_by_name(const char* name, procstat_status_t** list, size_t* count);
|
||||||
|
|
||||||
|
// determine parent process amongst children and set parent pointer to element in list
|
||||||
|
// process list must consist of parent and children processes,
|
||||||
|
// obtained from processes_by_name call. of course parent pointer shouldn't be NULL
|
||||||
|
int procstat_determine_parent(procstat_status_t* list, size_t count, procstat_status_t** parent);
|
||||||
|
|
||||||
|
// get all process threads. for list and count same rules applies as for processes_by_name
|
||||||
|
int procstat_get_threads(pid_t pid, procstat_status_t** list, size_t* count);
|
||||||
|
|
||||||
|
// returns 1 if state considered active for a process/thread
|
||||||
|
int procstat_is_considered_active(procstat_state_t state);
|
||||||
|
|
||||||
|
// find any active (running) thread and returns 0 and success, otherwise non zero
|
||||||
|
int procstat_find_active(procstat_status_t* list, size_t count, procstat_status_t** thread);
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Add table
Reference in a new issue