Merge branch 'collectd-5.5'
authorRuben Kerkhof <ruben@rubenkerkhof.com>
Fri, 15 Apr 2016 09:32:49 +0000 (11:32 +0200)
committerRuben Kerkhof <ruben@rubenkerkhof.com>
Fri, 15 Apr 2016 09:32:49 +0000 (11:32 +0200)
1  2 
src/collectd.conf.pod
src/processes.c

diff --combined src/collectd.conf.pod
@@@ -38,7 -38,7 +38,7 @@@ i.e. a C<E<lt>PluginE<nbsp>...E<gt>> bl
  The syntax of this config file is similar to the config file of the famous
  I<Apache> webserver. Each line contains either an option (a key and a list of
  one or more values) or a section-start or -end. Empty lines and everything
 -after a non-quoted hash-symbol (C<#>) is ignored. I<Keys> are unquoted
 +after a non-quoted hash-symbol (C<#>) are ignored. I<Keys> are unquoted
  strings, consisting only of alphanumeric characters and the underscore (C<_>)
  character. Keys are handled case insensitive by I<collectd> itself and all
  plugins included with it. I<Values> can either be an I<unquoted string>, a
@@@ -70,7 -70,7 +70,7 @@@ C<E<lt>B<Plugin> ...E<gt>> block
  
  =item B<BaseDir> I<Directory>
  
 -Sets the base directory. This is the directory beneath all RRD-files are
 +Sets the base directory. This is the directory beneath which all RRD-files are
  created. Possibly more subdirectories are created. This is also the working
  directory for the daemon.
  
@@@ -83,7 -83,7 +83,7 @@@ I<collectd> will be mostly useless
  Only the first B<LoadPlugin> statement or block for a given plugin name has any
  effect. This is useful when you want to split up the configuration into smaller
  files and want each file to be "self contained", i.e. it contains a B<Plugin>
 -block I<and> then appropriate B<LoadPlugin> statement. The downside is that if
 +block I<and> the appropriate B<LoadPlugin> statement. The downside is that if
  you have multiple conflicting B<LoadPlugin> blocks, e.g. when they specify
  different intervals, only one of them (the first one encountered) will take
  effect and all others will be silently ignored.
@@@ -127,17 -127,8 +127,17 @@@ the average user from ever having to de
  =item B<Interval> I<Seconds>
  
  Sets a plugin-specific interval for collecting metrics. This overrides the
 -global B<Interval> setting. If a plugin provides own support for specifying an
 -interval, that setting will take precedence.
 +global B<Interval> setting. If a plugin provides its own support for specifying
 +an interval, that setting will take precedence.
 +
 +=item B<FlushInterval> I<Seconds>
 +
 +Specifies the interval, in seconds, to call the flush callback if it's
 +defined in this plugin. By default, this is disabled.
 +
 +=item B<FlushTimeout> I<Seconds>
 +
 +Specifies the value of the timeout argument of the flush callback.
  
  =back
  
@@@ -208,13 -199,11 +208,11 @@@ I<pattern> may be specified to filter w
  in combination with recursively including a directory to easily be able to
  arbitrarily mix configuration files and other documents (e.g. README files).
  The given example is similar to the first example above but includes all files
- matching C<*.conf> in any subdirectory of C</etc/collectd.d>:
-   Include "/etc/collectd.d" "*.conf"
+ matching C<*.conf> in any subdirectory of C</etc/collectd.d>.
  
  =back
  
 -If more than one files are included by a single B<Include> option, the files
 +If more than one file is included by a single B<Include> option, the files
  will be included in lexicographical order (as defined by the C<strcmp>
  function). Thus, you can e.E<nbsp>g. use numbered prefixes to specify the
  order in which the files are loaded.
@@@ -255,7 -244,7 +253,7 @@@ magic! (Assuming you're using the I<RRD
  
  =item B<MaxReadInterval> I<Seconds>
  
 -Read plugin doubles interval between queries after each failed attempt
 +A read plugin doubles the interval between queries after each failed attempt
  to get data.
  
  This options limits the maximum value of the interval. The default value is
@@@ -528,9 -517,7 +526,9 @@@ are disabled by default
  The I<AMQP plugin> can be used to communicate with other instances of
  I<collectd> or third party applications using an AMQP message broker. Values
  are sent to or received from the broker, which handles routing, queueing and
 -possibly filtering or messages.
 +possibly filtering out messages.
 +
 +B<Synopsis:>
  
   <Plugin "amqp">
     # Send values to an AMQP broker
@@@ -828,22 -815,12 +826,22 @@@ B<apcupsd> can handle it
  
  TCP-Port to connect to. Defaults to B<3551>.
  
 -=item B<ReportSeconds> B<true|false>
 +=item B<ReportSeconds> B<true>|B<false>
  
  If set to B<true>, the time reported in the C<timeleft> metric will be
  converted to seconds. This is the recommended setting. If set to B<false>, the
  default for backwards compatibility, the time will be reported in minutes.
  
 +=item B<PersistentConnection> B<true>|B<false>
 +
 +By default, the plugin will try to keep the connection to UPS open between
 +reads. Since this appears to be somewhat brittle (I<apcupsd> appears to close
 +the connection due to inactivity quite quickly), the plugin will try to detect
 +this problem and switch to an open-read-close mode in such cases.
 +
 +You can instruct the plugin to close the connection after each read by setting
 +this option to B<false>.
 +
  =back
  
  =head2 Plugin C<aquaero>
@@@ -1842,7 -1819,6 +1840,7 @@@ than those of other plugins. It usuall
      </Query>
      <Database "product_information">
        Driver "mysql"
 +      Interval 120
        DriverOption "host" "localhost"
        DriverOption "username" "collectd"
        DriverOption "password" "aZo6daiw"
@@@ -2022,11 -1998,6 +2020,11 @@@ the daemon. Other than that, that name 
  
  =over 4
  
 +=item B<Interval> I<Interval>
 +
 +Sets the interval (in seconds) in which the values will be collected from this
 +database. By default the global B<Interval> setting will be used.
 +
  =item B<Driver> I<Driver>
  
  Specifies the driver to use to connect to the database. In many cases those
@@@ -2557,17 -2528,6 +2555,17 @@@ with I<veth> and all interfaces with na
  at least one digit.
  
  
 +=item B<UniqueName> I<true>|I<false>
 +
 +Interface name is not unique on Solaris (KSTAT), interface name is unique 
 +only within a module/instance. Following tuple is considered unique: 
 +   (ks_module, ks_instance, ks_name)
 +If this option is set to true, interface name contains above three fields
 +separated by an underscore. For more info on KSTAT, visit
 +L<http://docs.oracle.com/cd/E23824_01/html/821-1468/kstat-3kstat.html#REFMAN3Ekstat-3kstat>
 +
 +This option is only available on Solaris.
 +
  =back
  
  =head2 Plugin C<ipmi>
@@@ -3288,146 -3248,6 +3286,146 @@@ B<Collect> option is mandatory
  
  =back
  
 +=head2 Plugin C<mqtt>
 +
 +The I<MQTT plugin> can send metrics to MQTT (B<Publish> blocks) and receive
 +values from MQTT (B<Subscribe> blocks).
 +
 +B<Synopsis:>
 +
 + <Plugin mqtt>
 +   <Publish "name">
 +     Host "mqtt.example.com"
 +     Prefix "collectd"
 +   </Publish>
 +   <Subscribe "name">
 +     Host "mqtt.example.com"
 +     Topic "collectd/#"
 +   </Subscribe>
 + </Plugin>
 +
 +The plugin's configuration is in B<Publish> and/or B<Subscribe> blocks,
 +configuring the sending and receiving direction respectively. The plugin will
 +register a write callback named C<mqtt/I<name>> where I<name> is the string
 +argument given to the B<Publish> block. Both types of blocks share many but not
 +all of the following options. If an option is valid in only one of the blocks,
 +it will be mentioned explicitly.
 +
 +B<Options:>
 +
 +=over 4
 +
 +=item B<Host> I<Hostname>
 +
 +Hostname of the MQTT broker to connect to.
 +
 +=item B<Port> I<Service>
 +
 +Port number or service name of the MQTT broker to connect to.
 +
 +=item B<User> I<UserName>
 +
 +Username used when authenticating to the MQTT broker.
 +
 +=item B<Password> I<Password>
 +
 +Password used when authenticating to the MQTT broker.
 +
 +=item B<ClientId> I<ClientId>
 +
 +MQTT client ID to use. Defaults to the hostname used by I<collectd>.
 +
 +=item B<QoS> [B<0>-B<2>]
 +
 +Sets the I<Quality of Service>, with the values C<0>, C<1> and C<2> meaning:
 +
 +=over 4
 +
 +=item B<0>
 +
 +At most once
 +
 +=item B<1>
 +
 +At least once
 +
 +=item B<2>
 +
 +Exactly once
 +
 +=back
 +
 +In B<Publish> blocks, this option determines the QoS flag set on outgoing
 +messages and defaults to B<0>. In B<Subscribe> blocks, determines the maximum
 +QoS setting the client is going to accept and defaults to B<2>. If the QoS flag
 +on a message is larger than the maximum accepted QoS of a subscriber, the
 +message's QoS will be downgraded.
 +
 +=item B<Prefix> I<Prefix> (Publish only)
 +
 +This plugin will use one topic per I<value list> which will looks like a path.
 +I<Prefix> is used as the first path element and defaults to B<collectd>.
 +
 +An example topic name would be:
 +
 + collectd/cpu-0/cpu-user
 +
 +=item B<Retain> B<false>|B<true> (Publish only)
 +
 +Controls whether the MQTT broker will retain (keep a copy of) the last message
 +sent to each topic and deliver it to new subscribers. Defaults to B<false>.
 +
 +=item B<StoreRates> B<true>|B<false> (Publish only)
 +
 +Controls whether C<DERIVE> and C<COUNTER> metrics are converted to a I<rate>
 +before sending. Defaults to B<true>.
 +
 +=item B<CleanSession> B<true>|B<false> (Subscribe only)
 +
 +Controls whether the MQTT "cleans" the session up after the subscriber
 +disconnects or if it maintains the subscriber's subscriptions and all messages
 +that arrive while the subscriber is disconnected. Defaults to B<true>.
 +
 +=item B<Topic> I<TopicName> (Subscribe only)
 +
 +Configures the topic(s) to subscribe to. You can use the single level C<+> and
 +multi level C<#> wildcards. Defaults to B<collectd/#>, i.e. all topics beneath
 +the B<collectd> branch.
 +
 +=item B<CACert> I<file>
 +
 +Path to the PEM-encoded CA certificate file. Setting this option enables TLS
 +communication with the MQTT broker, and as such, B<Port> should be the TLS-enabled
 +port of the MQTT broker.
 +A valid TLS configuration requires B<CACert>, B<CertificateFile> and B<CertificateKeyFile>.
 +
 +=item B<CertificateFile> I<file>
 +
 +Path to the PEM-encoded certificate file to use as client certificate when
 +connecting to the MQTT broker.
 +A valid TLS configuration requires B<CACert>, B<CertificateFile> and B<CertificateKeyFile>.
 +
 +=item B<CertificateKeyFile> I<file>
 +
 +Path to the unencrypted PEM-encoded key file corresponding to B<CertificateFile>.
 +A valid TLS configuration requires B<CACert>, B<CertificateFile> and B<CertificateKeyFile>.
 +
 +=item B<TLSProtocol> I<protocol>
 +
 +If configured, this specifies the string protocol version (e.g. C<tlsv1>,
 +C<tlsv1.2>) to use for the TLS connection to the broker. If not set a default
 +version is used which depends on the version of OpenSSL the Mosquitto library
 +was linked against.
 +
 +=item B<CipherSuite> I<ciphersuite>
 +
 +A string describing the ciphers available for use. See L<ciphers(1)> and the
 +C<openssl ciphers> utility for more information. If unset, the default ciphers
 +will be used.
 +
 +
 +=back
 +
  =head2 Plugin C<mysql>
  
  The C<mysql plugin> requires B<mysqlclient> to be installed. It connects to
@@@ -4483,21 -4303,6 +4481,21 @@@ Default: C<Collectd notify: %s@%s
  
  =back
  
 +=head2 Plugin C<notify_nagios>
 +
 +The I<notify_nagios> plugin writes notifications to Nagios' I<command file> as
 +a I<passive service check result>.
 +
 +Available configuration options:
 +
 +=over 4
 +
 +=item B<CommandFile> I<Path>
 +
 +Sets the I<command file> to write to. Defaults to F</usr/local/nagios/var/rw/nagios.cmd>.
 +
 +=back
 +
  =head2 Plugin C<ntpd>
  
  The C<ntpd> plugin collects per-peer ntpd data such as time offset and time
@@@ -4735,16 -4540,6 +4733,16 @@@ The following options are accepted with
  Sets the URL to use to connect to the I<OpenLDAP> server. This option is
  I<mandatory>.
  
 +=item B<BindDN> I<BindDN>
 +
 +Name in the form of an LDAP distinguished name intended to be used for 
 +authentication. Defaults to empty string to establish an anonymous authorization.
 +
 +=item B<Password> I<Password>
 +
 +Password for simple bind authentication. If this option is not set, 
 +unauthenticated bind operation is used.
 +
  =item B<StartTLS> B<true|false>
  
  Defines whether TLS must be used when connecting to the I<OpenLDAP> server.
@@@ -5016,13 -4811,6 +5014,13 @@@ Default: B<0.9
  
  Sets the Time-To-Live of generated ICMP packets.
  
 +=item B<Size> I<size>
 +
 +Sets the size of the data payload in ICMP packet to specified I<size> (it
 +will be filled with regular ASCII pattern). If not set, default 56 byte
 +long string is used so that the packet size of an ICMPv4 packet is exactly
 +64 bytes, similar to the behaviour of normal ping(1) command.
 +
  =item B<SourceAddress> I<host>
  
  Sets the source address to use. I<host> may either be a numerical network
@@@ -5680,10 -5468,6 +5678,10 @@@ dispatched to the daemon using the spec
  allows to "group" several processes together. I<name> must not contain
  slashes.
  
 +=item B<CollectContextSwitch> I<Boolean>
 +
 +Collect context switch of the process.
 +
  =back
  
  =head2 Plugin C<protocols>
@@@ -6298,12 -6082,6 +6296,12 @@@ rate of counters and size of sets will 
  are unchanged. If set to B<True>, the such metrics are not dispatched and
  removed from the internal cache.
  
 +=item B<CounterSum> B<false>|B<true>
 +
 +When enabled, creates a C<count> metric which reports the change since the last
 +read. This option primarily exists for compatibility with the I<statsd>
 +implementation by Etsy.
 +
  =item B<TimerPercentile> I<Percent>
  
  Calculate and dispatch the configured percentile, i.e. compute the latency, so
@@@ -7258,17 -7036,17 +7256,17 @@@ setting B<name>
  B<address> means use the interface's mac address. This is useful since the
  interface path might change between reboots of a guest or across migrations.
  
 -=item B<PluginInstanceFormat> B<name|uuid>
 +=item B<PluginInstanceFormat> B<name|uuid|none>
  
  When the virt plugin logs data, it sets the plugin_instance of the collected
 -data according to this setting. The default is to use the guest name as provided
 -by the hypervisor, which is equal to setting B<name>.
 +data according to this setting. The default is to not set the plugin_instance.
  
 +B<name> means use the guest's name as provided by the hypervisor.
  B<uuid> means use the guest's UUID.
  
 -You can also specify combinations of these fields. For example B<name uuid>
 -means to concatenate the guest name and UUID (with a literal colon character
 -between, thus I<"foo:1234-1234-1234-1234">).
 +You can also specify combinations of the B<name> and B<uuid> fields.
 +For example B<name uuid> means to concatenate the guest name and UUID
 +(with a literal colon character between, thus I<"foo:1234-1234-1234-1234">).
  
  =back
  
@@@ -7343,14 -7121,6 +7341,14 @@@ Service name or port number to connect 
  
  Protocol to use when connecting to I<Graphite>. Defaults to C<tcp>.
  
 +=item B<ReconnectInterval> I<Seconds>
 +
 +When set to non-zero, forces the connection to the Graphite backend to be
 +closed and re-opend periodically. This behavior is desirable in environments
 +where the connection to the Graphite backend is done through load balancers,
 +for example. When set to zero, the default, the connetion is kept open for as
 +long as possible.
 +
  =item B<LogSendErrors> B<false>|B<true>
  
  If set to B<true> (the default), logs errors when sending data to I<Graphite>.
@@@ -7619,10 -7389,6 +7617,10 @@@ complete. When this limit is reached, t
  all the data in the current send buffer will probably be lost. Defaults to 0,
  which means the connection never times out.
  
 +=item B<LogHttpError> B<false>|B<true>
 +
 +Enables printing of HTTP error code to log. Turned off by default.
 +
  The C<write_http> plugin regularly submits the collected values to the HTTP
  server. How frequently this happens depends on how much data you are collecting
  and the size of B<BufferSize>. The optimal value to set B<Timeout> to is
@@@ -7749,20 -7515,14 +7747,20 @@@ Synopsis
          Host "localhost"
          Port "6379"
          Timeout 1000
 +        Prefix "collectd/"
 +        Database 1
 +        MaxSetSize -1
 +        StoreRates true
      </Node>
    </Plugin>
  
  Values are submitted to I<Sorted Sets>, using the metric name as the key, and
  the timestamp as the score. Retrieving a date range can then be done using the
  C<ZRANGEBYSCORE> I<Redis> command. Additionally, all the identifiers of these
 -I<Sorted Sets> are kept in a I<Set> called C<collectd/values> and can be
 -retrieved using the C<SMEMBERS> I<Redis> command. See
 +I<Sorted Sets> are kept in a I<Set> called C<collectd/values> (or
 +C<${prefix}/values> if the B<Prefix> option was specified) and can be retrieved
 +using the C<SMEMBERS> I<Redis> command. You can specify the database to use
 +with the B<Database> parameter (default is C<0>). See
  L<http://redis.io/commands#sorted_set> and L<http://redis.io/commands#set> for
  details.
  
@@@ -7797,28 -7557,6 +7795,28 @@@ that numerical port numbers must be giv
  
  The B<Timeout> option sets the socket connection timeout, in milliseconds.
  
 +=item B<Prefix> I<Prefix>
 +
 +Prefix used when constructing the name of the I<Sorted Sets> and the I<Set>
 +containing all metrics. Defaults to C<collectd/>, so metrics will have names
 +like C<collectd/cpu-0/cpu-user>. When setting this to something different, it
 +is recommended but not required to include a trailing slash in I<Prefix>.
 +
 +=item B<Database> I<Index>
 +
 +This index selects the redis database to use for writing operations. Defaults
 +to C<0>.
 +
 +=item B<MaxSetSize> I<Items>
 +
 +The B<MaxSetSize> option limits the number of items that the I<Sorted Sets> can
 +hold. Negative values for I<Items> sets no limit, which is the default behavior.
 +
 +=item B<StoreRates> B<true>|B<false>
 +
 +If set to B<true> (the default), convert counter values to rates. If set to
 +B<false> counter values are stored as is, i.e. as an increasing integer number.
 +
  =back
  
  =head2 Plugin C<write_riemann>
@@@ -8950,8 -8688,6 +8948,8 @@@ Available options
  
  =item B<TypeInstance> I<String>
  
 +=item B<MetaDataSet> I<String> I<String>
 +
  Set the appropriate field to the given string. The strings for plugin instance
  and type instance may be empty, the strings for host and plugin may not be
  empty. It's currently not possible to set the type of a value this way.
diff --combined src/processes.c
@@@ -180,9 -180,6 +180,9 @@@ typedef struct procstat_entry_
        derive_t io_syscr;
        derive_t io_syscw;
  
 +      derive_t cswitch_vol;
 +      derive_t cswitch_invol;
 +
        struct procstat_entry_s *next;
  } procstat_entry_t;
  
@@@ -214,17 -211,12 +214,17 @@@ typedef struct procsta
        derive_t io_syscr;
        derive_t io_syscw;
  
 +      derive_t cswitch_vol;
 +      derive_t cswitch_invol;
 +
        struct procstat   *next;
        struct procstat_entry_s *instances;
  } procstat_t;
  
  static procstat_t *list_head_g = NULL;
  
 +static _Bool report_ctx_switch = 0;
 +
  #if HAVE_THREAD_INFO
  static mach_port_t port_host_self;
  static mach_port_t port_task_self;
@@@ -262,19 -254,20 +262,19 @@@ static void ps_list_register (const cha
        procstat_t *ptr;
        int status;
  
 -      new = (procstat_t *) malloc (sizeof (procstat_t));
 +      new = calloc (1, sizeof (*new));
        if (new == NULL)
        {
 -              ERROR ("processes plugin: ps_list_register: malloc failed.");
 +              ERROR ("processes plugin: ps_list_register: calloc failed.");
                return;
        }
 -      memset (new, 0, sizeof (procstat_t));
        sstrncpy (new->name, name, sizeof (new->name));
  
  #if HAVE_REGEX_H
        if (regexp != NULL)
        {
                DEBUG ("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp, name);
 -              new->re = (regex_t *) malloc (sizeof (regex_t));
 +              new->re = malloc (sizeof (*new->re));
                if (new->re == NULL)
                {
                        ERROR ("processes plugin: ps_list_register: malloc failed.");
                if (status != 0)
                {
                        DEBUG ("ProcessMatch: compiling the regular expression \"%s\" failed.", regexp);
 -                      sfree(new->re);
 -                      sfree(new);
 +                      sfree (new->re);
 +                      sfree (new);
                        return;
                }
        }
@@@ -360,31 -353,6 +360,31 @@@ static int ps_list_match (const char *n
        return (0);
  } /* int ps_list_match */
  
 +static void ps_update_counter (
 +        _Bool init,
 +        derive_t *group_counter,
 +        derive_t *curr_counter, unsigned long *curr_value,
 +        derive_t new_counter, unsigned long new_value) {
 +    if (init)
 +    {
 +        *curr_value = new_value;
 +        *curr_counter += new_value;
 +        *group_counter += new_value;
 +        return;
 +    }
 +
 +    if (new_counter < *curr_counter)
 +    {
 +        *curr_value = new_counter + (ULONG_MAX - *curr_counter);
 +    }
 +    else
 +    {
 +        *curr_value = new_counter - *curr_counter;
 +    }
 +    *curr_counter = new_counter;
 +    *group_counter += *curr_value;
 +}
 +
  /* add process entry to 'instances' of process 'name' (or refresh it) */
  static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t *entry)
  {
  
        for (ps = list_head_g; ps != NULL; ps = ps->next)
        {
 +        _Bool want_init;
 +
                if ((ps_list_match (name, cmdline, ps)) == 0)
                        continue;
  
                {
                        procstat_entry_t *new;
  
 -                      new = (procstat_entry_t *) malloc (sizeof (procstat_entry_t));
 +                      new = calloc (1, sizeof (*new));
                        if (new == NULL)
                                return;
 -                      memset (new, 0, sizeof (procstat_entry_t));
                        new->id = entry->id;
  
                        if (pse == NULL)
                pse->io_wchar   = entry->io_wchar;
                pse->io_syscr   = entry->io_syscr;
                pse->io_syscw   = entry->io_syscw;
 +              pse->cswitch_vol   = entry->cswitch_vol;
 +              pse->cswitch_invol = entry->cswitch_invol;
  
                ps->num_proc   += pse->num_proc;
                ps->num_lwp    += pse->num_lwp;
                ps->io_syscr   += ((pse->io_syscr == -1)?0:pse->io_syscr);
                ps->io_syscw   += ((pse->io_syscw == -1)?0:pse->io_syscw);
  
 -              if ((entry->vmem_minflt_counter == 0)
 -                              && (entry->vmem_majflt_counter == 0))
 -              {
 -                      pse->vmem_minflt_counter += entry->vmem_minflt;
 -                      pse->vmem_minflt = entry->vmem_minflt;
 -
 -                      pse->vmem_majflt_counter += entry->vmem_majflt;
 -                      pse->vmem_majflt = entry->vmem_majflt;
 -              }
 -              else
 -              {
 -                      if (entry->vmem_minflt_counter < pse->vmem_minflt_counter)
 -                      {
 -                              pse->vmem_minflt = entry->vmem_minflt_counter
 -                                      + (ULONG_MAX - pse->vmem_minflt_counter);
 -                      }
 -                      else
 -                      {
 -                              pse->vmem_minflt = entry->vmem_minflt_counter - pse->vmem_minflt_counter;
 -                      }
 -                      pse->vmem_minflt_counter = entry->vmem_minflt_counter;
 -
 -                      if (entry->vmem_majflt_counter < pse->vmem_majflt_counter)
 -                      {
 -                              pse->vmem_majflt = entry->vmem_majflt_counter
 -                                      + (ULONG_MAX - pse->vmem_majflt_counter);
 -                      }
 -                      else
 -                      {
 -                              pse->vmem_majflt = entry->vmem_majflt_counter - pse->vmem_majflt_counter;
 -                      }
 -                      pse->vmem_majflt_counter = entry->vmem_majflt_counter;
 -              }
 -
 -              ps->vmem_minflt_counter += pse->vmem_minflt;
 -              ps->vmem_majflt_counter += pse->vmem_majflt;
 -
 -              if ((entry->cpu_user_counter == 0)
 -                              && (entry->cpu_system_counter == 0))
 -              {
 -                      pse->cpu_user_counter += entry->cpu_user;
 -                      pse->cpu_user = entry->cpu_user;
 -
 -                      pse->cpu_system_counter += entry->cpu_system;
 -                      pse->cpu_system = entry->cpu_system;
 -              }
 -              else
 -              {
 -                      if (entry->cpu_user_counter < pse->cpu_user_counter)
 -                      {
 -                              pse->cpu_user = entry->cpu_user_counter
 -                                      + (ULONG_MAX - pse->cpu_user_counter);
 -                      }
 -                      else
 -                      {
 -                              pse->cpu_user = entry->cpu_user_counter - pse->cpu_user_counter;
 -                      }
 -                      pse->cpu_user_counter = entry->cpu_user_counter;
 -
 -                      if (entry->cpu_system_counter < pse->cpu_system_counter)
 -                      {
 -                              pse->cpu_system = entry->cpu_system_counter
 -                                      + (ULONG_MAX - pse->cpu_system_counter);
 -                      }
 -                      else
 -                      {
 -                              pse->cpu_system = entry->cpu_system_counter - pse->cpu_system_counter;
 -                      }
 -                      pse->cpu_system_counter = entry->cpu_system_counter;
 -              }
 -
 -              ps->cpu_user_counter   += pse->cpu_user;
 -              ps->cpu_system_counter += pse->cpu_system;
 +              ps->cswitch_vol   += ((pse->cswitch_vol == -1)?0:pse->cswitch_vol);
 +              ps->cswitch_invol += ((pse->cswitch_invol == -1)?0:pse->cswitch_invol);
 +
 +              want_init = (entry->vmem_minflt_counter == 0)
 +                              && (entry->vmem_majflt_counter == 0);
 +              ps_update_counter (want_init,
 +                              &ps->vmem_minflt_counter,
 +                              &pse->vmem_minflt_counter, &pse->vmem_minflt,
 +                              entry->vmem_minflt_counter, entry->vmem_minflt);
 +              ps_update_counter (want_init,
 +                              &ps->vmem_majflt_counter,
 +                              &pse->vmem_majflt_counter, &pse->vmem_majflt,
 +                              entry->vmem_majflt_counter, entry->vmem_majflt);
 +
 +              want_init = (entry->cpu_user_counter == 0)
 +                              && (entry->cpu_system_counter == 0);
 +              ps_update_counter (want_init,
 +                              &ps->cpu_user_counter,
 +                              &pse->cpu_user_counter, &pse->cpu_user,
 +                              entry->cpu_user_counter, entry->cpu_user);
 +              ps_update_counter (want_init,
 +                              &ps->cpu_system_counter,
 +                              &pse->cpu_system_counter, &pse->cpu_system,
 +                              entry->cpu_system_counter, entry->cpu_system);
        }
  }
  
@@@ -497,8 -511,6 +497,8 @@@ static void ps_list_reset (void
                ps->io_wchar = -1;
                ps->io_syscr = -1;
                ps->io_syscw = -1;
 +              ps->cswitch_vol   = -1;
 +              ps->cswitch_invol = -1;
  
                pse_prev = NULL;
                pse = ps->instances;
@@@ -583,10 -595,6 +583,10 @@@ static int ps_config (oconfig_item_t *c
                        ps_list_register (c->values[0].value.string,
                                        c->values[1].value.string);
                }
 +              else if (strcasecmp (c->key, "CollectContextSwitch") == 0)
 +              {
 +                      cf_util_get_boolean (c, &report_ctx_switch);
 +              }
                else
                {
                        ERROR ("processes plugin: The `%s' configuration option is not "
@@@ -736,36 -744,19 +736,36 @@@ static void ps_submit_proc_list (procst
                plugin_dispatch_values (&vl);
        }
  
 +      if ( report_ctx_switch )
 +      {
 +              sstrncpy (vl.type, "contextswitch", sizeof (vl.type));
 +              sstrncpy (vl.type_instance, "voluntary", sizeof (vl.type_instance));
 +              vl.values[0].derive = ps->cswitch_vol;
 +              vl.values_len = 1;
 +              plugin_dispatch_values (&vl);
 +
 +              sstrncpy (vl.type, "contextswitch", sizeof (vl.type));
 +              sstrncpy (vl.type_instance, "involuntary", sizeof (vl.type_instance));
 +              vl.values[0].derive = ps->cswitch_invol;
 +              vl.values_len = 1;
 +              plugin_dispatch_values (&vl);
 +      }
 +
        DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; "
                        "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
                        "vmem_code = %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";",
 +                      "io_syscr = %"PRIi64"; io_syscw = %"PRIi64"; "
 +                      "cswitch_vol = %"PRIi64"; cswitch_invol = %"PRIi64";",
                        ps->name, ps->num_proc, ps->num_lwp,
                        ps->vmem_size, ps->vmem_rss,
                        ps->vmem_data, ps->vmem_code,
                        ps->vmem_minflt_counter, ps->vmem_majflt_counter,
                        ps->cpu_user_counter, ps->cpu_system_counter,
 -                      ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw);
 +                      ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw,
 +                      ps->cswitch_vol, ps->cswitch_invol);
  } /* void ps_submit_proc_list */
  
  #if KERNEL_LINUX || KERNEL_SOLARIS
@@@ -790,99 -781,42 +790,99 @@@ static void ps_submit_fork_rate (derive
  
  /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
  #if KERNEL_LINUX
 -static int ps_read_tasks (long pid)
 +static procstat_t *ps_read_tasks_status (long pid, procstat_t *ps)
  {
        char           dirname[64];
        DIR           *dh;
 +      char           filename[64];
 +      FILE          *fh;
        struct dirent *ent;
 -      int count = 0;
 +      derive_t cswitch_vol = 0;
 +      derive_t cswitch_invol = 0;
 +      char buffer[1024];
 +      char *fields[8];
 +      int numfields;
  
        ssnprintf (dirname, sizeof (dirname), "/proc/%li/task", pid);
  
        if ((dh = opendir (dirname)) == NULL)
        {
                DEBUG ("Failed to open directory `%s'", dirname);
 -              return (-1);
 +              return (NULL);
        }
  
        while ((ent = readdir (dh)) != NULL)
        {
 +              char *tpid;
 +
                if (!isdigit ((int) ent->d_name[0]))
                        continue;
 -              else
 -                      count++;
 +
 +              tpid = ent->d_name;
 +
 +              ssnprintf (filename, sizeof (filename), "/proc/%li/task/%s/status", pid, tpid);
 +              if ((fh = fopen (filename, "r")) == NULL)
 +              {
 +                      DEBUG ("Failed to open file `%s'", filename);
 +                      continue;
 +              }
 +
 +              while (fgets (buffer, sizeof(buffer), fh) != NULL)
 +              {
 +                      derive_t tmp;
 +                      char *endptr;
 +
 +                      if (strncmp (buffer, "voluntary_ctxt_switches", 23) != 0
 +                              && strncmp (buffer, "nonvoluntary_ctxt_switches", 26) != 0)
 +                              continue;
 +
 +                      numfields = strsplit (buffer, fields,
 +                              STATIC_ARRAY_SIZE (fields));
 +
 +                      if (numfields < 2)
 +                              continue;
 +
 +                      errno = 0;
 +                      endptr = NULL;
 +                      tmp = (derive_t) strtoll (fields[1], &endptr, /* base = */ 10);
 +                      if ((errno == 0) && (endptr != fields[1]))
 +                      {
 +                              if (strncmp (buffer, "voluntary_ctxt_switches", 23) == 0)
 +                              {
 +                                      cswitch_vol += tmp;
 +                              }
 +                              else if (strncmp (buffer, "nonvoluntary_ctxt_switches", 26) == 0)
 +                              {
 +                                      cswitch_invol += tmp;
 +                              }
 +                      }
 +              } /* while (fgets) */
 +
 +              if (fclose (fh))
 +              {
 +                      char errbuf[1024];
 +                              WARNING ("processes: fclose: %s",
 +                                      sstrerror (errno, errbuf, sizeof (errbuf)));
 +              }
        }
        closedir (dh);
  
 -      return ((count >= 1) ? count : 1);
 -} /* int *ps_read_tasks */
 +      ps->cswitch_vol = cswitch_vol;
 +      ps->cswitch_invol = cswitch_invol;
  
 -/* Read advanced virtual memory data from /proc/pid/status */
 -static procstat_t *ps_read_vmem (long pid, procstat_t *ps)
 +      return (ps);
 +} /* int *ps_read_tasks_status */
 +
 +/* Read data from /proc/pid/status */
 +static procstat_t *ps_read_status (long pid, procstat_t *ps)
  {
        FILE *fh;
        char buffer[1024];
        char filename[64];
 -      unsigned long long lib = 0;
 -      unsigned long long exe = 0;
 -      unsigned long long data = 0;
 +      unsigned long lib = 0;
 +      unsigned long exe = 0;
 +      unsigned long data = 0;
 +      unsigned long threads = 0;
        char *fields[8];
        int numfields;
  
  
        while (fgets (buffer, sizeof(buffer), fh) != NULL)
        {
 -              long long tmp;
 +              unsigned long tmp;
                char *endptr;
  
 -              if (strncmp (buffer, "Vm", 2) != 0)
 +              if (strncmp (buffer, "Vm", 2) != 0
 +                              && strncmp (buffer, "Threads", 7) != 0)
                        continue;
  
                numfields = strsplit (buffer, fields,
  
                errno = 0;
                endptr = NULL;
 -              tmp = strtoll (fields[1], &endptr, /* base = */ 10);
 +              tmp = strtoul (fields[1], &endptr, /* base = */ 10);
                if ((errno == 0) && (endptr != fields[1]))
                {
                        if (strncmp (buffer, "VmData", 6) == 0)
                        {
                                exe = tmp;
                        }
 +                      else if  (strncmp(buffer, "Threads", 7) == 0)
 +                      {
 +                              threads = tmp;
 +                      }
                }
        } /* while (fgets) */
  
  
        ps->vmem_data = data * 1024;
        ps->vmem_code = (exe + lib) * 1024;
 +      if (threads != 0)
 +              ps->num_lwp = threads;
  
        return (ps);
  } /* procstat_t *ps_read_vmem */
@@@ -1007,9 -934,9 +1007,9 @@@ static int ps_read_process (long pid, p
        char *fields[64];
        char  fields_len;
  
 -      int   buffer_len;
 +      size_t buffer_len;
  
 -      char *buffer_ptr;
 +      char  *buffer_ptr;
        size_t name_start_pos;
        size_t name_end_pos;
        size_t name_len;
        long long unsigned vmem_rss;
        long long unsigned stack_size;
  
 +      ssize_t status;
 +
        memset (ps, 0, sizeof (procstat_t));
  
        ssnprintf (filename, sizeof (filename), "/proc/%li/stat", pid);
  
 -      buffer_len = read_file_contents (filename,
 -                      buffer, sizeof(buffer) - 1);
 -      if (buffer_len <= 0)
 +      status = read_file_contents (filename, buffer, sizeof(buffer) - 1);
 +      if (status <= 0)
                return (-1);
 +      buffer_len = (size_t) status;
        buffer[buffer_len] = 0;
  
        /* The name of the process is enclosed in parens. Since the name can
        }
        else
        {
 -              if ( (ps->num_lwp = ps_read_tasks (pid)) == -1 )
 +              ps->num_lwp = strtoul (fields[17], /* endptr = */ NULL, /* base = */ 10);
 +              if ((ps_read_status(pid, ps)) == NULL)
                {
 -                      /* returns -1 => kernel 2.4 */
 -                      ps->num_lwp = 1;
 +                      /* No VMem data */
 +                      ps->vmem_data = -1;
 +                      ps->vmem_code = -1;
 +                      DEBUG("ps_read_process: did not get vmem data for pid %li", pid);
                }
 +              if (ps->num_lwp <= 0)
 +                      ps->num_lwp = 1;
                ps->num_proc = 1;
        }
  
        cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
        vmem_rss = vmem_rss * pagesize_g;
  
 -      if ( (ps_read_vmem(pid, ps)) == NULL)
 -      {
 -              /* No VMem data */
 -              ps->vmem_data = -1;
 -              ps->vmem_code = -1;
 -              DEBUG("ps_read_process: did not get vmem data for pid %li", pid);
 -      }
 -
        ps->cpu_user_counter = cpu_user_counter;
        ps->cpu_system_counter = cpu_system_counter;
        ps->vmem_size = (unsigned long) vmem_size;
                DEBUG("ps_read_process: not get io data for pid %li", pid);
        }
  
 +      if ( report_ctx_switch )
 +      {
 +              if ( (ps_read_tasks_status(pid, ps)) == NULL)
 +              {
 +                      ps->cswitch_vol = -1;
 +                      ps->cswitch_invol = -1;
 +
 +                      DEBUG("ps_read_tasks_status: not get context "
 +                                      "switch data for pid %li", pid);
 +              }
 +      }
 +
        /* success */
        return (0);
  } /* int ps_read_process (...) */
@@@ -1305,16 -1221,16 +1305,16 @@@ static char *ps_get_cmdline (long pid, 
  {
        char path[PATH_MAX];
        psinfo_t info;
 -      int status;
 +      ssize_t status;
  
        snprintf(path, sizeof (path), "/proc/%li/psinfo", pid);
  
        status = read_file_contents (path, (void *) &info, sizeof (info));
 -      if (status != sizeof (info))
 +      if ((status < 0) || (((size_t) status) != sizeof (info)))
        {
                ERROR ("processes plugin: Unexpected return value "
                                "while reading \"%s\": "
 -                              "Returned %i but expected %zu.",
 +                              "Returned %zd but expected %zu.",
                                path, status, buffer_size);
                return (NULL);
        }
@@@ -1346,15 -1262,18 +1346,15 @@@ static int ps_read_process(long pid, pr
        snprintf(f_usage, sizeof (f_usage), "/proc/%li/usage", pid);
  
  
 -      buffer = malloc(sizeof (pstatus_t));
 -      memset(buffer, 0, sizeof (pstatus_t));
 +      buffer = calloc(1, sizeof (pstatus_t));
        read_file_contents(filename, buffer, sizeof (pstatus_t));
        myStatus = (pstatus_t *) buffer;
  
 -      buffer = malloc(sizeof (psinfo_t));
 -      memset(buffer, 0, sizeof(psinfo_t));
 +      buffer = calloc(1, sizeof (psinfo_t));
        read_file_contents(f_psinfo, buffer, sizeof (psinfo_t));
        myInfo = (psinfo_t *) buffer;
  
 -      buffer = malloc(sizeof (prusage_t));
 -      memset(buffer, 0, sizeof(prusage_t));
 +      buffer = calloc(1, sizeof (prusage_t));
        read_file_contents(f_usage, buffer, sizeof (prusage_t));
        myUsage = (prusage_t *) buffer;
  
                ps->num_proc = 0;
                ps->num_lwp = 0;
                *state = (char) 'Z';
+               sfree(myStatus);
+               sfree(myInfo);
+               sfree(myUsage);
                return (0);
        } else {
                ps->num_proc = 1;
        ps->io_syscr = myUsage->pr_sysc;
        ps->io_syscw = myUsage->pr_sysc;
  
 +      /*
 +       * TODO: context switch counters for Solaris
 +   */
 +      ps->cswitch_vol   = -1;
 +      ps->cswitch_invol = -1;
 +
  
        /*
         * TODO: Find way of setting BLOCKED and PAGING status
   * are retrieved from kstat (module cpu, name sys, class misc, stat nthreads).
   * The result is the sum for all the threads created on each cpu
   */
 -static int read_fork_rate()
 +static int read_fork_rate (void)
  {
        extern kstat_ctl_t *kc;
        kstat_t *ksp_chain = NULL;
@@@ -1651,10 -1568,6 +1655,10 @@@ static int ps_read (void
  
                                pse.cpu_user_counter = task_absolutetime_info.total_user;
                                pse.cpu_system_counter = task_absolutetime_info.total_system;
 +
 +                              /* context switch counters not implemented */
 +                              pse.cswitch_vol   = -1;
 +                              pse.cswitch_invol = -1;
                        }
  
                        status = task_threads (task_list[task], &thread_list,
                pse.io_syscr = ps.io_syscr;
                pse.io_syscw = ps.io_syscw;
  
 +              pse.cswitch_vol = ps.cswitch_vol;
 +              pse.cswitch_invol = ps.cswitch_invol;
 +
                switch (state)
                {
                        case 'R': running++;  break;
                        pse.io_syscr = -1;
                        pse.io_syscw = -1;
  
 +                      /* context switch counters not implemented */
 +                      pse.cswitch_vol   = -1;
 +                      pse.cswitch_invol = -1;
 +
                        ps_list_add (procs[i].ki_comm, have_cmdline ? cmdline : NULL, &pse);
  
                        switch (procs[i].ki_stat)
                        pse.io_syscr = -1;
                        pse.io_syscw = -1;
  
 -                      pse.cswitch_vol = -1;
 +                      /* context switch counters not implemented */
 +                      pse.cswitch_vol   = -1;
                        pse.cswitch_invol = -1;
  
                        ps_list_add (procs[i].p_comm, have_cmdline ? cmdline : NULL, &pse);
                        pse.io_syscr = -1;
                        pse.io_syscw = -1;
  
 +                      pse.cswitch_vol   = -1;
 +                      pse.cswitch_invol = -1;
 +
                        ps_list_add (cmdline, cargs, &pse);
                } /* for (i = 0 .. nprocs) */