swap plugin: Improvements for the percent code:
[collectd.git] / src / swap.c
1 /**
2  * collectd - src/swap.c
3  * Copyright (C) 2005-2014  Florian octo Forster
4  * Copyright (C) 2009       Stefan Völkel
5  * Copyright (C) 2009       Manuel Sanmartin
6  * Copyright (C) 2010       Aurélien Reynaud
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; only version 2 of the License is applicable.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20  *
21  * Authors:
22  *   Florian octo Forster <octo at collectd.org>
23  *   Manuel Sanmartin
24  *   Aurélien Reynaud <collectd at wattapower.net>
25  **/
26
27 #if HAVE_CONFIG_H
28 # include "config.h"
29 # undef HAVE_CONFIG_H
30 #endif
31 /* avoid swap.h error "Cannot use swapctl in the large files compilation environment" */
32 #if HAVE_SYS_SWAP_H && !defined(_LP64) && _FILE_OFFSET_BITS == 64
33 #  undef _FILE_OFFSET_BITS
34 #  undef _LARGEFILE64_SOURCE
35 #endif
36
37 #include "collectd.h"
38 #include "common.h"
39 #include "plugin.h"
40
41 #if HAVE_SYS_SWAP_H
42 # include <sys/swap.h>
43 #endif
44 #if HAVE_VM_ANON_H
45 # include <vm/anon.h>
46 #endif
47 #if HAVE_SYS_PARAM_H
48 #  include <sys/param.h>
49 #endif
50 #if HAVE_SYS_SYSCTL_H
51 #  include <sys/sysctl.h>
52 #endif
53 #if HAVE_SYS_DKSTAT_H
54 #  include <sys/dkstat.h>
55 #endif
56 #if HAVE_KVM_H
57 #  include <kvm.h>
58 #endif
59
60 #if HAVE_STATGRAB_H
61 # include <statgrab.h>
62 #endif
63
64 #if HAVE_PERFSTAT
65 # include <sys/protosw.h>
66 # include <libperfstat.h>
67 #endif
68
69 #undef  MAX
70 #define MAX(x,y) ((x) > (y) ? (x) : (y))
71
72 #if KERNEL_LINUX
73 # define SWAP_HAVE_REPORT_BY_DEVICE 1
74 static derive_t pagesize;
75 static _Bool report_bytes = 0;
76 static _Bool report_by_device = 0;
77 /* #endif KERNEL_LINUX */
78
79 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
80 # define SWAP_HAVE_REPORT_BY_DEVICE 1
81 static derive_t pagesize;
82 static _Bool report_by_device = 0;
83 /* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS */
84
85 #elif defined(VM_SWAPUSAGE)
86 /* No global variables */
87 /* #endif defined(VM_SWAPUSAGE) */
88
89 #elif HAVE_LIBKVM_GETSWAPINFO
90 static kvm_t *kvm_obj = NULL;
91 int kvm_pagesize;
92 /* #endif HAVE_LIBKVM_GETSWAPINFO */
93
94 #elif HAVE_LIBSTATGRAB
95 /* No global variables */
96 /* #endif HAVE_LIBSTATGRAB */
97
98 #elif HAVE_PERFSTAT
99 static int pagesize;
100 /*# endif HAVE_PERFSTAT */
101
102 #else
103 # error "No applicable input method."
104 #endif /* HAVE_LIBSTATGRAB */
105
106 static _Bool values_absolute = 1;
107 static _Bool values_percentage = 0;
108
109 static const char *config_keys[] =
110 {
111         "ReportBytes",
112         "ReportByDevice",
113         "ValuesAbsolute",
114         "ValuesPercentage"
115 };
116 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
117
118 static int swap_config (const char *key, const char *value) /* {{{ */
119 {
120         if (strcasecmp ("ReportBytes", key) == 0)
121         {
122 #if KERNEL_LINUX
123                 report_bytes = IS_TRUE (value) ? 1 : 0;
124 #else
125                 WARNING ("swap plugin: The \"ReportBytes\" option is only "
126                                 "valid under Linux. "
127                                 "The option is going to be ignored.");
128 #endif
129         }
130         else if (strcasecmp ("ReportByDevice", key) == 0)
131         {
132 #if SWAP_HAVE_REPORT_BY_DEVICE
133                 if (IS_TRUE (value))
134                         report_by_device = 1;
135                 else
136                         report_by_device = 0;
137 #else
138                 WARNING ("swap plugin: The \"ReportByDevice\" option is not "
139                                 "supported on this platform. "
140                                 "The option is going to be ignored.");
141 #endif /* SWAP_HAVE_REPORT_BY_DEVICE */
142         }
143         else if (strcasecmp (key, "ValuesAbsolute") == 0)
144         {
145                 if (IS_TRUE (value))
146                         values_absolute = 1;
147                 else
148                         values_absolute = 0;
149
150                 return (0);
151         }
152         else if (strcasecmp (key, "ValuesPercentage") == 0)
153         {
154                 if (IS_TRUE (value))
155                         values_percentage = 1;
156                 else
157                         values_percentage = 0;
158
159                 return (0);
160         }
161         else
162         {
163                 return (-1);
164         }
165
166         return (0);
167 } /* }}} int swap_config */
168
169 static int swap_init (void) /* {{{ */
170 {
171 #if KERNEL_LINUX
172         pagesize = (derive_t) sysconf (_SC_PAGESIZE);
173 /* #endif KERNEL_LINUX */
174
175 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
176         /* getpagesize(3C) tells me this does not fail.. */
177         pagesize = (derive_t) getpagesize ();
178 /* #endif HAVE_SWAPCTL */
179
180 #elif defined(VM_SWAPUSAGE)
181         /* No init stuff */
182 /* #endif defined(VM_SWAPUSAGE) */
183
184 #elif HAVE_LIBKVM_GETSWAPINFO
185         if (kvm_obj != NULL)
186         {
187                 kvm_close (kvm_obj);
188                 kvm_obj = NULL;
189         }
190
191         kvm_pagesize = getpagesize ();
192
193         if ((kvm_obj = kvm_open (NULL, /* execfile */
194                                         NULL, /* corefile */
195                                         NULL, /* swapfile */
196                                         O_RDONLY, /* flags */
197                                         NULL)) /* errstr */
198                         == NULL)
199         {
200                 ERROR ("swap plugin: kvm_open failed.");
201                 return (-1);
202         }
203 /* #endif HAVE_LIBKVM_GETSWAPINFO */
204
205 #elif HAVE_LIBSTATGRAB
206         /* No init stuff */
207 /* #endif HAVE_LIBSTATGRAB */
208
209 #elif HAVE_PERFSTAT
210         pagesize = getpagesize();
211 #endif /* HAVE_PERFSTAT */
212
213         return (0);
214 } /* }}} int swap_init */
215
216 static void swap_submit (const char *plugin_instance, /* {{{ */
217                 const char *type, const char *type_instance,
218                 value_t value)
219 {
220         value_list_t vl = VALUE_LIST_INIT;
221
222         assert (type != NULL);
223
224         vl.values = &value;
225         vl.values_len = 1;
226         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
227         sstrncpy (vl.plugin, "swap", sizeof (vl.plugin));
228         if (plugin_instance != NULL)
229                 sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
230         sstrncpy (vl.type, type, sizeof (vl.type));
231         if (type_instance != NULL)
232                 sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
233
234         plugin_dispatch_values (&vl);
235 } /* }}} void swap_submit_inst */
236
237 static void swap_submit_gauge (char const *plugin_instance, char const *type, /* {{{ */
238                 char const *type_instance, gauge_t value)
239 {
240         value_t v;
241
242         v.gauge = value;
243         swap_submit (plugin_instance, type, type_instance, v);
244 } /* }}} void swap_submit_gauge */
245
246 #if KERNEL_LINUX || HAVE_PERFSTAT
247 static void swap_submit_derive (const char *plugin_instance, /* {{{ */
248                 const char *type_instance, derive_t value)
249 {
250         value_t v;
251
252         v.derive = value;
253         swap_submit (plugin_instance, "swap_io", type_instance, v);
254 } /* }}} void swap_submit_derive */
255 #endif
256
257 #if KERNEL_LINUX
258 static int swap_read_separate (void) /* {{{ */
259 {
260         FILE *fh;
261         char buffer[1024];
262
263         fh = fopen ("/proc/swaps", "r");
264         if (fh == NULL)
265         {
266                 char errbuf[1024];
267                 WARNING ("swap plugin: fopen (/proc/swaps) failed: %s",
268                                 sstrerror (errno, errbuf, sizeof (errbuf)));
269                 return (-1);
270         }
271
272         while (fgets (buffer, sizeof (buffer), fh) != NULL)
273         {
274                 char *fields[8];
275                 int numfields;
276                 char *endptr;
277
278                 char path[PATH_MAX];
279                 gauge_t size;
280                 gauge_t used;
281                 gauge_t free;
282
283                 numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
284                 if (numfields != 5)
285                         continue;
286
287                 sstrncpy (path, fields[0], sizeof (path));
288                 escape_slashes (path, sizeof (path));
289
290                 errno = 0;
291                 endptr = NULL;
292                 size = strtod (fields[2], &endptr);
293                 if ((endptr == fields[2]) || (errno != 0))
294                         continue;
295
296                 errno = 0;
297                 endptr = NULL;
298                 used = strtod (fields[3], &endptr);
299                 if ((endptr == fields[3]) || (errno != 0))
300                         continue;
301
302                 if (size < used)
303                         continue;
304
305                 free = size - used;
306
307                 if (values_absolute)
308                 {
309                         swap_submit_gauge (path, "swap", "used", used);
310                         swap_submit_gauge (path, "swap", "free", free);
311                 }
312                 if (values_percentage)
313                 {
314                         swap_submit_gauge (path, "percent", "used", 100.0 * used / (used + free));
315                         swap_submit_gauge (path, "percent", "free", 100.0 * free / (used + free));
316                 }
317         }
318
319         fclose (fh);
320
321         return (0);
322 } /* }}} int swap_read_separate */
323
324 static int swap_read_combined (void) /* {{{ */
325 {
326         FILE *fh;
327         char buffer[1024];
328
329         uint8_t have_data = 0;
330         gauge_t swap_used   = 0.0;
331         gauge_t swap_cached = 0.0;
332         gauge_t swap_free   = 0.0;
333         gauge_t swap_total  = 0.0;
334
335         fh = fopen ("/proc/meminfo", "r");
336         if (fh == NULL)
337         {
338                 char errbuf[1024];
339                 WARNING ("swap plugin: fopen (/proc/meminfo) failed: %s",
340                                 sstrerror (errno, errbuf, sizeof (errbuf)));
341                 return (-1);
342         }
343
344         while (fgets (buffer, sizeof (buffer), fh) != NULL)
345         {
346                 char *fields[8];
347                 int numfields;
348
349                 numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
350                 if (numfields < 2)
351                         continue;
352
353                 if (strcasecmp (fields[0], "SwapTotal:") == 0)
354                 {
355                         swap_total = strtod (fields[1], /* endptr = */ NULL);
356                         have_data |= 0x01;
357                 }
358                 else if (strcasecmp (fields[0], "SwapFree:") == 0)
359                 {
360                         swap_free = strtod (fields[1], /* endptr = */ NULL);
361                         have_data |= 0x02;
362                 }
363                 else if (strcasecmp (fields[0], "SwapCached:") == 0)
364                 {
365                         swap_cached = strtod (fields[1], /* endptr = */ NULL);
366                         have_data |= 0x04;
367                 }
368         }
369
370         fclose (fh);
371
372         if (have_data != 0x07)
373                 return (ENOENT);
374
375         if (isnan (swap_total)
376                         || (swap_total <= 0.0)
377                         || ((swap_free + swap_cached) > swap_total))
378                 return (EINVAL);
379
380         swap_used = swap_total - (swap_free + swap_cached);
381
382         if (values_absolute)
383         {
384                 swap_submit_gauge (NULL, "swap", "used",   1024.0 * swap_used);
385                 swap_submit_gauge (NULL, "swap", "free",   1024.0 * swap_free);
386                 swap_submit_gauge (NULL, "swap", "cached", 1024.0 * swap_cached);
387         }
388         if (values_percentage)
389         {
390                 swap_submit_gauge (NULL, "percent", "used",   100.0 * swap_used / swap_total);
391                 swap_submit_gauge (NULL, "percent", "free",   100.0 * swap_free / swap_total);
392                 swap_submit_gauge (NULL, "percent", "cached", 100.0 * swap_cached / swap_total);
393         }
394
395         return (0);
396 } /* }}} int swap_read_combined */
397
398 static int swap_read_io (void) /* {{{ */
399 {
400         FILE *fh;
401         char buffer[1024];
402
403         _Bool old_kernel = 0;
404
405         uint8_t have_data = 0;
406         derive_t swap_in  = 0;
407         derive_t swap_out = 0;
408
409         fh = fopen ("/proc/vmstat", "r");
410         if (fh == NULL)
411         {
412                 /* /proc/vmstat does not exist in kernels <2.6 */
413                 fh = fopen ("/proc/stat", "r");
414                 if (fh == NULL)
415                 {
416                         char errbuf[1024];
417                         WARNING ("swap: fopen: %s",
418                                         sstrerror (errno, errbuf, sizeof (errbuf)));
419                         return (-1);
420                 }
421                 else
422                         old_kernel = 1;
423         }
424
425         while (fgets (buffer, sizeof (buffer), fh) != NULL)
426         {
427                 char *fields[8];
428                 int numfields;
429
430                 numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
431
432                 if (!old_kernel)
433                 {
434                         if (numfields != 2)
435                                 continue;
436
437                         if (strcasecmp ("pswpin", fields[0]) == 0)
438                         {
439                                 strtoderive (fields[1], &swap_in);
440                                 have_data |= 0x01;
441                         }
442                         else if (strcasecmp ("pswpout", fields[0]) == 0)
443                         {
444                                 strtoderive (fields[1], &swap_out);
445                                 have_data |= 0x02;
446                         }
447                 }
448                 else /* if (old_kernel) */
449                 {
450                         if (numfields != 3)
451                                 continue;
452
453                         if (strcasecmp ("page", fields[0]) == 0)
454                         {
455                                 strtoderive (fields[1], &swap_in);
456                                 strtoderive (fields[2], &swap_out);
457                         }
458                 }
459         } /* while (fgets) */
460
461         fclose (fh);
462
463         if (have_data != 0x03)
464                 return (ENOENT);
465
466         if (report_bytes)
467         {
468                 swap_in = swap_in * pagesize;
469                 swap_out = swap_out * pagesize;
470         }
471
472         swap_submit_derive (NULL, "in",  swap_in);
473         swap_submit_derive (NULL, "out", swap_out);
474
475         return (0);
476 } /* }}} int swap_read_io */
477
478 static int swap_read (void) /* {{{ */
479 {
480         if (report_by_device)
481                 swap_read_separate ();
482         else
483                 swap_read_combined ();
484
485         swap_read_io ();
486
487         return (0);
488 } /* }}} int swap_read */
489 /* #endif KERNEL_LINUX */
490
491 /*
492  * Under Solaris, two mechanisms can be used to read swap statistics, swapctl
493  * and kstat. The former reads physical space used on a device, the latter
494  * reports the view from the virtual memory system. It was decided that the
495  * kstat-based information should be moved to the "vmem" plugin, but nobody
496  * with enough Solaris experience was available at that time to do this. The
497  * code below is still there for your reference but it won't be activated in
498  * *this* plugin again. --octo
499  */
500 #elif 0 && HAVE_LIBKSTAT
501 /* kstat-based read function */
502 static int swap_read_kstat (void) /* {{{ */
503 {
504         gauge_t swap_alloc;
505         gauge_t swap_resv;
506         gauge_t swap_avail;
507
508         struct anoninfo ai;
509
510         if (swapctl (SC_AINFO, &ai) == -1)
511         {
512                 char errbuf[1024];
513                 ERROR ("swap plugin: swapctl failed: %s",
514                                 sstrerror (errno, errbuf, sizeof (errbuf)));
515                 return (-1);
516         }
517
518         /*
519          * Calculations from:
520          * http://cvs.opensolaris.org/source/xref/on/usr/src/cmd/swap/swap.c
521          * Also see:
522          * http://www.itworld.com/Comp/2377/UIR980701perf/ (outdated?)
523          * /usr/include/vm/anon.h
524          *
525          * In short, swap -s shows: allocated + reserved = used, available
526          *
527          * However, Solaris does not allow to allocated/reserved more than the
528          * available swap (physical memory + disk swap), so the pedant may
529          * prefer: allocated + unallocated = reserved, available
530          *
531          * We map the above to: used + resv = n/a, free
532          *
533          * Does your brain hurt yet?  - Christophe Kalt
534          *
535          * Oh, and in case you wonder,
536          * swap_alloc = pagesize * ( ai.ani_max - ai.ani_free );
537          * can suffer from a 32bit overflow.
538          */
539         swap_alloc = (gauge_t) ((ai.ani_max - ai.ani_free) * pagesize);
540         swap_resv  = (gauge_t) ((ai.ani_resv + ai.ani_free - ai.ani_max) * pagesize);
541         swap_avail = (gauge_t) ((ai.ani_max - ai.ani_resv) * pagesize);
542
543         if (values_absolute)
544         {
545                 swap_submit_gauge (NULL, "swap", "used", swap_alloc);
546                 swap_submit_gauge (NULL, "swap", "free", swap_avail);
547                 swap_submit_gauge (NULL, "swap", "reserved", swap_resv);
548         }
549         if (values_percentage)
550         {
551                 gauge_t swap_total = swap_alloc + swap_avail + swap_resv;
552
553                 swap_submit_gauge (NULL, "percent", "used", 100.0 * swap_alloc / swap_total);
554                 swap_submit_gauge (NULL, "percent", "free", 100.0 * swap_avail / swap_total);
555                 swap_submit_gauge (NULL, "percent", "cached", 100.0 * swap_resv / swap_total);
556         }
557
558         return (0);
559 } /* }}} int swap_read_kstat */
560 /* #endif 0 && HAVE_LIBKSTAT */
561
562 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
563 /* swapctl-based read function */
564 static int swap_read (void) /* {{{ */
565 {
566         swaptbl_t *s;
567         char *s_paths;
568         int swap_num;
569         int status;
570         int i;
571
572         gauge_t avail = 0;
573         gauge_t total = 0;
574
575         swap_num = swapctl (SC_GETNSWP, NULL);
576         if (swap_num < 0)
577         {
578                 ERROR ("swap plugin: swapctl (SC_GETNSWP) failed with status %i.",
579                                 swap_num);
580                 return (-1);
581         }
582         else if (swap_num == 0)
583                 return (0);
584
585         /* Allocate and initialize the swaptbl_t structure */
586         s = (swaptbl_t *) smalloc (swap_num * sizeof (swapent_t) + sizeof (struct swaptable));
587         if (s == NULL)
588         {
589                 ERROR ("swap plugin: smalloc failed.");
590                 return (-1);
591         }
592
593         /* Memory to store the path names. We only use these paths when the
594          * separate option has been configured, but it's easier to just
595          * allocate enough memory in any case. */
596         s_paths = calloc (swap_num, PATH_MAX);
597         if (s_paths == NULL)
598         {
599                 ERROR ("swap plugin: malloc failed.");
600                 sfree (s);
601                 return (-1);
602         }
603         for (i = 0; i < swap_num; i++)
604                 s->swt_ent[i].ste_path = s_paths + (i * PATH_MAX);
605         s->swt_n = swap_num;
606
607         status = swapctl (SC_LIST, s);
608         if (status < 0)
609         {
610                 char errbuf[1024];
611                 ERROR ("swap plugin: swapctl (SC_LIST) failed: %s",
612                                 sstrerror (errno, errbuf, sizeof (errbuf)));
613                 sfree (s_paths);
614                 sfree (s);
615                 return (-1);
616         }
617         else if (swap_num < status)
618         {
619                 /* more elements returned than requested */
620                 ERROR ("swap plugin: I allocated memory for %i structure%s, "
621                                 "but swapctl(2) claims to have returned %i. "
622                                 "I'm confused and will give up.",
623                                 swap_num, (swap_num == 1) ? "" : "s",
624                                 status);
625                 sfree (s_paths);
626                 sfree (s);
627                 return (-1);
628         }
629         else if (swap_num > status)
630                 /* less elements returned than requested */
631                 swap_num = status;
632
633         for (i = 0; i < swap_num; i++)
634         {
635                 char path[PATH_MAX];
636                 gauge_t this_total;
637                 gauge_t this_avail;
638
639                 if ((s->swt_ent[i].ste_flags & ST_INDEL) != 0)
640                         continue;
641
642                 this_total = (gauge_t) (s->swt_ent[i].ste_pages * pagesize);
643                 this_avail = (gauge_t) (s->swt_ent[i].ste_free  * pagesize);
644
645                 /* Shortcut for the "combined" setting (default) */
646                 if (!report_by_device)
647                 {
648                         avail += this_avail;
649                         total += this_total;
650                         continue;
651                 }
652
653                 sstrncpy (path, s->swt_ent[i].ste_path, sizeof (path));
654                 escape_slashes (path, sizeof (path));
655
656                 if (values_absolute)
657                 {
658                         swap_submit_gauge (path, "swap", "used", this_total - this_avail);
659                         swap_submit_gauge (path, "swap", "free", this_avail);
660                 }
661                 if (values_percentage)
662                 {
663                         swap_submit_gauge (path, "percent", "used", 100.0 * (this_total - this_avail) / this_total);
664                         swap_submit_gauge (path, "percent", "free", 100.0 * this_avail / this_total);
665                 }
666
667         } /* for (swap_num) */
668
669         if (total < avail)
670         {
671                 ERROR ("swap plugin: Total swap space (%g) is less than free swap space (%g).",
672                                 total, avail);
673                 sfree (s_paths);
674                 sfree (s);
675                 return (-1);
676         }
677
678         /* If the "separate" option was specified (report_by_device == 2), all
679          * values have already been dispatched from within the loop. */
680         if (!report_by_device)
681         {
682                 if (values_absolute)
683                 {
684                         swap_submit_gauge (NULL, "swap", "used", (total - avail));
685                         swap_submit_gauge (NULL, "swap", "free", avail);
686                 }
687                 if (values_percentage)
688                 {
689                         swap_submit_gauge (NULL, "percent", "used", 100.0 * (total - avail) / total);
690                         swap_submit_gauge (NULL, "percent", "free", 100.0 * avail / total);
691                 }
692         }
693
694         sfree (s_paths);
695         sfree (s);
696         return (0);
697 } /* }}} int swap_read */
698 /* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS */
699
700 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS
701 static int swap_read (void) /* {{{ */
702 {
703         struct swapent *swap_entries;
704         int swap_num;
705         int status;
706         int i;
707
708         gauge_t used  = 0;
709         gauge_t total = 0;
710
711         swap_num = swapctl (SWAP_NSWAP, NULL, 0);
712         if (swap_num < 0)
713         {
714                 ERROR ("swap plugin: swapctl (SWAP_NSWAP) failed with status %i.",
715                                 swap_num);
716                 return (-1);
717         }
718         else if (swap_num == 0)
719                 return (0);
720
721         swap_entries = calloc (swap_num, sizeof (*swap_entries));
722         if (swap_entries == NULL)
723         {
724                 ERROR ("swap plugin: calloc failed.");
725                 return (-1);
726         }
727
728         status = swapctl (SWAP_STATS, swap_entries, swap_num);
729         if (status != swap_num)
730         {
731                 ERROR ("swap plugin: swapctl (SWAP_STATS) failed with status %i.",
732                                 status);
733                 sfree (swap_entries);
734                 return (-1);
735         }
736
737 #if defined(DEV_BSIZE) && (DEV_BSIZE > 0)
738 # define C_SWAP_BLOCK_SIZE ((gauge_t) DEV_BSIZE)
739 #else
740 # define C_SWAP_BLOCK_SIZE 512.0
741 #endif
742
743         for (i = 0; i < swap_num; i++)
744         {
745                 if ((swap_entries[i].se_flags & SWF_ENABLE) == 0)
746                         continue;
747
748                 used  += ((gauge_t) swap_entries[i].se_inuse) * C_SWAP_BLOCK_SIZE;
749                 total += ((gauge_t) swap_entries[i].se_nblks) * C_SWAP_BLOCK_SIZE;
750         }
751
752         if (total < used)
753         {
754                 ERROR ("swap plugin: Total swap space (%g) is less than used swap space (%g).",
755                                 total, used);
756                 return (-1);
757         }
758
759         if (values_absolute)
760         {
761                 swap_submit_gauge (NULL, "swap", "used", used);
762                 swap_submit_gauge (NULL, "swap", "free", (total - used));
763         }
764         if (values_percentage)
765         {
766                 swap_submit_gauge (NULL, "percent", "used", 100.0 * used / total);
767                 swap_submit_gauge (NULL, "percent", "free", 100.0 * (total - used) / total);
768         }
769
770         sfree (swap_entries);
771
772         return (0);
773 } /* }}} int swap_read */
774 /* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS */
775
776 #elif defined(VM_SWAPUSAGE)
777 static int swap_read (void) /* {{{ */
778 {
779         int              mib[3];
780         size_t           mib_len;
781         struct xsw_usage sw_usage;
782         size_t           sw_usage_len;
783
784         mib_len = 2;
785         mib[0]  = CTL_VM;
786         mib[1]  = VM_SWAPUSAGE;
787
788         sw_usage_len = sizeof (struct xsw_usage);
789
790         if (sysctl (mib, mib_len, &sw_usage, &sw_usage_len, NULL, 0) != 0)
791                 return (-1);
792
793         /* The returned values are bytes. */
794         if (values_absolute)
795         {
796                 swap_submit_gauge (NULL, "swap", "used", (gauge_t) sw_usage.xsu_used);
797                 swap_submit_gauge (NULL, "swap", "free", (gauge_t) sw_usage.xsu_avail);
798         }
799         if (values_percentage)
800         {
801                 gauge_t swap_total = (gauge_t) (sw_usage.xsu_used + sw_usage.xsu_avail);
802                 swap_submit_gauge (NULL, "percent", "used", 100.0 * ((gauge_t) sw_usage.xsu_used)  / swap_total);
803                 swap_submit_gauge (NULL, "percent", "free", 100.0 * ((gauge_t) sw_usage.xsu_avail) / swap_total);
804         }
805
806         return (0);
807 } /* }}} int swap_read */
808 /* #endif VM_SWAPUSAGE */
809
810 #elif HAVE_LIBKVM_GETSWAPINFO
811 static int swap_read (void) /* {{{ */
812 {
813         struct kvm_swap data_s;
814         int             status;
815
816         gauge_t used;
817         gauge_t free;
818         gauge_t total;
819
820         if (kvm_obj == NULL)
821                 return (-1);
822
823         /* only one structure => only get the grand total, no details */
824         status = kvm_getswapinfo (kvm_obj, &data_s, 1, 0);
825         if (status == -1)
826                 return (-1);
827
828         total = (gauge_t) data_s.ksw_total;
829         used  = (gauge_t) data_s.ksw_used;
830
831         total *= (gauge_t) kvm_pagesize;
832         used  *= (gauge_t) kvm_pagesize;
833
834         free = total - used;
835
836         if (values_absolute)
837         {
838                 swap_submit_gauge (NULL, "swap", "used", used);
839                 swap_submit_gauge (NULL, "swap", "free", free);
840         }
841         if (values_percentage)
842         {
843                 swap_submit_gauge (NULL, "percent", "used", 100.0 * used / total);
844                 swap_submit_gauge (NULL, "percent", "free", 100.0 * free / total);
845         }
846
847         return (0);
848 } /* }}} int swap_read */
849 /* #endif HAVE_LIBKVM_GETSWAPINFO */
850
851 #elif HAVE_LIBSTATGRAB
852 static int swap_read (void) /* {{{ */
853 {
854         sg_swap_stats *swap;
855
856         swap = sg_get_swap_stats ();
857
858         if (swap == NULL)
859                 return (-1);
860
861         if (values_absolute)
862         {
863                 swap_submit_gauge (NULL, "swap", "used", (gauge_t) swap->used);
864                 swap_submit_gauge (NULL, "swap", "free", (gauge_t) swap->free);
865         }
866         if (values_percentage)
867         {
868                 gauge_t swap_total = (gauge_t) (swap->used + swap->free);
869                 swap_submit_gauge (NULL, "percent", "used", 100.0 * ((gauge_t) swap->used) / swap_total);
870                 swap_submit_gauge (NULL, "percent", "free", 100.0 * ((gauge_t) swap->free) / swap_total);
871         }
872
873         return (0);
874 } /* }}} int swap_read */
875 /* #endif  HAVE_LIBSTATGRAB */
876
877 #elif HAVE_PERFSTAT
878 static int swap_read (void) /* {{{ */
879 {
880         perfstat_memory_total_t pmemory;
881         int status;
882
883         memset (&pmemory, 0, sizeof (pmemory));
884         status = perfstat_memory_total (NULL, &pmemory, sizeof(perfstat_memory_total_t), 1);
885         if (status < 0)
886         {
887                 char errbuf[1024];
888                 WARNING ("swap plugin: perfstat_memory_total failed: %s",
889                         sstrerror (errno, errbuf, sizeof (errbuf)));
890                 return (-1);
891         }
892
893         if (values_absolute)
894         {
895                 swap_submit_gauge (NULL, "swap", "used", (gauge_t) (pmemory.pgsp_total - pmemory.pgsp_free) * pagesize);
896                 swap_submit_gauge (NULL, "swap", "free", (gauge_t) pmemory.pgsp_free * pagesize);
897                 swap_submit_gauge (NULL, "swap", "reserved", (gauge_t) pmemory.pgsp_rsvd * pagesize);
898         }
899         if (values_percentage)
900         {
901                 gauge_t swap_total = (gauge_t) pmemory.pgsp_total;
902                 swap_submit_gauge (NULL, "percent", "used",     100.0 * ((gauge_t) (pmemory.pgsp_total - pmemory.pgsp_free)) / swap_total);
903                 swap_submit_gauge (NULL, "percent", "free",     100.0 * ((gauge_t) pmemory.pgsp_free) / swap_total);
904                 swap_submit_gauge (NULL, "percent", "reserved", 100.0 * ((gauge_t) pmemory.pgsp_rsvd) / swap_total);
905         }
906         swap_submit_derive (NULL, "in",  (derive_t) pmemory.pgspins * pagesize);
907         swap_submit_derive (NULL, "out", (derive_t) pmemory.pgspouts * pagesize);
908
909         return (0);
910 } /* }}} int swap_read */
911 #endif /* HAVE_PERFSTAT */
912
913 void module_register (void)
914 {
915         plugin_register_config ("swap", swap_config,
916                         config_keys, config_keys_num);
917         plugin_register_init ("swap", swap_init);
918         plugin_register_read ("swap", swap_read);
919 } /* void module_register */
920
921 /* vim: set fdm=marker : */