/* This file is part of genrc Copyryght (C) 2018 Sergey Poznyakoff License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. */ #include "genrc.h" struct ps_pid_closure { struct genrc_pid_closure generic; PROCSCANBUF buf; }; enum { UID_COL = 0, PID_COL, PPID_COL, C_COL, STIME_COL, TTY_COL, TIME_COL, CMD_COL }; static int match_cmdline(struct ps_pid_closure *clos, struct wordsplit const *ws) { grecs_txtacc_t acc; size_t i; int rc; char *cmdline; acc = grecs_txtacc_create(); grecs_txtacc_grow_string(acc, ws->ws_wordv[CMD_COL]); for (i = CMD_COL + 1; i < ws->ws_wordc; i++) { grecs_txtacc_grow_char(acc, ' '); grecs_txtacc_grow_string(acc, ws->ws_wordv[i]); } grecs_txtacc_grow_char(acc, 0); cmdline = grecs_txtacc_finish(acc, 0); rc = procscan_match(clos->buf, cmdline); grecs_txtacc_free(acc); return rc; } static int match_argv0(struct ps_pid_closure *clos, struct wordsplit const *ws) { return procscan_match(clos->buf, ws->ws_wordv[CMD_COL]); } static int match(struct ps_pid_closure *clos, struct wordsplit const *ws) { return ((clos->buf->flags & PROCF_CMDLINE) ? match_cmdline : match_argv0)(clos, ws); } static int pid_ps_get(GENRC_PID_CLOSURE *clos, PIDLIST *plist) { struct ps_pid_closure *ps_clos = (struct ps_pid_closure *)clos; FILE *fp; struct wordsplit ws; int wsflags; ssize_t n; char *buf = NULL; size_t size = 0; size_t line = 0; PIDLIST pplist; fp = popen("ps -ef", "r"); if (!fp) { system_error(errno, "failed to run ps"); return -1; } ws.ws_delim = " "; ws.ws_error = genrc_error; wsflags = WRDSF_NOCMD | WRDSF_NOVAR | WRDSF_QUOTE | WRDSF_DELIM | WRDSF_SQUEEZE_DELIMS | WRDSF_ENOMEMABRT | WRDSF_SHOWERR | WRDSF_ERROR; pidlist_init(&pplist); while ((n = grecs_getline(&buf, &size, fp)) > 0) { line++; if (line == 1) continue; buf[n-1] = 0; if (wordsplit(buf, &ws, wsflags)) exit(1); wsflags |= WRDSF_REUSE; if (match(ps_clos, &ws) == 0) { pid_t ppid = strtopid(ws.ws_wordv[PPID_COL]); if (!pidlist_member(&pplist, ppid)) pidlist_add(&pplist, ppid); pidlist_add(plist, strtopid(ws.ws_wordv[PID_COL])); } } fclose(fp); free(buf); if (wsflags & WRDSF_REUSE) wordsplit_free(&ws); if (plist->pidc > 1 && !(ps_clos->buf->flags & PROCF_ALL)) { size_t i; for (i = 0; i < pplist.pidc; ) { if (pidlist_member(plist, pplist.pidv[i])) i++; else { pidlist_remove(&pplist, i); } } pidlist_free(plist); *plist = pplist; } return 0; } GENRC_PID_CLOSURE * genrc_pid_ps_init(int argc, char **argv) { struct ps_pid_closure *clos; if (argc > 3) usage_error("expected format: PS[:[][:]]"); clos = xzalloc(sizeof *clos); clos->buf = procscan_init((argc >= 2 && argv[1][0] != 0) ? argv[1] : genrc_program, argc > 2 ? argv[2] : NULL); if (clos->buf->flags & PROCF_EXE) usage_error("flag r isn't compatible with PS"); clos->generic.pid = pid_ps_get; return (GENRC_PID_CLOSURE *)clos; }