Merge commit 'refs/merge-requests/13' of git://gitorious.org/procps/procps into merge-requests/13

Conflicts:
	pgrep.c
	ps/output.c
	ps/ps.1
This commit is contained in:
Craig Small 2013-09-11 21:34:05 +10:00
commit dd6f24dbed
12 changed files with 350 additions and 10 deletions

View File

@ -55,7 +55,7 @@ EXTRA_DIST = \
if BUILD_KILL if BUILD_KILL
bin_PROGRAMS = kill bin_PROGRAMS = kill
dist_man_MANS += kill.1 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 else
EXTRA_DIST += kill.1 EXTRA_DIST += kill.1
endif endif
@ -77,8 +77,8 @@ if BUILD_SKILL
usrbin_exec_PROGRAMS += \ usrbin_exec_PROGRAMS += \
skill \ skill \
snice snice
skill_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 snice_SOURCES = skill.c $(top_srcdir)/lib/strutils.c $(top_srcdir)/lib/fileutils.c $(top_srcdir)/lib/nsutils.c
dist_man_MANS += \ dist_man_MANS += \
skill.1 \ skill.1 \
snice.1 snice.1
@ -89,8 +89,8 @@ else
endif endif
free_SOURCES = free.c $(top_srcdir)/lib/strutils.c $(top_srcdir)/lib/fileutils.c free_SOURCES = free.c $(top_srcdir)/lib/strutils.c $(top_srcdir)/lib/fileutils.c
pgrep_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 pkill_SOURCES = pgrep.c $(top_srcdir)/lib/fileutils.c $(top_srcdir)/lib/nsutils.c
pmap_SOURCES = pmap.c $(top_srcdir)/lib/fileutils.c pmap_SOURCES = pmap.c $(top_srcdir)/lib/fileutils.c
pwdx_SOURCES = pwdx.c $(top_srcdir)/lib/fileutils.c pwdx_SOURCES = pwdx.c $(top_srcdir)/lib/fileutils.c
sysctl_SOURCES = sysctl.c $(top_srcdir)/lib/fileutils.c sysctl_SOURCES = sysctl.c $(top_srcdir)/lib/fileutils.c

7
include/nsutils.h Normal file
View File

@ -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

32
lib/nsutils.c Normal file
View File

@ -0,0 +1,32 @@
#include <errno.h>
#include <error.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#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;
}

View File

@ -146,6 +146,15 @@ than
\fB\-L\fR, \fB\-\-logpidfile\fR \fB\-L\fR, \fB\-\-logpidfile\fR
Fail if pidfile (see -F) not locked. Fail if pidfile (see -F) not locked.
.TP .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 \fB\-V\fR, \fB\-\-version\fR
Display version information and exit. Display version information and exit.
.TP .TP

69
pgrep.c
View File

