From: Craig Small Date: Thu, 3 Sep 2015 12:32:19 +0000 (+1000) Subject: library: rework namespace calls X-Git-Tag: v4.0.0~1052 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a61f78d6e03551616718332719da9de266a504e7;p=procps-ng library: rework namespace calls Functions related to namespaces were half-in half-out of the procps library and didn't fit the standard naming scheme. While struct { long ns[x]} is a bit clunky, its the only way to "lock in" x. The alternative is to use ns_* variables. This work was needed before pgrep could be converted. --- diff --git a/Makefile.am b/Makefile.am index b88ba074..1efa3327 100644 --- a/Makefile.am +++ b/Makefile.am @@ -78,7 +78,7 @@ endif if BUILD_KILL bin_PROGRAMS += kill dist_man_MANS += kill.1 -kill_SOURCES = skill.c lib/strutils.c lib/fileutils.c lib/nsutils.c lib/signals.c +kill_SOURCES = skill.c lib/strutils.c lib/fileutils.c lib/signals.c else EXTRA_DIST += kill.1 endif @@ -110,8 +110,8 @@ if BUILD_SKILL bin_PROGRAMS += \ skill \ snice -skill_SOURCES = skill.c lib/strutils.c lib/fileutils.c lib/nsutils.c lib/signals.c -snice_SOURCES = skill.c lib/strutils.c lib/fileutils.c lib/nsutils.c lib/signals.c +skill_SOURCES = skill.c lib/strutils.c lib/fileutils.c lib/signals.c +snice_SOURCES = skill.c lib/strutils.c lib/fileutils.c lib/signals.c dist_man_MANS += \ skill.1 \ snice.1 @@ -122,8 +122,8 @@ else endif free_SOURCES = free.c lib/strutils.c lib/fileutils.c -pgrep_SOURCES = pgrep.c lib/fileutils.c lib/nsutils.c lib/signals.c -pkill_SOURCES = pgrep.c lib/fileutils.c lib/nsutils.c lib/signals.c +pgrep_SOURCES = pgrep.c lib/fileutils.c lib/signals.c +pkill_SOURCES = pgrep.c lib/fileutils.c lib/signals.c pmap_SOURCES = pmap.c lib/fileutils.c pwdx_SOURCES = pwdx.c lib/fileutils.c pwdx_LDADD= @@ -164,6 +164,8 @@ proc_libprocps_la_SOURCES = \ proc/procps-private.h \ proc/meminfo.c \ proc/meminfo.h \ + proc/namespace.c \ + proc/namespace.h \ proc/pids.c \ proc/pids.h \ proc/procps.h \ @@ -233,28 +235,29 @@ ps_pscommand_SOURCES = \ noinst_PROGRAMS = \ lib/test_strutils \ lib/test_fileutils \ - lib/test_nsutils \ lib/test_process \ - proc/test_sysinfo + proc/test_sysinfo \ + proc/test_namespace lib_test_strutils_SOURCES = lib/test_strutils.c lib/strutils.c lib_test_strutils_LDADD = lib_test_fileutils_SOURCES = lib/test_fileutils.c lib/fileutils.c lib_test_fileutils_LDADD = -lib_test_nsutils_SOURCES = lib/test_nsutils.c lib/nsutils.c -lib_test_nsutils_LDADD = lib_test_process_SOURCES = lib/test_process.c lib_test_process_LDADD = proc_test_sysinfo_SOURCES = proc/test_sysinfo.c proc_test_sysinfo_LDADD = proc/libprocps.la +proc_test_namespace_SOURCES = proc/test_namespace.c +proc_test_namespace_LDADD = proc/libprocps.la if EXAMPLE_FILES sysconf_DATA = sysctl.conf endif BUILT_SOURCES = $(top_srcdir)/.version -TESTS = proc/test_sysinfo +TESTS = proc/test_sysinfo \ + proc/test_namespace $(top_srcdir)/.version: touch $(top_srcdir)/.version diff --git a/include/Makefile.am b/include/Makefile.am index 752557da..f2b6f1d3 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -2,7 +2,6 @@ dist_noinst_HEADERS = \ c.h \ fileutils.h \ nls.h \ - nsutils.h \ rpmatch.h \ signals.h \ strutils.h \ diff --git a/include/nsutils.h b/include/nsutils.h deleted file mode 100644 index 3235851e..00000000 --- a/include/nsutils.h +++ /dev/null @@ -1,7 +0,0 @@ -#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 deleted file mode 100644 index 673258ec..00000000 --- a/lib/nsutils.c +++ /dev/null @@ -1,30 +0,0 @@ -#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/lib/test_nsutils.c b/lib/test_nsutils.c deleted file mode 100644 index d8f3d2cf..00000000 --- a/lib/test_nsutils.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include - -#include "nsutils.h" - -const char *get_ns_name(int id) -{ - return NULL; -} - -int main(int argc, char *argv[]) -{ - printf("Hello, World!\n"); - return EXIT_SUCCESS; -} diff --git a/pgrep.c b/pgrep.c index 8dec30bf..b11ab075 100644 --- a/pgrep.c +++ b/pgrep.c @@ -46,13 +46,13 @@ #include "c.h" #include "fileutils.h" -#include "nsutils.h" #include "nls.h" #include "signals.h" #include "xalloc.h" #include "proc/readproc.h" #include "proc/devname.h" #include "proc/sysinfo.h" +#include static int i_am_pkill = 0; @@ -343,7 +343,7 @@ static int conv_ns (const char *restrict name, struct el *restrict e) int id; ns_flags = 0; - id = get_ns_id(name); + id = procps_ns_get_id(name); if (id == -1) return 0; ns_flags |= (1 << id); @@ -381,14 +381,15 @@ 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) +static int match_ns (const proc_t *task, + const struct procps_namespaces *namespaces) { 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]) + if (task->ns.ns[i] != namespaces->ns[i]) found = 0; } } @@ -493,7 +494,7 @@ static struct el * select_procs (int *num) char cmdline[CMDSTRSIZE]; char cmdsearch[CMDSTRSIZE]; char cmdoutput[CMDSTRSIZE]; - proc_t ns_task; + struct procps_namespaces namespaces; ptp = do_openproc(); preg = do_regcomp(); @@ -503,7 +504,7 @@ 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)) { + if (opt_ns_pid && procps_ns_read_pid(opt_ns_pid, &namespaces) < 0) { fputs(_("Error reading reference namespace information\n"), stderr); exit (EXIT_FATAL); @@ -533,7 +534,7 @@ 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)) + else if (opt_ns_pid && ! match_ns (&task, &namespaces)) match = 0; else if (opt_term) { if (task.tty == 0) { diff --git a/proc/.gitignore b/proc/.gitignore index 564a97d6..18247fc6 100644 --- a/proc/.gitignore +++ b/proc/.gitignore @@ -1 +1,2 @@ test_sysinfo +test_namespace diff --git a/proc/libprocps.sym b/proc/libprocps.sym index 6eba3dbd..c32258dd 100644 --- a/proc/libprocps.sym +++ b/proc/libprocps.sym @@ -7,8 +7,6 @@ global: escape_strlist; escaped_copy; freeproc; - get_ns_id; - get_ns_name; get_pid_digits; look_up_our_self; lookup_wchan; @@ -41,6 +39,9 @@ global: procps_meminfo_getstack; procps_meminfo_stack_fill; procps_meminfo_stack_alloc; + procps_ns_get_name; + procps_ns_get_id; + procps_ns_read_pid; procps_pids_new; procps_pids_read_next; procps_pids_read_open; diff --git a/proc/namespace.c b/proc/namespace.c new file mode 100644 index 00000000..68f9a9e0 --- /dev/null +++ b/proc/namespace.c @@ -0,0 +1,110 @@ +/* + * libprocps - Library to read proc filesystem + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include +#include "proc/procps-private.h" + +#define NSPATHLEN 64 + +static const char *ns_names[] = { + [PROCPS_NS_IPC] = "ipc", + [PROCPS_NS_MNT] = "mnt", + [PROCPS_NS_NET] = "net", + [PROCPS_NS_PID] = "pid", + [PROCPS_NS_USER] = "user", + [PROCPS_NS_UTS] = "uts", +}; + + +/* + * procps_ns_get_name: + * + * Find the name of the namespace with the given ID + * + * @id: The ID of the required namespace, see + * namespace_type + * + * Returns: static string of the namespace + */ +PROCPS_EXPORT const char *procps_ns_get_name(const int id) +{ + if (id >= PROCPS_NS_COUNT || id < 0) + return NULL; + return ns_names[id]; +} + +/* + * procps_ns_get_id: + * + * Find the namespace ID that matches the given + * name. + * + * @name: the name of the required namespace + * + * Returns: ID of found name + * < 0 means error + */ +PROCPS_EXPORT int procps_ns_get_id(const char *name) +{ + int i; + + if (name == NULL) + return -EINVAL; + for (i=0; i < PROCPS_NS_COUNT; i++) + if (!strcmp(ns_names[i], name)) + return i; + return -EINVAL; +} + +/* + * procs_ns_read_pid: + * + * Find all namespaces for the given process. + * @pid: Process ID for required process + * @nsp: Pointer to the struct procps_namespaces + * + * Returns: + * 0 on success + * < 0 on error + */ +PROCPS_EXPORT int procps_ns_read_pid( + const int pid, + struct procps_namespaces *nsp) +{ + char path[NSPATHLEN+1]; + struct stat st; + int i; + + if (nsp == NULL) + return -EINVAL; + if (pid < 1) + return -EINVAL; + + for (i=0; i < PROCPS_NS_COUNT; i++) { + snprintf(path, NSPATHLEN, "/proc/%d/ns/%s", pid, ns_names[i]); + if (0 == stat(path, &st)) + nsp->ns[i] = (long)st.st_ino; + else + nsp->ns[i] = 0; + } +} diff --git a/proc/namespace.h b/proc/namespace.h new file mode 100644 index 00000000..69254a80 --- /dev/null +++ b/proc/namespace.h @@ -0,0 +1,51 @@ +/* + * libprocps - Library to read proc filesystem + * + * Copyright (C) 1996 Charles Blake + * Copyright (C) 1998 Michael K. Johnson + * Copyright (C) 1998-2003 Albert Cahalan + * Copyright (C) 2015 Craig Small + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef PROC_NAMESPACE_H +#define PROC_NAMESPACE_H + +#include + +__BEGIN_DECLS + +enum namespace_type { + PROCPS_NS_IPC, + PROCPS_NS_MNT, + PROCPS_NS_NET, + PROCPS_NS_PID, + PROCPS_NS_USER, + PROCPS_NS_UTS, + PROCPS_NS_COUNT // total namespaces (fencepost) +}; + +struct procps_namespaces { + long ns[PROCPS_NS_COUNT]; +}; + +const char *procps_ns_get_name(const int id); + +int procps_ns_get_id(const char *name); + +int procps_ns_read_pid(const int pid, struct procps_namespaces *nsp); +__END_DECLS + +#endif diff --git a/proc/pids.c b/proc/pids.c index 592b3414..54432ccc 100644 --- a/proc/pids.c +++ b/proc/pids.c @@ -172,12 +172,12 @@ REG_set(MEM_VIRT, sl_int, size) CVT_set(MEM_VIRT_KIB, ul_int, size) REG_set(NICE, sl_int, nice) REG_set(NLWP, s_int, nlwp) -REG_set(NS_IPC, ul_int, ns[0]) -REG_set(NS_MNT, ul_int, ns[1]) -REG_set(NS_NET, ul_int, ns[2]) -REG_set(NS_PID, ul_int, ns[3]) -REG_set(NS_USER, ul_int, ns[4]) -REG_set(NS_UTS, ul_int, ns[5]) +REG_set(NS_IPC, ul_int, ns.ns[0]) +REG_set(NS_MNT, ul_int, ns.ns[1]) +REG_set(NS_NET, ul_int, ns.ns[2]) +REG_set(NS_PID, ul_int, ns.ns[3]) +REG_set(NS_USER, ul_int, ns.ns[4]) +REG_set(NS_UTS, ul_int, ns.ns[5]) REG_set(OOM_ADJ, s_int, oom_adj) REG_set(OOM_SCORE, s_int, oom_score) REG_set(PRIORITY, s_int, priority) diff --git a/proc/readproc.c b/proc/readproc.c index 061dde12..7784ad93 100644 --- a/proc/readproc.c +++ b/proc/readproc.c @@ -40,6 +40,7 @@ #ifdef WITH_SYSTEMD #include #endif +#include // sometimes it's easier to do this manually, w/o gcc helping #ifdef PROF @@ -469,46 +470,6 @@ static void oomadj2proc(const char* S, proc_t *restrict P) } /////////////////////////////////////////////////////////////////////// -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(const char *directory, proc_t *restrict p) { - char path[PROCPATHLEN]; - struct stat sb; - int i; - - for (i = 0; i < NUM_NS; i++) { - snprintf(path, sizeof(path), "%s/ns/%s", directory, ns_names[i]); - if (0 == stat(path, &sb)) - p->ns[i] = (long)sb.st_ino; -#if 0 - else // this allows a caller to distinguish - p->ns[i] = -errno; // between the ENOENT or EACCES errors -#endif - } -} - #ifdef WITH_SYSTEMD static void sd2proc(proc_t *restrict p) { char buf[64]; @@ -991,7 +952,8 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons } if (unlikely(flags & PROC_FILLNS)) // read /proc/#/ns/* - ns2proc(path, p); + procps_ns_read_pid(p->tid, &(p->ns)); + #ifdef WITH_SYSTEMD if (unlikely(flags & PROC_FILLSYSTEMD)) // get sd-login.h stuff @@ -1148,7 +1110,7 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restric } if (unlikely(flags & PROC_FILLNS)) // read /proc/#/task/#/ns/* - ns2proc(path, t); + procps_ns_read_pid(t->tid, &(t->ns)); return t; next_task: diff --git a/proc/readproc.h b/proc/readproc.h index 26d41242..8d69a991 100644 --- a/proc/readproc.h +++ b/proc/readproc.h @@ -12,6 +12,7 @@ #include #include +#include #define SIGNAL_STRING //#define QUICK_THREADS /* copy (vs. read) some thread info from parent proc_t */ @@ -163,8 +164,7 @@ typedef struct proc_t { int oom_score, // oom_score (badness for OOM killer) oom_adj; // oom_adj (adjustment to OOM score) - long - ns[NUM_NS]; // (ns subdir) inode number of namespaces + struct procps_namespaces ns; // (ns subdir) inode number of namespaces char *sd_mach, // n/a systemd vm/container name *sd_ouid, // n/a systemd session owner uid diff --git a/proc/test_namespace.c b/proc/test_namespace.c new file mode 100644 index 00000000..88d80247 --- /dev/null +++ b/proc/test_namespace.c @@ -0,0 +1,87 @@ +/* + * libprocps - Library to read proc filesystem + * Tests for namespace library calls + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include + +#include + +struct test_func { + int (*func)(void *data); + char *name; +}; + +int check_name_minus(void *data) +{ + return (procps_ns_get_name(-1) == NULL); +} + +int check_name_over(void *data) +{ + return (procps_ns_get_name(999) == NULL); +} + +int check_name_ipc(void *data) +{ + return (strcmp(procps_ns_get_name(PROCPS_NS_IPC),"ipc")==0); +} + +int check_id_null(void *data) +{ + return (procps_ns_get_id(NULL) < 0); +} + +int check_id_unfound(void *data) +{ + return (procps_ns_get_id("foobar") < 0); +} + +int check_id_mnt(void *data) +{ + return (procps_ns_get_id("mnt") == PROCPS_NS_MNT); +} + +struct test_func tests[] = { + { check_name_minus, "procps_ns_get_name() negative id"}, + { check_name_over, "procps_ns_get_name() id over limit"}, + { check_name_ipc, "procps_ns_get_name() ipc"}, + { check_id_null, "procps_ns_get_id(NULL)"}, + { check_id_unfound, "procps_ns_get_id(unknown)"}, + { check_id_mnt, "procps_ns_get_id(mnt)"}, + { NULL, NULL} +}; + +int main(int argc, char *argv[]) +{ + int i; + struct test_func *current; + + for(i=0; tests[i].func != NULL; i++) { + current = &tests[i]; + if (!current->func(NULL)) { + fprintf(stderr, "FAIL: %s\n", current->name); + return EXIT_FAILURE; + } else { + fprintf(stderr, "PASS: %s\n", current->name); + } + } + return EXIT_SUCCESS; +} + + diff --git a/ps/output.c b/ps/output.c index 48719f3b..f2eb44b4 100644 --- a/ps/output.c +++ b/ps/output.c @@ -171,8 +171,8 @@ static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \ #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; \ + if (P->ns.ns[ID] < Q->ns.ns[ID]) return -1; \ + if (P->ns.ns[ID] > Q->ns.ns[ID]) return 1; \ return 0; \ } @@ -1253,8 +1253,8 @@ static int pr_sd_slice(char *restrict const outbuf, const proc_t *restrict const #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]);\ + if (pp->ns.ns[ID])\ + return snprintf(outbuf, COLWID, "%li", pp->ns.ns[ID]);\ else\ return snprintf(outbuf, COLWID, "-");\ } diff --git a/skill.c b/skill.c index 8c6e8775..fcd5c54b 100644 --- a/skill.c +++ b/skill.c @@ -36,13 +36,13 @@ #include "c.h" #include "fileutils.h" -#include "nsutils.h" #include "signals.h" #include "strutils.h" #include "nls.h" #include "xalloc.h" #include "proc/pwcache.h" #include "proc/devname.h" +#include #include "rpmatch.h" #define DEFAULT_NICE 4 @@ -62,7 +62,7 @@ static const char **cmds; static int *pids; static char **namespaces; static int ns_pid; -static proc_t ns_task; +static struct procps_namespaces ns; #define ENLIST(thing,addme) do{ \ if(!thing##s) thing##s = xmalloc(sizeof(*thing##s)*saved_argc); \ @@ -103,7 +103,7 @@ static int parse_namespaces(char *optarg) tmp = strndup(ptr, len); } - id = get_ns_id(tmp); + id = procps_ns_get_id(tmp); if (id == -1) { fprintf(stderr, "%s is not a valid namespace\n", tmp); free(tmp); @@ -238,7 +238,7 @@ static void check_proc(int pid, struct run_time_conf_t *run_time) { char buf[128]; struct stat statbuf; - proc_t task; + struct procps_namespaces pid_ns; char *tmp; int tty; int fd; @@ -293,11 +293,11 @@ static void check_proc(int pid, struct run_time_conf_t *run_time) goto closure; } if (ns_pid) { - if (ns_read(pid, &task)) + if (procps_ns_read_pid(pid, &pid_ns) < 0) goto closure; - for (i = 0; i < NUM_NS; i++) { + for (i = 0; i < PROCPS_NS_COUNT; i++) { if (ns_flags & (1 << i)) { - if (task.ns[i] != ns_task.ns[i]) + if (pid_ns.ns[i] != ns.ns[i]) goto closure; } } @@ -722,7 +722,7 @@ static void skillsnice_parse(int argc, xwarnx(_("invalid pid number %s"), optarg); kill_usage(stderr); } - if (ns_read(ns_pid, &ns_task)) { + if (procps_ns_read_pid(ns_pid, &ns) < 0) { xwarnx(_("error reading reference namespace " "information")); kill_usage(stderr);