Merge branch 'collectd-4.10' into collectd-5.0
[collectd.git] / src / processes.c
index 7a5a9c0..8a1436e 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/processes.c
  * Copyright (C) 2005       Lyonel Vincent
- * Copyright (C) 2006-2008  Florian octo Forster
+ * Copyright (C) 2006-2010  Florian octo Forster
  * Copyright (C) 2008       Oleg King
  * Copyright (C) 2009       Sebastian Harl
  * Copyright (C) 2009       Andrés J. Díaz
 #  define ARG_MAX 4096
 #endif
 
-static const char *config_keys[] =
-{
-       "Process",
-       "ProcessMatch"
-};
-static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
-
 typedef struct procstat_entry_s
 {
        unsigned long id;
@@ -143,13 +136,13 @@ typedef struct procstat_entry_s
 
        unsigned long vmem_minflt;
        unsigned long vmem_majflt;
-       unsigned long vmem_minflt_counter;
-       unsigned long vmem_majflt_counter;
+       derive_t      vmem_minflt_counter;
+       derive_t      vmem_majflt_counter;
 
        unsigned long cpu_user;
        unsigned long cpu_system;
-       unsigned long cpu_user_counter;
-       unsigned long cpu_system_counter;
+       derive_t      cpu_user_counter;
+       derive_t      cpu_system_counter;
 
        /* io data */
        derive_t io_rchar;
@@ -176,11 +169,11 @@ typedef struct procstat
        unsigned long vmem_code;
        unsigned long stack_size;
 
-       unsigned long vmem_minflt_counter;
-       unsigned long vmem_majflt_counter;
+       derive_t vmem_minflt_counter;
+       derive_t vmem_majflt_counter;
 
-       unsigned long cpu_user_counter;
-       unsigned long cpu_system_counter;
+       derive_t cpu_user_counter;
+       derive_t cpu_system_counter;
 
        /* io data */
        derive_t io_rchar;
@@ -520,42 +513,61 @@ static void ps_list_reset (void)
 }
 
 /* put all pre-defined 'Process' names from config to list_head_g tree */
