最近在使用wazuh时,发现在收集agent端的进程信息的时候,使用了一个第三方库 procps来处理/proc下面的信息的。
使用方式
1:调用openproc进行初始化,设置需要收集的信息标志;创建PROCTAB对象;
2:调用readproc循环读取数据,每次遍历需要调用freeproc释放资源;
3:最后调用closeproc,也是释放资源比如关闭打开的目录;
看一下 wazuh如何使用的:
void sys_proc_linux(int queue_fd, const char* LOCATION) {
...
//初始化,
PROCTAB* proc = openproc(PROC_FILLMEM | PROC_FILLSTAT | PROC_FILLSTATUS | PROC_FILLARG | PROC_FILLGRP | PROC_FILLUSR | PROC_FILLCOM | PROC_FILLENV);
proc_t * proc_info;
char *string;
if (!proc) {
mterror(WM_SYS_LOGTAG, "Running process inventory: could not create libproc context.");
free(timestamp);
return;
}
int i = 0;
cJSON *item;
cJSON *proc_array = cJSON_CreateArray();
mtdebug1(WM_SYS_LOGTAG, "Starting running processes inventory.");
// 循环遍历 获取信息
while (proc_info = readproc(proc, NULL), proc_info != NULL) {
cJSON *object = cJSON_CreateObject();
cJSON *process = cJSON_CreateObject();
cJSON_AddStringToObject(object, "type", "process");
cJSON_AddNumberToObject(object, "ID", random_id);
cJSON_AddStringToObject(object, "timestamp", timestamp);
cJSON_AddItemToObject(object, "process", process);
cJSON_AddNumberToObject(process,"pid",proc_info->tid);
cJSON_AddStringToObject(process,"name",proc_info->cmd);
cJSON_AddStringToObject(process,"state",&proc_info->state);
cJSON_AddNumberToObject(process,"ppid",proc_info->ppid);
cJSON_AddNumberToObject(process,"utime",proc_info->utime);
cJSON_AddNumberToObject(process,"stime",proc_info->stime);
if (proc_info->cmdline && proc_info->cmdline[0]) {
cJSON *argvs = cJSON_CreateArray();
cJSON_AddStringToObject(process, "cmd", proc_info->cmdline[0]);
for (i = 1; proc_info->cmdline[i]; i++) {
if (!strlen(proc_info->cmdline[i])==0) {
cJSON_AddItemToArray(argvs, cJSON_CreateString(proc_info->cmdline[i]));
}
}
if (cJSON_GetArraySize(argvs) > 0) {
cJSON_AddItemToObject(process, "argvs", argvs);
} else {
cJSON_Delete(argvs);
}
}
cJSON_AddStringToObject(process,"euser",proc_info->euser);
cJSON_AddStringToObject(process,"ruser",proc_info->ruser);
cJSON_AddStringToObject(process,"suser",proc_info->suser);
cJSON_AddStringToObject(process,"egroup",proc_info->egroup);
cJSON_AddStringToObject(process,"rgroup",proc_info->rgroup);
cJSON_AddStringToObject(process,"sgroup",proc_info->sgroup);
cJSON_AddStringToObject(process,"fgroup",proc_info->fgroup);
cJSON_AddNumberToObject(process,"priority",proc_info->priority);
cJSON_AddNumberToObject(process,"nice",proc_info->nice);
cJSON_AddNumberToObject(process,"size",proc_info->size);
cJSON_AddNumberToObject(process,"vm_size",proc_info->vm_size);
cJSON_AddNumberToObject(process,"resident",proc_info->resident);
cJSON_AddNumberToObject(process,"share",proc_info->share);
cJSON_AddNumberToObject(process,"start_time",proc_info->start_time);
cJSON_AddNumberToObject(process,"pgrp",proc_info->pgrp);
cJSON_AddNumberToObject(process,"session",proc_info->session);
cJSON_AddNumberToObject(process,"nlwp",proc_info->nlwp);
cJSON_AddNumberToObject(process,"tgid",proc_info->tgid);
cJSON_AddNumberToObject(process,"tty",proc_info->tty);
cJSON_AddNumberToObject(process,"processor",proc_info->processor);
cJSON_AddItemToArray(proc_array, object);
//释放资源
freeproc(proc_info);
}
...
//释放资源
closeproc(proc);
...
}
procps的实现 版本3.2.7
初始化
// 初始化
PROCTAB* openproc(int flags, ...) {
va_list ap;
struct stat sbuf;
static int did_stat;
PROCTAB* PT = xmalloc(sizeof(PROCTAB));
if(!did_stat){
task_dir_missing = stat("/proc/self/task", &sbuf);
did_stat = 1;
}
PT->taskdir = NULL;
PT->taskdir_user = -1;
PT->taskfinder = simple_nexttid;
PT->taskreader = simple_readtask;
//设置读取回调函数
PT->reader = simple_readproc;
if (flags & PROC_PID){
PT->procfs = NULL;
//设置查找pid的函数
PT->finder = listed_nextpid;
}else{
PT->procfs = opendir("/proc");
if(!PT->procfs) return NULL;
PT->finder = simple_nextpid;
}
PT->flags = flags;
va_start(ap, flags); /* Init args list */
if (flags & PROC_PID)
PT->pids = va_arg(ap, pid_t*);
else if (flags & PROC_UID) {
PT->uids = va_arg(ap, uid_t*);
PT->nuid = va_arg(ap, int);
}
va_end(ap); /* Clean up args list */
return PT;
}
读取:
proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p) {
proc_t *ret;
proc_t *saved_p;
PT->did_fake=0;
// if (PT->taskdir) {
// closedir(PT->taskdir);
// PT->taskdir = NULL;
// PT->taskdir_user = -1;
// }
saved_p = p;
if(!p) p = xcalloc(p, sizeof *p); /* passed buf or alloced mem */
for(;;){
// 填充路径
if (unlikely(! PT->finder(PT,p) )) goto out;
// 读取进程数据
ret = PT->reader(PT,p);
if(ret) return ret;
}
out:
if(!saved_p) free(p);
// FIXME: maybe set tid to -1 here, for "-" in display?
return NULL;
}
读取回调函数:
static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict const p) {
static struct stat sb; // stat() buffer
static char sbuf[1024]; // buffer for stat,statm
char *restrict const path = PT->path;
unsigned flags = PT->flags;
if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
goto next_proc;
if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
goto next_proc; /* not one of the requested uids */
p->euid = sb.st_uid; /* need a way to get real uid */
p->egid = sb.st_gid; /* need a way to get real gid */
// file2str 读取文件内容 /proc/pid/stat
if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */
if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
goto next_proc; /* error reading /proc/#/stat */
stat2proc(sbuf, p); /* 解析 /proc/#/stat */
}
if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */
if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
statm2proc(sbuf, p); /* ignore statm errors here */
} /* statm fields just zero */
if (flags & PROC_FILLSTATUS) { /* 读取 解析 /proc/#/status 包含进程名 */
if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
status2proc(sbuf, p, 1);
}
}
// if multithreaded, some values are crap
if(p->nlwp > 1){
p->wchan = (KLONG)~0ull;
}
/* some number->text resolving which is time consuming and kind of insane */
if (flags & PROC_FILLUSR){
memcpy(p->euser, user_from_uid(p->euid), sizeof p->euser);
if(flags & PROC_FILLSTATUS) {
memcpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser);
memcpy(p->suser, user_from_uid(p->suid), sizeof p->suser);
memcpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser);
}
}
/* some number->text resolving which is time consuming and kind of insane */
if (flags & PROC_FILLGRP){
memcpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup);
if(flags & PROC_FILLSTATUS) {
memcpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup);
memcpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup);
memcpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup);
}
}
if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
p->cmdline = file2strvec(path, "cmdline");
else
p->cmdline = NULL;
if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */
p->environ = file2strvec(path, "environ");
else
p->environ = NULL;
return p;
next_proc:
return NULL;
}
其他的代码都比较简单,不再分析了。