virProcessGetNamespaces;
virProcessGetPids;
virProcessGetStartTime;
+virProcessGetStat;
virProcessGroupGet;
virProcessGroupKill;
virProcessKill;
}
#endif /* !WITH_SCHED_SETSCHEDULER */
+
+/*
+ * Get all stat fields for a process based on pid and tid:
+ * - pid == 0 && tid == 0 => /proc/self/stat
+ * - pid != 0 && tid == 0 => /proc/<pid>/stat
+ * - pid == 0 && tid != 0 => /proc/self/task/<tid>/stat
+ * - pid != 0 && tid != 0 => /proc/<pid>/task/<tid>/stat
+ * and return them as array of strings.
+ */
+GStrv
+virProcessGetStat(pid_t pid,
+ pid_t tid)
+{
+ int len = 10 * 1024; /* 10kB ought to be enough for everyone */
+ g_autofree char *buf = NULL;
+ g_autofree char *path = NULL;
+ GStrv rest = NULL;
+ GStrv ret = NULL;
+ char *comm = NULL;
+ char *rparen = NULL;
+ size_t nrest = 0;
+
+ if (pid) {
+ if (tid)
+ path = g_strdup_printf("/proc/%d/task/%d/stat", (int)pid, (int)tid);
+ else
+ path = g_strdup_printf("/proc/%d/stat", (int)pid);
+ } else {
+ if (tid)
+ path = g_strdup_printf("/proc/self/task/%d/stat", (int)tid);
+ else
+ path = g_strdup("/proc/self/stat");
+ }
+
+ len = virFileReadAllQuiet(path, len, &buf);
+ if (len < 0)
+ return NULL;
+
+ /* eliminate trailing spaces */
+ while (len > 0 && g_ascii_isspace(buf[--len]))
+ buf[len] = '\0';
+
+ /* Find end of the first field */
+ if (!(comm = strchr(buf, ' ')))
+ return NULL;
+ *comm = '\0';
+
+ /* Check start of the second field (filename of the executable, in
+ * parentheses) */
+ comm++;
+ if (*comm != '(')
+ return NULL;
+ comm++;
+
+ /* Check end of the second field (last closing parenthesis) */
+ rparen = strrchr(comm, ')');
+ if (!rparen)
+ return NULL;
+ *rparen = '\0';
+
+ /* We need to check that the next char is not '\0', but why not just opt in
+ * for the safer way of checking whether it is ' ' (space) instead */
+ if (rparen[1] != ' ')
+ return NULL;
+
+ rest = g_strsplit(rparen + 2, " ", 0);
+ nrest = g_strv_length(rest);
+ ret = g_new0(char *, nrest + 3);
+ ret[0] = g_strdup(buf);
+ ret[1] = g_strdup(comm);
+ memcpy(ret + 2, rest, nrest * sizeof(char *));
+
+ /* Do not use g_strfreev() as individual elements they were moved to @ret. */
+ VIR_FREE(rest);
+
+ return ret;
+}
int virProcessSetScheduler(pid_t pid,
virProcessSchedPolicy policy,
int priority);
+
+GStrv virProcessGetStat(pid_t pid, pid_t tid);
+
+/* These constants are modelled after proc(5) */
+enum {
+ VIR_PROCESS_STAT_PID,
+ VIR_PROCESS_STAT_COMM,
+ VIR_PROCESS_STAT_STATE,
+ VIR_PROCESS_STAT_PPID,
+ VIR_PROCESS_STAT_PGRP,
+ VIR_PROCESS_STAT_SESSION,
+ VIR_PROCESS_STAT_TTY_NR,
+ VIR_PROCESS_STAT_TPGID,
+ VIR_PROCESS_STAT_FLAGS,
+ VIR_PROCESS_STAT_MINFLT,
+ VIR_PROCESS_STAT_CMINFLT,
+ VIR_PROCESS_STAT_MAJFLT,
+ VIR_PROCESS_STAT_CMAJFLT,
+ VIR_PROCESS_STAT_UTIME,
+ VIR_PROCESS_STAT_STIME,
+ VIR_PROCESS_STAT_CUTIME,
+ VIR_PROCESS_STAT_CSTIME,
+ VIR_PROCESS_STAT_PRIORITY,
+ VIR_PROCESS_STAT_NICE,
+ VIR_PROCESS_STAT_NUM_THREADS,
+ VIR_PROCESS_STAT_ITREALVALUE,
+ VIR_PROCESS_STAT_STARTTIME,
+ VIR_PROCESS_STAT_VSIZE,
+ VIR_PROCESS_STAT_RSS,
+ VIR_PROCESS_STAT_RSSLIM,
+ VIR_PROCESS_STAT_STARTCODE,
+ VIR_PROCESS_STAT_ENDCODE,
+ VIR_PROCESS_STAT_STARTSTACK,
+ VIR_PROCESS_STAT_KSTKESP,
+ VIR_PROCESS_STAT_KSTKEIP,
+ VIR_PROCESS_STAT_SIGNAL,
+ VIR_PROCESS_STAT_BLOCKED,
+ VIR_PROCESS_STAT_SIGIGNORE,
+ VIR_PROCESS_STAT_SIGCATCH,
+ VIR_PROCESS_STAT_WCHAN,
+ VIR_PROCESS_STAT_NSWAP,
+ VIR_PROCESS_STAT_CNSWAP,
+ VIR_PROCESS_STAT_EXIT_SIGNAL,
+ VIR_PROCESS_STAT_PROCESSOR,
+ VIR_PROCESS_STAT_RT_PRIORITY,
+ VIR_PROCESS_STAT_POLICY,
+ VIR_PROCESS_STAT_DELAYACCT_BLKIO_TICKS,
+ VIR_PROCESS_STAT_GUEST_TIME,
+ VIR_PROCESS_STAT_CGUEST_TIME,
+ VIR_PROCESS_STAT_START_DATA,
+ VIR_PROCESS_STAT_END_DATA,
+ VIR_PROCESS_STAT_START_BRK,
+ VIR_PROCESS_STAT_ARG_START,
+ VIR_PROCESS_STAT_ARG_END,
+ VIR_PROCESS_STAT_ENV_START,
+ VIR_PROCESS_STAT_ENV_END,
+ VIR_PROCESS_STAT_EXIT_CODE,
+};
+
+/*
+ * At the time of writing there are 52 values reported in /proc/.../stat, the
+ * line below checks that the last one has the right value, increase accordingly
+ * based on proc(5) whenever adding new fields.
+*/
+G_STATIC_ASSERT(VIR_PROCESS_STAT_EXIT_CODE == 51);
+
typedef enum {
VIR_PROCESS_NAMESPACE_MNT = (1 << 1),
VIR_PROCESS_NAMESPACE_IPC = (1 << 2),
{ 'name': 'scsihosttest' },
{ 'name': 'vircaps2xmltest', 'link_whole': [ test_file_wrapper_lib ] },
{ 'name': 'virnetdevbandwidthtest' },
+ { 'name': 'virprocessstattest', 'link_whole': [ test_file_wrapper_lib ] },
{ 'name': 'virresctrltest', 'link_whole': [ test_file_wrapper_lib ] },
{ 'name': 'virscsitest' },
{ 'name': 'virusbtest' },
--- /dev/null
+1 (this) is ( a weird )
+)( (command ( ) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
--- /dev/null
+1 (command) 3 4 5
--- /dev/null
+#include <config.h>
+
+#include "testutils.h"
+#include "virfilewrapper.h"
+#include "virprocess.h"
+
+
+struct testData {
+ const char *filename;
+ const char *command;
+ size_t count;
+ bool self;
+};
+
+
+static int
+test_virProcessGetStat(const void *opaque)
+{
+ struct testData *data = (struct testData *) opaque;
+ g_autofree char *data_dir = NULL;
+ g_auto(GStrv) proc_stat = NULL;
+ size_t len = 0;
+ id_t id = data->self ? 0 : -1;
+ const char *command = NULL;
+
+ data_dir = g_strdup_printf("%s/virprocessstatdata/%s/",
+ abs_srcdir, data->filename);
+
+ /* We are using predictable id of -1 because this case we will clearly see
+ * that the test failed in case of virFileWrapper failure */
+ if (id)
+ virFileWrapperAddPrefix("/proc/-1/task/-1/", data_dir);
+ else
+ virFileWrapperAddPrefix("/proc/self/", data_dir);
+
+ proc_stat = virProcessGetStat(id, id);
+
+ virFileWrapperClearPrefixes();
+
+ if (!proc_stat) {
+ fprintf(stderr, "Could not get process stats\n");
+ return -1;
+ }
+
+ len = g_strv_length(proc_stat);
+ if (data->count != len) {
+ fprintf(stderr, "Count incorrect, expected %zu, got %zu\n",
+ data->count, len);
+ return -1;
+ }
+
+ command = proc_stat[VIR_PROCESS_STAT_COMM];
+ if (!STREQ_NULLABLE(data->command, command)) {
+ fprintf(stderr, "Command incorrect, expected %s, got %s\n",
+ data->command, command);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+mymain(void)
+{
+ struct testData data = {0};
+ int ret = 0;
+
+#define DO_TEST(_filename, _command, _count, _self) \
+ do { \
+ data = (struct testData){ \
+ .filename = _filename, \
+ .command = _command, \
+ .count = _count, \
+ .self = _self, \
+ }; \
+ if (virTestRun("Reading process stat: " _filename, \
+ test_virProcessGetStat, &data) < 0) \
+ ret = -1; \
+ } while (0)
+
+ DO_TEST("simple", "command", 5, true);
+ DO_TEST("complex", "this) is ( a \t weird )\n)( (command ( ", 100, false);
+
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIR_TEST_MAIN(mymain)