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