diff --git a/Makefile.am b/Makefile.am index 9e900ac0..3d66d60f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -55,7 +55,7 @@ EXTRA_DIST = \ if BUILD_KILL bin_PROGRAMS = kill dist_man_MANS += kill.1 -kill_SOURCES = skill.c $(top_srcdir)/lib/strutils.c $(top_srcdir)/lib/fileutils.c +kill_SOURCES = skill.c $(top_srcdir)/lib/strutils.c $(top_srcdir)/lib/fileutils.c $(top_srcdir)/lib/nsutils.c else EXTRA_DIST += kill.1 endif @@ -77,8 +77,8 @@ if BUILD_SKILL usrbin_exec_PROGRAMS += \ skill \ snice -skill_SOURCES = skill.c $(top_srcdir)/lib/strutils.c $(top_srcdir)/lib/fileutils.c -snice_SOURCES = skill.c $(top_srcdir)/lib/strutils.c $(top_srcdir)/lib/fileutils.c +skill_SOURCES = skill.c $(top_srcdir)/lib/strutils.c $(top_srcdir)/lib/fileutils.c $(top_srcdir)/lib/nsutils.c +snice_SOURCES = skill.c $(top_srcdir)/lib/strutils.c $(top_srcdir)/lib/fileutils.c $(top_srcdir)/lib/nsutils.c dist_man_MANS += \ skill.1 \ snice.1 @@ -89,8 +89,8 @@ else endif free_SOURCES = free.c $(top_srcdir)/lib/strutils.c $(top_srcdir)/lib/fileutils.c -pgrep_SOURCES = pgrep.c $(top_srcdir)/lib/fileutils.c -pkill_SOURCES = pgrep.c $(top_srcdir)/lib/fileutils.c +pgrep_SOURCES = pgrep.c $(top_srcdir)/lib/fileutils.c $(top_srcdir)/lib/nsutils.c +pkill_SOURCES = pgrep.c $(top_srcdir)/lib/fileutils.c $(top_srcdir)/lib/nsutils.c pmap_SOURCES = pmap.c $(top_srcdir)/lib/fileutils.c pwdx_SOURCES = pwdx.c $(top_srcdir)/lib/fileutils.c sysctl_SOURCES = sysctl.c $(top_srcdir)/lib/fileutils.c diff --git a/include/nsutils.h b/include/nsutils.h new file mode 100644 index 00000000..3235851e --- /dev/null +++ b/include/nsutils.h @@ -0,0 +1,7 @@ +#ifndef PROCPS_NG_NSUTILS +#define PROCPS_NG_NSUTILS + +#include "proc/readproc.h" +int ns_read(pid_t pid, proc_t *ns_task); + +#endif diff --git a/lib/nsutils.c b/lib/nsutils.c new file mode 100644 index 00000000..b97f4469 --- /dev/null +++ b/lib/nsutils.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "proc/readproc.h" +#include "nsutils.h" + +/* we need to fill in only namespace information */ +int ns_read(pid_t pid, proc_t *ns_task) +{ + struct stat st; + char buff[50]; + int i, rc = 0; + + for (i = 0; i < NUM_NS; i++) { + snprintf(buff, sizeof(buff), "/proc/%i/ns/%s", pid, + get_ns_name(i)); + if (stat(buff, &st)) { + if (errno != ENOENT) + rc = errno; + ns_task->ns[i] = 0; + continue; + } + ns_task->ns[i] = st.st_ino; + } + return rc; +} + diff --git a/pgrep.1 b/pgrep.1 index a0e7376b..c9860c7f 100644 --- a/pgrep.1 +++ b/pgrep.1 @@ -146,6 +146,15 @@ than \fB\-L\fR, \fB\-\-logpidfile\fR Fail if pidfile (see -F) not locked. .TP +\fB\-\-ns \fIpid\fP +Match processes that belong to the same namespaces. Required to run as +root to match processes from other users. See \-\-nslist for how to limit +which namespaces to match. +.TP +\fB\-\-nslist \fIname\fP,... +Match only the provided namespaces. Available namespaces: +ipc, mnt, net, pid, user,uts. +.TP \fB\-V\fR, \fB\-\-version\fR Display version information and exit. .TP diff --git a/pgrep.c b/pgrep.c index 6bc38ef7..c7535f77 100644 --- a/pgrep.c +++ b/pgrep.c @@ -46,6 +46,7 @@ #include "c.h" #include "fileutils.h" +#include "nsutils.h" #include "nls.h" #include "xalloc.h" #include "proc/readproc.h" @@ -76,6 +77,7 @@ static int opt_lock = 0; static int opt_case = 0; static int opt_echo = 0; static int opt_threads = 0; +static pid_t opt_ns_pid = 0; static const char *opt_delim = "\n"; static struct el *opt_pgrp = NULL; @@ -86,9 +88,13 @@ static struct el *opt_sid = NULL; static struct el *opt_term = NULL; static struct el *opt_euid = NULL; static struct el *opt_ruid = NULL; +static struct el *opt_nslist = NULL; static char *opt_pattern = NULL; static char *opt_pidfile = NULL; +/* by default, all namespaces will be checked */ +static int ns_flags = 0x3f; + static int __attribute__ ((__noreturn__)) usage(int opt) { int err = (opt == '?'); @@ -121,7 +127,12 @@ static int __attribute__ ((__noreturn__)) usage(int opt) " -U, --uid match by real IDs\n" " -x, --exact match exactly with the command name\n" " -F, --pidfile read PIDs from file\n" - " -L, --logpidfile fail if PID file is not locked\n"), fp); + " -L, --logpidfile fail if PID file is not locked\n" + " --ns match the processes that belong to the same\n" + " namespace as \n" + " --nslist list which namespaces will be considered for\n" + " the --ns option.\n" + " Available namespaces: ipc, mnt, net, pid, user, uts\n"), fp); fputs(USAGE_SEPARATOR, fp); fputs(USAGE_HELP, fp); fputs(USAGE_VERSION, fp); @@ -320,6 +331,20 @@ static int conv_str (const char *restrict name, struct el *restrict e) } +static int conv_ns (const char *restrict name, struct el *restrict e) +{ + int rc = conv_str(name, e); + int id; + + ns_flags = 0; + id = get_ns_id(name); + if (id == -1) + return 0; + ns_flags |= (1 << id); + + return rc; +} + static int match_numlist (long value, const struct el *restrict list) { int found = 0; @@ -350,6 +375,21 @@ static int match_strlist (const char *restrict value, const struct el *restrict return found; } +static int match_ns (const proc_t *task, const proc_t *ns_task) +{ + int found = 1; + int i; + + for (i = 0; i < NUM_NS; i++) { + if (ns_flags & (1 << i)) { + if (task->ns[i] != ns_task->ns[i]) + found = 0; + } + } + + return found; +} + static void output_numlist (const struct el *restrict list, int num) { int i; @@ -386,6 +426,8 @@ static PROCTAB *do_openproc (void) flags |= PROC_FILLSTAT; if (!(flags & PROC_FILLSTAT)) flags |= PROC_FILLSTATUS; /* FIXME: need one, and PROC_FILLANY broken */ + if (opt_ns_pid) + flags |= PROC_FILLNS; if (opt_euid && !opt_negate) { int num = opt_euid[0].num; int i = num; @@ -442,6 +484,7 @@ static struct el * select_procs (int *num) char cmdline[CMDSTRSIZE]; char cmdsearch[CMDSTRSIZE]; char cmdoutput[CMDSTRSIZE]; + proc_t ns_task; ptp = do_openproc(); preg = do_regcomp(); @@ -451,7 +494,12 @@ static struct el * select_procs (int *num) if (opt_newest) saved_pid = 0; if (opt_oldest) saved_pid = INT_MAX; - + if (opt_ns_pid && ns_read(opt_ns_pid, &ns_task)) { + fputs(_("Error reading reference namespace information\n"), + stderr); + exit (EXIT_FATAL); + } + memset(&task, 0, sizeof (task)); while(readproc(ptp, &task)) { int match = 1; @@ -476,6 +524,8 @@ static struct el * select_procs (int *num) match = 0; else if (opt_sid && ! match_numlist (task.session, opt_sid)) match = 0; + else if (opt_ns_pid && ! match_ns (&task, &ns_task)) + match = 0; else if (opt_term) { if (task.tty == 0) { match = 0; @@ -622,7 +672,9 @@ static void parse_opts (int argc, char **argv) int criteria_count = 0; enum { - SIGNAL_OPTION = CHAR_MAX + 1 + SIGNAL_OPTION = CHAR_MAX + 1, + NS_OPTION, + NSLIST_OPTION, }; static const struct option longopts[] = { {"signal", required_argument, NULL, SIGNAL_OPTION}, @@ -646,6 +698,8 @@ static void parse_opts (int argc, char **argv) {"pidfile", required_argument, NULL, 'F'}, {"logpidfile", no_argument, NULL, 'L'}, {"echo", no_argument, NULL, 'e'}, + {"ns", required_argument, NULL, NS_OPTION}, + {"nslist", required_argument, NULL, NSLIST_OPTION}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0} @@ -792,6 +846,17 @@ static void parse_opts (int argc, char **argv) break; /* case 'z': / * Solaris: match by zone ID * / * break; */ + case NS_OPTION: + opt_ns_pid = atoi(optarg); + if (opt_ns_pid == 0) + usage (opt); + ++criteria_count; + break; + case NSLIST_OPTION: + opt_nslist = split_list (optarg, conv_ns); + if (opt_nslist == NULL) + usage (opt); + break; case 'h': usage (opt); break; diff --git a/proc/libprocps.sym b/proc/libprocps.sym index 51d53c95..d4f692ee 100644 --- a/proc/libprocps.sym +++ b/proc/libprocps.sym @@ -11,6 +11,8 @@ global: escaped_copy; free_slabinfo; freeproc; + get_ns_id; + get_ns_name; get_pid_digits; get_slabinfo; getbtime; diff --git a/proc/readproc.c b/proc/readproc.c index 11f08985..05d0189a 100644 --- a/proc/readproc.c +++ b/proc/readproc.c @@ -457,6 +457,51 @@ static void oomadj2proc(const char* S, proc_t *restrict P) #endif /////////////////////////////////////////////////////////////////////// +static ino_t _ns2proc(unsigned pid, const char *ns) +{ + struct stat s; + char filename[40]; + + snprintf(filename, sizeof(filename), "/proc/%i/ns/%s", pid, ns); + + if (stat(filename, &s) == -1) + return 0; + + return s.st_ino; +} + +static const char *ns_names[] = { + [IPCNS] = "ipc", + [MNTNS] = "mnt", + [NETNS] = "net", + [PIDNS] = "pid", + [USERNS] = "user", + [UTSNS] = "uts", +}; + +const char *get_ns_name(int id) { + if (id >= NUM_NS) + return NULL; + return ns_names[id]; +} + +int get_ns_id(const char *name) { + int i; + + for (i = 0; i < NUM_NS; i++) + if (!strcmp(ns_names[i], name)) + return i; + return -1; +} + +static void ns2proc(proc_t *restrict P) { + int i; + + for (i = 0; i < NUM_NS; i++) + P->ns[i] = _ns2proc(P->tgid, ns_names[i]); +} +/////////////////////////////////////////////////////////////////////// + // Reads /proc/*/stat files, being careful not to trip over processes with // names like ":-) 1 2 3 4 5 6". @@ -757,6 +802,7 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons static struct stat sb; // stat() buffer char *restrict const path = PT->path; unsigned flags = PT->flags; + int i; if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */ goto next_proc; @@ -844,6 +890,12 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons } #endif + if (unlikely(flags & PROC_FILLNS)) // read /proc/#/ns/* + ns2proc(p); + else + for (i = 0; i < NUM_NS; i++) + p->ns[i] = 0; + return p; next_proc: return NULL; @@ -862,6 +914,7 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restric static struct utlbuf_s ub = { NULL, 0 }; // buf for stat,statm,status static struct stat sb; // stat() buffer unsigned flags = PT->flags; + int i; if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */ goto next_task; @@ -974,6 +1027,11 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restric oomadj2proc(ub.buf, t); } #endif + if (unlikely(flags & PROC_FILLNS)) + ns2proc(t); + else + for (i = 0; i < NUM_NS; i++) + t->ns[i] = 0; return t; next_task: diff --git a/proc/readproc.h b/proc/readproc.h index fb83dda6..643fb269 100644 --- a/proc/readproc.h +++ b/proc/readproc.h @@ -31,6 +31,18 @@ EXTERN_C_BEGIN // neither tgid nor tid seemed correct. (in other words, FIXME) #define XXXID tid +#define NUM_NS 6 +enum ns_type { + IPCNS = 0, + MNTNS, + NETNS, + PIDNS, + USERNS, + UTSNS +}; +extern const char *get_ns_name(int id); +extern int get_ns_id(const char *name); + // Basic data structure which holds all information we can get about a process. // (unless otherwise specified, fields are read from /proc/#/stat) // @@ -157,6 +169,8 @@ typedef struct proc_t { oom_score, // oom_score (badness for OOM killer) oom_adj; // oom_adj (adjustment to OOM score) #endif + ino_t + ns[NUM_NS]; // ns/* inode number of /proc//ns/* } proc_t; // PROCTAB: data structure holding the persistent information readproc needs @@ -266,6 +280,7 @@ extern proc_t * get_proc_stats(pid_t pid, proc_t *p); #define PROC_FILLCGROUP 0x0200 // alloc and fill in `cgroup` #define PROC_FILLSUPGRP 0x0400 // resolve supplementary group id -> group name #define PROC_FILLOOM 0x0800 // fill in proc_t oom_score and oom_adj +#define PROC_FILLNS 0x8000 // fill in proc_t namespace information #define PROC_LOOSE_TASKS 0x2000 // treat threads as if they were processes diff --git a/ps/output.c b/ps/output.c index d457a89c..2e9056e5 100644 --- a/ps/output.c +++ b/ps/output.c @@ -139,6 +139,13 @@ static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \ return 0; \ } +#define CMP_NS(NAME, ID) \ +static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \ + if (P->ns[ID] < Q->ns[ID]) return -1; \ + if (P->ns[ID] > Q->ns[ID]) return 1; \ + return 0; \ +} + CMP_INT(rtprio) CMP_SMALL(sched) CMP_INT(cutime) @@ -216,6 +223,13 @@ CMP_SMALL(state) CMP_COOKED_TIME(time) CMP_COOKED_TIME(etime) +CMP_NS(ipcns, IPCNS); +CMP_NS(mntns, MNTNS); +CMP_NS(netns, NETNS); +CMP_NS(pidns, PIDNS); +CMP_NS(userns, USERNS); +CMP_NS(utsns, UTSNS); + /* approximation to: kB of address space that could end up in swap */ static int sr_swapable(const proc_t* P, const proc_t* Q) { unsigned long p_swapable = P->vm_data + P->vm_stack; @@ -1279,6 +1293,22 @@ fail: } #endif +/************************ Linux namespaces ******************************/ + +#define _pr_ns(NAME, ID)\ +static int pr_##NAME(char *restrict const outbuf, const proc_t *restrict const pp) {\ + if (pp->ns[ID])\ + return snprintf(outbuf, COLWID, "%li", pp->ns[ID]);\ + else\ + return snprintf(outbuf, COLWID, "-");\ +} +_pr_ns(ipcns, IPCNS); +_pr_ns(mntns, MNTNS); +_pr_ns(netns, NETNS); +_pr_ns(pidns, PIDNS); +_pr_ns(userns, USERNS); +_pr_ns(utsns, UTSNS); + /****************** FLASK & seLinux security stuff **********************/ // move the bulk of this to libproc sometime @@ -1440,6 +1470,7 @@ static int pr_t_left2(char *restrict const outbuf, const proc_t *restrict const #define USR PROC_FILLUSR /* uid_t -> user names */ #define GRP PROC_FILLGRP /* gid_t -> group names */ #define WCH PROC_FILLWCHAN /* do WCHAN lookup */ +#define NS PROC_FILLNS /* read namespace information */ #define SGRP PROC_FILLSTATUS | PROC_FILLSUPGRP /* supgid -> supgrp (names) */ #define CGRP PROC_FILLCGROUP | PROC_EDITCGRPCVT /* read cgroup */ @@ -1528,6 +1559,7 @@ static const format_struct format_array[] = { {"inblk", "INBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*inblock*/ {"inblock", "INBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*inblk*/ {"intpri", "PRI", pr_opri, sr_priority, 3, 0, HPU, TO|RIGHT}, +{"ipcns", "IPCNS", pr_ipcns, sr_ipcns, 10, NS, LNX, ET|RIGHT}, {"jid", "JID", pr_nop, sr_nop, 1, 0, SGI, PO|RIGHT}, {"jobc", "JOBC", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT}, {"ktrace", "KTRACE", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, @@ -1560,9 +1592,11 @@ static const format_struct format_array[] = { {"majflt", "MAJFLT", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT}, {"min_flt", "MINFL", pr_minflt, sr_min_flt, 6, 0, LNX, AN|RIGHT}, {"minflt", "MINFLT", pr_minflt, sr_min_flt, 6, 0, XXX, AN|RIGHT}, +{"mntns", "MNTNS", pr_mntns, sr_mntns, 10, NS, LNX, ET|RIGHT}, {"msgrcv", "MSGRCV", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT}, {"msgsnd", "MSGSND", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT}, {"mwchan", "MWCHAN", pr_nop, sr_nop, 6, WCH, BSD, TO|WCHAN}, /* mutex (FreeBSD) */ +{"netns", "NETNS", pr_netns, sr_netns, 10, NS, LNX, ET|RIGHT}, {"ni", "NI", pr_nice, sr_nice, 3, 0, BSD, TO|RIGHT}, /*nice*/ {"nice", "NI", pr_nice, sr_nice, 3, 0, U98, TO|RIGHT}, /*ni*/ {"nivcsw", "IVCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, @@ -1587,6 +1621,7 @@ static const format_struct format_array[] = { {"pgid", "PGID", pr_pgid, sr_pgrp, 5, 0, U98, PO|PIDMAX|RIGHT}, {"pgrp", "PGRP", pr_pgid, sr_pgrp, 5, 0, LNX, PO|PIDMAX|RIGHT}, {"pid", "PID", pr_procs, sr_procs, 5, 0, U98, PO|PIDMAX|RIGHT}, +{"pidns", "PIDNS", pr_pidns, sr_pidns, 10, NS, LNX, ET|RIGHT}, {"pmem", "%MEM", pr_pmem, sr_rss, 4, 0, XXX, PO|RIGHT}, /*%mem*/ {"poip", "-", pr_nop, sr_nop, 1, 0, BSD, AN|RIGHT}, {"policy", "POL", pr_class, sr_sched, 3, 0, DEC, TO|LEFT}, @@ -1694,10 +1729,12 @@ static const format_struct format_array[] = { {"upr", "UPR", pr_nop, sr_nop, 3, 0, BSD, TO|RIGHT}, /*usrpri*/ {"uprocp", "UPROCP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, {"user", "USER", pr_euser, sr_euser, 8, USR, U98, ET|USER}, /* BSD n forces this to UID */ +{"userns", "USERNS", pr_userns, sr_userns, 10, NS, LNX, ET|RIGHT}, {"usertime", "USER", pr_nop, sr_nop, 4, 0, DEC, ET|RIGHT}, {"usrpri", "UPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT}, /*upr*/ {"util", "C", pr_c, sr_pcpu, 2, 0, SGI, ET|RIGHT}, // not sure about "C" {"utime", "UTIME", pr_nop, sr_utime, 6, 0, LNx, ET|RIGHT}, +{"utsns", "UTSNS", pr_utsns, sr_utsns, 10, NS, LNX, ET|RIGHT}, #ifdef WITH_SYSTEMD {"uunit", "UUNIT", pr_sd_uunit, sr_nop, 31, 0, LNX, ET|LEFT}, #endif diff --git a/ps/ps.1 b/ps/ps.1 index 6311b48b..80e5ce87 100644 --- a/ps/ps.1 +++ b/ps/ps.1 @@ -1299,6 +1299,10 @@ format is displayed. (alias .BR sig_ignore , \ sigignore ). T} +ipcns IPCNS T{ +Unique inode number describing the namespace the process belongs to. See namespaces(7). +T} + label LABEL T{ security label, most commonly used for SELinux context data. This is for the @@ -1335,6 +1339,14 @@ min_flt MINFLT T{ The number of minor page faults that have occurred with this process. T} +mntns MNTNS T{ +Unique inode number describing the namespace the process belongs to. See namespaces(7). +T} + +netns NETNS T{ +Unique inode number describing the namespace the process belongs to. See namespaces(7). +T} + ni NI T{ nice value. This ranges from 19 (nicest) to \-20 (not nice to others), see @@ -1403,6 +1415,10 @@ a number representing the process ID (alias .BR tgid ). T} +pidns PIDNS T{ +Unique inode number describing the namespace the process belongs to. See namespaces(7). +T} + pmem %MEM T{ see .BR %mem . @@ -1735,6 +1751,14 @@ see .BR euser , \ uname ). T} +userns USERNS T{ +Unique inode number describing the namespace the process belongs to. See namespaces(7). +T} + +utsns UTSNS T{ +Unique inode number describing the namespace the process belongs to. See namespaces(7). +T} + uunit UUNIT T{ displays systemd user unit which a process belongs to. T} diff --git a/skill.1 b/skill.1 index 9748a1de..8ef76835 100644 --- a/skill.1 +++ b/skill.1 @@ -77,6 +77,13 @@ The next expression is a process ID number. .TP \fB\-c\fR, \fB\-\-command\fR \fIcommand\fR The next expression is a command name. +.TP +\fB\-\-ns \fIpid\fR +Match the processes that belong to the same namespace as pid. +.TP +\fB\-\-nslist \fIns,...\fR +list which namespaces will be considered for the --ns option. +Available namespaces: ipc, mnt, net, pid, user, uts. .PD .SH SIGNALS The behavior of signals is explained in diff --git a/skill.c b/skill.c index fb57305a..ab83a030 100644 --- a/skill.c +++ b/skill.c @@ -36,6 +36,7 @@ #include "c.h" #include "fileutils.h" +#include "nsutils.h" #include "strutils.h" #include "nls.h" #include "xalloc.h" @@ -43,6 +44,7 @@ #include "proc/sig.h" #include "proc/devname.h" #include "proc/procps.h" /* char *user_from_uid(uid_t uid) */ +#include "proc/readproc.h" #include "proc/version.h" /* procps_version */ #include "rpmatch.h" @@ -56,11 +58,14 @@ struct run_time_conf_t { int noaction; int debugging; }; -static int tty_count, uid_count, cmd_count, pid_count; +static int tty_count, uid_count, cmd_count, pid_count, namespace_count; static int *ttys; static uid_t *uids; static const char **cmds; static int *pids; +static char **namespaces; +static int ns_pid; +static proc_t ns_task; #define ENLIST(thing,addme) do{ \ if(!thing##s) thing##s = xmalloc(sizeof(*thing##s)*saved_argc); \ @@ -85,6 +90,39 @@ static void display_kill_version(void) fprintf(stdout, PROCPS_NG_VERSION); } +static int ns_flags = 0x3f; +static int parse_namespaces(char *optarg) +{ + char *ptr = optarg, *tmp; + int len, id; + + ns_flags = 0; + while (1) { + if (strchr(ptr, ',') == NULL) { + len = -1; + tmp = strdup(ptr); + } else { + len = strchr(ptr, ',') - ptr; + tmp = strndup(ptr, len); + } + + id = get_ns_id(tmp); + if (id == -1) { + fprintf(stderr, "%s is not a valid namespace\n", tmp); + free(tmp); + return 1; + } + ns_flags |= (1 << id); + ENLIST(namespace, tmp); + + if (len == -1) + break; + + ptr+= len + 1; + } + return 0; +} + /* kill or nice a process */ static void hurt_proc(int tty, int uid, int pid, const char *restrict const cmd, struct run_time_conf_t *run_time) @@ -131,6 +169,7 @@ static void check_proc(int pid, struct run_time_conf_t *run_time) { char buf[128]; struct stat statbuf; + proc_t task; char *tmp; int tty; int fd; @@ -183,6 +222,16 @@ static void check_proc(int pid, struct run_time_conf_t *run_time) if (i == -1) goto closure; } + if (ns_pid) { + if (ns_read(pid, &task)) + goto closure; + for (i = 0; i < NUM_NS; i++) { + if (ns_flags & (1 << i)) { + if (task.ns[i] != ns_task.ns[i]) + goto closure; + } + } + } /* This is where we kill/nice something. */ /* for debugging purposes? fprintf(stderr, "PID %d, UID %d, TTY %d,%d, COMM %s\n", @@ -317,6 +366,15 @@ static void __attribute__ ((__noreturn__)) skillsnice_usage(FILE * out) " -t, --tty expression is a terminal\n" " -u, --user expression is a username\n"), out); fputs(USAGE_SEPARATOR, out); + fputs(_("Alternatively, expression can be:\n" + " --ns match the processes that belong to the same\n" + " namespace as \n" + " --nslist list which namespaces will be considered for\n" + " the --ns option.\n" + " Available namespaces: ipc, mnt, net, pid, user, uts\n"), out); + + fputs(USAGE_SEPARATOR, out); + fputs(USAGE_SEPARATOR, out); fputs(USAGE_HELP, out); fputs(USAGE_VERSION, out); if (program == PROG_SKILL) { @@ -488,6 +546,11 @@ static void skillsnice_parse(int argc, int prino = DEFAULT_NICE; int ch, i; + enum { + NS_OPTION = CHAR_MAX + 1, + NSLIST_OPTION, + }; + static const struct option longopts[] = { {"command", required_argument, NULL, 'c'}, {"debug", no_argument, NULL, 'd'}, @@ -499,6 +562,8 @@ static void skillsnice_parse(int argc, {"table", no_argument, NULL, 'L'}, {"tty", required_argument, NULL, 't'}, {"user", required_argument, NULL, 'u'}, + {"ns", required_argument, NULL, NS_OPTION}, + {"nslist", required_argument, NULL, NSLIST_OPTION}, {"verbose", no_argument, NULL, 'v'}, {"warnings", no_argument, NULL, 'w'}, {"help", no_argument, NULL, 'h'}, @@ -572,6 +637,25 @@ static void skillsnice_parse(int argc, } } break; + case NS_OPTION: + ns_pid = atoi(optarg); + if (ns_pid == 0) { + xwarnx(_("invalid pid number %i"), optarg); + kill_usage(stderr); + } + if (ns_read(ns_pid, &ns_task)) { + xwarnx(_("error reading reference namespace " + "information")); + kill_usage(stderr); + } + + break; + case NSLIST_OPTION: + if (parse_namespaces(optarg)) { + xwarnx(_("invalid namespace list")); + kill_usage(stderr); + } + break; case 'v': run_time->verbose = 1; break; @@ -605,7 +689,7 @@ static void skillsnice_parse(int argc, } /* No more arguments to process. Must sanity check. */ - if (!tty_count && !uid_count && !cmd_count && !pid_count) + if (!tty_count && !uid_count && !cmd_count && !pid_count && !ns_pid) xerrx(EXIT_FAILURE, _("no process selection criteria")); if ((run_time->fast | run_time->interactive | run_time-> verbose | run_time->warnings | run_time->noaction) & ~1)