1 /*
2  Uptime Client v5.0 beta
3  $Id: stats-sol.c,v 1.40 2002/12/22 17:53:42 carstenklapp Exp $
4 
5  Logs system uptime and statistics with Uptimes Project servers
6 
7  Copyright (C) 1999-2002 Martijn Broenland, Alex C. de Haas, Carsten Klapp
8 
9  This program is free software; you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 2 of the License, or
12  (at your option) any later version.
13 
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with this program; if not, write to the Free Software
21  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 
23  Carsten Klapp <carstenklapp@users.sourceforge.net>
24  Alex C. de Haas <alex@uptimes.net>
25  Martijn Broenland <tgm@uptimes.net>
26  */
27 
28 /**
29  * @filename    stats-sol.c
30  *
31  * @desc        Retrieve stats for the Solaris 6 / Irix platforms
32  */
33 
34 #if defined PLATFORM_SOLARIS
35 
36 /*@unused@*/ static const char rcsid[] =
37     "@(#)$Id: stats-sol.c,v 1.40 2002/12/22 17:53:42 carstenklapp Exp $";
38 
39 /* My includes */
40 #include "upclient.h"
41 #include "options.h"
42 #include "stats.h"
43 #include "uplog.h"      /* wrapper for <syslog.h> */
44 
45 /* System includes */
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <stdio.h>
49 #include <strings.h>
50 #include <sysexits.h>
51 #include <unistd.h>
52 #include <utmpx.h>
53 #include <sys/stat.h>
54 #include <sys/systeminfo.h>
55 #include <sys/types.h>
56 #include <sys/utsname.h>
57 
58 #include "locale.h"     /* gettext */
59 
60 /**
61  * @desc    Verbose level 3 logging of calulations
62  */
63 void
logcalc(char * whatwascalculateddesc,char * value)64 logcalc(char *whatwascalculateddesc, char *value)
65 {
66 #if defined DEBUG
67     uplog(LOG_DEBUG, _("%s calculated: %s"), whatwascalculateddesc, value);
68 #endif /* DEBUG */
69 }
70 
71 void
initCPU(char * cpu)72 initCPU(char *cpu)
73 {
74     static int initialized = 0;
75 
76 #if !defined SI_PLATFORM
77 #   define SI_PLATFORM SI_MACHINE       /* for Irix 6.5.17 */
78 #endif /* !defined SI_PLATFORM */
79 
80     if (!initialized) {
81         initialized = 1;
82 
83         if (sysinfo
84             ((cfg_sendcpudetail) ? SI_PLATFORM : SI_ARCHITECTURE, cpu,
85              CPU_SIZE - 1) < 0) {
86             uplog(LOG_ERR, "%s cfg_sendcpu sysinfo(): %s", _("ERROR:"),
87                   strerror(errno));
88         }
89         logcalc(_("CPU"), cpu);
90     }
91 }
92 
93 /**
94  * @desc    Get os & osversion once
95  */
96 void
initOS(char * osname,char * osversion)97 initOS(char *osname, char *osversion)
98 {
99     static int initialized = 0;
100 
101     if (!initialized) {
102         initialized = 1;
103         if (cfg_sendosversion) {
104             if (sysinfo(SI_RELEASE, osversion, OSVERSION_SIZE - 1) < 0) {
105                 uplog(LOG_ERR,
106                       "%s cfg_sendosname && cfg_sendosversion sysinfo(): %s",
107                       _("ERROR:"), strerror(errno));
108             }
109             logcalc(_("OS version"), osversion);
110         }
111         if (cfg_sendosname) {
112             if (sysinfo(SI_SYSNAME, osname, OS_SIZE - 1) < 0) {
113                 uplog(LOG_ERR, "%s cfg_sendosname sysinfo(): %s", _("ERROR:"),
114                       strerror(errno));
115             }
116             logcalc(_("OS"), osname);
117         }
118     }
119 }
120 
121 /**
122  * @desc    Only Solaris 7 provides getloadavg()
123  */
124 void
getLoadavg(double * loadavg)125 getLoadavg(double *loadavg)
126 {
127     uplog(LOG_WARNING, _("%s %s not implemented for this operating system %s"),
128           _("WARNING:"), _("Load-average"), strerror(errno));
129     cfg_sendloadavg = 0;
130     return;
131 }
132 
133 /* LIBS: -lkvm -lkstat*/
134 #if defined NOT_FINISHED
135 
136 #if OSREV >= 53
137 #   define USE_KSTAT
138 #endif
139 #ifdef USE_KSTAT
140 #   include <kstat.h>
141 /**
142  * Some kstats are fixed at 32 bits, these will be specified as ui32; some
143  * are "natural" size (32 bit on 32 bit Solaris, 64 on 64 bit Solaris
144  * we'll make those unsigned long)
145  * Older Solaris doesn't define KSTAT_DATA_UINT32, those are always 32 bit.
146  */
147 #   if !defined KSTAT_DATA_UINT32
148 #      define ui32 ul
149 #   endif
150 #endif
151 
152 #define CPUSTATES     5
153 
154 #ifdef USE_KSTAT
155 
156 #define UPDKCID(nk,ok) \
157 if (nk == -1) { \
158     perror("kstat_read "); \
159         quit(1); \
160 } \
161 if (nk != ok)\
162 goto kcid_changed;
163 
164 int
kupdate(int avenrun[3])165 kupdate(int avenrun[3])
166 {
167     kstat_t *ks;
168     kid_t  nkcid;
169     int    i;
170     int    changed = 0;
171     static int ncpu = 0;
172     static kid_t kcid = 0;
173     kstat_named_t *kn;
174 
175    /**
176     * 0. kstat_open
177     */
178 
179     if (!kc) {
180         kc = kstat_open();
181         if (!kc) {
182             perror("kstat_open ");
183             quit(1);
184         }
185         changed = 1;
186         kcid = kc->kc_chain_id;
187     }
188 
189    /* keep doing it until no more changes */
190   kcid_changed:
191 
192    /**
193     * 1.  kstat_chain_update
194     */
195     nkcid = kstat_chain_update(kc);
196     if (nkcid) {
197        /* UPDKCID will abort if nkcid is -1, so no need to check */
198         changed = 1;
199         kcid = nkcid;
200     }
201     UPDKCID(nkcid, 0);
202 
203     ks = kstat_lookup(kc, "unix", 0, "system_misc");
204     if (kstat_read(kc, ks, 0) == -1) {
205         perror("kstat_read");
206         quit(1);
207     }
208 
209    /* load average */
210     kn = kstat_data_lookup(ks, "avenrun_1min");
211     if (kn)
212         avenrun[0] = kn->value.ui32;
213     kn = kstat_data_lookup(ks, "avenrun_5min");
214     if (kn)
215         avenrun[1] = kn->value.ui32;
216     kn = kstat_data_lookup(ks, "avenrun_15min");
217     if (kn)
218         avenrun[2] = kn->value.ui32;
219 
220    /* nproc */
221     kn = kstat_data_lookup(ks, "nproc");
222     if (kn) {
223         nproc = kn->value.ui32;
224 #ifdef NO_NPROC
225         if (nproc > maxprocs)
226             reallocproc(2 * nproc);
227 #endif
228     }
229 
230     if (changed) {
231 
232        /**
233         * 2. get data addresses
234         */
235 
236         ncpu = 0;
237 
238         kn = kstat_data_lookup(ks, "ncpus");
239         if (kn && kn->value.ui32 > ncpus) {
240             ncpus = kn->value.ui32;
241             cpu_ks = (kstat_t **) realloc(cpu_ks, ncpus * sizeof(kstat_t *));
242             cpu_stat =
243                 (cpu_stat_t *) realloc(cpu_stat, ncpus * sizeof(cpu_stat_t));
244         }
245 
246         for (ks = kc->kc_chain; ks; ks = ks->ks_next) {
247             if (strncmp(ks->ks_name, "cpu_stat", 8) == 0) {
248                 nkcid = kstat_read(kc, ks, NULL);
249                /* if kcid changed, pointer might be invalid */
250                 UPDKCID(nkcid, kcid);
251 
252                 cpu_ks[ncpu] = ks;
253                 ncpu++;
254                 if (ncpu > ncpus) {
255                     fprintf(stderr, "kstat finds too many cpus: should be %d\n",
256                             ncpus);
257                     quit(1);
258                 }
259             }
260         }
261        /* note that ncpu could be less than ncpus, but that's okay */
262         changed = 0;
263     }
264 
265    /**
266     * 3. get data
267     */
268 
269     for (i = 0; i < ncpu; i++) {
270         nkcid = kstat_read(kc, cpu_ks[i], &cpu_stat[i]);
271        /* if kcid changed, pointer might be invalid */
272         UPDKCID(nkcid, kcid);
273     }
274 
275    /* return the number of cpus found */
276     return (ncpu);
277 }
278 #endif /* USE_KSTAT */
279 #endif /* NOT_FINISHED */
280 
281 void
getLoadIdle(double * UsagePercent,double * IdlePercent)282 getLoadIdle(double *UsagePercent, double *IdlePercent)
283 {
284     if (cfg_SendUsage || cfg_SendIdle) {
285         uplog(LOG_WARNING,
286               _("%s %s not implemented for this operating system %s"),
287               _("WARNING:"), _("Usage and idle percentages"), "(stats-sol.c)");
288         cfg_SendUsage = 0;
289         cfg_SendIdle = 0;
290     }
291 
292 #if defined NOT_FINISHED
293    /* code taken from top m_sunos5.c */
294     int    cpu_states[CPUSTATES];
295     char  *cpustatenames[] =
296         { "idle", "user", "kernel", "iowait", "swap", NULL };
297 #define CPUSTATE_IOWAIT 3
298 #define CPUSTATE_SWAP   4
299     static long cp_time[CPUSTATES];
300     static long cp_old[CPUSTATES];
301     static long cp_diff[CPUSTATES];
302 
303 #ifdef USE_KSTAT
304     kstat_t *ks;
305     kstat_named_t *kn;
306     int    cpus_found;
307 #else
308     struct cpu cpu;
309 #endif
310 
311    /* get the cp_time array */
312     for (j = 0; j < CPUSTATES; j++)
313         cp_time[j] = 0L;
314 
315 #ifdef USE_KSTAT
316    /* use kstat to update all processor information */
317     cpus_found = kupdate(avenrun);
318     for (i = 0; i < cpus_found; i++) {
319        /* sum counters up to, but not including, wait state counter */
320         for (j = 0; j < CPU_WAIT; j++)
321             cp_time[j] += (long)cpu_stat[i].cpu_sysinfo.cpu[j];
322 
323        /* add in wait state breakdown counters */
324         cp_time[CPUSTATE_IOWAIT] +=
325             (long)cpu_stat[i].cpu_sysinfo.wait[W_IO] +
326             (long)cpu_stat[i].cpu_sysinfo.wait[W_PIO];
327         cp_time[CPUSTATE_SWAP] += (long)cpu_stat[i].cpu_sysinfo.wait[W_SWAP];
328     }
329 
330 #else /* !USE_KSTAT */
331 
332     for (i = 0; i < ncpus; i++)
333         if (cpu_offset[i] != 0) {
334            /* get struct cpu for this processor */
335             (void)getkval(cpu_offset[i], &cpu, sizeof(struct cpu), "cpu");
336 
337            /* sum counters up to, but not including, wait state counter */
338             for (j = 0; j < CPU_WAIT; j++)
339                 cp_time[j] += (long)cpu.cpu_stat.cpu_sysinfo.cpu[j];
340 
341            /* add in wait state breakdown counters */
342             cp_time[CPUSTATE_IOWAIT] +=
343                 (long)cpu.cpu_stat.cpu_sysinfo.wait[W_IO] +
344                 (long)cpu.cpu_stat.cpu_sysinfo.wait[W_PIO];
345             cp_time[CPUSTATE_SWAP] +=
346                 (long)cpu.cpu_stat.cpu_sysinfo.wait[W_SWAP];
347         }
348 
349    /* get load average array */
350     (void)getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun), "avenrun");
351 
352 #endif /* USE_KSTAT */
353 
354    /* convert cp_time counts to percentages */
355     (void)percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
356 #endif /* NOT_FINISHED */
357 }
358 
359 time_t
initBoottime(void)360 initBoottime(void)
361 {
362     static time_t boottimesecs;
363     static int initialized = 0;
364 
365     if (!initialized) {
366         int    fd;
367         struct utmpx ut;
368         int    found;
369 
370         fd = open(UTMPX_FILE, O_RDONLY);
371         if (fd < 0) {
372             uplog(LOG_ERR, "%s UTMPX_FILE: %s", _("FATAL ERROR:"),
373                   strerror(errno));
374             exit(EX_OSFILE);
375         }
376 
377         found = 0;
378         while (!found) {
379             if (read(fd, &ut, sizeof(ut)) < 0)
380                 found = -1;
381             else if (ut.ut_type == BOOT_TIME)
382                 found = 1;
383         }
384         close(fd);
385 
386         if (found == -1) {
387             uplog(LOG_ERR, _("%s Could not find %s."), _("FATAL ERROR:"),
388                   "uptime");
389             exit(EX_OSFILE);
390         }
391 
392         boottimesecs = ut.ut_tv.tv_sec;
393 #if defined DEBUG
394         uplog(LOG_DEBUG, "initBoottime() initialized, boottime.tv_sec: %d",
395               boottimesecs);
396 #endif /* DEBUG */
397     }
398     return boottimesecs;
399 }
400 
401 void
getUptime(unsigned long * uptimeminutes)402 getUptime(unsigned long *uptimeminutes)
403 {
404     static time_t boottimesecs;
405 
406     boottimesecs = initBoottime();
407 
408     if (boottimesecs) {
409         time_t now;
410 
411         time(&now);
412 
413         *uptimeminutes = (now - boottimesecs) / 60;
414 #if defined DEBUG
415         uplog(LOG_DEBUG, "getUptime() uptime: %d", *uptimeminutes);
416 #endif /* DEBUG */
417     }
418     else {
419         uplog(LOG_ERR, "getUptime() boottime.tv_sec: failed");
420     }
421 }
422 
423 /**
424  * @desc    Get statistics
425  */
426 void
getstats(unsigned long * uptimeminutes,double * UsagePercent,double * IdlePercent,char * osname,char * osversion,char * cpu,double * loadavg)427 getstats(unsigned long *uptimeminutes, double *UsagePercent,
428          double *IdlePercent, char *osname, char *osversion, char *cpu,
429          double *loadavg)
430 {
431 
432     getUptime(&*uptimeminutes);
433 
434     if (cfg_SendUsage || cfg_SendIdle)
435         getLoadIdle(&*UsagePercent, &*IdlePercent);
436 
437     if (cfg_sendosname || cfg_sendosversion)
438         initOS(&*osname, &*osversion);
439 
440     if (cfg_sendcpu)
441         initCPU(&*cpu);
442 
443     if (cfg_sendloadavg)
444         getLoadavg(&*loadavg);
445 }
446 #endif /* PLATFORM_SOLARIS */
447