1 /*
2 * This file is an amended version of solaris.c included in ganglia 2.5.4
3 * and ganglia 2.5.5. It has been modified by Adeyemi Adesanya
4 * (yemi@slac.stanford.edu) to allow gmond to run as a non-root account. The
5 * kvm dependency has been removed and all statistics are now obtained via
6 * kstat.
7 *
8 * It appears to run just fine in Solaris 9 and should be OK in 8 also.
9 * Earlier versions of solaris may not provide all the data via kstat.
10 * Try running 'kstat cpu_stat' from the command line.
11 *
12 * Modifications made by Michael Hom:
13 *
14 * - Stop gmond from coredumping when CPU is "unusual".
15 *
16 * Modifications made by JB Kim:
17 *
18 * - Stop gmond from coredumping when CPUs are "offline".
19 *
20 * Modifications made by Robert Petkus:
21 *
22 * - Take care of the case that the number of active CPUs is smaller than
23 * number of installed CPUs. That would result in a sparse numering of
24 * CPUs and lead to a core dump with the old algorithm.
25 *
26 * Modifications made by Martin Knoblauch:
27 *
28 * - Add proc_run statistics - may need finetuning
29 * - Add bytes_in, bytes_out, pkts_in and pkts_out
30 * - Fix misallocation of "buffers" (3 needed instead of 2 !!!)
31 * array in determine_cpu_percentages
32 * - Port to new get_ifi_info() functionality
33 * - Optimize use of kstat_open(). Assuming that the number and
34 * composition of the kstat headers is relatively static compared
35 * to the frequency of metrics calls in the server thread, it is
36 * a lot cheaper to call kstat_open() once [in the context of the
37 * server thread !!!] and then call kstat_chain_update() for each
38 * metrics retrieval. kstat_chain_update() is a noop if the kstat
39 * header chain has not changed between calls.
40 * - move cpu_speed and boottime to metric_init
41 * - kill get_metric_val. Dead code.
42 * - fix potential data corruption when calculating CPU percentages
43 *
44 * Modifications made by Carlo Marcelo Arenas Belon:
45 * - Add disk_total, disk_free, part_max_used
46 *
47 * Tested on Solaris 7 x86 (32-bit) with gcc-2.8.1
48 * Tested on Solaris 8 (64-bit) with gcc-3.3.1
49 * Tested on Solaris 9 (64-bit) with gcc-3.4.4
50 * Tested on Solaris 10 SPARC (64-bit) and x86 (32-bit and 64-bit)
51 */
52
53 #include "interface.h"
54 #include "libmetrics.h"
55
56 #include <kstat.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <unistd.h>
60 #include <sys/utsname.h>
61 #include <strings.h>
62 #include <sys/types.h>
63 #include <dirent.h>
64 #include <procfs.h>
65 #include <errno.h>
66
67 /*
68 * used for swap space determination - swapctl()
69 * and anon.h has the data structure needed for swapctl to be useful
70 */
71
72 #include <sys/stat.h>
73 #include <sys/swap.h>
74 #include <vm/anon.h>
75 #include <fcntl.h>
76
77 /*
78 * we get the cpu struct from cpuvar, maybe other mojo too
79 */
80
81 #include <sys/var.h>
82 #include <sys/cpuvar.h>
83 #include <sys/time.h>
84 #include <sys/processor.h>
85 /*
86 * functions spackled in by swagner -- the CPU-percentage-specific code is
87 * largely an imitation (if not a shameless copy) of the Solaris-specific
88 * code for top.
89 */
90
91 /*
92 * used for disk space determination - getmntent(), statvfs()
93 */
94
95 #include <sys/mnttab.h>
96 #include <sys/statvfs.h>
97
98 /* number of seconds to wait before refreshing/recomputing values off kstat */
99
100 #define TICK_SECONDS 30
101
102 #ifndef FSCALE
103 #define FSHIFT 8 /* bits to right of fixed binary point */
104 #define FSCALE (1<<FSHIFT)
105 #endif /* FSCALE */
106
107 /* also imported from top, i'm basically using the same CPU cycle summing algo */
108
109 #define CPUSTATES 5
110 #define CPUSTATE_IDLE 0
111 #define CPUSTATE_USER 1
112 #define CPUSTATE_KERNEL 2
113 #define CPUSTATE_IOWAIT 3
114 #define CPUSTATE_SWAP 4
115
116 int first_run = 1;
117
118 /* support macros for the percentage computations */
119
120 #define loaddouble(la) ((double)(la) / FSCALE)
121
122 static struct utsname unamedata;
123
124 struct cpu_info {
125 unsigned long bread;
126 unsigned long bwrite;
127 unsigned long lread;
128 unsigned long lwrite;
129 unsigned long phread;
130 unsigned long phwrite;
131 };
132
133 struct g_metrics_struct {
134 g_val_t boottime;
135 g_val_t cpu_wio;
136 g_val_t cpu_idle;
137 g_val_t cpu_aidle;
138 g_val_t cpu_nice;
139 g_val_t cpu_system;
140 g_val_t cpu_user;
141 g_val_t cpu_num;
142 g_val_t cpu_speed;
143 g_val_t load_one;
144 g_val_t load_five;
145 g_val_t load_fifteen;
146 char * machine_type;
147 g_val_t mem_buffers;
148 g_val_t mem_cached;
149 g_val_t mem_free;
150 g_val_t mem_shared;
151 g_val_t mem_total;
152 char * os_name;
153 char * os_release;
154 g_val_t proc_run;
155 g_val_t proc_total;
156 g_val_t swap_free;
157 g_val_t swap_total;
158 g_val_t sys_clock;
159 g_val_t bread_sec;
160 g_val_t bwrite_sec;
161 g_val_t lread_sec;
162 g_val_t lwrite_sec;
163 g_val_t phread_sec;
164 g_val_t phwrite_sec;
165 g_val_t rcache;
166 g_val_t wcache;
167 g_val_t pkts_in;
168 g_val_t pkts_out;
169 g_val_t bytes_in;
170 g_val_t bytes_out;
171 };
172
173 static struct g_metrics_struct metriclist;
174 static kstat_ctl_t *kc=NULL;
175 static int g_ncpus;
176
177 int
get_kstat_val(g_val_t * val,char * km_name,char * ks_name,char * name)178 get_kstat_val(g_val_t *val, char *km_name, char *ks_name, char *name)
179 {
180 /* Warning.. always assuming a KSTAT_DATA_ULONG here */
181 kstat_t *ks;
182 kstat_named_t *kn;
183
184 /*
185 * Get a kstat_ctl handle, or update the kstat chain.
186 */
187 if (kc == NULL)
188 kc = kstat_open();
189 else
190 kstat_chain_update(kc);
191
192 if (kc == NULL)
193 {
194 debug_msg("couldn't open kc...");
195 err_ret("get_kstat_val() kstat_open() error");
196 return SYNAPSE_FAILURE;
197 }
198
199 debug_msg( "Lookup up kstat: km (unix?)='%s', ks (system_misc?)='%s',kn (resulting metric?)='%s'", km_name, ks_name, name);
200 debug_msg( "%s: kc is %p", name, kc);
201 ks = kstat_lookup(kc, km_name, 0, ks_name);
202 debug_msg("%s: Just did kstat_lookup().",name);
203
204 /*
205 * A hack contributed by Michael Hom <michael_hom_work@yahoo.com>
206 * cpu_info0 doesn't always exist on Solaris, as the first CPU
207 * need not be in slot 0.
208 * Therefore, if ks == NULL after kstat_lookup(), we try
209 * to find the first valid instance using the query:
210 * ks = kstat_lookup(kc, km_name, -1, NULL);
211 */
212
213 if ((strcmp(km_name, "cpu_info") == 0) && (ks == NULL)) {
214 debug_msg( "Lookup up kstat: km (unix?)='%s', ks (system_misc?)='NULL',kn (resulting metric?)='%s'", km_name, name);
215 ks = kstat_lookup(kc, km_name, -1, NULL);
216 debug_msg("Just did kstat_lookup() on first instance of module %s.\n",km_name);
217 }
218
219 if (ks == NULL)
220 {
221 perror("ks");
222 }
223 debug_msg("%s: Looked up.", name);
224 if (kstat_read(kc, ks, 0) == -1) {
225 perror("kstat_read");
226 return SYNAPSE_FAILURE;
227 }
228 kn = kstat_data_lookup(ks, name);
229 if ( kn == NULL )
230 {
231 err_ret("get_kstat_val() kstat_data_lookup() kstat_read() error");
232 return SYNAPSE_FAILURE;
233 }
234 debug_msg( "%s: Kstat data type: %d, Value returned: %u, %d %u %d", name, (int)kn->data_type, (int)kn->value.ui32, (int)kn->value.l, kn->value.ul, kn->value.ui64);
235 // ks = kstat_lookup(kc, "unix", 0, "system_misc");
236
237 if (kn->value.ui32 == 0)
238 val->uint32 = (unsigned long)kn->value.ul;
239 else
240 val->uint32 = (int)kn->value.ui32;
241 sleep(0);
242 debug_msg("%s: Kernel close. Val returned: %d", name, val->uint32);
243
244 return SYNAPSE_SUCCESS;
245 }
246
247 unsigned int
pagetok(int pageval)248 pagetok( int pageval )
249 {
250 unsigned int foo;
251 foo = pageval;
252 foo = foo * (sysconf(_SC_PAGESIZE) / 1024);
253 debug_msg("PageToK(): %u * PAGESIZE (%u) / 1024 (Kb conversion) = %u", pageval, sysconf(_SC_PAGESIZE), foo);
254 return foo;
255 }
256
257 /*
258 * there's too much legwork for each function to handle its metric.
259 * hence the updater.
260 */
261
262 void
determine_swap_space(unsigned int * total,unsigned int * fr)263 determine_swap_space( unsigned int *total, unsigned int *fr )
264 {
265 struct anoninfo anon;
266 if (swapctl(SC_AINFO, &anon) == -1 ) {
267 *total = *fr = 0;
268 return;
269 }
270 /* we are going from swap pages to kilobytes, so the conversion works... */
271 *total = pagetok(anon.ani_max);
272 *fr = pagetok((anon.ani_max - anon.ani_resv));
273 debug_msg("Old/new: Total = %u/%u , Free = %u/%u", anon.ani_max,*total,(anon.ani_max - anon.ani_resv),*fr);
274 return;
275 }
276
277 int
update_metric_data(void)278 update_metric_data ( void )
279 {
280 debug_msg("running update_metric_data() ... ");
281 get_kstat_val(&metriclist.load_fifteen, "unix", "system_misc","avenrun_15min");
282 get_kstat_val(&metriclist.load_five, "unix", "system_misc","avenrun_5min");
283 get_kstat_val(&metriclist.load_one, "unix", "system_misc","avenrun_1min");
284 get_kstat_val(&metriclist.proc_total, "unix", "system_misc", "nproc");
285 /*
286 * memory usage stats are arguably VERY broken.
287 */
288 get_kstat_val(&metriclist.mem_free, "unix", "system_pages", "pagesfree");
289 get_kstat_val(&metriclist.mem_total, "unix", "system_pages", "pagestotal");
290 debug_msg("Before PageToK(): mem_free = %u, mem_total = %u",metriclist.mem_free.uint32,metriclist.mem_total.uint32);
291 metriclist.mem_free.uint32 = pagetok(metriclist.mem_free.uint32);
292 metriclist.mem_total.uint32 = pagetok(metriclist.mem_total.uint32);
293 determine_swap_space(&metriclist.swap_total.uint32,&metriclist.swap_free.uint32);
294 // (void)determine_cpu_percentages();
295 // sleep(5);
296
297 /* update the timestamp. we use this to determine freshening times as well. */
298 metriclist.sys_clock.uint32 = time(NULL);
299 return 0;
300 }
301
302 /*
303 * another function ripped from top. after all we want the CPU percentage
304 * stuff to match.
305 */
306
percentages(int cnt,int * out,register unsigned long * new,register unsigned long * old,unsigned long * diffs)307 long percentages(int cnt, int *out, register unsigned long *new, register unsigned long *old, unsigned long *diffs)
308 {
309 register int i;
310 register long change;
311 register long total_change;
312 register unsigned long *dp;
313 long half_total;
314
315 /* initialization */
316 total_change = 0;
317 dp = diffs;
318
319 /* calculate changes for each state and the overall change */
320 for (i = 0; i < cnt; i++)
321 {
322 if ((change = *new - *old) < 0)
323 {
324 /* this only happens when the counter wraps */
325 change = (int)
326 ((unsigned long)*new-(unsigned long)*old);
327 }
328 total_change += (*dp++ = change);
329 *old++ = *new++;
330 }
331
332 /* avoid divide by zero potential */
333 if (total_change == 0)
334 {
335 total_change = 1;
336 }
337
338 /* calculate percentages based on overall change, rounding up */
339 half_total = total_change / 2l;
340 for (i = 0; i < cnt; i++)
341 {
342 *out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
343 }
344
345 /* return the total in case the caller wants to use it */
346 return(total_change);
347 }
348
349 /*
350 * the process for figuring out CPU usage is a little involved, so it has
351 * been folded into this function. also because this way it's easier to
352 * rip off top. :)
353 */
354
355 int
determine_cpu_percentages(void)356 determine_cpu_percentages ( void )
357 {
358 /*
359 * hopefully this doesn't get too confusing.
360 * cpu_snap is a structure from <sys/cpuvar.h> and is the container into which
361 * we read the current CPU metrics.
362 * the static array "cpu_old" contains the last iteration's summed cycle
363 * counts.
364 * the array "cpu_now" contains the current iteration's summed cycle
365 * counts.
366 * "cpu_diff" holds the delta.
367 * across CPUs of course. :)
368 * buffers[0..2] holds past, present and diff info for the "other" CPU stats.
369 */
370
371 static struct cpu_info buffers[3];
372 static struct timeval lasttime = {0, 0};
373 struct timeval thistime;
374 double timediff;
375 int cpu_states[CPUSTATES];
376 unsigned int ncpus;
377 static unsigned long cpu_old[CPUSTATES];
378 static unsigned long cpu_now[CPUSTATES];
379 static unsigned long cpu_diff[CPUSTATES];
380 unsigned long diff_cycles = 0L;
381 unsigned long time_delta = 0L;
382 double alpha, beta; // lambda lambda lambda!!!
383 static unsigned long last_refresh;
384 register int j;
385 char* ks_name;
386 kstat_t *ks;
387 char* km_name = "cpu_stat";
388 cpu_stat_t cpuKstats;
389 int ki;
390 processorid_t i;
391 int cpu_id = sysconf(_SC_NPROCESSORS_ONLN);
392
393 /*
394 * ripped from top by swagner in the hopes of getting
395 * top-like CPU percentages ...
396 */
397 gettimeofday (&thistime, NULL);
398
399 if (lasttime.tv_sec)
400 timediff = ((double) thistime.tv_sec * 1.0e7 +
401 ((double) thistime.tv_usec * 10.0)) -
402 ((double) lasttime.tv_sec * 1.0e7 +
403 ((double) lasttime.tv_usec * 10.0));
404 else
405 timediff = 1.0e7;
406
407 /*
408 * constants for exponential average. avg = alpha * new + beta * avg
409 * The goal is 50% decay in 30 sec. However if the sample period
410 * is greater than 30 sec, there's not a lot we can do.
411 */
412 if (timediff < 30.0e7)
413 {
414 alpha = 0.5 * (timediff / 30.0e7);
415 beta = 1.0 - alpha;
416 debug_msg("* * * * Setting alpha to %f and beta to %f because timediff = %d",alpha,beta,timediff);
417 }
418 else
419 {
420 alpha = 0.5;
421 beta = 0.5;
422 }
423
424 lasttime = thistime;
425
426 /* END SECTION RIPPED BLATANTLY FROM TOP :) */
427
428 ncpus = metriclist.cpu_num.uint32;
429
430 for (j = 0; j < CPUSTATES; j++)
431 cpu_now[j] = 0;
432
433 // will C let me do this? :)
434
435 buffers[0].bread = 0L;
436 buffers[0].bwrite = 0L;
437 buffers[0].lread = 0L;
438 buffers[0].lwrite = 0L;
439 buffers[0].phread = 0L;
440 buffers[0].phwrite = 0L;
441
442 if (first_run == 1)
443 {
444 debug_msg("Initializing old read/write buffer... ");
445 buffers[1].bread = 0L;
446 buffers[1].bwrite = 0L;
447 buffers[1].lread = 0L;
448 buffers[1].lwrite = 0L;
449 buffers[1].phread = 0L;
450 buffers[1].phwrite = 0L;
451 time_delta = 0L;
452 }
453
454 /*
455 * Get a kstat_ctl handle, or update the kstat chain.
456 */
457 if (kc == NULL)
458 kc = kstat_open();
459 else
460 kstat_chain_update(kc);
461
462 if (kc == NULL)
463 {
464 debug_msg("couldn't open kc...");
465 err_ret("determine_cpu_percentages() kstat_open() error");
466 return SYNAPSE_FAILURE;
467 }
468
469 ks_name = (char*) malloc(30 * sizeof (char) );
470
471 /*
472 * Modified by Robert Petkus <rpetkus@bnl.gov>
473 * Get stats only for online CPUs. Previously, gmond segfaulted if
474 * the CPUs were not numbered sequentially; i.e., cpu0, cpu2, etc.
475 * Tested on 64 bit Solaris 8 and 9 with GCC 3.3 and 3.3.2
476 */
477 for (i = 0; cpu_id > 0; i++)
478 {
479 /*
480 * Submitted by JB Kim <jbremnant@hotmail.com>
481 * also skip the loop if CPU is "off-line"
482 */
483 int n = p_online(i, P_STATUS);
484 if (n == 1) continue;
485
486 if (n == -1 && errno == EINVAL) continue;
487
488 sprintf(ks_name,"cpu_stat%d",i);
489 ki = i;
490 cpu_id--;
491
492 debug_msg( "getting kstat: km ='%s', ki ='%d',ks='%s'", km_name, ki, ks_name);
493 ks = kstat_lookup(kc, km_name, ki, ks_name);
494 if(ks == NULL)
495 continue; /* could be a CPU in state P_FAILED, see bug 321
496 http://bugzilla.ganglia.info/cgi-bin/bugzilla/show_bug.cgi?id=321 */
497
498 if (kstat_read(kc, ks,&cpuKstats) == -1) {
499 perror("kstat_read");
500 return SYNAPSE_FAILURE;
501 }
502
503 /* sum up to the wait state counter, the last two we determine ourselves */
504 for (j = 0; j < CPU_WAIT; j++){
505 cpu_now[j] += (unsigned long) cpuKstats.cpu_sysinfo.cpu[j];
506 }
507
508
509 cpu_now[CPUSTATE_IOWAIT] += (unsigned long) cpuKstats.cpu_sysinfo.wait[W_IO] +
510 (unsigned long) cpuKstats.cpu_sysinfo.wait[W_PIO];
511 cpu_now[CPUSTATE_SWAP] += (unsigned long) cpuKstats.cpu_sysinfo.wait[W_SWAP];
512
513 buffers[0].bread += (long)cpuKstats.cpu_sysinfo.bread;
514 buffers[0].bwrite += (long)cpuKstats.cpu_sysinfo.bwrite;
515 buffers[0].lread += (long)cpuKstats.cpu_sysinfo.lread;
516 buffers[0].lwrite += (long)cpuKstats.cpu_sysinfo.lwrite;
517 buffers[0].phread += (long)cpuKstats.cpu_sysinfo.phread;
518 buffers[0].phwrite += (long)cpuKstats.cpu_sysinfo.phwrite;
519
520 }
521 free(ks_name);
522
523 /*
524 * now we have our precious "data" and have to manipulate it - compare new
525 * to old and calculate percentages and sums.
526 */
527 buffers[2].bread = buffers[0].bread - buffers[1].bread;
528 buffers[2].bwrite = buffers[0].bwrite - buffers[1].bwrite;
529 buffers[2].lread = buffers[0].lread - buffers[1].lread;
530 buffers[2].lwrite = buffers[0].lwrite - buffers[1].lwrite;
531 buffers[2].phread = buffers[0].phread - buffers[1].phread;
532 buffers[2].phwrite = buffers[0].phwrite - buffers[1].phwrite;
533
534 debug_msg("Raw: bread / bwrite / lread / lwrite / phread / phwrite\n%u,%u,%u / %u,%u,%u / %u,%u,%u / %u,%u,%u / %u,%u,%u / %u,%u,%u",
535 buffers[0].bread,buffers[1].bread,buffers[2].bread,
536 buffers[0].bwrite,buffers[1].bwrite,buffers[2].bwrite,
537 buffers[0].lread,buffers[1].lread,buffers[2].lread,
538 buffers[0].lwrite,buffers[1].lwrite,buffers[2].lwrite,
539 buffers[0].phread,buffers[1].phread,buffers[2].phread,
540 buffers[0].phwrite,buffers[1].phwrite,buffers[2].phwrite);
541
542 time_delta = (unsigned long)time(NULL) - (unsigned long)last_refresh;
543 if (time_delta == 0)
544 time_delta = 1;
545
546 /*
547 * decay stuff
548 * semi-stolen from top. :) added by swagner on 8/20/02
549 */
550 if (time_delta < 30) {
551 alpha = 0.5 * (time_delta / 30);
552 beta = 1.0 - alpha;
553 } else {
554 alpha = 0.5;
555 beta = 0.5;
556 }
557 metriclist.bread_sec.f = (float)buffers[2].bread / (float)time_delta;
558 if (buffers[1].bread == buffers[0].bread)
559 metriclist.bread_sec.f = 0.;
560
561 metriclist.bwrite_sec.f = (float)buffers[2].bwrite / (float)time_delta;
562 if (buffers[1].bwrite == buffers[0].bwrite)
563 metriclist.bwrite_sec.f = 0.;
564
565 metriclist.lread_sec.f = (float)buffers[2].lread / (float)time_delta;
566 if (buffers[1].bwrite == buffers[0].lread)
567 metriclist.lread_sec.f = 0.;
568
569 metriclist.lwrite_sec.f = (float)buffers[2].lwrite / (float)time_delta;
570 if (buffers[1].bwrite == buffers[0].lwrite)
571 metriclist.lwrite_sec.f = 0.;
572
573 metriclist.phread_sec.f = (float)buffers[2].phread / (float)time_delta;
574 if (buffers[1].bwrite == buffers[0].phread)
575 metriclist.phread_sec.f = 0.;
576
577 metriclist.phwrite_sec.f = (float)buffers[2].phwrite / (float)time_delta;
578 if (buffers[1].bwrite == buffers[0].phwrite)
579 metriclist.phwrite_sec.f = 0.;
580
581 debug_msg("Aftermath: %f %f %f %f %f %f delta = %u",
582 metriclist.bread_sec.f, metriclist.bwrite_sec.f,
583 metriclist.lread_sec.f, metriclist.lwrite_sec.f,
584 metriclist.phread_sec.f, metriclist.phwrite_sec.f,
585 time_delta
586 );
587
588 buffers[1].bread = buffers[0].bread;
589 buffers[1].bwrite = buffers[0].bwrite;
590 buffers[1].lread = buffers[0].lread;
591 buffers[1].lwrite = buffers[0].lwrite;
592 buffers[1].phread = buffers[0].phread;
593 buffers[1].phwrite = buffers[0].phwrite;
594
595 diff_cycles = percentages(CPUSTATES, cpu_states, cpu_now, cpu_old, cpu_diff);
596
597 debug_msg ("** ** ** ** ** Are percentages electric? Try %d%%, %d%% , %d%% , %d%% , %d%% %d%%", cpu_states[0],cpu_states[1],cpu_states[2],cpu_states[3],cpu_states[4]);
598
599 /*
600 * i don't know how you folks do things in new york city, but around here folks
601 * don't go around dividing by zero.
602 */
603 if (diff_cycles < 1)
604 {
605 debug_msg("diff_cycles < 1 ... == %f %u!", diff_cycles, diff_cycles);
606 diff_cycles = 1;
607 }
608
609 /*
610 * could this be ANY HARDER TO READ? sorry. through hacking around i found
611 * that explicitly casting everything as floats seems to work...
612 */
613 metriclist.cpu_idle.f = (float) cpu_states[CPUSTATE_IDLE] / 10;
614 metriclist.cpu_user.f = (float) cpu_states[CPUSTATE_USER] / 10;
615 metriclist.cpu_system.f = (float)(cpu_states[CPUSTATE_KERNEL] + cpu_states[CPUSTATE_SWAP]) / 10;
616 metriclist.cpu_wio.f = (float) cpu_states[CPUSTATE_IOWAIT] / 10;
617
618 metriclist.rcache.f = 100.0 * ( 1.0 - ( (float)buffers[0].bread / (float)buffers[0].lread ) );
619 metriclist.wcache.f = 100.0 * ( 1.0 - ( (float)buffers[0].bwrite / (float)buffers[0].lwrite ) );
620
621 last_refresh = time(NULL);
622 return(0);
623 }
624
625 /*
626 * The following two functions retrieve statistics from all physical
627 * network interfaces.
628 */
629 static uint64_t oifctr[4];
630 static uint64_t nifctr[4];
631
632 static int
extract_if_data(kstat_t * ks)633 extract_if_data(kstat_t *ks)
634 {
635 kstat_named_t *kn;
636
637 if (strcmp(ks->ks_name, "lo0") == 0)
638 return 0;
639
640 if (kstat_read(kc, ks, 0) == -1) {
641 debug_msg("couldn't open kc...");
642 err_ret("extract_if_data() kstat_read() error");
643 return SYNAPSE_FAILURE;
644 }
645
646 kn = kstat_data_lookup(ks, "rbytes64");
647 if (kn) nifctr[0] += kn->value.ui64;
648 kn = kstat_data_lookup(ks, "obytes64");
649 if (kn) nifctr[1] += kn->value.ui64;
650 kn = kstat_data_lookup(ks, "ipackets64");
651 if (kn) nifctr[2] += kn->value.ui64;
652 kn = kstat_data_lookup(ks, "opackets64");
653 if (kn) nifctr[3] += kn->value.ui64;
654 /* fprintf(stderr,"kn = %x %u\n",kn,kn->value.ui64); */
655
656 return 0;
657 }
658
659 static void
update_if_data(void)660 update_if_data(void)
661 {
662 static int init_done = 0;
663 static struct timeval lasttime={0,0};
664 struct timeval thistime;
665 double timediff;
666 kstat_t *info;
667 char buff[20];
668
669 /*
670 * Compute time between calls
671 */
672 gettimeofday (&thistime, NULL);
673 if (lasttime.tv_sec)
674 timediff = ((double) thistime.tv_sec * 1.0e6 +
675 (double) thistime.tv_usec -
676 (double) lasttime.tv_sec * 1.0e6 -
677 (double) lasttime.tv_usec) / 1.0e6;
678 else
679 timediff = 1.0;
680
681 /*
682 * Do nothing if we are called to soon after the last call
683 */
684 if (init_done && (timediff < 10.)) return;
685
686 lasttime = thistime;
687
688 /*
689 * Get a kstat_ctl handle, or update the kstat chain.
690 */
691 if (kc == NULL)
692 kc = kstat_open();
693 else
694 kstat_chain_update(kc);
695
696 if (kc == NULL)
697 {
698 debug_msg("couldn't open kc...");
699 err_ret("update_if_data() kstat_open() error");
700 return;
701 }
702 /* fprintf(stderr,"kc = %x\n",kc); */
703
704 /*
705 * Loop over all interfaces to get statistics
706 */
707 nifctr[0] = nifctr[1] = nifctr[2] = nifctr[3] = 0;
708
709 info = kc->kc_chain;
710 while (info) {
711 if (strcmp(info->ks_class, "net") == 0) {
712 sprintf(buff, "%s%d", info->ks_module, info->ks_instance);
713 if (strcmp(info->ks_name, buff) == 0) {
714 extract_if_data(info);
715 }
716 }
717 info = info->ks_next;
718 }
719
720 if (init_done) {
721 if (nifctr[0] >= oifctr[0]) metriclist.bytes_in.f = (double)(nifctr[0] - oifctr[0])/timediff;
722 if (nifctr[1] >= oifctr[1]) metriclist.bytes_out.f = (double)(nifctr[1] - oifctr[1])/timediff;
723 if (nifctr[2] >= oifctr[2]) metriclist.pkts_in.f = (double)(nifctr[2] - oifctr[2])/timediff;
724 if (nifctr[3] >= oifctr[3]) metriclist.pkts_out.f = (double)(nifctr[3] - oifctr[3])/timediff;
725 }
726 else {
727 init_done = 1;
728 metriclist.bytes_in.f = 0.;
729 metriclist.bytes_out.f = 0.;
730 metriclist.pkts_in.f = 0.;
731 metriclist.pkts_out.f = 0.;
732 }
733
734 oifctr[0] = nifctr[0];
735 oifctr[1] = nifctr[1];
736 oifctr[2] = nifctr[2];
737 oifctr[3] = nifctr[3];
738
739 /*
740 fprintf(stderr,"inb = %f\n",metriclist.bytes_in.f);
741 fprintf(stderr,"onb = %f\n",metriclist.bytes_out.f);
742 fprintf(stderr,"ipk = %f\n",metriclist.pkts_in.f);
743 fprintf(stderr,"opk = %f\n",metriclist.pkts_out.f);
744 */
745
746 return;
747 }
748
749 /*
750 * This function is called only once by the gmond. Use to
751 * initialize data structures, etc or just return SYNAPSE_SUCCESS;
752 */
753 g_val_t
metric_init(void)754 metric_init( void )
755 {
756
757 /*
758 * swagner's stuff below, initialization for reading running kernel data ...
759 */
760
761 g_val_t val;
762
763 get_kstat_val(&metriclist.cpu_num, "unix","system_misc","ncpus");
764 debug_msg("metric_init: Assigning cpu_num value (%d) to ncpus.",(int)metriclist.cpu_num.uint32);
765 g_ncpus = metriclist.cpu_num.uint32;
766
767 get_kstat_val(&metriclist.boottime, "unix","system_misc","boot_time");
768 get_kstat_val(&metriclist.cpu_speed, "cpu_info","cpu_info0","clock_MHz");
769
770 /* first we get the uname data (hence my including <sys/utsname.h> ) */
771 (void) uname( &unamedata );
772 /*
773 * these values don't change from tick to tick. at least, they shouldn't ...
774 * also, these strings don't use the ganglia metric struct!
775 */
776 metriclist.os_name = unamedata.sysname;
777 metriclist.os_release = unamedata.release;
778 metriclist.machine_type = unamedata.machine;
779 update_metric_data();
780 update_if_data();
781 debug_msg("solaris.c: metric_init() ok.");
782 val.int32 = SYNAPSE_SUCCESS;
783 first_run = 0;
784 /*
785 * We need to make sure that every server thread gets their own copy of "kc".
786 * The next metric that needs a kc-handle will reopen it for the server thread.
787 */
788 if (kc) {
789 kstat_close(kc);
790 kc = NULL;
791 }
792 return val;
793 }
794
795 void
metric_tick(void)796 metric_tick ( void )
797 {
798 double thetime = time(NULL);
799 /* update every 30 seconds */
800 if ( thetime >= ( metriclist.sys_clock.uint32 + TICK_SECONDS) ) {
801 update_metric_data();
802 }
803 }
804
805 g_val_t
cpu_num_func(void)806 cpu_num_func ( void )
807 {
808 g_val_t val;
809
810 val.uint16 = metriclist.cpu_num.uint32;
811 return val;
812 }
813
814 g_val_t
mtu_func(void)815 mtu_func ( void )
816 {
817 g_val_t val;
818 val.uint32 = get_min_mtu();
819 /* A val of 0 means there are no UP interfaces. Shouldn't happen. */
820 return val;
821 }
822
823 /* ------------------------------------------------------------------------- */
824
825 g_val_t
bytes_in_func(void)826 bytes_in_func(void)
827 {
828 g_val_t val;
829
830 update_if_data();
831 val.f = metriclist.bytes_in.f;
832 return val;
833 }
834
835 g_val_t
bytes_out_func(void)836 bytes_out_func(void)
837 {
838 g_val_t val;
839
840 update_if_data();
841 val.f = metriclist.bytes_out.f;
842 return val;
843 }
844
845 g_val_t
pkts_in_func(void)846 pkts_in_func(void)
847 {
848 g_val_t val;
849
850 update_if_data();
851 val.f = metriclist.pkts_in.f;
852 return val;
853 }
854
855 g_val_t
pkts_out_func(void)856 pkts_out_func(void)
857 {
858 g_val_t val;
859
860 update_if_data();
861 val.f = metriclist.pkts_out.f;
862 return val;
863 }
864
865 /* --- snip! preceding code lifted from linux.c --- */
866
867 g_val_t
cpu_speed_func(void)868 cpu_speed_func ( void )
869 {
870 g_val_t val;
871
872 val.uint32 = metriclist.cpu_speed.uint32;
873 return val;
874 }
875
876 g_val_t
mem_total_func(void)877 mem_total_func ( void )
878 {
879 g_val_t val;
880
881 val.f = metriclist.mem_total.uint32;
882 // val.uint32 = pagetok(sysconf(_SC_PHYS_PAGES));
883 return val;
884 }
885
886 g_val_t
swap_total_func(void)887 swap_total_func ( void )
888 {
889 g_val_t val;
890
891 metric_tick();
892 val.f = metriclist.swap_total.uint32;
893 return val;
894 }
895
896 g_val_t
boottime_func(void)897 boottime_func ( void )
898 {
899 g_val_t val;
900
901 val.uint32 = metriclist.boottime.uint32;
902 return val;
903 }
904
905 g_val_t
sys_clock_func(void)906 sys_clock_func ( void )
907 {
908 g_val_t val;
909
910 metric_tick();
911 val.uint32 = (uint32_t)time(NULL);
912 return val;
913 }
914
915 g_val_t
machine_type_func(void)916 machine_type_func ( void )
917 {
918 g_val_t val;
919
920 strncpy( val.str, metriclist.machine_type, MAX_G_STRING_SIZE );
921 return val;
922 }
923
924 g_val_t
os_name_func(void)925 os_name_func ( void )
926 {
927 g_val_t val;
928
929 strncpy( val.str, unamedata.sysname, MAX_G_STRING_SIZE );
930 return val;
931 }
932
933 g_val_t
os_release_func(void)934 os_release_func ( void )
935 {
936 g_val_t val;
937
938 strncpy( val.str, unamedata.release, MAX_G_STRING_SIZE );
939 return val;
940 }
941
942 g_val_t
cpu_user_func(void)943 cpu_user_func ( void )
944 {
945 g_val_t val;
946
947 determine_cpu_percentages();
948 val.f = metriclist.cpu_user.f;
949 return val;
950 }
951
952 /* FIXME: ? */
953 g_val_t
cpu_nice_func(void)954 cpu_nice_func ( void )
955 {
956 g_val_t val;
957
958 val.f = 0.0; /* no more mr. nice procs ... */
959
960 return val;
961 }
962
963 g_val_t
cpu_system_func(void)964 cpu_system_func ( void )
965 {
966 g_val_t val;
967
968 val.f = metriclist.cpu_system.f;
969 return val;
970 }
971
972 g_val_t
cpu_idle_func(void)973 cpu_idle_func ( void )
974 {
975 g_val_t val;
976
977 val.f = metriclist.cpu_idle.f;
978 return val;
979 }
980
981 /* FIXME: always 0? */
982 g_val_t
cpu_wio_func(void)983 cpu_wio_func ( void )
984 {
985 g_val_t val;
986
987 val.f = metriclist.cpu_wio.f;
988 return val;
989 }
990
991 g_val_t
load_one_func(void)992 load_one_func ( void )
993 {
994 g_val_t val;
995
996 metric_tick();
997 val.f = metriclist.load_one.uint32;
998 val.f = loaddouble(val.f);
999 return val;
1000 }
1001
1002 g_val_t
load_five_func(void)1003 load_five_func ( void )
1004 {
1005 g_val_t val;
1006
1007 metric_tick();
1008 val.f = metriclist.load_five.uint32;
1009 val.f = loaddouble(val.f);
1010 return val;
1011 }
1012
1013 g_val_t
load_fifteen_func(void)1014 load_fifteen_func ( void )
1015 {
1016 g_val_t val;
1017
1018 metric_tick();
1019 val.f = metriclist.load_fifteen.uint32;
1020 val.f = loaddouble(val.f);
1021 return val;
1022 }
1023
1024 /*
1025 * The definition of a "running" Process seems to be different from Linux :-)
1026 * Anyway, the numbers look sane. Suggestions are welcome. (MKN)
1027 */
1028 #define PROCFS "/proc"
1029 g_val_t
proc_run_func(void)1030 proc_run_func( void )
1031 {
1032 char filename_buf[64];
1033 DIR *procdir;
1034 struct dirent *direntp;
1035 psinfo_t psinfo;
1036 int fd,proc_no;
1037 g_val_t val;
1038
1039 val.uint32 = 0;
1040
1041 if (!(procdir = opendir(PROCFS))) {
1042 (void) fprintf(stderr, "Unable to open %s\n",PROCFS);
1043 return val;
1044 }
1045
1046 strcpy(filename_buf, "/proc/");
1047 for (proc_no = 0; (direntp = readdir (procdir)); ) {
1048 if (direntp->d_name[0] == '.')
1049 continue;
1050
1051 sprintf(&filename_buf[6],"%s/psinfo", direntp->d_name);
1052 if ((fd = open (filename_buf, O_RDONLY)) < 0)
1053 continue;
1054
1055 if (read (fd, &psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)) {
1056 (void) close(fd);
1057 continue;
1058 }
1059 (void) close(fd);
1060
1061 if (psinfo.pr_lwp.pr_sname == 'O') {
1062 val.uint32++;
1063 /*fprintf(stderr, "Filename = <%s> %i <%c>\n",filename_buf,psinfo.pr_lwp.pr_state,psinfo.pr_lwp.pr_sname);*/
1064 }
1065 }
1066 closedir(procdir);
1067
1068 return val;
1069 }
1070
1071 g_val_t
proc_total_func(void)1072 proc_total_func ( void )
1073 {
1074 g_val_t val;
1075
1076 metric_tick();
1077 val = metriclist.proc_total;
1078 return val;
1079 }
1080
1081 g_val_t
mem_free_func(void)1082 mem_free_func ( void )
1083 {
1084 g_val_t val;
1085
1086 metric_tick();
1087 val.f = metriclist.mem_free.uint32;
1088 return val;
1089 }
1090
1091 /*
1092 * FIXME ? MKN
1093 */
1094 g_val_t
mem_shared_func(void)1095 mem_shared_func ( void )
1096 {
1097 g_val_t val;
1098
1099 val.f = 0;
1100
1101 return val;
1102 }
1103
1104 /*
1105 * FIXME ? MKN
1106 */
1107 g_val_t
mem_buffers_func(void)1108 mem_buffers_func ( void )
1109 {
1110 g_val_t val;
1111
1112 val.f = 0;
1113
1114 return val;
1115 }
1116
1117 /*
1118 * FIXME ? MKN
1119 */
1120 g_val_t
mem_cached_func(void)1121 mem_cached_func ( void )
1122 {
1123 g_val_t val;
1124
1125 val.f = 0;
1126
1127 return val;
1128 }
1129
1130 g_val_t
swap_free_func(void)1131 swap_free_func ( void )
1132 {
1133 g_val_t val;
1134
1135 metric_tick();
1136 val.f = metriclist.swap_free.uint32;
1137 return val;
1138 }
1139
1140 /* some solaris-specific stuff. enjoy. */
1141
1142 g_val_t
bread_sec_func(void)1143 bread_sec_func(void)
1144 {
1145 g_val_t val;
1146
1147 val.f = metriclist.bread_sec.f;
1148 return val;
1149 }
1150
1151 g_val_t
bwrite_sec_func(void)1152 bwrite_sec_func(void)
1153 {
1154 g_val_t val;
1155
1156 val.f = metriclist.bwrite_sec.f;
1157 return val;
1158 }
1159
1160 g_val_t
lread_sec_func(void)1161 lread_sec_func(void)
1162 {
1163 g_val_t val;
1164
1165 val.f = metriclist.lread_sec.f;
1166 return val;
1167 }
1168
1169 g_val_t
lwrite_sec_func(void)1170 lwrite_sec_func(void)
1171 {
1172 g_val_t val;
1173
1174 val.f = metriclist.lwrite_sec.f;
1175 return val;
1176 }
1177
1178 g_val_t
phread_sec_func(void)1179 phread_sec_func(void)
1180 {
1181 g_val_t val;
1182
1183 val.f = metriclist.phread_sec.f;
1184 return val;
1185 }
1186
1187 g_val_t
phwrite_sec_func(void)1188 phwrite_sec_func(void)
1189 {
1190 g_val_t val;
1191
1192 val.f = metriclist.phwrite_sec.f;
1193 return val;
1194 }
1195
1196 g_val_t
rcache_func(void)1197 rcache_func(void)
1198 {
1199 g_val_t val;
1200
1201 val.f = metriclist.rcache.f;
1202 return val;
1203 }
1204
1205 g_val_t
wcache_func(void)1206 wcache_func(void)
1207 {
1208 g_val_t val;
1209
1210 val.f = metriclist.wcache.f;
1211 return val;
1212 }
1213
1214 /*
1215 * FIXME
1216 */
1217 g_val_t
cpu_aidle_func(void)1218 cpu_aidle_func ( void )
1219 {
1220 g_val_t val;
1221 val.f = 0.0;
1222 return val;
1223 }
1224
1225 /*
1226 * FIXME
1227 */
1228 g_val_t
cpu_intr_func(void)1229 cpu_intr_func ( void )
1230 {
1231 g_val_t val;
1232 val.f = 0.0;
1233 return val;
1234 }
1235
1236 /*
1237 * FIXME
1238 */
1239 g_val_t
cpu_sintr_func(void)1240 cpu_sintr_func ( void )
1241 {
1242 g_val_t val;
1243 val.f = 0.0;
1244 return val;
1245 }
1246
1247 /*
1248 * Solaris Specific path. but this is a Solaris file even if mostly
1249 * stolen from the Linux one
1250 */
1251 #define MOUNTS "/etc/mnttab"
1252
1253 /*
1254 * Prior to Solaris 8 was a regular plain text file which should be locked
1255 * on read to ensure consistency; a read-only filesystem in newer releases
1256 */
1257
1258 /* ------------------------------------------------------------------------- */
valid_mount_type(const char * type)1259 int valid_mount_type(const char *type)
1260 {
1261 return ((strncmp(type, "ufs", 3) == 0) || (strncmp(type, "vxfs", 4) == 0));
1262 }
1263
1264 /* ------------------------------------------------------------------------- */
device_space(char * mount,char * device,double * total_size,double * total_free)1265 float device_space(char *mount, char *device, double *total_size, double *total_free)
1266 {
1267 struct statvfs buf;
1268 u_long blocksize;
1269 fsblkcnt_t free, size;
1270 float pct = 0.0;
1271
1272 statvfs(mount, &buf);
1273 size = buf.f_blocks;
1274 free = buf.f_bavail;
1275 blocksize = buf.f_frsize;
1276 /* Keep running sum of total used, free local disk space. */
1277 *total_size += size * (double)blocksize;
1278 *total_free += free * (double)blocksize;
1279 pct = size ? ((size - free) / (float)size) * 100 : 0.0;
1280 return pct;
1281 }
1282
1283 /* ------------------------------------------------------------------------- */
find_disk_space(double * total_size,double * total_free)1284 float find_disk_space(double *total_size, double *total_free)
1285 {
1286 FILE *mounts;
1287 struct mnttab mp;
1288 char *mount, *device, *type;
1289 /* We report in GB = 1 thousand million bytes */
1290 const double reported_units = 1e9;
1291 /* Track the most full disk partition, report with a percentage. */
1292 float thispct, max=0.0;
1293
1294 /* Read all currently mounted filesystems. */
1295 mounts=fopen(MOUNTS,"r");
1296 if (!mounts) {
1297 debug_msg("Df Error: could not open mounts file %s. Are we on the right OS?\n", MOUNTS);
1298 return max;
1299 }
1300
1301 while (getmntent(mounts, &mp) == 0) {
1302 mount = mp.mnt_mountp;
1303 device = mp.mnt_special;
1304 type = mp.mnt_fstype;
1305
1306 if (!valid_mount_type(type)) continue;
1307
1308 thispct = device_space(mount, device, total_size, total_free);
1309 debug_msg("Counting device %s (%.2f %%)", device, thispct);
1310 if (!max || max<thispct)
1311 max = thispct;
1312 }
1313 fclose(mounts);
1314
1315 *total_size = *total_size / reported_units;
1316 *total_free = *total_free / reported_units;
1317 debug_msg("For all disks: %.3f GB total, %.3f GB free for users.", *total_size, *total_free);
1318
1319 return max;
1320 }
1321
1322 g_val_t
disk_free_func(void)1323 disk_free_func ( void )
1324 {
1325 double total_free = 0.0;
1326 double total_size = 0.0;
1327 g_val_t val;
1328
1329 find_disk_space(&total_size, &total_free);
1330
1331 val.d = total_free;
1332 return val;
1333 }
1334
1335 g_val_t
disk_total_func(void)1336 disk_total_func ( void )
1337 {
1338 double total_free = 0.0;
1339 double total_size = 0.0;
1340 g_val_t val;
1341
1342 find_disk_space(&total_size, &total_free);
1343
1344 val.d = total_size;
1345 return val;
1346 }
1347
1348 g_val_t
part_max_used_func(void)1349 part_max_used_func ( void )
1350 {
1351 double total_free = 0.0;
1352 double total_size = 0.0;
1353 float most_full;
1354 g_val_t val;
1355
1356 most_full = find_disk_space(&total_size, &total_free);
1357
1358 val.f = most_full;
1359 return val;
1360 }
1361
1362 g_val_t
cpu_steal_func(void)1363 cpu_steal_func ( void )
1364 {
1365 static g_val_t val=0;
1366 return val;
1367 }
1368
1369