@ -46,6 +46,7 @@
#include "c.h" #include "c.h"
#include "fileutils.h" #include "fileutils.h"
#include "nsutils.h"
#include "nls.h" #include "nls.h"
#include "xalloc.h" #include "xalloc.h"
#include "proc/readproc.h" #include "proc/readproc.h"
@ -76,6 +77,7 @@ static int opt_lock = 0;
static int opt_case = 0; static int opt_case = 0;
static int opt_echo = 0; static int opt_echo = 0;
static int opt_threads = 0; static int opt_threads = 0;
static pid_t opt_ns_pid = 0;
static const char *opt_delim = "\n"; static const char *opt_delim = "\n";
static struct el *opt_pgrp = NULL; 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_term = NULL;
static struct el *opt_euid = NULL; static struct el *opt_euid = NULL;
static struct el *opt_ruid = NULL; static struct el *opt_ruid = NULL;
static struct el *opt_nslist = NULL;
static char *opt_pattern = NULL; static char *opt_pattern = NULL;
static char *opt_pidfile = 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) static int __attribute__ ((__noreturn__)) usage(int opt)
{ {
int err = (opt == '?'); int err = (opt == '?');
@ -121,7 +127,12 @@ static int __attribute__ ((__noreturn__)) usage(int opt)
" -U, --uid <id,...> match by real IDs\n" " -U, --uid <id,...> match by real IDs\n"
" -x, --exact match exactly with the command name\n" " -x, --exact match exactly with the command name\n"
" -F, --pidfile <file> read PIDs from file\n" " -F, --pidfile <file> 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 <pid> match the processes that belong to the same\n"
" namespace as <pid>\n"
" --nslist <ns,...> 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_SEPARATOR, fp);
fputs(USAGE_HELP, fp); fputs(USAGE_HELP, fp);
fputs(USAGE_VERSION, 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) static int match_numlist (long value, const struct el *restrict list)
{ {
int found = 0; int found = 0;
@ -350,6 +375,21 @@ static int match_strlist (const char *restrict value, const struct el *restrict
return found; 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) static void output_numlist (const struct el *restrict list, int num)
{ {
int i; int i;
@ -386,6 +426,8 @@ static PROCTAB *do_openproc (void)
flags |= PROC_FILLSTAT; flags |= PROC_FILLSTAT;
if (!(flags & PROC_FILLSTAT)) if (!(flags & PROC_FILLSTAT))
flags |= PROC_FILLSTATUS; /* FIXME: need one, and PROC_FILLANY broken */ flags |= PROC_FILLSTATUS; /* FIXME: need one, and PROC_FILLANY broken */
if (opt_ns_pid)
flags |= PROC_FILLNS;
if (opt_euid && !opt_negate) { if (opt_euid && !opt_negate) {
int num = opt_euid[0].num; int num = opt_euid[0].num;
int i = num; int i = num;
@ -442,6 +484,7 @@ static struct el * select_procs (int *num)
char cmdline[CMDSTRSIZE]; char cmdline[CMDSTRSIZE];
char cmdsearch[CMDSTRSIZE]; char cmdsearch[CMDSTRSIZE];
char cmdoutput[CMDSTRSIZE]; char cmdoutput[CMDSTRSIZE];
proc_t ns_task;
ptp = do_openproc(); ptp = do_openproc();
preg = do_regcomp(); preg = do_regcomp();
@ -451,6 +494,11 @@ static struct el * select_procs (int *num)
if (opt_newest) saved_pid = 0; if (opt_newest) saved_pid = 0;
if (opt_oldest) saved_pid = INT_MAX; 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)); memset(&task, 0, sizeof (task));
while(readproc(ptp, &task)) { while(readproc(ptp, &task)) {
@ -476,6 +524,8 @@ static struct el * select_procs (int *num)
match = 0; match = 0;
else if (opt_sid && ! match_numlist (task.session, opt_sid)) else if (opt_sid && ! match_numlist (task.session, opt_sid))
match = 0; match = 0;
else if (opt_ns_pid && ! match_ns (&task, &ns_task))
match = 0;
else if (opt_term) { else if (opt_term) {
if (task.tty == 0) { if (task.tty == 0) {
match = 0; match = 0;
@ -622,7 +672,9 @@ static void parse_opts (int argc, char **argv)
int criteria_count = 0; int criteria_count = 0;
enum { enum {
SIGNAL_OPTION = CHAR_MAX + 1 SIGNAL_OPTION = CHAR_MAX + 1,
NS_OPTION,
NSLIST_OPTION,
}; };
static const struct option longopts[] = { static const struct option longopts[] = {
{"signal", required_argument, NULL, SIGNAL_OPTION}, {"signal", required_argument, NULL, SIGNAL_OPTION},
@ -646,6 +698,8 @@ static void parse_opts (int argc, char **argv)
{"pidfile", required_argument, NULL, 'F'}, {"pidfile", required_argument, NULL, 'F'},
{"logpidfile", no_argument, NULL, 'L'}, {"logpidfile", no_argument, NULL, 'L'},
{"echo", no_argument, NULL, 'e'}, {"echo", no_argument, NULL, 'e'},
{"ns", required_argument, NULL, NS_OPTION},
{"nslist", required_argument, NULL, NSLIST_OPTION},
{"help", no_argument, NULL, 'h'}, {"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'}, {"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0} {NULL, 0, NULL, 0}
@ -792,6 +846,17 @@ static void parse_opts (int argc, char **argv)
break; break;
/* case 'z': / * Solaris: match by zone ID * / /* case 'z': / * Solaris: match by zone ID * /
* break; */ * 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': case 'h':
usage (opt); usage (opt);
break; break;

View File

@ -11,6 +11,8 @@ global:
escaped_copy; escaped_copy;
free_slabinfo; free_slabinfo;
freeproc; freeproc;
get_ns_id;
get_ns_name;
get_pid_digits; get_pid_digits;
get_slabinfo; get_slabinfo;
getbtime; getbtime;

View File

@ -457,6 +457,51 @@ static void oomadj2proc(const char* S, proc_t *restrict P)
#endif #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 // Reads /proc/*/stat files, being careful not to trip over processes with
// names like ":-) 1 2 3 4 5 6". // 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 static struct stat sb; // stat() buffer
char *restrict const path = PT->path; char *restrict const path = PT->path;
unsigned flags = PT->flags; unsigned flags = PT->flags;
int i;
if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */ if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
goto next_proc; goto next_proc;
@ -844,6 +890,12 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons
} }
#endif #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; return p;
next_proc: next_proc:
return NULL; 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 utlbuf_s ub = { NULL, 0 }; // buf for stat,statm,status
static struct stat sb; // stat() buffer static struct stat sb; // stat() buffer
unsigned flags = PT->flags; unsigned flags = PT->flags;
int i;
if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */ if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
goto next_task; goto next_task;
@ -974,6 +1027,11 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restric
oomadj2proc(ub.buf, t); oomadj2proc(ub.buf, t);
} }
#endif #endif
if (unlikely(flags & PROC_FILLNS))
ns2proc(t);
else
for (i = 0; i < NUM_NS; i++)
t->ns[i] = 0;
return t; return t;
next_task: next_task:

View File

@ -31,6 +31,18 @@ EXTERN_C_BEGIN
// neither tgid nor tid seemed correct. (in other words, FIXME) // neither tgid nor tid seemed correct. (in other words, FIXME)
#define XXXID tid #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. // Basic data structure which holds all information we can get about a process.
// (unless otherwise specified, fields are read from /proc/#/stat) // (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_score, // oom_score (badness for OOM killer)
oom_adj; // oom_adj (adjustment to OOM score) oom_adj; // oom_adj (adjustment to OOM score)
#endif #endif
ino_t
ns[NUM_NS]; // ns/* inode number of /proc/<pid>/ns/*
} proc_t; } proc_t;
// PROCTAB: data structure holding the persistent information readproc needs // 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_FILLCGROUP 0x0200 // alloc and fill in `cgroup`
#define PROC_FILLSUPGRP 0x0400 // resolve supplementary group id -> group name #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_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 #define PROC_LOOSE_TASKS 0x2000 // treat threads as if they were processes

View File

@ -139,6 +139,13 @@ static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
return 0; \ 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_INT(rtprio)
CMP_SMALL(sched) CMP_SMALL(sched)
CMP_INT(cutime) CMP_INT(cutime)
@ -216,6 +223,13 @@ CMP_SMALL(state)
CMP_COOKED_TIME(time) CMP_COOKED_TIME(time)
CMP_COOKED_TIME(etime) 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 */ /* approximation to: kB of address space that could end up in swap */
static int sr_swapable(const proc_t* P, const proc_t* Q) { static int sr_swapable(const proc_t* P, const proc_t* Q) {
unsigned long p_swapable = P->vm_data + P->vm_stack; unsigned long p_swapable = P->vm_data + P->vm_stack;
@ -1279,6 +1293,22 @@ fail:
} }
#endif #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 **********************/ /****************** FLASK & seLinux security stuff **********************/
// move the bulk of this to libproc sometime // 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 USR PROC_FILLUSR /* uid_t -> user names */
#define GRP PROC_FILLGRP /* gid_t -> group names */ #define GRP PROC_FILLGRP /* gid_t -> group names */
#define WCH PROC_FILLWCHAN /* do WCHAN lookup */ #define WCH PROC_FILLWCHAN /* do WCHAN lookup */
#define NS PROC_FILLNS /* read namespace information */
#define SGRP PROC_FILLSTATUS | PROC_FILLSUPGRP /* supgid -> supgrp (names) */ #define SGRP PROC_FILLSTATUS | PROC_FILLSUPGRP /* supgid -> supgrp (names) */
#define CGRP PROC_FILLCGROUP | PROC_EDITCGRPCVT /* read cgroup */ #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*/ {"inblk", "INBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*inblock*/
{"inblock", "INBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*inblk*/ {"inblock", "INBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*inblk*/
{"intpri", "PRI", pr_opri, sr_priority, 3, 0, HPU, TO|RIGHT}, {"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}, {"jid", "JID", pr_nop, sr_nop, 1, 0, SGI, PO|RIGHT},
{"jobc", "JOBC", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT}, {"jobc", "JOBC", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
{"ktrace", "KTRACE", pr_nop, sr_nop, 8, 0, BSD, 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}, {"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}, {"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}, {"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}, {"msgrcv", "MSGRCV", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT},
{"msgsnd", "MSGSND", 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) */ {"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*/ {"ni", "NI", pr_nice, sr_nice, 3, 0, BSD, TO|RIGHT}, /*nice*/
{"nice", "NI", pr_nice, sr_nice, 3, 0, U98, TO|RIGHT}, /*ni*/ {"nice", "NI", pr_nice, sr_nice, 3, 0, U98, TO|RIGHT}, /*ni*/
{"nivcsw", "IVCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, {"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}, {"pgid", "PGID", pr_pgid, sr_pgrp, 5, 0, U98, PO|PIDMAX|RIGHT},
{"pgrp", "PGRP", pr_pgid, sr_pgrp, 5, 0, LNX, 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}, {"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*/ {"pmem", "%MEM", pr_pmem, sr_rss, 4, 0, XXX, PO|RIGHT}, /*%mem*/
{"poip", "-", pr_nop, sr_nop, 1, 0, BSD, AN|RIGHT}, {"poip", "-", pr_nop, sr_nop, 1, 0, BSD, AN|RIGHT},
{"policy", "POL", pr_class, sr_sched, 3, 0, DEC, TO|LEFT}, {"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*/ {"upr", "UPR", pr_nop, sr_nop, 3, 0, BSD, TO|RIGHT}, /*usrpri*/
{"uprocp", "UPROCP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, {"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 */ {"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}, {"usertime", "USER", pr_nop, sr_nop, 4, 0, DEC, ET|RIGHT},
{"usrpri", "UPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT}, /*upr*/ {"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" {"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}, {"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 #ifdef WITH_SYSTEMD
{"uunit", "UUNIT", pr_sd_uunit, sr_nop, 31, 0, LNX, ET|LEFT}, {"uunit", "UUNIT", pr_sd_uunit, sr_nop, 31, 0, LNX, ET|LEFT},
#endif #endif

24
ps/ps.1
View File

@ -1299,6 +1299,10 @@ format is displayed. (alias
.BR sig_ignore , \ sigignore ). .BR sig_ignore , \ sigignore ).
T} T}
ipcns IPCNS T{
Unique inode number describing the namespace the process belongs to. See namespaces(7).
T}
label LABEL T{ label LABEL T{
security label, most commonly used for SELinux context data. This is for security label, most commonly used for SELinux context data. This is for
the the
@ -1335,6 +1339,14 @@ min_flt MINFLT T{
The number of minor page faults that have occurred with this process. The number of minor page faults that have occurred with this process.
T} 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{ ni NI T{
nice value. This ranges from 19 (nicest) to \-20 (not nice to others), nice value. This ranges from 19 (nicest) to \-20 (not nice to others),
see see
@ -1403,6 +1415,10 @@ a number representing the process ID (alias
.BR tgid ). .BR tgid ).
T} T}
pidns PIDNS T{
Unique inode number describing the namespace the process belongs to. See namespaces(7).
T}
pmem %MEM T{ pmem %MEM T{
see see
.BR %mem . .BR %mem .
@ -1735,6 +1751,14 @@ see
.BR euser , \ uname ). .BR euser , \ uname ).
T} 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{ uunit UUNIT T{
displays systemd user unit which a process belongs to. displays systemd user unit which a process belongs to.
T} T}

View File

@ -77,6 +77,13 @@ The next expression is a process ID number.
.TP .TP
\fB\-c\fR, \fB\-\-command\fR \fIcommand\fR \fB\-c\fR, \fB\-\-command\fR \fIcommand\fR
The next expression is a command name. 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 .PD
.SH SIGNALS .SH SIGNALS
The behavior of signals is explained in The behavior of signals is explained in

88
skill.c
View File

@ -36,6 +36,7 @@
#include "c.h" #include "c.h"
#include "fileutils.h" #include "fileutils.h"
#include "nsutils.h"
#include "strutils.h" #include "strutils.h"
#include "nls.h" #include "nls.h"
#include "xalloc.h" #include "xalloc.h"
@ -43,6 +44,7 @@
#include "proc/sig.h" #include "proc/sig.h"
#include "proc/devname.h" #include "proc/devname.h"
#include "proc/procps.h" /* char *user_from_uid(uid_t uid) */ #include "proc/procps.h" /* char *user_from_uid(uid_t uid) */
#include "proc/readproc.h"
#include "proc/version.h" /* procps_version */ #include "proc/version.h" /* procps_version */
#include "rpmatch.h" #include "rpmatch.h"
@ -56,11 +58,14 @@ struct run_time_conf_t {
int noaction; int noaction;
int debugging; 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 int *ttys;
static uid_t *uids; static uid_t *uids;
static const char **cmds; static const char **cmds;
static int *pids; static int *pids;
static char **namespaces;
static int ns_pid;
static proc_t ns_task;
#define ENLIST(thing,addme) do{ \ #define ENLIST(thing,addme) do{ \
if(!thing##s) thing##s = xmalloc(sizeof(*thing##s)*saved_argc); \ 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); 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 */ /* kill or nice a process */
static void hurt_proc(int tty, int uid, int pid, const char *restrict const cmd, static void hurt_proc(int tty, int uid, int pid, const char *restrict const cmd,
struct run_time_conf_t *run_time) 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]; char buf[128];
struct stat statbuf; struct stat statbuf;
proc_t task;
char *tmp; char *tmp;
int tty; int tty;
int fd; int fd;
@ -183,6 +222,16 @@ static void check_proc(int pid, struct run_time_conf_t *run_time)
if (i == -1) if (i == -1)
goto closure; 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. */ /* This is where we kill/nice something. */
/* for debugging purposes? /* for debugging purposes?
fprintf(stderr, "PID %d, UID %d, TTY %d,%d, COMM %s\n", 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 <tty> expression is a terminal\n" " -t, --tty <tty> expression is a terminal\n"
" -u, --user <username> expression is a username\n"), out); " -u, --user <username> expression is a username\n"), out);
fputs(USAGE_SEPARATOR, out); fputs(USAGE_SEPARATOR, out);
fputs(_("Alternatively, expression can be:\n"
" --ns <pid> match the processes that belong to the same\n"
" namespace as <pid>\n"
" --nslist <ns,...> 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_HELP, out);
fputs(USAGE_VERSION, out); fputs(USAGE_VERSION, out);
if (program == PROG_SKILL) { if (program == PROG_SKILL) {
@ -488,6 +546,11 @@ static void skillsnice_parse(int argc,
int prino = DEFAULT_NICE; int prino = DEFAULT_NICE;
int ch, i; int ch, i;
enum {
NS_OPTION = CHAR_MAX + 1,
NSLIST_OPTION,
};
static const struct option longopts[] = { static const struct option longopts[] = {
{"command", required_argument, NULL, 'c'}, {"command", required_argument, NULL, 'c'},
{"debug", no_argument, NULL, 'd'}, {"debug", no_argument, NULL, 'd'},
@ -499,6 +562,8 @@ static void skillsnice_parse(int argc,
{"table", no_argument, NULL, 'L'}, {"table", no_argument, NULL, 'L'},
{"tty", required_argument, NULL, 't'}, {"tty", required_argument, NULL, 't'},
{"user", required_argument, NULL, 'u'}, {"user", required_argument, NULL, 'u'},
{"ns", required_argument, NULL, NS_OPTION},
{"nslist", required_argument, NULL, NSLIST_OPTION},
{"verbose", no_argument, NULL, 'v'}, {"verbose", no_argument, NULL, 'v'},
{"warnings", no_argument, NULL, 'w'}, {"warnings", no_argument, NULL, 'w'},
{"help", no_argument, NULL, 'h'}, {"help", no_argument, NULL, 'h'},
@ -572,6 +637,25 @@ static void skillsnice_parse(int argc,
} }
} }
break; 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': case 'v':
run_time->verbose = 1; run_time->verbose = 1;
break; break;
@ -605,7 +689,7 @@ static void skillsnice_parse(int argc,
} }
/* No more arguments to process. Must sanity check. */ /* 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")); xerrx(EXIT_FAILURE, _("no process selection criteria"));
if ((run_time->fast | run_time->interactive | run_time-> if ((run_time->fast | run_time->interactive | run_time->
verbose | run_time->warnings | run_time->noaction) & ~1) verbose | run_time->warnings | run_time->noaction) & ~1)