-static int ps_config (const char *key, const char *value)
+static int ps_config (oconfig_item_t *ci)
 {
-       if (strcasecmp (key, "Process") == 0)
-       {
-               ps_list_register (value, NULL);
-       }
-       else if (strcasecmp (key, "ProcessMatch") == 0)
-       {
-               char *new_val;
-               char *fields[3];
-               int fields_num;
-
-               new_val = strdup (value);
-               if (new_val == NULL) {
-                       ERROR ("processes plugin: strdup failed when processing "
-                                       "`ProcessMatch %s'.", value);
-                       return (1);
+       int i;
+
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *c = ci->children + i;
+
+               if (strcasecmp (c->key, "Process") == 0)
+               {
+                       if ((c->values_num != 1)
+                                       || (OCONFIG_TYPE_STRING != c->values[0].type)) {
+                               ERROR ("processes plugin: `Process' expects exactly "
+                                               "one string argument (got %i).",
+                                               c->values_num);
+                               continue;
+                       }
+
+                       if (c->children_num != 0) {
+                               WARNING ("processes plugin: the `Process' config option "
+                                               "does not expect any child elements -- ignoring "
+                                               "content (%i elements) of the <Process '%s'> block.",
+                                               c->children_num, c->values[0].value.string);
+                       }
+
+                       ps_list_register (c->values[0].value.string, NULL);
                }
+               else if (strcasecmp (c->key, "ProcessMatch") == 0)
+               {
+                       if ((c->values_num != 2)
+                                       || (OCONFIG_TYPE_STRING != c->values[0].type)
+                                       || (OCONFIG_TYPE_STRING != c->values[1].type))
+                       {
+                               ERROR ("processes plugin: `ProcessMatch' needs exactly "
+                                               "two string arguments (got %i).",
+                                               c->values_num);
+                               continue;
+                       }
 
-               fields_num = strsplit (new_val, fields,
-                               STATIC_ARRAY_SIZE (fields));
-               if (fields_num != 2)
+                       if (c->children_num != 0) {
+                               WARNING ("processes plugin: the `ProcessMatch' config option "
+                                               "does not expect any child elements -- ignoring "
+                                               "content (%i elements) of the <ProcessMatch '%s' '%s'> "
+                                               "block.", c->children_num, c->values[0].value.string,
+                                               c->values[1].value.string);
+                       }
+
+                       ps_list_register (c->values[0].value.string,
+                                       c->values[1].value.string);
+               }
+               else
                {
-                       ERROR ("processes plugin: `ProcessMatch' needs exactly "
-                                       "two string arguments.");
-                       sfree (new_val);
-                       return (1);
+                       ERROR ("processes plugin: The `%s' configuration option is not "
+                                       "understood and will be ignored.", c->key);
+                       continue;
                }
-               ps_list_register (fields[0], fields[1]);
-               sfree (new_val);
-       }
-       else
-       {
-               ERROR ("processes plugin: The `%s' configuration option is not "
-                               "understood and will be ignored.", key);
-               return (-1);
        }
 
        return (0);
@@ -664,8 +676,8 @@ static void ps_submit_proc_list (procstat_t *ps)
        plugin_dispatch_values (&vl);
 
        sstrncpy (vl.type, "ps_cputime", sizeof (vl.type));
-       vl.values[0].counter = ps->cpu_user_counter;
-       vl.values[1].counter = ps->cpu_system_counter;
+       vl.values[0].derive = ps->cpu_user_counter;
+       vl.values[1].derive = ps->cpu_system_counter;
        vl.values_len = 2;
        plugin_dispatch_values (&vl);
 
@@ -676,8 +688,8 @@ static void ps_submit_proc_list (procstat_t *ps)
        plugin_dispatch_values (&vl);
 
        sstrncpy (vl.type, "ps_pagefaults", sizeof (vl.type));
-       vl.values[0].counter = ps->vmem_minflt_counter;
-       vl.values[1].counter = ps->vmem_majflt_counter;
+       vl.values[0].derive = ps->vmem_minflt_counter;
+       vl.values[1].derive = ps->vmem_majflt_counter;
        vl.values_len = 2;
        plugin_dispatch_values (&vl);
 
@@ -702,8 +714,8 @@ static void ps_submit_proc_list (procstat_t *ps)
        DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; "
                         "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
                        "vmem_code = %lu; "
-                       "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; "
-                       "cpu_user_counter = %lu; cpu_system_counter = %lu; "
+                       "vmem_minflt_counter = %"PRIi64"; vmem_majflt_counter = %"PRIi64"; "
+                       "cpu_user_counter = %"PRIi64"; cpu_system_counter = %"PRIi64"; "
                        "io_rchar = %"PRIi64"; io_wchar = %"PRIi64"; "
                        "io_syscr = %"PRIi64"; io_syscw = %"PRIi64";",
                        ps->name, ps->num_proc, ps->num_lwp,
@@ -869,13 +881,15 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
        char *fields[64];
        char  fields_len;
 
-       int   i;
+       int   buffer_len;
 
-       int   ppid;
-       int   name_len;
+       char *buffer_ptr;
+       size_t name_start_pos;
+       size_t name_end_pos;
+       size_t name_len;
 
-       long long unsigned cpu_user_counter;
-       long long unsigned cpu_system_counter;
+       derive_t cpu_user_counter;
+       derive_t cpu_system_counter;
        long long unsigned vmem_size;
        long long unsigned vmem_rss;
        long long unsigned stack_size;
@@ -884,34 +898,56 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
 
        ssnprintf (filename, sizeof (filename), "/proc/%i/stat", pid);
 
-       i = read_file_contents (filename, buffer, sizeof(buffer) - 1);
-       if (i <= 0)
+       buffer_len = read_file_contents (filename,
+                       buffer, sizeof(buffer) - 1);
+       if (buffer_len <= 0)
                return (-1);
-       buffer[i] = 0;
-
-       fields_len = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
-       if (fields_len < 24)
+       buffer[buffer_len] = 0;
+
+       /* The name of the process is enclosed in parens. Since the name can
+        * contain parens itself, spaces, numbers and pretty much everything
+        * else, use these to determine the process name. We don't use
+        * strchr(3) and strrchr(3) to avoid pointer arithmetic which would
+        * otherwise be required to determine name_len. */
+       name_start_pos = 0;
+       while ((buffer[name_start_pos] != '(')
+                       && (name_start_pos < buffer_len))
+               name_start_pos++;
+
+       name_end_pos = buffer_len;
+       while ((buffer[name_end_pos] != ')')
+                       && (name_end_pos > 0))
+               name_end_pos--;
+
+       /* Either '(' or ')' is not found or they are in the wrong order.
+        * Anyway, something weird that shouldn't happen ever. */
+       if (name_start_pos >= name_end_pos)
        {
-               DEBUG ("processes plugin: ps_read_process (pid = %i):"
-                               " `%s' has only %i fields..",
-                               (int) pid, filename, fields_len);
+               ERROR ("processes plugin: name_start_pos = %zu >= name_end_pos = %zu",
+                               name_start_pos, name_end_pos);
                return (-1);
        }
 
-       /* copy the name, strip brackets in the process */
-       name_len = strlen (fields[1]) - 2;
-       if ((fields[1][0] != '(') || (fields[1][name_len + 1] != ')'))
+       name_len = (name_end_pos - name_start_pos) - 1;
+       if (name_len >= sizeof (ps->name))
+               name_len = sizeof (ps->name) - 1;
+
+       sstrncpy (ps->name, &buffer[name_start_pos + 1], name_len + 1);
+
+       if ((buffer_len - name_end_pos) < 2)
+               return (-1);
+       buffer_ptr = &buffer[name_end_pos + 2];
+
+       fields_len = strsplit (buffer_ptr, fields, STATIC_ARRAY_SIZE (fields));
+       if (fields_len < 22)
        {
-               DEBUG ("No brackets found in process name: `%s'", fields[1]);
+               DEBUG ("processes plugin: ps_read_process (pid = %i):"
+                               " `%s' has only %i fields..",
+                               (int) pid, filename, fields_len);
                return (-1);
        }
-       fields[1] = fields[1] + 1;
-       fields[1][name_len] = '\0';
-       strncpy (ps->name, fields[1], PROCSTAT_NAME_LEN);
-
-       ppid = atoi (fields[3]);
 
-       *state = fields[2][0];
+       *state = fields[0][0];
 
        if (*state == 'Z')
        {
@@ -936,16 +972,16 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
                return (0);
        }
 
-       cpu_user_counter   = atoll (fields[13]);
-       cpu_system_counter = atoll (fields[14]);
-       vmem_size          = atoll (fields[22]);
-       vmem_rss           = atoll (fields[23]);
-       ps->vmem_minflt_counter = atol (fields[9]);
-       ps->vmem_majflt_counter = atol (fields[11]);
+       cpu_user_counter   = atoll (fields[11]);
+       cpu_system_counter = atoll (fields[12]);
+       vmem_size          = atoll (fields[20]);
+       vmem_rss           = atoll (fields[21]);
+       ps->vmem_minflt_counter = atol (fields[7]);
+       ps->vmem_majflt_counter = atol (fields[9]);
 
        {
-               unsigned long long stack_start = atoll (fields[27]);
-               unsigned long long stack_ptr   = atoll (fields[28]);
+               unsigned long long stack_start = atoll (fields[25]);
+               unsigned long long stack_ptr   = atoll (fields[26]);
 
                stack_size = (stack_start > stack_ptr)
                        ? stack_start - stack_ptr
@@ -965,8 +1001,8 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
                DEBUG("ps_read_process: did not get vmem data for pid %i",pid);
        }
 
-       ps->cpu_user_counter = (unsigned long) cpu_user_counter;
-       ps->cpu_system_counter = (unsigned long) cpu_system_counter;
+       ps->cpu_user_counter = cpu_user_counter;
+       ps->cpu_system_counter = cpu_system_counter;
        ps->vmem_size = (unsigned long) vmem_size;
        ps->vmem_rss = (unsigned long) vmem_rss;
        ps->stack_size = (unsigned long) stack_size;
@@ -1829,8 +1865,7 @@ static int ps_read (void)
 
 void module_register (void)
 {
-       plugin_register_config ("processes", ps_config,
-                       config_keys, config_keys_num);
+       plugin_register_complex_config ("processes", ps_config);
        plugin_register_init ("processes", ps_init);
        plugin_register_read ("processes", ps_read);
 } /* void module_register */