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