1 /*
2  *  Rewrite of AIX metrics using libperfstat API
3  *
4  *  Libperfstat can deal with a 32-bit and a 64-bit Kernel and does not require root authority.
5  *
6  *  The code is tested with AIX 5.2 (32Bit- and 64Bit-Kernel), but 5.1 and 5.3 shoud be OK too
7  *
8  *  by Andreas Schoenfeld, TU Darmstadt, Germany (4/2005)
9  *  E-Mail: Schoenfeld@hrz.tu-darmstadt.de
10  *
11  *  Its based on the
12  *  First stab at support for metrics in AIX
13  *  by Preston Smith <psmith@physics.purdue.edu>
14  *  Wed Feb 27 14:55:33 EST 2002
15  *
16  *  AIX V5 support, bugfixes added by Davide Tacchella <tack@cscs.ch>
17  *  May 10, 2002
18  *
19  *  you may still find some code (like "int bos_level(..)"  ) and the basic structure of this version.
20  *
21  *  Some code fragments of the network statistics are "borowed" from
22  *  the Solaris Metrics
23  *
24  *  Fix proc_total, proc_run, swap_free and swap_total. Implement mem_cached (MKN, 16-Jan-2006)
25  *
26  */
27 
28 #include "interface.h"
29 #include <stdlib.h>
30 #include <utmp.h>
31 #include <stdio.h>
32 #include <procinfo.h>
33 #include <strings.h>
34 #include <signal.h>
35 #include <odmi.h>
36 #include <cf.h>
37 #include <sys/utsname.h>
38 #include <sys/proc.h>
39 #include <sys/types.h>
40 #include <time.h>
41 
42 #include <libperfstat.h>
43 
44 #include "libmetrics.h"
45 
46 
47 
48 
49 struct Class *My_CLASS;
50 
51 struct product {
52         char filler[12];
53         char lpp_name[145];      /* offset: 0xc ( 12) */
54         char comp_id[20];        /* offset: 0x9d ( 157) */
55         short update;            /* offset: 0xb2 ( 178) */
56         long cp_flag;            /* offset: 0xb4 ( 180) */
57         char fesn[10];           /* offset: 0xb8 ( 184) */
58         char *name;              /*[42] offset: 0xc4 ( 196) */
59         short state;             /* offset: 0xc8 ( 200) */
60         short ver;               /* offset: 0xca ( 202) */
61         short rel;               /* offset: 0xcc ( 204) */
62         short mod;               /* offset: 0xce ( 206) */
63         short fix;               /* offset: 0xd0 ( 208) */
64         char ptf[10];            /* offset: 0xd2 ( 210) */
65         short media;             /* offset: 0xdc ( 220) */
66         char sceded_by[10];      /* offset: 0xde ( 222) */
67         char *fixinfo;           /* [1024] offset: 0xe8 ( 232) */
68         char *prereq;            /* [1024] offset: 0xec ( 236) */
69         char *description;       /* [1024] offset: 0xf0 ( 240) */
70         char *supersedes;        /* [512] offset: 0xf4 ( 244) */
71 };
72 
73 #if defined(_AIX43)
74 #ifndef SBITS
75 /*
76  * For multiplication of fractions that are stored as integers, including
77  * p_pctcpu.  Not allowed to do floating point arithmetic in the kernel.
78  */
79 #define SBITS   16
80 #endif
81 #endif
82 
83 #define MAX_CPUS  64
84 
85 #define INFO_TIMEOUT   10
86 #define CPU_INFO_TIMEOUT INFO_TIMEOUT
87 
88 #define MEM_KB_PER_PAGE (4096/1024)
89 
90 
91 struct cpu_info {
92   time_t timestamp;
93   u_longlong_t total_ticks;
94   u_longlong_t user;        /*  raw total number of clock ticks spent in user mode */
95   u_longlong_t sys;         /* raw total number of clock ticks spent in system mode */
96   u_longlong_t idle;        /* raw total number of clock ticks spent idle */
97   u_longlong_t wait;        /* raw total number of clock ticks spent waiting for I/O */
98 };
99 
100 struct net_stat{
101   double ipackets;
102   double opackets;
103   double ibytes;
104   double obytes;
105 } cur_net_stat;
106 
107 
108 
109 
110 
111 
112 int ci_flag=0;
113 int ni_flag=0;
114 
115 perfstat_cpu_total_t cpu_total_buffer;
116 perfstat_netinterface_total_t ninfo[2],*last_ninfo, *cur_ninfo ;
117 
118 
119 struct cpu_info cpu_info[2],
120   *last_cpu_info,
121   *cur_cpu_info;
122 
123 
124 
125 
126 int aixver, aixrel, aixlev, aixfix;
127 static time_t boottime;
128 
129 static int isVIOserver;
130 
131 /* Prototypes
132  */
133 void  update_ifdata(void);
134 void get_cpuinfo(void);
135 int bos_level(int *aix_version, int *aix_release, int *aix_level, int *aix_fix);
136 
137 
138 
139 
140 
141 /*
142  * This function is called only once by the gmond.  Use to
143  * initialize data structures, etc or just return SYNAPSE_SUCCESS;
144  */
145 g_val_t
metric_init(void)146 metric_init(void)
147 {
148    g_val_t val;
149    FILE *f;
150 
151 
152 /* find out if we are running on a VIO server */
153 
154    f = fopen( "/usr/ios/cli/ioscli", "r" );
155 
156    if (f)
157    {
158       isVIOserver = 1;
159       fclose( f );
160    }
161    else
162       isVIOserver = 0;
163 
164 
165    last_cpu_info = &cpu_info[ci_flag];
166    ci_flag^=1;
167    cur_cpu_info  = &cpu_info[ci_flag];
168    cur_cpu_info->total_ticks = 0;
169 
170    update_ifdata();
171 
172    get_cpuinfo();
173    sleep(CPU_INFO_TIMEOUT+1);
174    get_cpuinfo();
175 
176    update_ifdata();
177 
178    bos_level(&aixver, &aixrel, &aixlev, &aixfix);
179 
180    val.int32 = SYNAPSE_SUCCESS;
181    return val;
182 }
183 
184 g_val_t
cpu_speed_func(void)185 cpu_speed_func ( void )
186 {
187    g_val_t val;
188    perfstat_cpu_total_t c;
189 
190    if (perfstat_cpu_total(NULL,  &c, sizeof(perfstat_cpu_total_t), 1) == -1)
191       val.uint32 = 0;
192    else
193       val.uint32 = c.processorHZ/1000000;
194 
195    return val;
196 }
197 
198 g_val_t
boottime_func(void)199 boottime_func ( void )
200 {
201    g_val_t val;
202    struct utmp buf;
203    FILE *utmp;
204 
205    if (!boottime) {
206       utmp = fopen(UTMP_FILE, "r");
207 
208       if (utmp == NULL) {
209          /* Can't open utmp, use current time as boottime */
210          boottime = time(NULL);
211       } else {
212          while (fread((char *) &buf, sizeof(buf), 1, utmp) == 1) {
213             if (buf.ut_type == BOOT_TIME) {
214                boottime = buf.ut_time;
215                break;
216             }
217          }
218 	 fclose (utmp);
219       }
220    }
221    val.uint32 = boottime;
222 
223    return val;
224 }
225 
226 g_val_t
sys_clock_func(void)227 sys_clock_func ( void )
228 {
229    g_val_t val;
230 
231    val.uint32 = time(NULL);
232    return val;
233 }
234 
235 g_val_t
machine_type_func(void)236 machine_type_func ( void )
237 {
238    g_val_t val;
239    perfstat_cpu_total_t c;
240 
241    if (perfstat_cpu_total (NULL, &c, sizeof( perfstat_cpu_total_t), 1) == -1)
242       strcpy (val.str, "unknown");
243    else
244       strncpy( val.str, c.description, MAX_G_STRING_SIZE );
245 
246    return val;
247 }
248 
249 g_val_t
os_name_func(void)250 os_name_func ( void )
251 {
252    g_val_t val;
253    struct utsname uts;
254 
255    if (isVIOserver)
256       strcpy( val.str, "Virtual I/O Server" );
257    else
258    {
259       uname( &uts );
260       strncpy( val.str, uts.sysname, MAX_G_STRING_SIZE );
261    }
262 
263    return val;
264 }
265 
266 
267 g_val_t
os_release_func(void)268 os_release_func ( void )
269 {
270    g_val_t val;
271    char oslevel[MAX_G_STRING_SIZE];
272 
273    sprintf(oslevel, "%d.%d.%d.%d", aixver, aixrel, aixlev, aixfix);
274    strncpy( val.str, oslevel, MAX_G_STRING_SIZE );
275 
276    return val;
277 }
278 
279 
280 /* AIX  defines
281    CPU_IDLE, CPU_USER, CPU_SYS(CPU_KERNEL), CPU_WAIT
282    so no metrics for cpu_nice, or cpu_aidle
283 */
284 
285 
286 #define CALC_CPUINFO(type) ((100.0*(cur_cpu_info->type - last_cpu_info->type))/(1.0*(cur_cpu_info->total_ticks - last_cpu_info->total_ticks)))
287 
288 g_val_t
cpu_user_func(void)289 cpu_user_func ( void )
290 {
291    g_val_t val;
292 
293 
294    get_cpuinfo();
295 
296    val.f = CALC_CPUINFO(user);
297 
298    if(val.f < 0) val.f = 0.0;
299    return val;
300 }
301 
302 
303 /*
304 ** AIX does not have this
305 ** FIXME --
306 */
307 g_val_t
cpu_nice_func(void)308 cpu_nice_func ( void )
309 {
310    g_val_t val;
311    val.f = 0;
312    return val;
313 }
314 
315 g_val_t
cpu_system_func(void)316 cpu_system_func ( void )
317 {
318    g_val_t val;
319 
320    get_cpuinfo();
321    val.f = CALC_CPUINFO(sys) ;
322    if(val.f < 0) val.f = 0.0;
323    return val;
324 }
325 g_val_t
326 
cpu_wio_func(void)327 cpu_wio_func ( void )
328 {
329    g_val_t val;
330 
331    get_cpuinfo();
332    val.f = CALC_CPUINFO(wait);
333 
334 
335    if(val.f < 0) val.f = 0.0;
336    return val;
337 }
338 
339 g_val_t
cpu_idle_func(void)340 cpu_idle_func ( void )
341 {
342    g_val_t val;
343 
344 
345    get_cpuinfo();
346    val.f = CALC_CPUINFO(idle);
347 
348 
349    if(val.f < 0) val.f = 0.0;
350    return val;
351 }
352 
353 /*
354 ** AIX does not have this
355 ** FIXME --
356 */
357 g_val_t
cpu_aidle_func(void)358 cpu_aidle_func ( void )
359 {
360    g_val_t val;
361    val.f = 0.0;
362    return val;
363 }
364 
365 /*
366 ** Don't know what it is
367 ** FIXME --
368 */
369 g_val_t
cpu_intr_func(void)370 cpu_intr_func ( void )
371 {
372    g_val_t val;
373    val.f = 0.0;
374    return val;
375 }
376 
377 /* Don't know what it is
378 ** FIXME --
379 */
380 g_val_t
cpu_sintr_func(void)381 cpu_sintr_func ( void )
382 {
383    g_val_t val;
384    val.f = 0.0;
385    return val;
386 }
387 
388 
389 g_val_t
bytes_in_func(void)390 bytes_in_func ( void )
391 {
392    g_val_t val;
393    update_ifdata();
394    val.f = cur_net_stat.ibytes;
395    return val;
396 }
397 
398 
399 g_val_t
bytes_out_func(void)400 bytes_out_func ( void )
401 {
402    g_val_t val;
403 
404    update_ifdata();
405    val.f = cur_net_stat.obytes;
406 
407    return val;
408 }
409 
410 
411 g_val_t
pkts_in_func(void)412 pkts_in_func ( void )
413 {
414    g_val_t val;
415 
416    update_ifdata();
417    val.f = cur_net_stat.ipackets;
418 
419    return val;
420 }
421 
422 
423 g_val_t
pkts_out_func(void)424 pkts_out_func ( void )
425 {
426    g_val_t val;
427 
428 
429    update_ifdata();
430    val.f = cur_net_stat.opackets;
431 
432    return val;
433 }
434 
435 g_val_t
disk_free_func(void)436 disk_free_func ( void )
437 {
438    g_val_t val;
439    perfstat_disk_total_t d;
440 
441    if (perfstat_disk_total(NULL, &d, sizeof(perfstat_disk_total_t), 1) == -1)
442       val.d = 0.0;
443    else
444       val.d = (double)d.free / 1024;
445 
446    return val;
447 }
448 
449 g_val_t
disk_total_func(void)450 disk_total_func ( void )
451 {
452    g_val_t val;
453    perfstat_disk_total_t d;
454 
455    if (perfstat_disk_total(NULL, &d, sizeof(perfstat_disk_total_t), 1) == -1)
456       val.d = 0.0;
457    else
458       val.d = (double)d.size / 1024;
459 
460    return val;
461 }
462 
463 /* FIXME */
464 g_val_t
part_max_used_func(void)465 part_max_used_func ( void )
466 {
467    g_val_t val;
468    val.f = 0.0;
469    return val;
470 }
471 
472 g_val_t
load_one_func(void)473 load_one_func ( void )
474 {
475    g_val_t val;
476    perfstat_cpu_total_t c;
477 
478    if (perfstat_cpu_total(NULL,  &c, sizeof(perfstat_cpu_total_t), 1) == -1)
479       val.f = 0.0;
480    else
481       val.f = (float)c.loadavg[0]/(float)(1<<SBITS);
482    return val;
483 }
484 
485 g_val_t
load_five_func(void)486 load_five_func ( void )
487 {
488    g_val_t val;
489    perfstat_cpu_total_t c;
490 
491    if (perfstat_cpu_total(NULL,  &c, sizeof(perfstat_cpu_total_t), 1) == -1)
492       val.f = 0.0;
493    else
494       val.f = (float)c.loadavg[1]/(float)(1<<SBITS);
495    return val;
496 }
497 
498 g_val_t
load_fifteen_func(void)499 load_fifteen_func ( void )
500 {
501    g_val_t val;
502    perfstat_cpu_total_t c;
503 
504    if (perfstat_cpu_total(NULL, &c, sizeof(perfstat_cpu_total_t), 1) == -1)
505       val.f = 0.0;
506    else
507       val.f = (float)c.loadavg[2]/(float)(1<<SBITS);
508 
509    return val;
510 }
511 
512 g_val_t
cpu_num_func(void)513 cpu_num_func ( void )
514 {
515    g_val_t val;
516    perfstat_cpu_total_t c;
517 
518    if (perfstat_cpu_total(NULL,  &c, sizeof(perfstat_cpu_total_t), 1) == -1)
519       val.uint16 = 0;
520    else
521       val.uint16 = c.ncpus;
522 
523    return val;
524 }
525 
526 #define MAXPROCS 20
527 
528 #if !defined(_AIX61)
529 /*
530 ** These missing prototypes have caused me an afternoon of real grief !!!
531 */
532 int getprocs64 (struct procentry64 *ProcessBuffer, int ProcessSize,
533                 struct fdsinfo64 *FileBuffer, int FileSize,
534                 pid_t *IndexPointer, int Count);
535 int getthrds64 (pid_t ProcessIdentifier, struct thrdentry64 *ThreadBuffer,
536             int  ThreadSize, tid64_t *IndexPointer, int Count);
537 #endif
538 
539 /*
540 ** count_threads(pid) finds all runnable threads belonging to
541 ** process==pid. We do not count threads with the TFUNNELLED
542 ** flag set, as they do not seem to count against the load
543 ** averages (pure WOODOO, also known as "heuristics" :-)
544 */
count_threads(pid_t pid)545 int count_threads(pid_t pid) {
546 
547   struct thrdentry64 ThreadsBuffer[MAXPROCS];
548   tid64_t IndexPointer = 0;
549   int stat_val;
550   int nth = 0;
551   int i;
552 
553   while ((stat_val = getthrds64(pid,
554 			      ThreadsBuffer,
555 			      sizeof(struct thrdentry64),
556 			      &IndexPointer,
557 			      MAXPROCS )) > 0 )
558 
559     {
560       for ( i=0; i<stat_val; i++) {
561         /*
562         ** Do not count FUNNELED threads, as they do not seem to
563         ** be counted in loadavg.
564         */
565         if (ThreadsBuffer[i].ti_flag & TFUNNELLED) continue;
566 	if(ThreadsBuffer[i].ti_state == TSRUN) {
567           /*fprintf(stderr,"i=%d pid=%d tid=%lld state=%x flags=%x\n",i,ThreadsBuffer[i].ti_pid,
568 			ThreadsBuffer[i].ti_tid,ThreadsBuffer[i].ti_state,
569 			ThreadsBuffer[i].ti_flag);*/
570           nth++;
571         }
572       }
573       if (stat_val < MAXPROCS) break;
574     }
575   return nth;
576 }
577 
578 /*
579 ** count_procs() computes the number of processes as shown in "ps -A".
580 **    Pass 0 as flag if you want to get a list of all processes.
581 **    Pass 1 if you want to get a list of all processes with
582 **    runnable threads.
583 */
count_procs(int flag)584 int count_procs(int flag) {
585 
586   struct procentry64 ProcessBuffer[MAXPROCS];
587   pid_t IndexPointer = 0;
588   int np = 0;
589   int stat_val;
590   int i;
591 
592   while ((stat_val = getprocs64(
593 			      &ProcessBuffer[0],
594 			      sizeof(struct procentry64),
595 			      NULL,
596 			      sizeof(struct fdsinfo64),
597 			      &IndexPointer,
598 			      MAXPROCS )) > 0 )
599 
600     {
601       for ( i=0; i<stat_val; i++) {
602         if(flag != 0) {
603           /*fprintf(stderr,"i=%d pid=%d state=%x flags=%x flags2=%x thcount=%d\n",i,
604 				ProcessBuffer[i].pi_pid,ProcessBuffer[i].pi_state,
605 				ProcessBuffer[i].pi_flags,ProcessBuffer[i].pi_flags2,
606 				ProcessBuffer[i].pi_thcount);*/
607 	  np += count_threads(ProcessBuffer[i].pi_pid);
608         }
609         else {
610           np++;
611         }
612       }
613       if (stat_val < MAXPROCS) break;
614     }
615 
616 /*
617 ** Reduce by one to make proc_run more Linux "compliant".
618 */
619   if ((flag != 0) && (np > 0)) np--;
620 
621   return np;
622 }
623 
624 
625 g_val_t
proc_total_func(void)626 proc_total_func ( void )
627 {
628   g_val_t foo;
629 
630   foo.uint32 = count_procs(0);
631 
632   return foo;
633 }
634 
635 
636 g_val_t
proc_run_func(void)637 proc_run_func( void )
638 {
639   g_val_t val;
640 
641   val.uint32 = count_procs(1);
642 
643   return val;
644 }
645 
646 g_val_t
mem_total_func(void)647 mem_total_func ( void )
648 {
649    g_val_t val;
650    perfstat_memory_total_t m;
651 
652    if (perfstat_memory_total(NULL, &m, sizeof(perfstat_memory_total_t), 1) == -1)
653       val.f = 0;
654    else
655       val.f = m.real_total * MEM_KB_PER_PAGE;
656 
657    return val;
658 }
659 
660 g_val_t
mem_free_func(void)661 mem_free_func ( void )
662 {
663    g_val_t val;
664    perfstat_memory_total_t m;
665 
666    if (perfstat_memory_total(NULL, &m, sizeof(perfstat_memory_total_t), 1) == -1)
667       val.f = 0;
668    else
669       val.f = m.real_free * MEM_KB_PER_PAGE;
670 
671    return val;
672 }
673 
674 /* FIXME? */
675 g_val_t
mem_shared_func(void)676 mem_shared_func ( void )
677 {
678    g_val_t val;
679 
680    val.f = 0;
681 
682    return val;
683 }
684 
685 /* FIXME? */
686 g_val_t
mem_buffers_func(void)687 mem_buffers_func ( void )
688 {
689    g_val_t val;
690 
691    val.f = 0;
692 
693    return val;
694 }
695 
696 g_val_t
mem_cached_func(void)697 mem_cached_func ( void )
698 {
699    g_val_t val;
700    perfstat_memory_total_t m;
701 
702    if (perfstat_memory_total(NULL, &m, sizeof(perfstat_memory_total_t), 1) == -1)
703       val.f = 0;
704    else
705       val.f = m.numperm * MEM_KB_PER_PAGE;
706 
707    return val;
708 }
709 
710 g_val_t
swap_total_func(void)711 swap_total_func ( void )
712 {
713    g_val_t val;
714    perfstat_memory_total_t m;
715 
716    if (perfstat_memory_total(NULL, &m, sizeof(perfstat_memory_total_t), 1) == -1)
717       val.f = 0;
718    else
719       val.f = m.pgsp_total * MEM_KB_PER_PAGE;
720 
721    return val;
722 
723 }
724 
725 g_val_t
swap_free_func(void)726 swap_free_func ( void )
727 {
728    g_val_t val;
729    perfstat_memory_total_t m;
730 
731    if (perfstat_memory_total(NULL, &m, sizeof(perfstat_memory_total_t), 1) == -1)
732       val.f = 0;
733    else
734       val.f =m.pgsp_free * MEM_KB_PER_PAGE;
735 
736    return val;
737 }
738 
739 g_val_t
mtu_func(void)740 mtu_func ( void )
741 {
742    /* We want to find the minimum MTU (Max packet size) over all UP interfaces.
743 */
744    unsigned int min=0;
745    g_val_t val;
746    val.uint32 = get_min_mtu();
747    /* A val of 0 means there are no UP interfaces. Shouldn't happen. */
748    return val;
749 }
750 
751 
752 
753 
754 
755 
756 
757 
get_cpuinfo()758 void get_cpuinfo()
759 {
760   u_longlong_t cpu_total;
761   time_t new_time;
762 
763 
764   new_time = time(NULL);
765 
766   if (new_time - CPU_INFO_TIMEOUT > cur_cpu_info->timestamp )
767     {
768 
769       perfstat_cpu_total(NULL,  &cpu_total_buffer, sizeof(perfstat_cpu_total_t), 1);
770 
771 
772       cpu_total = cpu_total_buffer.user +  cpu_total_buffer.sys
773 	+  cpu_total_buffer.idle +  cpu_total_buffer.wait;
774 
775 
776       last_cpu_info=&cpu_info[ci_flag];
777       ci_flag^=1;
778       cur_cpu_info=&cpu_info[ci_flag];
779 
780       cur_cpu_info->timestamp   = new_time;
781       cur_cpu_info->total_ticks = cpu_total;
782       cur_cpu_info->user        = cpu_total_buffer.user;
783       cur_cpu_info->sys         = cpu_total_buffer.sys;
784       cur_cpu_info->idle        = cpu_total_buffer.idle;
785       cur_cpu_info->wait        = cpu_total_buffer.wait;
786     }
787 } /*      get_cpuinfo  */
788 
789 
790 
791 /* int bos_level(int *aix_version, int *aix_release, int *aix_level, int *aix_fix)
792  *  is copied form
793  *
794  *  First stab at support for metrics in AIX
795  *  by Preston Smith <psmith@physics.purdue.edu>
796  *  Wed Feb 27 14:55:33 EST 2002
797  *
798  *  AIX V5 support, bugfixes added by Davide Tacchella <tack@cscs.ch>
799  *  May 10, 2002
800  *
801  */
802 
bos_level(int * aix_version,int * aix_release,int * aix_level,int * aix_fix)803 int bos_level(int *aix_version, int *aix_release, int *aix_level, int *aix_fix)
804 {
805     struct Class *my_cl;   /* customized devices class ptr */
806     struct product  productobj;     /* customized device object storage */
807     int rc, getit, found = 0;
808     char *path;
809 
810     /*
811      * start up odm
812      */
813     if (odm_initialize() == -1)
814         return E_ODMINIT;
815 
816     /*
817      * Make sure we take the right database
818      */
819     if ((path = odm_set_path("/usr/lib/objrepos")) == (char *) -1)
820         return odmerrno;
821 
822     /*
823      * Mount the lpp class
824      */
825     if ((My_CLASS = odm_mount_class("product")) == (CLASS_SYMBOL) -1)
826         return odmerrno;
827 
828     /*
829      * open customized devices object class
830      */
831     if ((int)(my_cl = odm_open_class(My_CLASS)) == -1)
832         return E_ODMOPEN;
833 
834     /*
835      * Loop trough all entries for the lpp name, ASSUMING the last
836      * one denotes the up to date number!!!
837      */
838     /*
839      * AIX > 4.2 uses bos.mp or bos.up
840      * AIX >= 6.1 uses bos.mp64
841      */
842     getit = ODM_FIRST;
843     while ((rc = (int)odm_get_obj(my_cl, "name like bos.?p*",
844                                   &productobj, getit)) != 0) {
845         getit = ODM_NEXT;
846         if (rc == -1) {
847             /* ODM failure */
848             break;
849         }
850         else {
851             *aix_version = productobj.ver;
852             *aix_release = productobj.rel;
853             *aix_level   = productobj.mod;
854             *aix_fix     = productobj.fix;
855             found++;
856         }
857     }
858     /*
859      * AIX < 4.2 uses bos.rte.mp or bos.rte.up
860      */
861     if (!found) {
862         getit = ODM_FIRST;
863         while ((rc = (int)odm_get_obj(my_cl, "name like bos.rte.?p",
864                                       &productobj, getit)) != 0) {
865             getit = ODM_NEXT;
866             if (rc == -1) {
867                 /* ODM failure */
868                 break;
869             }
870             else {
871                 *aix_version = productobj.ver;
872                 *aix_release = productobj.rel;
873                 *aix_level   = productobj.mod;
874                 *aix_fix     = productobj.fix;
875                 found++;
876             }
877         }
878     }
879 
880 
881 
882     /*
883      * close lpp object class
884      */
885     odm_close_class(my_cl);
886 
887     odm_terminate();
888 
889     free(path);
890     return (found ? 0 : -1);
891 
892 } /* bos_level */
893 
894 
895 
896 
897 #define CALC_NETSTAT(type) (double) ((cur_ninfo->type<last_ninfo->type)?-1:(cur_ninfo->type - last_ninfo->type)/timediff)
898 void
update_ifdata(void)899 update_ifdata(void){
900 
901    static int init_done = 0;
902    static struct timeval lasttime={0,0};
903    struct timeval thistime;
904    double timediff;
905 
906 
907    /*
908    ** Compute time between calls
909    */
910    gettimeofday (&thistime, NULL);
911    if (lasttime.tv_sec)
912      timediff = ((double) thistime.tv_sec * 1.0e6 +
913                  (double) thistime.tv_usec -
914                  (double) lasttime.tv_sec * 1.0e6 -
915                  (double) lasttime.tv_usec) / 1.0e6;
916    else
917      timediff = 1.0;
918 
919    /*
920    ** Do nothing if we are called to soon after the last call
921    */
922    if (init_done && (timediff < INFO_TIMEOUT)) return;
923 
924    lasttime = thistime;
925 
926    last_ninfo = &ninfo[ni_flag];
927 
928    ni_flag^=1;
929 
930    cur_ninfo = &ninfo[ni_flag];
931 
932    perfstat_netinterface_total(NULL, cur_ninfo, sizeof(perfstat_netinterface_total_t), 1);
933 
934    if (init_done) {
935       cur_net_stat.ipackets = (CALC_NETSTAT(ipackets)<0)?cur_net_stat.ipackets:CALC_NETSTAT(ipackets);
936       cur_net_stat.opackets = (CALC_NETSTAT(opackets)<0)?cur_net_stat.opackets:CALC_NETSTAT(opackets);
937       cur_net_stat.ibytes   = (CALC_NETSTAT(ibytes ) <0)?cur_net_stat.ibytes:CALC_NETSTAT(ibytes );
938       cur_net_stat.obytes   = (CALC_NETSTAT(obytes  )<0)?cur_net_stat.obytes:CALC_NETSTAT(obytes  );
939    }
940    else
941      {
942        init_done = 1;
943 
944        cur_net_stat.ipackets = 0;
945        cur_net_stat.opackets = 0;
946        cur_net_stat.ibytes   = 0;
947        cur_net_stat.obytes   = 0;
948      }
949 
950 }  /* update_ifdata */
951 
952 
953