1 #include <net-snmp/net-snmp-config.h>
2 #include <net-snmp/net-snmp-features.h>
3 #include <net-snmp/net-snmp-includes.h>
4 #include <net-snmp/agent/net-snmp-agent-includes.h>
5 #include <net-snmp/agent/hardware/cpu.h>
6 #include "cpu.h"
7 
8 netsnmp_feature_child_of(hardware_cpu, libnetsnmpmibs);
9 
10 netsnmp_feature_child_of(hardware_cpu_copy_stats, hardware_cpu);
11 netsnmp_feature_child_of(hardware_cpu_load, hardware_cpu);
12 netsnmp_feature_child_of(hardware_cpu_get_cache, hardware_cpu);
13 netsnmp_feature_child_of(hardware_cpu_get_byName, hardware_cpu);
14 
15 static void _cpu_update_stats( unsigned int, void* );
16 
17 static int _cpuAutoUpdate =  5;
18 static int _cpuHistoryLen;
19 int  cpu_num = 0;
20 
21 static netsnmp_cpu_info *_cpu_head  = NULL;
22 static netsnmp_cpu_info *_cpu_tail  = NULL;
23 static netsnmp_cache    *_cpu_cache = NULL;
24 
init_cpu(void)25 void init_cpu( void ) {
26     oid nsCPU[] = { 1, 3, 6, 1, 4, 1, 8072, 1, 33 };
27     /*
28      * If we're sampling the CPU statistics automatically,
29      *   then arrange for this to be triggered regularly,
30      *   keeping sufficient samples to cover the last minute.
31      * If the system-specific code has already initialised
32      *   the list of CPU entries, then retrieve the first set
33      *   of stats immediately.
34      * Otherwise, wait until the regular sampling kicks in.
35      *
36      * If we're not sampling these statistics regularly,
37      *   create a suitable cache handler instead.
38      */
39     if ( _cpuAutoUpdate ) {
40 
41         _cpuHistoryLen = 60/_cpuAutoUpdate;
42         snmp_alarm_register( _cpuAutoUpdate, SA_REPEAT, _cpu_update_stats,
43                              NULL );
44         if ( _cpu_head )
45             _cpu_update_stats( 0, NULL );
46     } else
47         _cpu_cache = netsnmp_cache_create( 5, netsnmp_cpu_arch_load, NULL,
48                                               nsCPU, OID_LENGTH(nsCPU));
49 }
50 
shutdown_cpu(void)51 void shutdown_cpu( void ) {
52     while ( _cpu_head ) {
53         netsnmp_cpu_info *tmp = _cpu_head;
54         _cpu_head = _cpu_head->next;
55         SNMP_FREE(tmp->history);
56         SNMP_FREE(tmp);
57     }
58     _cpu_tail = NULL;
59 }
60 
61 
netsnmp_cpu_get_first(void)62 netsnmp_cpu_info *netsnmp_cpu_get_first( void ) {
63     return _cpu_head;
64 }
netsnmp_cpu_get_next(netsnmp_cpu_info * this_ptr)65 netsnmp_cpu_info *netsnmp_cpu_get_next( netsnmp_cpu_info *this_ptr ) {
66     return ( this_ptr ? this_ptr->next : NULL );
67 }
68 
69     /*
70      * Work with a list of CPU entries, indexed numerically
71      */
netsnmp_cpu_get_byIdx(int idx,int create)72 netsnmp_cpu_info *netsnmp_cpu_get_byIdx(  int idx, int create ) {
73     netsnmp_cpu_info *cpu, *cpu2;
74 
75         /*
76          * Find the specified CPU entry
77          */
78     DEBUGMSGTL(("cpu", "cpu_get_byIdx %d ", idx));
79     for ( cpu=_cpu_head; cpu; cpu=cpu->next ) {
80         if ( cpu->idx == idx ) {
81             DEBUGMSG(("cpu", "(found)\n"));
82             return cpu;
83         }
84     }
85     if (!create) {
86         DEBUGMSG(("cpu", "(not found)\n"));
87         return NULL;
88     }
89 
90         /*
91          * Create a new CPU entry, and insert it into the list....
92          */
93     cpu = SNMP_MALLOC_TYPEDEF( netsnmp_cpu_info );
94     if (!cpu) {
95         DEBUGMSG(("cpu", "(failed)\n"));
96         return NULL;
97     }
98     DEBUGMSG(("cpu", "(created)\n"));
99     cpu->idx = idx;
100         /* ... either as the first (or only) entry....  */
101     if ( !_cpu_head || _cpu_head->idx > idx ) {
102         cpu->next = _cpu_head;
103         _cpu_head = cpu;
104         if (!_cpu_tail)
105             _cpu_tail = cpu;
106         return cpu;
107     }
108         /* ... or in the appropriate position  */
109     for ( cpu2=_cpu_head; cpu2; cpu2=cpu2->next ) {
110         if ( !cpu2->next || cpu2->next->idx > idx ) {
111             cpu->next  = cpu2->next;
112             cpu2->next = cpu;
113             if (!cpu->next)
114                 _cpu_tail = cpu;
115             return cpu;
116         }
117     }
118     SNMP_FREE(cpu); /* just in case */
119     return NULL;  /* Shouldn't happen! */
120 }
121 
122     /*
123      * Work with a list of CPU entries, indexed by name
124      */
125 #ifndef NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_GET_BYNAME
netsnmp_cpu_get_byName(char * name,int create)126 netsnmp_cpu_info *netsnmp_cpu_get_byName( char *name, int create ) {
127     netsnmp_cpu_info *cpu;
128 
129         /*
130          * Find the specified CPU entry
131          */
132     for ( cpu=_cpu_head; cpu; cpu=cpu->next ) {
133         if ( !strcmp(cpu->name, name))
134             return cpu;
135     }
136     if (!create)
137         return NULL;
138 
139         /*
140          * Create a new CPU entry, and append it to the list
141          */
142     cpu = SNMP_MALLOC_TYPEDEF( netsnmp_cpu_info );
143     if (!cpu)
144         return NULL;
145     if (strlen(name) >= sizeof(cpu->name)) {
146         free(cpu);
147         snmp_log(LOG_ERR, "Name of CPU is too large: %s\n", name);
148         return NULL;
149     }
150 
151     strlcpy(cpu->name, name, sizeof(cpu));
152     if ( _cpu_tail ) {
153         cpu->idx = _cpu_tail->idx+1;
154         _cpu_tail->next = cpu;
155         _cpu_tail       = cpu;
156     } else {
157         cpu->idx = 0;
158         _cpu_head = cpu;
159         _cpu_tail = cpu;
160     }
161     return cpu;
162 }
163 #endif /* NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_GET_BYNAME */
164 
165 #ifndef NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_GET_CACHE
netsnmp_cpu_get_cache(void)166 netsnmp_cache *netsnmp_cpu_get_cache( void ) {
167     return _cpu_cache;
168 }
169 #endif /* NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_GET_CACHE */
170 
171 #ifndef NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_LOAD
netsnmp_cpu_load(void)172 int netsnmp_cpu_load( void ) {
173         /*
174          * If we're automatically updating the stats regularly,
175          * then don't invoke the cache handling.
176          */
177     return ( _cpuAutoUpdate ? 1
178                             : netsnmp_cache_check_and_reload( _cpu_cache ));
179 }
180 #endif /* NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_LOAD */
181 
182     /*
183      * Call the system-specific load routine regularly,
184      * keeping track of the relevant earlier results.
185      */
186 static void
_cpu_update_stats(unsigned int reg,void * magic)187 _cpu_update_stats( unsigned int reg, void* magic ) {
188     netsnmp_cpu_info *cpu;
189     int i;
190 
191     for ( cpu=_cpu_head; cpu; cpu=cpu->next ) {
192         if ( !cpu->history ) {
193             /*
194              * First time through, we need to create buffers
195              * for the historical stats
196              */
197             cpu->history  = (struct netsnmp_cpu_history *)calloc( _cpuHistoryLen, sizeof(struct netsnmp_cpu_history));
198         } else {
199             /*
200              * Otherwise, rotate these values - in descending order
201              *   with the earliest (relevant) statistics in entry 0.
202              * This means that the code to calculate the rolling averages
203              *   is independent of the number of historical samples saved.
204              */
205             for (i=0; i<_cpuHistoryLen-2; i++) {
206                 cpu->history[i] = cpu->history[i+1];
207             }
208             cpu->history[i].user_hist  = cpu->user_ticks;
209             cpu->history[i].sys_hist   = cpu->sys_ticks;
210             cpu->history[i].idle_hist  = cpu->idle_ticks;
211             cpu->history[i].nice_hist  = cpu->nice_ticks;
212             cpu->history[i].total_hist = cpu->total_ticks;
213 
214             cpu->history[i].ctx_hist   = cpu->nCtxSwitches;
215             cpu->history[i].intr_hist  = cpu->nInterrupts;
216             cpu->history[i].swpi_hist  = cpu->swapIn;
217             cpu->history[i].swpo_hist  = cpu->swapOut;
218             cpu->history[i].pagei_hist = cpu->pageIn;
219             cpu->history[i].pageo_hist = cpu->pageOut;
220         }
221     }
222 
223     /*
224      * Now call the system-specific load routine, to
225      * retrieve the latest set of data.
226      */
227     netsnmp_cpu_arch_load( NULL, NULL );
228     for ( cpu=_cpu_head; cpu; cpu=cpu->next ) {
229         cpu->total_ticks = cpu->user_ticks +
230                            cpu->nice_ticks +
231                            cpu->sys_ticks +
232                            cpu->idle_ticks +
233                            cpu->wait_ticks +
234                            cpu->kern_ticks +
235                            cpu->intrpt_ticks +
236                            cpu->sirq_ticks +
237                            cpu->steal_ticks +
238                            cpu->guest_ticks +
239                            cpu->guestnice_ticks;
240     }
241 }
242 
243 #ifndef NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_COPY_STATS
_cpu_copy_stats(netsnmp_cpu_info * cpu)244 void _cpu_copy_stats( netsnmp_cpu_info *cpu )
245 {
246     netsnmp_cpu_info *cpu2;
247 
248         /*
249          * Copy "overall" statistics to the 'cpu0' entry
250          *  on single CPU systems where this isn't done automatically
251          */
252     cpu2 = netsnmp_cpu_get_byIdx( 0, 1 );
253     if (!cpu || !cpu2) return;
254     cpu2->user_ticks = cpu->user_ticks;
255     cpu2->nice_ticks = cpu->nice_ticks;
256     cpu2->sys_ticks  = cpu->sys_ticks;
257     cpu2->sys2_ticks = cpu->sys2_ticks;
258     cpu2->idle_ticks = cpu->idle_ticks;
259     cpu2->wait_ticks = cpu->wait_ticks;
260     cpu2->kern_ticks = cpu->kern_ticks;
261     cpu2->intrpt_ticks = cpu->intrpt_ticks;
262     cpu2->sirq_ticks = cpu->sirq_ticks;
263     cpu2->steal_ticks = cpu->steal_ticks;
264     cpu2->guest_ticks = cpu->guest_ticks;
265     cpu2->guestnice_ticks = cpu->guestnice_ticks;
266 
267     cpu2->nInterrupts  = cpu->nInterrupts;
268     cpu2->nCtxSwitches = cpu->nCtxSwitches;
269     cpu2->swapIn     = cpu->swapIn;
270     cpu2->swapOut    = cpu->swapOut;
271     cpu2->pageIn     = cpu->pageIn;
272     cpu2->pageOut    = cpu->pageOut;
273 }
274 #endif /* NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_COPY_STATS */
275