34e02df3cb1f013984d907d77d4a47dc8054a0e7
[collectd.git] / src / irq.c
1 /**
2  * collectd - src/irq.c
3  * Copyright (C) 2007  Peter Holik
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Peter Holik <peter at holik.at>
21  **/
22
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26 #include "configfile.h"
27
28 #define MODULE_NAME "irq"
29
30 #if KERNEL_LINUX
31 # define IRQ_HAVE_READ 1
32 #else
33 # define IRQ_HAVE_READ 0
34 #endif
35
36 #define BUFSIZE 128
37
38 /*
39  * (Module-)Global variables
40  */
41 static char *irq_file   = "irq-%s.rrd";
42
43 static char *config_keys[] =
44 {
45         "Irq",
46         "IgnoreSelected",
47         NULL
48 };
49 static int config_keys_num = 2;
50
51 static char *ds_def[] =
52 {
53         "DS:irq:COUNTER:"COLLECTD_HEARTBEAT":0:U",
54         NULL
55 };
56 static int ds_num = 1;
57
58 static unsigned int *irq_list;
59 static unsigned int irq_list_num;
60
61 static int base = 10;
62
63 /* 
64  * irq_list_action:
65  * 0 => default is to collect selected irqs
66  * 1 => ignore selcted irqs
67  */
68 static int irq_list_action;
69
70 static int irq_config (char *key, char *value)
71 {
72         unsigned int *temp;
73         unsigned int irq;
74         char *endptr;
75
76         if (strcasecmp (key, "Irq") == 0)
77         {
78                 temp = (unsigned int *) realloc (irq_list, (irq_list_num + 1) * sizeof (unsigned int *));
79                 if (temp == NULL)
80                 {
81                         syslog (LOG_EMERG, "Cannot allocate more memory.");
82                         return (1);
83                 }
84                 irq_list = temp;
85
86                 irq = strtol(value, &endptr, base);
87
88                 if (endptr == value ||
89                     (errno == ERANGE && (irq == LONG_MAX || irq == LONG_MIN)) ||
90                     (errno != 0 && irq == 0))
91                 {
92                         syslog (LOG_EMERG, "Irq value is not a number.");
93                         return (1);
94                 }
95                 irq_list[irq_list_num] = irq;
96                 irq_list_num++;
97         }
98         else if (strcasecmp (key, "IgnoreSelected") == 0)
99         {
100                 if ((strcasecmp (value, "True") == 0)
101                                 || (strcasecmp (value, "Yes") == 0)
102                                 || (strcasecmp (value, "On") == 0))
103                         irq_list_action = 1;
104                 else
105                         irq_list_action = 0;
106         }
107         else
108         {
109                 return (-1);
110         }
111         return (0);
112 }
113
114 /*
115  * Check if this interface/instance should be ignored. This is called from
116  * both, `submit' and `write' to give client and server the ability to
117  * ignore certain stuff..
118  */
119 static int check_ignore_irq (const unsigned int irq)
120 {
121         int i;
122
123         if (irq_list_num < 1)
124                 return (0);
125
126         for (i = 0; i < irq_list_num; i++)
127                 if (irq == irq_list[i])
128                         return (irq_list_action);
129
130         return (1 - irq_list_action);
131 }
132
133 static void irq_write (char *host, char *inst, char *value)
134 {
135         char file[BUFSIZE];
136         int status;
137
138         if (check_ignore_irq (atoi(inst)))
139                 return;
140
141         status = snprintf (file, BUFSIZE, irq_file, inst);
142         if (status < 1)
143                 return;
144         else if (status >= BUFSIZE)
145                 return;
146
147         rrd_update_file (host, file, value, ds_def, ds_num);
148 }
149
150 #if IRQ_HAVE_READ
151 static void irq_submit (unsigned int irq, unsigned int value, char *devices)
152 {
153         char buf[BUFSIZE];
154         char desc[BUFSIZE];
155         int  status;
156
157         if (check_ignore_irq (irq))
158                 return;
159
160         status = snprintf (buf, BUFSIZE, "%u:%u",
161                                 (unsigned int) curtime, value);
162
163         if ((status >= BUFSIZE) || (status < 1))
164                 return;
165
166         status = snprintf (desc, BUFSIZE, "%d-%s", irq, devices);
167
168         if ((status >= BUFSIZE) || (status < 1))
169                 return;
170
171         plugin_submit (MODULE_NAME, desc, buf);
172 }
173
174 static void irq_read (void)
175 {
176 #if KERNEL_LINUX
177
178 #undef BUFSIZE
179 #define BUFSIZE 256
180
181         FILE *fh;
182         char buffer[BUFSIZE];
183         unsigned int irq;
184         unsigned int irq_value;
185         long value;
186         char *ptr, *endptr;
187
188         if ((fh = fopen ("/proc/interrupts", "r")) == NULL)
189         {
190                 syslog (LOG_WARNING, "irq: fopen: %s", strerror (errno));
191                 return;
192         }
193         while (fgets (buffer, BUFSIZE, fh) != NULL)
194         {
195                 errno = 0;    /* To distinguish success/failure after call */
196                 irq = strtol(buffer, &endptr, base);
197
198                 if (endptr == buffer ||
199                     (errno == ERANGE && (irq == LONG_MAX || irq == LONG_MIN)) ||
200                     (errno != 0 && irq == 0)) continue;
201
202                 if (*endptr != ':') continue;
203
204                 ptr = ++endptr;
205
206                 irq_value = 0;
207                 /* sum irq's for all CPUs */
208                 while (1)
209                 {
210                         errno = 0;
211                         value = strtol(ptr, &endptr, base);
212
213                         if (endptr == ptr ||
214                             (errno == ERANGE &&
215                                 (value == LONG_MAX || value == LONG_MIN)) ||
216                             (errno != 0 && value == 0)) break;
217
218                         irq_value += value;
219                         ptr = endptr;
220                 }
221                 while (*ptr == ' ') ptr++;
222                 while (*ptr && *ptr != ' ') ptr++;
223                 while (*ptr == ' ') ptr++;
224
225                 if (!*ptr) continue;
226
227                 endptr = ptr;
228
229                 while (*(++endptr))
230                         if (!isalnum(*endptr)) *endptr='_';
231
232                 ptr[strlen(ptr)-1] = '\0';
233
234                 irq_submit (irq, irq_value, ptr);
235         }
236         fclose (fh);
237 #endif /* KERNEL_LINUX */
238 }
239 #else
240 #define irq_read NULL
241 #endif /* IRQ_HAVE_READ */
242
243 void module_register (void)
244 {
245         plugin_register (MODULE_NAME, NULL, irq_read, irq_write);
246         cf_register (MODULE_NAME, irq_config, config_keys, config_keys_num);
247 }
248
249 #undef BUFSIZE
250 #undef MODULE_NAME