summaryrefslogtreecommitdiff
path: root/src/platform/linux/platform.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/linux/platform.c')
-rw-r--r--src/platform/linux/platform.c2212
1 files changed, 2212 insertions, 0 deletions
diff --git a/src/platform/linux/platform.c b/src/platform/linux/platform.c
new file mode 100644
index 0000000..8b3e2a4
--- /dev/null
+++ b/src/platform/linux/platform.c
@@ -0,0 +1,2212 @@
+/*--------------------------------------------------------------------
+ * Copyright © 2016 James Hunt <jamesodhunt@ubuntu.com>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *--------------------------------------------------------------------
+ */
+
+#include "platform-linux.h"
+
+static struct procenv_map scheduler_map[] = {
+
+ mk_map_entry (SCHED_OTHER),
+ mk_map_entry (SCHED_FIFO),
+ mk_map_entry (SCHED_RR),
+ mk_map_entry (SCHED_BATCH),
+
+#ifdef SCHED_IDLE
+ mk_map_entry (SCHED_IDLE),
+#endif
+
+ { 0, NULL }
+};
+
+static struct procenv_map signal_map_linux[] = {
+
+ mk_map_entry (SIGABRT),
+ mk_map_entry (SIGALRM),
+ mk_map_entry (SIGBUS),
+
+ { SIGCHLD, "SIGCHLD|SIGCLD" },
+
+ mk_map_entry (SIGCONT),
+ mk_map_entry (SIGFPE),
+ mk_map_entry (SIGHUP),
+ mk_map_entry (SIGILL),
+ mk_map_entry (SIGINT),
+ mk_map_entry (SIGKILL),
+ mk_map_entry (SIGPIPE),
+ mk_map_entry (SIGQUIT),
+ mk_map_entry (SIGSEGV),
+ mk_map_entry (SIGSTOP),
+ mk_map_entry (SIGTERM),
+ mk_map_entry (SIGTRAP),
+ mk_map_entry (SIGTSTP),
+ mk_map_entry (SIGTTIN),
+ mk_map_entry (SIGTTOU),
+ mk_map_entry (SIGUSR1),
+ mk_map_entry (SIGUSR2),
+ mk_map_entry (SIGIO),
+ mk_map_entry (SIGIOT),
+
+ {SIGPOLL, "SIGPOLL|SIGIO" },
+
+ mk_map_entry (SIGPROF),
+
+ mk_map_entry (SIGPWR),
+#ifdef SIGSTKFLT
+ mk_map_entry (SIGSTKFLT),
+#endif
+
+ mk_map_entry (SIGSYS),
+
+#ifdef SIGUNUSED
+ mk_map_entry (SIGUNUSED),
+#endif
+ mk_map_entry (SIGURG),
+ mk_map_entry (SIGVTALRM),
+ mk_map_entry (SIGWINCH),
+ mk_map_entry (SIGXCPU),
+ mk_map_entry (SIGXFSZ),
+
+ { 0, NULL },
+};
+
+static struct procenv_map if_flag_map_linux[] = {
+ mk_map_entry (IFF_UP),
+ mk_map_entry (IFF_BROADCAST),
+ mk_map_entry (IFF_DEBUG),
+ mk_map_entry (IFF_LOOPBACK),
+ mk_map_entry (IFF_POINTOPOINT),
+ mk_map_entry (IFF_RUNNING),
+ mk_map_entry (IFF_NOARP),
+ mk_map_entry (IFF_PROMISC),
+ mk_map_entry (IFF_NOTRAILERS),
+ mk_map_entry (IFF_ALLMULTI),
+ mk_map_entry (IFF_MASTER),
+ mk_map_entry (IFF_SLAVE),
+ mk_map_entry (IFF_MULTICAST),
+ mk_map_entry (IFF_PORTSEL),
+ mk_map_entry (IFF_AUTOMEDIA),
+ mk_map_entry (IFF_DYNAMIC),
+ mk_map_entry (IFF_LOWER_UP),
+ mk_map_entry (IFF_DORMANT),
+#ifdef IFF_ECHO
+ mk_map_entry (IFF_ECHO),
+#endif
+
+ { 0, NULL }
+};
+
+static struct procenv_map if_extended_flag_map_linux[] = {
+#if defined (IFF_802_1Q_VLAN)
+ mk_map_entry (IFF_802_1Q_VLAN),
+#endif
+#if defined (IFF_EBRIDGE)
+ mk_map_entry (IFF_EBRIDGE),
+#endif
+#if defined (IFF_SLAVE_INACTIVE)
+ mk_map_entry (IFF_SLAVE_INACTIVE),
+#endif
+#if defined (IFF_MASTER_8023AD)
+ mk_map_entry (IFF_MASTER_8023AD),
+#endif
+#if defined (IFF_MASTER_ALB)
+ mk_map_entry (IFF_MASTER_ALB),
+#endif
+#if defined (IFF_BONDING)
+ mk_map_entry (IFF_BONDING),
+#endif
+#if defined (IFF_SLAVE_NEEDARP)
+ mk_map_entry (IFF_SLAVE_NEEDARP),
+#endif
+#if defined (IFF_ISATAP)
+ mk_map_entry (IFF_ISATAP),
+#endif
+ { 0, NULL }
+};
+
+static struct procenv_map personality_map_linux[] = {
+ mk_map_entry (PER_LINUX),
+ mk_map_entry (PER_LINUX_32BIT),
+ mk_map_entry (PER_SVR4),
+ mk_map_entry (PER_SVR3),
+ mk_map_entry (PER_SCOSVR3),
+ mk_map_entry (PER_OSR5),
+ mk_map_entry (PER_WYSEV386),
+ mk_map_entry (PER_ISCR4),
+ mk_map_entry (PER_BSD),
+ mk_map_entry (PER_SUNOS),
+ mk_map_entry (PER_XENIX),
+#if defined (PER_LINUX32)
+ mk_map_entry (PER_LINUX32),
+#endif
+#if defined (PER_LINUX32_3GB)
+ mk_map_entry (PER_LINUX32_3GB),
+#endif
+ mk_map_entry (PER_IRIX32),
+ mk_map_entry (PER_IRIXN32),
+ mk_map_entry (PER_IRIX64),
+ mk_map_entry (PER_RISCOS),
+ mk_map_entry (PER_SOLARIS),
+ mk_map_entry (PER_UW7),
+ mk_map_entry (PER_OSF4),
+ mk_map_entry (PER_HPUX),
+
+ { 0, NULL }
+};
+
+static struct procenv_map personality_flag_map_linux[] = {
+#if defined (ADDR_COMPAT_LAYOUT)
+ mk_map_entry (ADDR_COMPAT_LAYOUT),
+#endif
+#if defined (ADDR_LIMIT_32BIT)
+ mk_map_entry (ADDR_LIMIT_32BIT),
+#endif
+#if defined (ADDR_LIMIT_3GB)
+ mk_map_entry (ADDR_LIMIT_3GB),
+#endif
+#if defined (ADDR_NO_RANDOMIZE)
+ mk_map_entry (ADDR_NO_RANDOMIZE),
+#endif
+#if defined (MMAP_PAGE_ZERO)
+ mk_map_entry (MMAP_PAGE_ZERO),
+#endif
+#if defined (READ_IMPLIES_EXEC)
+ mk_map_entry (READ_IMPLIES_EXEC),
+#endif
+#if defined (SHORT_INODE)
+ mk_map_entry (SHORT_INODE),
+#endif
+#if defined (STICKY_TIMEOUTS)
+ mk_map_entry (STICKY_TIMEOUTS),
+#endif
+#if defined (WHOLE_SECONDS)
+ mk_map_entry (WHOLE_SECONDS),
+#endif
+
+ { 0, NULL }
+};
+
+/* lifted from include/linux/ioprio.h since not available in libc */
+#define IOPRIO_CLASS_SHIFT (13)
+#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)
+#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)
+#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
+
+enum {
+ IOPRIO_WHO_PROCESS = 1,
+ IOPRIO_WHO_PGRP,
+ IOPRIO_WHO_USER,
+};
+
+enum {
+ IOPRIO_CLASS_NONE, /* FIXME: is this valid? */
+ IOPRIO_CLASS_RT,
+ IOPRIO_CLASS_BE,
+ IOPRIO_CLASS_IDLE,
+ IOPRIO_CLASS_NORMAL,
+};
+
+static struct procenv_map io_priorities_class_map[] = {
+ mk_map_entry (IOPRIO_CLASS_NONE),
+ mk_map_entry (IOPRIO_CLASS_RT),
+ mk_map_entry (IOPRIO_CLASS_BE),
+ mk_map_entry (IOPRIO_CLASS_IDLE),
+ mk_map_entry (IOPRIO_CLASS_NORMAL),
+
+ { 0, NULL }
+};
+
+static char *
+pid_to_name (pid_t pid)
+{
+ char path[PATH_MAX];
+ char *name = NULL;
+ FILE *f = NULL;
+
+ sprintf (path, "/proc/%d/cmdline", (int)pid);
+
+ f = fopen (path, "r");
+ if (! f)
+ goto out;
+
+ /* Reuse buffer */
+ if (! fgets (path, sizeof (path), f))
+ goto out;
+
+ /* Nul delimiting within /proc file will ensure we only get the
+ * program name.
+ */
+ append (&name, path);
+
+out:
+ if (f)
+ fclose (f);
+
+ return name;
+}
+
+static void
+get_user_misc_linux (struct procenv_user *user,
+ struct procenv_misc *misc)
+{
+ assert (user);
+ assert (misc);
+ get_canonical_generic_linux (ROOT_PATH, misc->root, sizeof (misc->root));
+}
+
+static void
+show_capabilities_linux (void)
+{
+#if defined (HAVE_SYS_CAPABILITY_H)
+ int last_known;
+ cap_t caps;
+
+ /* Most recently-added capability that procenv knew about at
+ * compile time.
+ */
+ last_known = CAP_LAST_CAP;
+#endif
+
+#if defined (HAVE_SYS_CAPABILITY_H)
+ caps = cap_get_proc ();
+
+ entry ("count (CAP_LAST_CAP+1)", "%d", CAP_LAST_CAP+1);
+
+ if (! caps)
+ goto out;
+
+ section_open ("known");
+
+ show_capability (caps, CAP_CHOWN);
+ show_capability (caps, CAP_DAC_OVERRIDE);
+ show_capability (caps, CAP_DAC_READ_SEARCH);
+ show_capability (caps, CAP_FOWNER);
+ show_capability (caps, CAP_FSETID);
+ show_capability (caps, CAP_KILL);
+ show_capability (caps, CAP_SETGID);
+ show_capability (caps, CAP_SETUID);
+ show_capability (caps, CAP_SETPCAP);
+ show_capability (caps, CAP_LINUX_IMMUTABLE);
+ show_capability (caps, CAP_NET_BIND_SERVICE);
+ show_capability (caps, CAP_NET_BROADCAST);
+ show_capability (caps, CAP_NET_ADMIN);
+ show_capability (caps, CAP_NET_RAW);
+ show_capability (caps, CAP_IPC_LOCK);
+ show_capability (caps, CAP_IPC_OWNER);
+ show_capability (caps, CAP_SYS_MODULE);
+ show_capability (caps, CAP_SYS_RAWIO);
+ show_capability (caps, CAP_SYS_CHROOT);
+ show_capability (caps, CAP_SYS_PTRACE);
+ show_capability (caps, CAP_SYS_PACCT);
+ show_capability (caps, CAP_SYS_ADMIN);
+ show_capability (caps, CAP_SYS_BOOT);
+ show_capability (caps, CAP_SYS_NICE);
+ show_capability (caps, CAP_SYS_RESOURCE);
+ show_capability (caps, CAP_SYS_TIME);
+ show_capability (caps, CAP_SYS_TTY_CONFIG);
+
+ if (LINUX_KERNEL_MM (2, 4)) {
+ show_capability (caps, CAP_MKNOD);
+ show_capability (caps, CAP_LEASE);
+ }
+
+ if (LINUX_KERNEL_MMR (2, 6, 11)) {
+ show_capability (caps, CAP_AUDIT_WRITE);
+ show_capability (caps, CAP_AUDIT_CONTROL);
+ }
+ if (LINUX_KERNEL_MMR (2, 6, 24))
+ show_capability (caps, CAP_SETFCAP);
+ if (LINUX_KERNEL_MMR (2, 6, 25)) {
+ show_capability (caps, CAP_MAC_OVERRIDE);
+ show_capability (caps, CAP_MAC_ADMIN);
+ }
+
+#ifdef CAP_SYSLOG
+ if (LINUX_KERNEL_MMR (2, 6, 37))
+ show_capability (caps, CAP_SYSLOG);
+#endif /* CAP_SYSLOG */
+
+#ifdef CAP_WAKE_ALARM
+ if (LINUX_KERNEL_MM (3, 0))
+ show_capability (caps, CAP_WAKE_ALARM);
+#endif /* CAP_WAKE_ALARM */
+
+#ifdef CAP_BLOCK_SUSPEND
+ if (LINUX_KERNEL_MM (3, 5))
+ show_capability (caps, CAP_BLOCK_SUSPEND);
+#endif /* CAP_BLOCK_SUSPEND */
+
+#ifdef CAP_AUDIT_READ
+ if (LINUX_KERNEL_MM (3, 16)) {
+ show_capability (caps, CAP_AUDIT_READ);
+ }
+#endif /* CAP_AUDIT_READ */
+
+ section_close ();
+
+ /* It's possible that procenv is running on a system which has
+ * more capabilities that the system it was built on (for
+ * example, it might be running in a chroot with a newer kernel
+ * than the chroot environment). So display any unknown
+ * capabilities. We don't have their names, but it's useful to
+ * see that there are additional capabilities in available in
+ * the environment.
+ */
+ section_open ("unknown");
+
+#if defined (PR_CAPBSET_READ)
+ for (int i = 1+last_known; ; i++) {
+ int ret;
+ char *name = NULL;
+
+ ret = cap_get_bound (i);
+ if (ret < 0)
+ break;
+
+ /* Found an "unknown" */
+
+ appendf (&name, "CAP_LAST_CAP+%d", i);
+
+ _show_capability (caps, i, name);
+
+ free (name);
+ }
+#endif
+
+ cap_free (caps);
+
+ section_close ();
+
+#ifdef PR_GET_KEEPCAPS
+ if (LINUX_KERNEL_MMR (2, 2, 18)) {
+ int ret;
+ ret = prctl (PR_GET_KEEPCAPS, 0, 0, 0, 0);
+ if (ret < 0)
+ entry ("keep", "%s", UNKNOWN_STR);
+ else
+ entry ("keep", "%s", ret ? YES_STR : NO_STR);
+ }
+#endif
+
+
+#if defined (PR_GET_SECUREBITS) && defined (HAVE_LINUX_SECUREBITS_H)
+ if (LINUX_KERNEL_MMR (2, 6, 26)) {
+ int ret;
+
+ ret = prctl (PR_GET_SECUREBITS, 0, 0, 0, 0);
+ if (ret < 0)
+ entry ("securebits", "%s", UNKNOWN_STR);
+ else {
+ struct securebits_t {
+ unsigned int securebits;
+ } flags;
+ flags.securebits = (unsigned int)ret;
+
+ section_open ("securebits");
+
+ entry ("value", "0x%x", flags.securebits);
+
+ container_open ("fields");
+
+ show_const (flags, securebits, SECBIT_KEEP_CAPS);
+ show_const (flags, securebits, SECBIT_NO_SETUID_FIXUP);
+ show_const (flags, securebits, SECBIT_NOROOT);
+
+ container_close ();
+
+ section_close ();
+ }
+ }
+#endif
+
+out:
+ /* compiler appeasement */
+ return;
+#endif
+}
+
+static void
+show_cgroups_linux (void)
+{
+ const char *delim = ":";
+ char *file = "/proc/self/cgroup";
+ FILE *f;
+ char buffer[1024];
+ size_t len;
+
+ f = fopen (file, "r");
+
+ if (! f)
+ goto out;
+
+ while (fgets (buffer, sizeof (buffer), f)) {
+ char *buf, *b;
+ char *hierarchy;
+ char *subsystems = NULL;
+ char *path;
+
+ len = strlen (buffer);
+ /* Remove NL */
+ buffer[len-1] = '\0';
+
+ buf = b = strdup (buffer);
+ if (! buf)
+ die ("failed to alloate storage");
+
+ hierarchy = strsep (&b, delim);
+ if (! hierarchy)
+ goto next;
+
+ /* don't fail if this returns '\0' to tolerate cgroup2 where the
+ * subsystem is always empty.
+ */
+ subsystems = strsep (&b, delim);
+
+ path = strsep (&b, delim);
+ if (! path)
+ goto next;
+
+ /* FIXME: should sort by hierarchy */
+ container_open (hierarchy);
+
+ object_open (false);
+
+ /* FIXME: should split this on comma */
+ if (subsystems && *subsystems)
+ entry ("subsystems", "%s", subsystems);
+
+ entry ("path", "%s", path);
+
+ object_close (false);
+
+ container_close ();
+
+next:
+ free (buf);
+ }
+
+ fclose (f);
+
+out:
+
+ /* compiler appeasement */
+ return;
+}
+
+static void
+show_fds_linux (void)
+{
+ DIR *dir;
+ struct dirent *ent;
+ char *prefix_path = "/proc/self/fd";
+ struct stat st;
+ char path[MAXPATHLEN];
+ char link[MAXPATHLEN];
+ ssize_t len;
+
+ dir = opendir (prefix_path);
+ if (! dir)
+ return;
+
+ while ((ent=readdir (dir)) != NULL) {
+ int fd;
+ char *num = NULL;
+
+ if (! strcmp (ent->d_name, ".") || ! strcmp (ent->d_name, ".."))
+ continue;
+
+ sprintf (path, "%s/%s", prefix_path, ent->d_name);
+ fd = atoi (ent->d_name);
+
+ len = readlink (path, link, sizeof (link)-1);
+ if (len < 0)
+ /* ignore errors */
+ continue;
+
+ appendf (&num, "%d", fd);
+
+ assert (len);
+ link[len] = '\0';
+
+ if (link[0] == '/') {
+
+ if (stat (link, &st) < 0) {
+ free (num);
+ continue;
+ }
+
+ /* Ignore the last (invalid) entry */
+ if (S_ISDIR (st.st_mode)) {
+ free (num);
+ continue;
+ }
+ }
+
+ object_open (false);
+
+ section_open (num);
+ free (num);
+
+ entry ("terminal", "%s", isatty (fd) ? YES_STR : NO_STR);
+ entry ("valid", "%s", fd_valid (fd) ? YES_STR : NO_STR);
+ entry ("device", "%s", link);
+
+ section_close ();
+
+ object_close (false);
+ }
+
+ closedir (dir);
+}
+
+static void
+show_namespaces_linux (void)
+{
+ DIR *dir;
+ struct dirent *ent;
+ char *prefix_path = "/proc/self/ns";
+ char path[MAXPATHLEN];
+ char link[MAXPATHLEN];
+ ssize_t len;
+ PRList *list = NULL;
+
+ dir = opendir (prefix_path);
+ if (! dir)
+ goto end;
+
+ list = pr_list_new (NULL);
+ assert (list);
+
+ while ((ent=readdir (dir)) != NULL) {
+ PRList *entry;
+
+ if (! strcmp (ent->d_name, ".") || ! strcmp (ent->d_name, ".."))
+ continue;
+
+ sprintf (path, "%s/%s", prefix_path, ent->d_name);
+
+ len = readlink (path, link, sizeof (link)-1);
+ if (len < 0)
+ /* ignore errors */
+ continue;
+
+ assert (len);
+ link[len] = '\0';
+
+ entry = pr_list_new (strdup (link));
+ assert (entry);
+
+ assert (pr_list_prepend_str_sorted (list, entry));
+ }
+
+ closedir (dir);
+
+ PR_LIST_FOREACH_SAFE (list, iter) {
+ char *tmp;
+ char *name;
+ char *value;
+
+ pr_list_remove (iter);
+
+ tmp = iter->data;
+
+ name = strsep (&tmp, ":");
+ if (! name)
+ goto give_up;
+
+ value = strsep (&tmp, "]");
+ if (! value)
+ goto give_up;
+
+ if (*value == '[' && *(value+1)) {
+ value++;
+ }
+
+ object_open (false);
+ entry (name, "%s", value);
+ object_close (false);
+
+give_up:
+ free ((char *)iter->data);
+ free(iter);
+ }
+
+ free_if_set (list);
+
+end:
+ /* compiler appeasement */
+ return;
+}
+
+static void
+show_oom_linux (void)
+{
+ char *dir = "/proc/self";
+ char *files[] = { "oom_score", "oom_adj", "oom_score_adj", NULL };
+ char **file;
+ FILE *f;
+ char buffer[PROCENV_BUFFER];
+ char path[PATH_MAX];
+ size_t len;
+ int ret;
+ int seen = false;
+
+ for (file = files; file && *file; file++) {
+ ret = sprintf (path, "%s/%s", dir, *file);
+ if (ret < 0)
+ continue;
+
+ f = fopen (path, "r");
+ if (! f)
+ continue;
+
+ seen = true;
+
+ while (fgets (buffer, sizeof (buffer), f)) {
+ len = strlen (buffer);
+ buffer[len-1] = '\0';
+ entry (*file, "%s", buffer);
+ }
+
+ fclose (f);
+ }
+
+ if (! seen)
+ entry ("%s", UNKNOWN_STR);
+}
+
+#if ! defined (HAVE_SCHED_GETCPU)
+
+/* Crutch function for RHEL 5 */
+static int
+procenv_getcpu (void)
+{
+ int cpu = -1;
+ FILE *f;
+ char **fields;
+ const char *field;
+ char buffer[1024];
+ size_t len;
+ size_t count;
+
+ f = fopen ("/proc/self/stat", "r");
+ if (! f)
+ goto out;
+
+ if (! fgets (buffer, sizeof (buffer), f))
+ goto out;
+
+ fclose (f);
+
+ len = strlen (buffer);
+ buffer[len-1] = '\0';
+
+ count = split_fields (buffer, ' ', true, &fields);
+
+ if (! count)
+ return -1;
+
+ if (count != 42)
+ goto cleanup;
+
+ field = fields[41];
+ assert (field);
+
+ cpu = atoi (field);
+
+cleanup:
+
+ for (len = 0; len < count; len++)
+ free (fields[len]);
+ free (fields);
+
+out:
+ return cpu;
+}
+
+#endif
+
+static void
+show_cpu_linux (void)
+{
+ int cpu = -1;
+ long max;
+
+ max = get_sysconf (_SC_NPROCESSORS_ONLN);
+
+#if HAVE_SCHED_GETCPU
+ cpu = sched_getcpu ();
+ if (cpu < 0)
+ goto unknown_sched_cpu;
+
+#else
+ cpu = procenv_getcpu ();
+ if (cpu < 0)
+ goto unknown_sched_cpu;
+#endif
+
+ /* adjust to make 1-based */
+ cpu++;
+
+ entry ("number", "%u of %ld", cpu, max);
+ return;
+
+unknown_sched_cpu:
+
+ entry ("number", "%s of %ld", UNKNOWN_STR, max);
+}
+
+static void
+show_msg_queues_linux (void)
+{
+ int i;
+ int id;
+ int max;
+ struct msginfo info;
+ struct msqid_ds msqid_ds;
+ struct ipc_perm *perm;
+ char formatted_stime[CTIME_BUFFER];
+ char formatted_rtime[CTIME_BUFFER];
+ char formatted_ctime[CTIME_BUFFER];
+ char *modestr = NULL;
+ char *lspid = NULL;
+ char *lrpid = NULL;
+
+ max = msgctl (0, MSG_INFO, (void *)&info);
+ if (max < 0)
+ goto out;
+
+ section_open ("info");
+
+ entry ("msgpool", "%d", info.msgpool);
+ entry ("msgmap", "%d", info.msgmap);
+ entry ("msgmax", "%d", info.msgmax);
+ entry ("msgmnb", "%d", info.msgmnb);
+ entry ("msgmni", "%d", info.msgmni);
+ entry ("msgssz", "%d", info.msgssz);
+ entry ("msgtql", "%d", info.msgtql);
+ entry ("msgseg", "%d", info.msgseg);
+
+ section_close ();
+
+ container_open ("sets");
+
+ object_open (false);
+
+ for (i = 0; i <= max; i++) {
+ char *id_str = NULL;
+
+ id = msgctl (i, MSG_STAT, &msqid_ds);
+ if (id < 0) {
+ /* found an unused slot, so ignore it */
+ continue;
+ }
+
+ perm = &msqid_ds.msg_perm;
+
+ modestr = format_perms (perm->mode);
+ if (! modestr)
+ die ("failed to allocate space for permissions string");
+
+ /* May not have been set */
+ if (msqid_ds.msg_stime)
+ format_time (&msqid_ds.msg_stime, formatted_stime, sizeof (formatted_stime));
+ else
+ sprintf (formatted_stime, "%s", NA_STR);
+
+ /* May not have been set */
+ if (msqid_ds.msg_rtime)
+ format_time (&msqid_ds.msg_rtime, formatted_rtime, sizeof (formatted_rtime));
+ else
+ sprintf (formatted_rtime, "%s", NA_STR);
+
+ /* May not have been set */
+ if (msqid_ds.msg_ctime)
+ format_time (&msqid_ds.msg_ctime, formatted_ctime, sizeof (formatted_ctime));
+ else
+ sprintf (formatted_ctime, "%s", NA_STR);
+
+ lspid = pid_to_name (msqid_ds.msg_lspid);
+ lrpid = pid_to_name (msqid_ds.msg_lrpid);
+
+ appendf (&id_str, "%d", id);
+
+ container_open (id_str);
+ free (id_str);
+
+ object_open (false);
+
+ /* pad out to max pointer size represented in hex */
+ entry ("key", "0x%.*x", POINTER_SIZE * 2, perm->__key);
+ entry ("sequence", "%u", perm->__seq);
+
+ section_open ("permissions");
+ entry ("octal", "%4.4o", perm->mode);
+ entry ("symbolic", "%s", modestr);
+ section_close ();
+
+ section_open ("creator");
+ entry ("euid", "%u ('%s')", perm->cuid, get_user_name (perm->cuid));
+ entry ("egid", "%u ('%s')", perm->cgid, get_group_name (perm->cgid));
+ section_close ();
+
+ section_open ("owner");
+ entry ("uid", "%u ('%s')", perm->uid, get_user_name (perm->uid));
+ entry ("gid", "%u ('%s')", perm->gid, get_group_name (perm->gid));
+ section_close ();
+
+ section_open ("times");
+ entry ("last send (stime)", "%lu (%s)", msqid_ds.msg_stime, formatted_stime);
+ entry ("last receive (rtime)", "%lu (%s)", msqid_ds.msg_rtime, formatted_rtime);
+ entry ("last change (ctime)", "%lu (%s)", msqid_ds.msg_ctime, formatted_ctime);
+ section_close ();
+
+ entry ("queue_bytes", "%lu", msqid_ds.__msg_cbytes);
+
+ entry ("msg_qnum", "%lu", msqid_ds.msg_qnum);
+ entry ("msg_qbytes", "%lu", msqid_ds.msg_qbytes);
+
+ entry ("last msgsnd pid", "%d (%s)", msqid_ds.msg_lspid,
+ lspid ? lspid : UNKNOWN_STR);
+
+ entry ("last msgrcv pid", "%d (%s)", msqid_ds.msg_lrpid,
+ lrpid ? lrpid : UNKNOWN_STR);
+
+ object_close (false);
+
+ container_close ();
+
+ free (modestr);
+ if (lspid)
+ free (lspid);
+ if (lrpid)
+ free (lrpid);
+ }
+
+ object_close (false);
+
+ container_close ();
+
+out:
+ /* compiler appeasement */
+ return;
+}
+
+static void
+show_prctl_linux (void)
+{
+ int rc;
+ int arg2;
+ char name[17] = { 0 };
+
+#ifdef PR_GET_ENDIAN
+ if (LINUX_KERNEL_MMR (2, 6, 18)) {
+ const char *value;
+
+ rc = prctl (PR_GET_ENDIAN, &arg2, 0, 0, 0);
+ if (rc < 0)
+ value = UNKNOWN_STR;
+ else {
+ switch (arg2) {
+ case PR_ENDIAN_BIG:
+ value = BIG_STR;
+ break;
+ case PR_ENDIAN_LITTLE:
+ value = LITTLE_STR;
+ break;
+ case PR_ENDIAN_PPC_LITTLE:
+ value = "PowerPC pseudo little endian";
+ break;
+ default:
+ value = UNKNOWN_STR;
+ break;
+ }
+ }
+ entry ("process endian", "%s", value);
+ }
+#endif
+
+#ifdef PR_GET_DUMPABLE
+ if (LINUX_KERNEL_MMR (2, 3, 20)) {
+ const char *value;
+ rc = prctl (PR_GET_DUMPABLE, 0, 0, 0, 0);
+ if (rc < 0)
+ value = UNKNOWN_STR;
+ else {
+ switch (rc) {
+ case 0:
+ value = NO_STR;
+ break;
+ case 1:
+ value = YES_STR;
+ break;
+ case 2:
+ value = "root-only";
+ break;
+ default:
+ value = UNKNOWN_STR;
+ break;
+ }
+ }
+ entry ("dumpable", "%s", value);
+ }
+#endif
+
+#ifdef PR_GET_FPEMU
+ /* Use the earliest version where this option was introduced
+ * (for some architectures).
+ */
+ if (LINUX_KERNEL_MMR (2, 4, 18)) {
+ const char *value;
+
+ rc = prctl (PR_GET_FPEMU, &arg2, 0, 0, 0);
+ if (rc < 0)
+ value = UNKNOWN_STR;
+ else {
+ switch (arg2) {
+ case PR_FPEMU_NOPRINT:
+ value = YES_STR;
+ break;
+ case PR_FPEMU_SIGFPE:
+ value = "send SIGFPE";
+ break;
+ default:
+ value = UNKNOWN_STR;
+ break;
+ }
+ }
+ entry ("floating point emulation", "%s", value);
+ }
+#endif
+
+#ifdef PR_GET_FPEXC
+ /* Use the earliest version where this option was introduced
+ * (for some architectures).
+ */
+ if (LINUX_KERNEL_MMR (2, 4, 21)) {
+ const char *value;
+
+ rc = prctl (PR_GET_FPEXC, &arg2, 0, 0, 0);
+ if (rc < 0)
+ value = UNKNOWN_STR;
+ else {
+ switch (arg2) {
+ case PR_FP_EXC_SW_ENABLE:
+ value = "software";
+ break;
+ case PR_FP_EXC_DISABLED:
+ value = "disabled";
+ break;
+ case PR_FP_EXC_NONRECOV:
+ value = "non-recoverable";
+ break;
+ case PR_FP_EXC_ASYNC:
+ value = "asynchronous";
+ break;
+ case PR_FP_EXC_PRECISE:
+ value = "precise";
+ break;
+ default:
+ value = UNKNOWN_STR;
+ break;
+ }
+ }
+ entry ("floating point exceptions", "%s", value);
+ }
+#endif
+
+#ifdef PR_GET_NAME
+ if (LINUX_KERNEL_MMR (2, 6, 11)) {
+ rc = prctl (PR_GET_NAME, name, 0, 0, 0);
+ if (rc < 0)
+ entry ("process name", "%s", UNKNOWN_STR);
+ else
+ entry ("process name", "%s", name);
+ }
+
+#endif
+
+#ifdef PR_GET_PDEATHSIG
+ if (LINUX_KERNEL_MMR (2, 3, 15)) {
+ rc = prctl (PR_GET_PDEATHSIG, &arg2, 0, 0, 0);
+ if (rc < 0)
+ entry ("parent death signal", "%s", UNKNOWN_STR);
+ else if (rc == 0)
+ entry ("parent death signal", "disabled");
+ else
+ entry ("parent death signal", "%d", arg2);
+ }
+#endif
+
+#ifdef PR_GET_SECCOMP
+ if (LINUX_KERNEL_MMR (2, 6, 23)) {
+ const char *value;
+
+ rc = prctl (PR_GET_SECCOMP, 0, 0, 0, 0);
+ if (rc < 0)
+ value = UNKNOWN_STR;
+ else {
+ switch (rc) {
+ case 0:
+ value = "disabled";
+ break;
+ case 1:
+ value = "read/write/exit (mode 1)";
+ break;
+ case 2:
+ value = "BPF (mode 2)";
+ break;
+ default:
+ value = UNKNOWN_STR;
+ break;
+ }
+ }
+ entry ("secure computing", "%s", value);
+ }
+#endif
+
+#ifdef PR_GET_TIMING
+ /* Not 100% accurate - this option was actually
+ * introduced in 2.6.0-test4
+ */
+ if (LINUX_KERNEL_MMR (2, 6, 1)) {
+ const char *value;
+ rc = prctl (PR_GET_TIMING, 0, 0, 0, 0);
+ if (rc < 0)
+ value = UNKNOWN_STR;
+ else {
+ switch (rc) {
+ case PR_TIMING_STATISTICAL:
+ value = "statistical";
+ break;
+ case PR_TIMING_TIMESTAMP:
+ value = "time-stamp";
+ break;
+ default:
+ value = UNKNOWN_STR;
+ break;
+ }
+ }
+ entry ("process timing", "%s", value);
+ }
+#endif
+
+#if defined (PR_GET_TSC)
+ if (LINUX_KERNEL_MMR (2, 6, 26)) {
+ const char *value;
+
+ rc = prctl (PR_GET_TSC, &arg2, 0, 0, 0);
+ if (rc < 0)
+ value = UNKNOWN_STR;
+ else {
+ switch (arg2) {
+ case PR_TSC_ENABLE:
+ value = "enabled";
+ break;
+ case PR_TSC_SIGSEGV:
+ value = "segmentation fault";
+ break;
+ default:
+ value = UNKNOWN_STR;
+ break;
+ }
+ }
+ entry ("timestamp counter read", "%s", value);
+ }
+#endif
+
+#ifdef PR_GET_UNALIGNED
+ if (LINUX_KERNEL_MMR (2, 3, 48)) {
+ const char *value;
+
+ rc = prctl (PR_GET_UNALIGNED, &arg2, 0, 0, 0);
+ if (rc < 0)
+ value = UNKNOWN_STR;
+ else {
+ switch (arg2) {
+ case PR_UNALIGN_NOPRINT:
+ value = "fix-up";
+ break;
+ case PR_UNALIGN_SIGBUS:
+ value = "send SIGBUS";
+ break;
+ default:
+ value = UNKNOWN_STR;
+ break;
+ }
+ }
+ entry ("unaligned access", "%s", value);
+ }
+#endif
+
+#ifdef PR_MCE_KILL_GET
+ if (LINUX_KERNEL_MMR (2, 6, 32)) {
+ const char *value;
+
+ rc = prctl (PR_MCE_KILL_GET, 0, 0, 0, 0);
+ if (rc < 0)
+ value = UNKNOWN_STR;
+ else {
+ switch (rc) {
+ case PR_MCE_KILL_DEFAULT:
+ value = "system default";
+ break;
+ case PR_MCE_KILL_EARLY:
+ value = "early kill";
+ break;
+ case PR_MCE_KILL_LATE:
+ value = "late kill";
+ break;
+ default:
+ value = UNKNOWN_STR;
+ break;
+ }
+ }
+ entry ("machine-check exception", "%s", value);
+ }
+#endif
+
+#ifdef PR_GET_NO_NEW_PRIVS
+ if (LINUX_KERNEL_MM (3, 5)) {
+ const char *value;
+
+ rc = prctl (PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0);
+ if (rc < 0)
+ value = UNKNOWN_STR;
+ else {
+ switch (rc) {
+ case 0:
+ value = "normal execve";
+ break;
+ case 1:
+ value = "enabled";
+ break;
+ default:
+ value = UNKNOWN_STR;
+ break;
+ }
+ }
+ entry ("no new privileges", "%s", value);
+ }
+#endif
+
+#ifdef PR_GET_TIMERSLACK
+ if (LINUX_KERNEL_MMR (2, 6, 28)) {
+ rc = prctl (PR_GET_TIMERSLACK, 0, 0, 0, 0);
+ if (rc < 0)
+ entry ("timer slack", "%s", UNKNOWN_STR);
+ else
+ entry ("timer slack", "%dns", rc);
+ }
+#endif
+
+#ifdef PR_GET_CHILD_SUBREAPER
+ if (LINUX_KERNEL_MM (3, 4)) {
+ rc = prctl (PR_GET_CHILD_SUBREAPER, &arg2, 0, 0, 0);
+ if (rc < 0)
+ entry ("child subreaper", "%s", UNKNOWN_STR);
+ else
+ entry ("child subreaper", "%s", arg2 ? YES_STR : NO_STR);
+ }
+#endif
+
+#ifdef PR_GET_TID_ADDRESS
+ rc = prctl (PR_GET_TID_ADDRESS, &arg2, 0, 0, 0);
+ if (rc < 0)
+ entry ("clear child tid address", "%s", UNKNOWN_STR);
+ else
+ entry ("clear child tid address", "%p", arg2);
+#endif
+}
+
+static void
+handle_proc_branch_linux (void)
+{
+ char buffer[1024];
+ char path[PATH_MAX];
+ char name[16];
+ char pid[16];
+ char ppid[16];
+ size_t len;
+ char *p;
+ FILE *f;
+ char *str = NULL;
+
+ sprintf (pid, "%d", (int)getpid ());
+
+ /* This is one God-awful interface */
+ while (true) {
+ sprintf (path, "/proc/%s/status", pid);
+
+ f = fopen (path, "r");
+ if (! f) {
+ appendf (&str, "%s", UNKNOWN_STR);
+ goto out;
+ }
+
+ while (fgets (buffer, sizeof (buffer), f)) {
+ len = strlen (buffer);
+ buffer[len-1] = '\0';
+
+ if ((p=strstr (buffer, "Name:")) == buffer) {
+ p += 1+strlen ("Name:"); /* jump over tab char */
+ sprintf (name, "%s", p);
+ }
+
+ if ((p=strstr (buffer, "PPid:")) == buffer) {
+ p += 1+strlen ("PPid:"); /* jump over tab char */
+ sprintf (ppid, "%s", p);
+
+ /* got all we need now */
+ break;
+ }
+ }
+
+ fclose (f);
+
+ /* ultimate parent == PID 1 == '/sbin/init' */
+ if (! strcmp (pid, "1")) {
+ appendf (&str, "%s ('%s')", pid, name);
+ break;
+ } else {
+ appendf (&str, "%s ('%s'), ", pid, name);
+ }
+
+ /* parent is now the pid to search for */
+ sprintf (pid, "%s", ppid);
+ }
+out:
+
+ entry ("ancestry", "%s", str);
+ free (str);
+}
+
+static void
+show_security_module_context_linux (void)
+{
+ char *context = NULL;
+ char *mode = NULL;
+
+#if defined (HAVE_APPARMOR)
+ if (aa_is_enabled ()) {
+ /* XXX: The mode string is *NOT* be freed since it forms
+ * part of the allocation returned in context.
+ *
+ * See aa_gettaskcon(2) for details.
+ */
+ if (aa_gettaskcon (user.pid, &context, &mode) < 0)
+ die ("failed to query AppArmor context");
+ }
+#endif
+
+#if defined (HAVE_SELINUX_SELINUX_H)
+ if (is_selinux_enabled ()) {
+ if (getpidcon (user.pid, &context) < 0)
+ die ("failed to query SELinux context");
+ }
+#endif
+ if (context) {
+ if (mode) {
+ entry ("context", "%s (%s)", context, mode);
+ } else {
+ entry ("context", "%s", context);
+ }
+ } else {
+ entry ("context", "%s", UNKNOWN_STR);
+ }
+
+ free (context);
+}
+
+static void
+show_security_module_linux (void)
+{
+ char *lsm = UNKNOWN_STR;
+
+#if defined (HAVE_APPARMOR)
+ if (aa_is_enabled ())
+ lsm = "AppArmor";
+#endif
+
+#if defined (HAVE_SELINUX_SELINUX_H)
+ if (is_selinux_enabled () == 1) {
+
+ if (is_selinux_mls_enabled () == 1)
+ lsm = "SELinux (MLS)";
+ else
+ lsm = "SELinux";
+ }
+#endif
+
+ entry ("name", "%s", lsm);
+
+ show_security_module_context_linux ();
+}
+
+static void
+show_semaphores_linux (void)
+{
+ int i;
+ int id;
+ int max;
+ struct semid_ds semid_ds;
+ struct seminfo info;
+ struct ipc_perm *perm;
+ char formatted_otime[CTIME_BUFFER];
+ char formatted_ctime[CTIME_BUFFER];
+ char *modestr = NULL;
+ union semun arg;
+
+ arg.array = (unsigned short int *)(void *)&info;
+ max = semctl (0, 0, SEM_INFO, arg);
+ if (max < 0)
+ goto out;
+
+ section_open ("info");
+
+ entry ("semmap", "%d", info.semmap);
+ entry ("semmni", "%d", info.semmni);
+ entry ("semmns", "%d", info.semmns);
+ entry ("semmnu", "%d", info.semmnu);
+ entry ("semmsl", "%d", info.semmsl);
+ entry ("semopm", "%d", info.semopm);
+ entry ("semume", "%d", info.semume);
+ entry ("semusz", "%d", info.semusz);
+ entry ("semvmx", "%d", info.semvmx);
+ entry ("semaem", "%d", info.semaem);
+
+ section_close ();
+
+ container_open ("set");
+
+ object_open (false);
+
+ for (i = 0; i <= max; i++) {
+ char *id_str = NULL;
+
+ /* see semctl(2) */
+ arg.buf = (struct semid_ds *)&semid_ds;
+
+ id = semctl (i, 0, SEM_STAT, arg);
+ if (id < 0) {
+ /* found an unused slot, so ignore it */
+ continue;
+ }
+
+ perm = &semid_ds.sem_perm;
+
+ modestr = format_perms (perm->mode);
+ if (! modestr)
+ die ("failed to allocate space for permissions string");
+
+ /* May not have been set */
+ if (semid_ds.sem_otime)
+ format_time (&semid_ds.sem_otime, formatted_otime, sizeof (formatted_otime));
+ else
+ sprintf (formatted_otime, "%s", NA_STR);
+
+ format_time (&semid_ds.sem_ctime, formatted_ctime, sizeof (formatted_ctime));
+
+ appendf (&id_str, "%d", id);
+
+ container_open (id_str);
+ free (id_str);
+
+ object_open (false);
+
+ /* pad out to max pointer size represented in hex.
+ */
+ entry ("key", "0x%.*x", POINTER_SIZE * 2, perm->__key);
+ entry ("sequence", "%u", perm->__seq);
+
+ entry ("number in set", "%lu", semid_ds.sem_nsems);
+
+ section_open ("permissions");
+ entry ("octal", "%4.4o", perm->mode);
+ entry ("symbolic", "%s", modestr);
+ free (modestr);
+ section_close ();
+
+ section_open ("creator");
+ entry ("euid", "%u ('%s')", perm->cuid, get_user_name (perm->cuid));
+ entry ("egid", "%u ('%s')", perm->cgid, get_group_name (perm->cgid));
+ section_close ();
+
+ section_open ("owner");
+ entry ("uid", "%u ('%s')", perm->uid, get_user_name (perm->uid));
+ entry ("gid", "%u ('%s')", perm->gid, get_group_name (perm->gid));
+ section_close ();
+
+ section_open ("times");
+ entry ("last semop (otime)", "%lu (%s)", semid_ds.sem_otime, formatted_otime);
+ entry ("last change (ctime)", "%lu (%s)", semid_ds.sem_ctime, formatted_ctime);
+ section_close ();
+
+ object_close (false);
+
+ container_close ();
+ }
+
+ object_close (false);
+
+ container_close ();
+
+out:
+ /* compiler appeasement */
+ return;
+}
+
+static void
+show_shared_mem_linux (void)
+{
+ int i;
+ int id;
+ int max;
+ struct shm_info info;
+ struct shmid_ds shmid_ds;
+ struct ipc_perm *perm;
+ char formatted_atime[CTIME_BUFFER];
+ char formatted_ctime[CTIME_BUFFER];
+ char formatted_dtime[CTIME_BUFFER];
+ char *modestr = NULL;
+ int locked = -1;
+ int destroy = -1;
+ char *cpid = NULL;
+ char *lpid = NULL;
+
+ max = shmctl (0, SHM_INFO, (void *)&info);
+ if (max < 0)
+ goto out;
+
+ /* Display summary details */
+
+ section_open ("info");
+
+ entry ("segments", "%u", info.used_ids);
+ entry ("pages", "%lu", info.shm_tot);
+ entry ("shm_rss", "%lu", info.shm_rss);
+ entry ("shm_swp", "%lu", info.shm_swp);
+
+ /* Apparently unused */
+ entry ("swap_attempts", "%lu", info.swap_attempts);
+ entry ("swap_successes", "%lu", info.swap_successes);
+
+ section_close ();
+
+ container_open ("segments");
+
+ object_open (false);
+
+ for (i = 0; i <= max; i++) {
+ char *id_str = NULL;
+
+ id = shmctl (i, SHM_STAT, &shmid_ds);
+ if (id < 0) {
+ /* found an unused slot, so ignore it */
+ continue;
+ }
+
+ perm = &shmid_ds.shm_perm;
+
+ modestr = format_perms (perm->mode);
+ if (! modestr)
+ die ("failed to allocate space for permissions string");
+
+ locked = (perm->mode & SHM_LOCKED);
+ destroy = (perm->mode & SHM_DEST);
+
+ format_time (&shmid_ds.shm_atime, formatted_atime, sizeof (formatted_atime));
+ format_time (&shmid_ds.shm_ctime, formatted_ctime, sizeof (formatted_ctime));
+ format_time (&shmid_ds.shm_dtime, formatted_dtime, sizeof (formatted_dtime));
+
+ cpid = pid_to_name (shmid_ds.shm_cpid);
+ lpid = pid_to_name (shmid_ds.shm_lpid);
+
+ appendf (&id_str, "%d", id);
+
+ container_open (id_str);
+ free (id_str);
+
+ object_open (false);
+
+ /* pad out to max pointer size represented in hex.
+ */
+ entry ("key", "0x%.*x", POINTER_SIZE * 2, perm->__key);
+ entry ("sequence", "%u", perm->__seq);
+
+ section_open ("permissions");
+ entry ("octal", "%4.4o", perm->mode);
+ entry ("symbolic", "%s", modestr);
+ section_close ();
+
+ section_open ("pids");
+ entry ("create", "%d (%s)", shmid_ds.shm_cpid, cpid ? cpid : UNKNOWN_STR);
+ entry ("last", "%d (%s)", shmid_ds.shm_lpid, lpid ? lpid : UNKNOWN_STR);
+ section_close ();
+
+ entry ("attachers", "%lu", shmid_ds.shm_nattch);
+
+ section_open ("creator");
+ entry ("euid", "%u ('%s')", perm->cuid, get_user_name (perm->cuid));
+ entry ("egid", "%u ('%s')", perm->cgid, get_group_name (perm->cgid));
+ section_close ();
+
+ section_open ("owner");
+ entry ("uid", "%u ('%s')", perm->uid, get_user_name (perm->uid));
+ entry ("gid", "%u ('%s')", perm->gid, get_group_name (perm->gid));
+ section_close ();
+
+ entry ("segment size", "%lu", shmid_ds.shm_segsz);
+
+ section_open ("times");
+ entry ("last attach (atime)", "%lu (%s)", shmid_ds.shm_atime, formatted_atime);
+ entry ("last detach (dtime)", "%lu (%s)", shmid_ds.shm_dtime, formatted_dtime);
+ entry ("last change (ctime)", "%lu (%s)", shmid_ds.shm_ctime, formatted_ctime);
+ section_close ();
+
+ entry ("locked", "%s", locked == 0 ? NO_STR
+ : locked > 0 ? YES_STR
+ : NA_STR);
+ entry ("destroy", "%s", destroy == 0 ? NO_STR
+ : destroy > 0 ? YES_STR
+ : NA_STR);
+
+ object_close (false);
+
+ container_close ();
+
+ free (modestr);
+ if (cpid)
+ free (cpid);
+ if (lpid)
+ free (lpid);
+ }
+
+ object_close (false);
+
+ container_close ();
+
+out:
+ /* compiler appeasement */
+ return;
+}
+
+static void
+show_timezone_linux (void)
+{
+ tzset ();
+
+ entry ("tzname[0]", "'%s'", tzname[0]);
+ entry ("tzname[1]", "'%s'", tzname[1]);
+ entry ("timezone", "%ld", timezone);
+ entry ("daylight", "%d", daylight);
+}
+
+static const char *
+get_ioprio_class_name (int ioprio)
+{
+ struct procenv_map *p;
+
+ for (p = io_priorities_class_map; p && p->name; p++) {
+ if (ioprio == p->num)
+ return p->name;
+ }
+
+ return NULL;
+}
+
+/* No GLib wrapper, so create one */
+static int
+io_prio_get (int which, int who)
+{
+ return syscall(SYS_ioprio_get, which, who);
+}
+
+static void
+get_io_priorities_linux (struct procenv_priority *iop)
+{
+ iop->process = io_prio_get (IOPRIO_WHO_PROCESS, 0);
+ iop->pgrp = io_prio_get (IOPRIO_WHO_PGRP , 0);
+ iop->user = io_prio_get (IOPRIO_WHO_USER , 0);
+}
+
+static void
+show_io_priorities_linux (void)
+{
+ section_open ("process");
+ entry ("class", "%s", get_ioprio_class_name (IOPRIO_PRIO_CLASS (priority_io.process)));
+ entry ("priority", "%d", IOPRIO_PRIO_DATA (priority_io.process));
+ section_close ();
+
+ section_open ("group");
+ entry ("class", "%s", get_ioprio_class_name (IOPRIO_PRIO_CLASS (priority_io.pgrp)));
+ entry ("priority", "%d", IOPRIO_PRIO_DATA (priority_io.pgrp));
+ section_close ();
+
+ section_open ("user");
+ entry ("class", "%s", get_ioprio_class_name (IOPRIO_PRIO_CLASS (priority_io.user)));
+ entry ("priority", "%d", IOPRIO_PRIO_DATA (priority_io.user));
+ section_close ();
+}
+
+static void
+show_rlimits_linux (void)
+{
+ show_limit (RLIMIT_AS);
+ show_limit (RLIMIT_CORE);
+ show_limit (RLIMIT_CPU);
+ show_limit (RLIMIT_DATA);
+ show_limit (RLIMIT_FSIZE);
+
+#if defined (RLIMIT_RTTIME)
+ if (LINUX_KERNEL_MMR (2, 6, 25)) {
+ show_limit (RLIMIT_RTTIME);
+ }
+#endif
+
+ show_limit (RLIMIT_LOCKS);
+ show_limit (RLIMIT_MEMLOCK);
+
+#if defined (RLIMIT_MSGQUEUE)
+ if (LINUX_KERNEL_MMR (2, 6, 8)) {
+ show_limit (RLIMIT_MSGQUEUE);
+ }
+#endif
+
+#if defined RLIMIT_NICE
+ if (LINUX_KERNEL_MMR (2, 6, 12)) {
+ show_limit (RLIMIT_NICE);
+ }
+#endif
+
+ show_limit (RLIMIT_NOFILE);
+ show_limit (RLIMIT_NPROC);
+ show_limit (RLIMIT_RSS);
+ show_limit (RLIMIT_RTPRIO);
+
+#if defined (RLIMIT_SIGPENDING)
+ if (LINUX_KERNEL_MMR (2, 6, 8)) {
+ show_limit (RLIMIT_SIGPENDING);
+ }
+#endif
+
+ show_limit (RLIMIT_STACK);
+}
+
+static void
+get_proc_name_linux (struct procenv_user *user)
+{
+ assert (user);
+
+ if (LINUX_KERNEL_MMR (2, 6, 11)) {
+ if (prctl (PR_GET_NAME, user->proc_name, 0, 0, 0) < 0)
+ strcpy (user->proc_name, UNKNOWN_STR);
+ }
+}
+
+#if defined (HAVE_NUMA_H)
+
+static struct procenv_map numa_mempolicy_map[] = {
+ mk_map_entry (MPOL_DEFAULT),
+ mk_map_entry (MPOL_PREFERRED),
+ mk_map_entry (MPOL_BIND),
+ mk_map_entry (MPOL_INTERLEAVE),
+};
+
+static const char *
+get_numa_policy (int policy)
+{
+ struct procenv_map *p;
+
+ for (p = numa_mempolicy_map; p && p->name; p++) {
+ if (p->num == policy)
+ return p->name;
+ }
+
+ return NULL;
+}
+#endif /* HAVE_NUMA_H */
+
+static void
+handle_numa_memory_linux (void)
+{
+#if defined (HAVE_NUMA_H)
+ int policy;
+ const char *policy_name;
+ char *allowed_list = NULL;
+ int ret;
+ unsigned long node;
+ unsigned long allowed_size;
+
+#if defined (HAVE_NUMA_H)
+#if LIBNUMA_API_VERSION == 2
+ struct bitmask *allowed;
+#else
+ nodemask_t allowed;
+#endif
+#endif
+
+ /* part of the libnuma public API - stop the library calling
+ * exit(3) on error.
+ */
+ numa_exit_on_error = 0;
+
+ /* true if any numa nodes have been displayed yet */
+ int displayed = false;
+
+ /* Number of numa nodes in *current* range */
+ size_t count = 0;
+
+ /* Only valid to read these when count is >0 */
+ size_t last = 0;
+ size_t first = 0;
+#endif /* HAVE_NUMA_H */
+
+ header ("numa");
+
+#if defined (HAVE_NUMA_H)
+ if (numa_available () < 0)
+ /* NUMA not supported on this system */
+ goto out;
+
+#if LIBNUMA_API_VERSION == 2
+ entry ("api version", "%d", 2);
+#else
+ entry ("api version", "%d", 1);
+#endif
+
+ ret = get_mempolicy (&policy, NULL, 0, 0, 0);
+
+ if (ret < 0) {
+ entry ("policy", "%s", UNKNOWN_STR);
+ goto out;
+ }
+
+ policy_name = get_numa_policy (policy);
+
+ entry ("policy", "%s", policy_name ? policy_name : UNKNOWN_STR);
+
+#if LIBNUMA_API_VERSION == 2
+ entry ("maximum nodes", "%d", numa_num_possible_nodes ());
+ entry ("configured nodes", "%d", numa_num_configured_nodes ());
+
+ allowed = numa_get_mems_allowed ();
+ if (! allowed)
+ die ("failed to query NUMA allowed list");
+
+ allowed_size = allowed->size;
+
+#else
+ entry ("maximum nodes", "%s", UNKNOWN_STR);
+ entry ("configured nodes", "%d", numa_max_node ());
+
+ allowed = numa_get_run_node_mask ();
+ allowed_size = NUMA_NUM_NODES;
+#endif
+
+ for (node = 0; node < allowed_size; node++) {
+ if (PROCENV_NUMA_BITMASK_ISSET (allowed, node)) {
+ /* Record first entry in the range */
+ if (! count)
+ first = node;
+
+ last = node;
+ count++;
+ } else {
+ if (count) {
+ if (first == last) {
+ appendf (&allowed_list, "%s%d",
+ displayed ? "," : "",
+ first);
+ } else {
+ appendf (&allowed_list, "%s%d-%d",
+ displayed ? "," : "",
+ first, last);
+ }
+ displayed = true;
+ }
+
+ /* Reset */
+ count = 0;
+ }
+ }
+
+ if (count) {
+ if (first == last) {
+ appendf (&allowed_list, "%s%d",
+ displayed ? "," : "",
+ first);
+ } else {
+ appendf (&allowed_list, "%s%d-%d",
+ displayed ? "," : "",
+ first, last);
+ }
+ }
+
+ entry ("allowed list", "%s", allowed_list);
+
+#if LIBNUMA_API_VERSION == 2
+ numa_free_nodemask (allowed);
+#endif
+
+ free (allowed_list);
+
+out:
+#endif /* HAVE_NUMA_H */
+ footer ();
+}
+
+/**
+ * linux_kernel_version:
+ *
+ * @major: major kernel version number,
+ * @minor: minor kernel version number,
+ * @revision: kernel revision version,
+ *
+ * @minor and @revision may be -1 to denote that those version
+ * elements are not important to the caller. Once a parameter
+ * has been specified as -1, subsequent parameters are ignored
+ * (treated as -1 too).
+ *
+ * Returns: true if running Linux kernel is atleast at version
+ * specified by (@major, @minor, @revision), else false.
+ **/
+static bool
+linux_kernel_version (int major, int minor, int revision)
+{
+ int actual_version = 0x000000;
+ int requested_version = 0x000000;
+ int actual_major = -1;
+ int actual_minor = -1;
+ int actual_revision = -1;
+ int ret;
+
+ assert (uts.release);
+ assert (sizeof (int) >= 4);
+
+ /* We need something to work with */
+ assert (major > 0);
+
+ ret = sscanf (uts.release, "%d.%d.%d",
+ &actual_major, &actual_minor,
+ &actual_revision);
+
+ /* We need something to compare against */
+ assert (ret && actual_major != -1);
+
+ requested_version |= (0xFF0000 & (major << 16));
+
+ if (minor != -1) {
+ requested_version |= (0x00FF00 & (minor << 8));
+
+ if (revision != -1)
+ requested_version |= (0x0000FF & revision);
+ }
+
+ if (actual_revision != -1) {
+ actual_version |= (0x0000FF & actual_revision);
+ }
+
+ if (actual_minor != -1)
+ actual_version |= (0x00FF00 & (actual_minor << 8));
+
+ if (actual_major != -1)
+ actual_version |= (0xFF0000 & (actual_major << 16));
+
+
+ if (actual_version >= requested_version)
+ return true;
+
+ return false;
+}
+
+#if defined (HAVE_SYS_CAPABILITY_H)
+static int
+get_capability_by_flag_type (cap_t cap_p, cap_flag_t type, cap_value_t cap)
+{
+ int ret;
+ cap_flag_value_t result;
+
+ assert (cap_p);
+
+ ret = cap_get_flag (cap_p, cap, type, &result);
+
+ return ret < 0 ? ret : result;
+}
+#endif /* HAVE_SYS_CAPABILITY_H */
+
+#if defined (HAVE_SYS_CAPABILITY_H)
+#ifdef PROCENV_NEED_LOCAL_CAP_GET_BOUND
+
+static int
+cap_get_bound (cap_value_t cap)
+{
+#if defined (PR_CAPBSET_READ)
+ return prctl (PR_CAPBSET_READ, cap);
+#else
+ return -1;
+#endif
+}
+
+#endif /* PROCENV_NEED_LOCAL_CAP_GET_BOUND */
+#endif /* HAVE_SYS_CAPABILITY_H */
+
+static const char *
+get_scheduler_name (int sched)
+{
+ struct procenv_map *p;
+
+ for (p = scheduler_map; p && p->name; p++) {
+ if (p->num == sched)
+ return p->name;
+ }
+
+ return NULL;
+}
+
+static void
+get_tty_locked_status_linux (struct termios *lock_status)
+{
+ assert (lock_status);
+ assert (user.tty_fd != -1);
+
+ if (ioctl (user.tty_fd, TIOCGLCKTRMIOS, lock_status) < 0) {
+ /* Set to unlocked */
+ memset (lock_status, '\0', sizeof (struct termios));
+ }
+}
+
+static void
+handle_scheduler_type_linux (void)
+{
+ int sched;
+
+ sched = sched_getscheduler (0);
+
+ entry ("type", "%s",
+ sched < 0 ? UNKNOWN_STR :
+ get_scheduler_name (sched));
+}
+
+static void
+show_extended_if_flags_linux (const char *interface, unsigned short *flags)
+{
+ int sock;
+ struct ifreq ifr;
+ const struct procenv_map *p;
+
+ assert (interface);
+ assert (flags);
+
+ /* We need to create a socket to query an interfaces mac
+ * address. Don't ask me why...
+ */
+ sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+ if (sock < 0)
+ return;
+
+ memset (&ifr, 0, sizeof (struct ifreq));
+ strncpy (ifr.ifr_name, interface, IFNAMSIZ-1);
+
+ if (ioctl (sock, SIOCGIFPFLAGS, &ifr) < 0)
+ goto out;
+
+ *flags = ifr.ifr_flags;
+
+ for (p = if_extended_flag_map_linux; p && p->name; p++) {
+ if (*flags & p->num) {
+ object_open (false);
+ entry (p->name, "0x%x", p->num);
+ object_close (false);
+ }
+ }
+out:
+ close (sock);
+}
+
+static PROCENV_CPU_SET_TYPE *
+get_cpuset_linux (void)
+{
+ int ret = 0;
+
+#if ! defined (CPU_ALLOC)
+ /* RHEL 5 */
+ static PROCENV_CPU_SET_TYPE cpu_set_real;
+#else
+ long max;
+#endif
+
+ size_t size;
+ PROCENV_CPU_SET_TYPE *cs = NULL;
+
+#if defined (CPU_ALLOC)
+
+ max = get_sysconf (_SC_NPROCESSORS_ONLN);
+ if (max < 0)
+ return NULL;
+
+ cs = CPU_ALLOC (max);
+ if (! cs)
+ return NULL;
+
+ size = CPU_ALLOC_SIZE (max);
+ CPU_ZERO_S (size, cs);
+
+#else /* ! CPU_ALLOC */
+
+ size = sizeof (PROCENV_CPU_SET_TYPE);
+
+ CPU_ZERO (&cpu_set_real);
+ cs = &cpu_set_real;
+
+#endif /* CPU_ALLOC */
+
+ /* We could use sched_getaffinity(2) rather than
+ * sched_getaffinity() on Linux (2.5.8+) but
+ * pthread_getaffinity_np() provides the same information...
+ * Except it is missing on kFreeBSD systems (!) so we have to
+ * use sched_getaffinity() there. :(
+ */
+#if ! defined (CPU_ALLOC)
+ ret = sched_getaffinity (0, size, cs);
+#else
+
+ /* On a hyperthreaded system, "size" as above may not actually
+ * be big enough, and we get EINVAL (hwloc has a similar
+ * workaround).
+ */
+ {
+ int mult = 0;
+ while ((ret = pthread_getaffinity_np (pthread_self (), size, cs))
+ == EINVAL) {
+ CPU_FREE (cs);
+ /* Bail out at an arbitrary value. */
+ if (mult > 128) break;
+ mult += 2;
+ cs = CPU_ALLOC (mult * max);
+ size = CPU_ALLOC_SIZE (mult * max);
+ CPU_ZERO_S (size, cs);
+ }
+ }
+#endif
+ if (ret)
+ goto err;
+
+ return cs;
+err:
+#if defined (CPU_ALLOC)
+ CPU_FREE (cs);
+#endif
+ return NULL;
+}
+
+static void
+free_cpuset_linux (PROCENV_CPU_SET_TYPE *cs)
+{
+#if defined (CPU_ALLOC)
+ CPU_FREE (cs);
+#endif
+}
+
+static bool
+cpuset_has_cpu_linux (const PROCENV_CPU_SET_TYPE *cs,
+ PROCENV_CPU_TYPE cpu)
+{
+ return CPU_ISSET (cpu, cs);
+}
+
+static bool
+in_vm_linux (void)
+{
+ bool result = false;
+ const char *cpuinfo = "/proc/cpuinfo";
+ FILE *f = NULL;
+ char buffer[1024];
+
+ f = fopen (cpuinfo, "r");
+ if (! f) {
+ return false;
+ }
+
+ while (fgets (buffer, sizeof (buffer), f)) {
+ if (strstr (buffer, "flags") != buffer) {
+ continue;
+ }
+
+ if (strstr (buffer, "hypervisor") != NULL) {
+ result = true;
+ }
+
+ /* only test the first CPU */
+ goto out;
+ }
+
+out:
+ fclose (f);
+
+ return result;
+}
+
+struct procenv_ops platform_ops =
+{
+ .driver = PROCENV_SET_DRIVER (linux),
+
+ .get_cpuset = get_cpuset_linux,
+ .free_cpuset = free_cpuset_linux,
+ .cpuset_has_cpu = cpuset_has_cpu_linux,
+ .get_user_misc = get_user_misc_linux,
+ .get_proc_name = get_proc_name_linux,
+ .get_io_priorities = get_io_priorities_linux,
+ .get_mtu = get_mtu_generic,
+ .get_time = get_time_generic,
+
+ .get_tty_locked_status = get_tty_locked_status_linux,
+ .get_kernel_bits = get_kernel_bits_generic,
+
+ .signal_map = signal_map_linux,
+ .if_flag_map = if_flag_map_linux,
+ .in_vm = in_vm_linux,
+ .personality_map = personality_map_linux,
+ .personality_flag_map = personality_flag_map_linux,
+
+ .show_capabilities = show_capabilities_linux,
+ .show_cgroups = show_cgroups_linux,
+ .show_confstrs = show_confstrs_generic,
+
+ .show_cpu_affinities = show_cpu_affinities_generic,
+
+ .show_cpu = show_cpu_linux,
+ .show_clocks = show_clocks_generic,
+ .show_extended_if_flags = show_extended_if_flags_linux,
+ .show_fds = show_fds_linux,
+ .show_io_priorities = show_io_priorities_linux,
+ .show_mounts = show_mounts_generic_linux,
+ .show_msg_queues = show_msg_queues_linux,
+ .show_namespaces = show_namespaces_linux,
+ .show_oom = show_oom_linux,
+ .show_prctl = show_prctl_linux,
+ .show_rlimits = show_rlimits_linux,
+ .show_security_module = show_security_module_linux,
+ .show_semaphores = show_semaphores_linux,
+ .show_shared_mem = show_shared_mem_linux,
+ .show_timezone = show_timezone_linux,
+
+ .handle_numa_memory = handle_numa_memory_linux,
+ .handle_proc_branch = handle_proc_branch_linux,
+ .handle_scheduler_type = handle_scheduler_type_linux,
+
+};