当前位置: 首页 > 工具软件 > procps > 使用案例 >

procps 获取进程信息

柯升
2023-12-01

最近在使用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;
}

其他的代码都比较简单,不再分析了。

 类似资料: