procps: add support for linux namespaces

Each process in Linux has a /proc/<pid>/ns directory which contains
symbolic links to pipes that identify which namespaces that process
belongs to. This patch adds support for ps to display that information
optionally.

Signed-off-by: Aristeu Rozanski <arozansk@redhat.com>
This commit is contained in:
Aristeu Rozanski 2013-04-08 15:03:13 -04:00
parent fc503b442d
commit a01ee3c0b3
5 changed files with 136 additions and 0 deletions

View File

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

View File

@ -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".
@ -758,6 +803,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;
@ -845,6 +891,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;
@ -863,6 +915,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;
@ -975,6 +1028,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:

View File

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

View File

@ -135,6 +135,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)
@ -212,6 +219,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;
@ -1170,6 +1184,22 @@ static int pr_sgi_p(char *restrict const outbuf, const proc_t *restrict const pp
}
/************************ 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
@ -1326,6 +1356,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 */
@ -1414,6 +1445,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},
@ -1440,9 +1472,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},
@ -1464,6 +1498,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},
@ -1565,10 +1600,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},
{"vm_data", "DATA", pr_nop, sr_vm_data, 5, 0, LNx, PO|RIGHT},
{"vm_exe", "EXE", pr_nop, sr_vm_exe, 5, 0, LNx, PO|RIGHT},
{"vm_lib", "LIB", pr_nop, sr_vm_lib, 5, 0, LNx, PO|RIGHT},

24
ps/ps.1
View File

@ -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
@ -1327,6 +1331,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
@ -1391,6 +1403,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 .
@ -1715,6 +1731,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}
vsize VSZ T{
see
.BR vsz .