Contents in 1999-06-04-13 of release-21-2.
[chise/xemacs-chise.git.1] / src / getloadavg.c
1 /* Get the system load averages.
2    Copyright (C) 1985, 86, 87, 88, 89, 91, 92, 93, 1994, 1995
3         Free Software Foundation, Inc.
4
5 This file is part of XEmacs.
6
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING.  If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 /* Compile-time symbols that this file uses:
23
24    FIXUP_KERNEL_SYMBOL_ADDR()   Adjust address in returned struct nlist.
25    KERNEL_FILE                  Pathname of the kernel to nlist.
26    LDAV_CVT()                   Scale the load average from the kernel.
27                                 Returns a double.
28    LDAV_SYMBOL                  Name of kernel symbol giving load average.
29    LOAD_AVE_TYPE                Type of the load average array in the kernel.
30                                 Must be defined unless one of
31                                 apollo, DGUX, NeXT, or UMAX is defined;
32                                 otherwise, no load average is available.
33    NLIST_STRUCT                 Include nlist.h, not a.out.h, and
34                                 the nlist n_name element is a pointer,
35                                 not an array.
36    NLIST_NAME_UNION             struct nlist has an n_un member, not n_name.
37    LINUX_LDAV_FILE              [__linux__]: File containing load averages.
38
39    Specific system predefines this file uses, aside from setting
40    default values if not emacs:
41
42    apollo
43    BSD                          Real BSD, not just BSD-like.
44    convex
45    DGUX
46    hpux
47    MSDOS                        No-op for MSDOS.
48    NeXT
49    sgi
50    sequent                      Sequent Dynix 3.x.x (BSD)
51    _SEQUENT_                    Sequent DYNIX/ptx 1.x.x (SYSV)
52    sony_news                    NEWS-OS (works at least for 4.1C)
53    UMAX
54    UMAX4_3
55    WIN32                        No-op for Windows95/NT.
56    __linux__                    Linux: assumes /proc filesystem mounted.
57                                 Support from Michael K. Johnson.
58    __NetBSD__                   NetBSD: assumes /kern filesystem mounted.
59    __OpenBSD__                  OpenBSD: ditto.
60
61    In addition, to avoid nesting many #ifdefs, we internally set
62    LDAV_DONE to indicate that the load average has been computed.
63
64    We also #define LDAV_PRIVILEGED if a program will require
65    special installation to be able to call getloadavg.  */
66
67 /* This should always be first.  */
68 #ifdef HAVE_CONFIG_H
69 #include <config.h>
70 #endif
71
72 #ifndef WINDOWSNT
73 #ifndef __CYGWIN32__
74
75 #include <sys/types.h>
76
77 /* Both the Emacs and non-Emacs sections want this.  Some
78    configuration files' definitions for the LOAD_AVE_CVT macro (like
79    sparc.h's) use macros like FSCALE, defined here.  */
80 #ifdef unix
81 #include <sys/param.h>
82 #endif
83
84 #ifdef XEMACS
85 #include "lisp.h"
86 #include "sysfile.h" /* for encapsulated open, close, read, write */
87 #endif /* XEMACS */
88
89 /* Exclude all the code except the test program at the end
90    if the system has its own `getloadavg' function.
91
92    The declaration of `errno' is needed by the test program
93    as well as the function itself, so it comes first.  */
94
95 #include <errno.h>
96
97 #ifndef errno
98 extern int errno;
99 #endif
100
101 #ifndef HAVE_GETLOADAVG
102
103 /* The existing Emacs configuration files define a macro called
104    LOAD_AVE_CVT, which accepts a value of type LOAD_AVE_TYPE, and
105    returns the load average multiplied by 100.  What we actually want
106    is a macro called LDAV_CVT, which returns the load average as an
107    unmultiplied double.
108
109    For backwards compatibility, we'll define LDAV_CVT in terms of
110    LOAD_AVE_CVT, but future machine config files should just define
111    LDAV_CVT directly.  */
112
113 #if !defined(LDAV_CVT) && defined(LOAD_AVE_CVT)
114 #define LDAV_CVT(n) (LOAD_AVE_CVT (n) / 100.0)
115 #endif
116
117 #ifdef XEMACS
118 #if defined (HAVE_KSTAT_H)
119 #include <kstat.h>
120 #endif /* HAVE_KSTAT_H */
121 #endif /* XEMACS */
122
123 #if !defined (BSD) && defined (ultrix)
124 /* Ultrix behaves like BSD on Vaxen.  */
125 #define BSD
126 #endif
127
128 #ifdef NeXT
129 /* NeXT in the 2.{0,1,2} releases defines BSD in <sys/param.h>, which
130    conflicts with the definition understood in this file, that this
131    really is BSD. */
132 #undef BSD
133
134 /* NeXT defines FSCALE in <sys/param.h>.  However, we take FSCALE being
135    defined to mean that the nlist method should be used, which is not true.  */
136 #undef FSCALE
137 #endif
138
139 /* Set values that are different from the defaults, which are
140    set a little farther down with #ifndef.  */
141
142
143 /* Some shorthands.  */
144
145 #if defined (HPUX) && !defined (hpux)
146 #define hpux
147 #endif
148
149 #if defined(hp300) && !defined(hpux)
150 #define MORE_BSD
151 #endif
152
153 #if defined(ultrix) && defined(mips)
154 #define decstation
155 #endif
156
157 #if (defined(sun) && defined(SVR4)) || defined (SOLARIS2)
158 #define SUNOS_5
159 #endif
160
161 #if defined (__osf__) && (defined (__alpha) || defined (__alpha__))
162 #define OSF_ALPHA
163 #include <netdb.h>
164 #include <netinet/in.h>         /* Needed for Digital UNIX V3 */
165 #include <net/proto_net.h>
166 #include <sys/table.h>
167 #endif
168
169 #if defined (__osf__) && (defined (mips) || defined (__mips__))
170 #define OSF_MIPS
171 #include <sys/table.h>
172 #endif
173
174 /* UTek's /bin/cc on the 4300 has no architecture specific cpp define by
175    default, but _MACH_IND_SYS_TYPES is defined in <sys/types.h>.  Combine
176    that with a couple of other things and we'll have a unique match.  */
177 #if !defined (tek4300) && defined (unix) && defined (m68k) && defined (mc68000) && defined (mc68020) && defined (_MACH_IND_SYS_TYPES)
178 #define tek4300                 /* Define by emacs, but not by other users.  */
179 #endif
180
181
182 /* VAX C can't handle multi-line #ifs, or lines longer than 256 chars.  */
183 #ifndef LOAD_AVE_TYPE
184
185 #ifdef MORE_BSD
186 #define LOAD_AVE_TYPE long
187 #endif
188
189 #ifdef sun
190 #define LOAD_AVE_TYPE long
191 #endif
192
193 #ifdef decstation
194 #define LOAD_AVE_TYPE long
195 #endif
196
197 #ifdef _SEQUENT_
198 #define LOAD_AVE_TYPE long
199 #endif
200
201 #ifdef sgi
202 #define LOAD_AVE_TYPE long
203 #endif
204
205 #ifdef SVR4
206 #define LOAD_AVE_TYPE long
207 #endif
208
209 #ifdef sony_news
210 #define LOAD_AVE_TYPE long
211 #endif
212
213 #ifdef sequent
214 #define LOAD_AVE_TYPE long
215 #endif
216
217 #ifdef OSF_ALPHA
218 #define LOAD_AVE_TYPE long
219 #endif
220
221 #if defined (ardent) && defined (titan)
222 #define LOAD_AVE_TYPE long
223 #endif
224
225 #ifdef tek4300
226 #define LOAD_AVE_TYPE long
227 #endif
228
229 #if defined(alliant) && defined(i860) /* Alliant FX/2800 */
230 #define LOAD_AVE_TYPE long
231 #endif
232
233 #ifdef _AIX
234 #define LOAD_AVE_TYPE long
235 #endif
236
237 #ifdef convex
238 #define LOAD_AVE_TYPE double
239 #ifndef LDAV_CVT
240 #define LDAV_CVT(n) (n)
241 #endif
242 #endif
243
244 #endif /* No LOAD_AVE_TYPE.  */
245
246 #ifdef OSF_ALPHA
247 /* <sys/param.h> defines an incorrect value for FSCALE on Alpha OSF/1,
248    according to ghazi@noc.rutgers.edu.  */
249 #undef FSCALE
250 #define FSCALE 1024.0
251 #endif
252
253 #if defined(alliant) && defined(i860) /* Alliant FX/2800 */
254 /* <sys/param.h> defines an incorrect value for FSCALE on an
255    Alliant FX/2800 Concentrix 2.2, according to ghazi@noc.rutgers.edu.  */
256 #undef FSCALE
257 #define FSCALE 100.0
258 #endif
259
260
261 #ifndef FSCALE
262
263 /* SunOS and some others define FSCALE in sys/param.h.  */
264
265 #ifdef MORE_BSD
266 #define FSCALE 2048.0
267 #endif
268
269 #if defined(MIPS) || defined(SVR4) || defined(decstation)
270 #define FSCALE 256
271 #endif
272
273 #if defined (sgi) || defined (sequent)
274 /* Sometimes both MIPS and sgi are defined, so FSCALE was just defined
275    above under #ifdef MIPS.  But we want the sgi value.  */
276 #undef FSCALE
277 #define FSCALE 1000.0
278 #endif
279
280 #if defined (ardent) && defined (titan)
281 #define FSCALE 65536.0
282 #endif
283
284 #ifdef tek4300
285 #define FSCALE 100.0
286 #endif
287
288 #ifdef _AIX
289 #define FSCALE 65536.0
290 #endif
291
292 #endif  /* Not FSCALE.  */
293
294 #if !defined (LDAV_CVT) && defined (FSCALE)
295 #define LDAV_CVT(n) (((double) (n)) / FSCALE)
296 #endif
297
298 /* VAX C can't handle multi-line #ifs, or lines longer that 256 characters.  */
299 #ifndef NLIST_STRUCT
300
301 #ifdef MORE_BSD
302 #define NLIST_STRUCT
303 #endif
304
305 #ifdef sun
306 #define NLIST_STRUCT
307 #endif
308
309 #ifdef decstation
310 #define NLIST_STRUCT
311 #endif
312
313 #ifdef hpux
314 #define NLIST_STRUCT
315 #endif
316
317 #if defined (_SEQUENT_) || defined (sequent)
318 #define NLIST_STRUCT
319 #endif
320
321 #ifdef sgi
322 #define NLIST_STRUCT
323 #endif
324
325 #ifdef SVR4
326 #define NLIST_STRUCT
327 #endif
328
329 #ifdef sony_news
330 #define NLIST_STRUCT
331 #endif
332
333 #ifdef OSF_ALPHA
334 #define NLIST_STRUCT
335 #endif
336
337 #if defined (ardent) && defined (titan)
338 #define NLIST_STRUCT
339 #endif
340
341 #ifdef tek4300
342 #define NLIST_STRUCT
343 #endif
344
345 #ifdef butterfly
346 #define NLIST_STRUCT
347 #endif
348
349 #if defined(alliant) && defined(i860) /* Alliant FX/2800 */
350 #define NLIST_STRUCT
351 #endif
352
353 #ifdef _AIX
354 #define NLIST_STRUCT
355 #endif
356
357 #endif /* defined (NLIST_STRUCT) */
358
359
360 #if defined(sgi) || (defined(mips) && !defined(BSD))
361 #define FIXUP_KERNEL_SYMBOL_ADDR(nl) ((nl)[0].n_value &= ~(1 << 31))
362 #endif
363
364
365 #if !defined (KERNEL_FILE) && defined (sequent)
366 #define KERNEL_FILE "/dynix"
367 #endif
368
369 #if !defined (KERNEL_FILE) && defined (hpux)
370 #define KERNEL_FILE "/hp-ux"
371 #endif
372
373 #if !defined(KERNEL_FILE) && (defined(_SEQUENT_) || defined(MIPS) || defined(SVR4) || defined(ISC) || defined (sgi) || defined(SVR4) || (defined (ardent) && defined (titan)))
374 #define KERNEL_FILE "/unix"
375 #endif
376
377
378 #if !defined (LDAV_SYMBOL) && defined (alliant)
379 #define LDAV_SYMBOL "_Loadavg"
380 #endif
381
382 #if !defined(LDAV_SYMBOL) && ((defined(hpux) && !defined(hp9000s300)) || defined(_SEQUENT_) || defined(SVR4) || defined(ISC) || defined(sgi) || (defined (ardent) && defined (titan)) || defined (_AIX))
383 #define LDAV_SYMBOL "avenrun"
384 #endif
385
386 #ifdef HAVE_UNISTD_H
387 #include <unistd.h>
388 #endif
389
390 #include <stdio.h>
391
392 /* LOAD_AVE_TYPE should only get defined if we're going to use the
393    nlist method.  */
394 #if !defined(LOAD_AVE_TYPE) && (defined(BSD) || defined(LDAV_CVT) || defined(KERNEL_FILE) || defined(LDAV_SYMBOL))
395 #define LOAD_AVE_TYPE double
396 #endif
397
398 #ifdef LOAD_AVE_TYPE
399
400 #ifndef NLIST_STRUCT
401 #include <a.out.h>
402 #else /* NLIST_STRUCT */
403 #include <nlist.h>
404 #endif /* NLIST_STRUCT */
405
406 #ifdef SUNOS_5
407 #include <fcntl.h>
408 #include <kvm.h>
409 #endif
410
411 #ifndef KERNEL_FILE
412 #define KERNEL_FILE "/vmunix"
413 #endif /* KERNEL_FILE */
414
415 #ifndef LDAV_SYMBOL
416 #define LDAV_SYMBOL "_avenrun"
417 #endif /* LDAV_SYMBOL */
418
419 #ifndef LDAV_CVT
420 #define LDAV_CVT(n) ((double) (n))
421 #endif /* !LDAV_CVT */
422
423 #endif /* LOAD_AVE_TYPE */
424
425 #ifdef NeXT
426 #ifdef HAVE_MACH_MACH_H
427 #include <mach/mach.h>
428 #else
429 #include <mach.h>
430 #endif
431 #endif /* NeXT */
432
433 #ifdef sgi
434 #include <sys/sysmp.h>
435 #endif /* sgi */
436
437 #ifdef UMAX
438 #include <stdio.h>
439 #include <signal.h>
440 #include <sys/time.h>
441 #include <sys/wait.h>
442 #include <sys/syscall.h>
443
444 #ifdef UMAX_43
445 #include <machine/cpu.h>
446 #include <inq_stats/statistics.h>
447 #include <inq_stats/sysstats.h>
448 #include <inq_stats/cpustats.h>
449 #include <inq_stats/procstats.h>
450 #else /* Not UMAX_43.  */
451 #include <sys/sysdefs.h>
452 #include <sys/statistics.h>
453 #include <sys/sysstats.h>
454 #include <sys/cpudefs.h>
455 #include <sys/cpustats.h>
456 #include <sys/procstats.h>
457 #endif /* Not UMAX_43.  */
458 #endif /* UMAX */
459
460 #ifdef DGUX
461 #include <sys/dg_sys_info.h>
462 #endif
463
464 #ifdef XEMACS
465 #if defined (HAVE_SYS_PSTAT_H)
466 #include <sys/pstat.h>
467 #endif /* HAVE_SYS_PSTAT_H (on HPUX) */
468 #endif /* XEMACS */
469
470 #if defined(HAVE_FCNTL_H) || defined(_POSIX_VERSION)
471 #include <fcntl.h>
472 #else
473 #include <sys/file.h>
474 #endif
475 \f
476 /* Avoid static vars inside a function since in HPUX they dump as pure.  */
477
478 #ifdef NeXT
479 static processor_set_t default_set;
480 static int getloadavg_initialized;
481 #endif /* NeXT */
482
483 #ifdef UMAX
484 static unsigned int cpus = 0;
485 static unsigned int samples;
486 #endif /* UMAX */
487
488 #ifdef DGUX
489 static struct dg_sys_info_load_info load_info;  /* what-a-mouthful! */
490 #endif /* DGUX */
491
492 #ifdef LOAD_AVE_TYPE
493 /* File descriptor open to /dev/kmem */
494 static int channel;
495 /* Nonzero iff channel is valid.  */
496 static int getloadavg_initialized;
497 /* Offset in kmem to seek to read load average, or 0 means invalid.  */
498 static long offset;
499
500 #ifndef sgi
501 static struct nlist nl[2];
502 #endif /* not sgi */
503
504 #ifdef SUNOS_5
505 static kvm_t *kd;
506 #endif /* SUNOS_5 */
507
508 #ifndef countof
509 # define countof(x) (sizeof (x) / sizeof (*(x)))
510 #endif
511
512 #endif /* LOAD_AVE_TYPE */
513 \f
514 /* Put the 1 minute, 5 minute and 15 minute load averages
515    into the first NELEM elements of LOADAVG.
516    Return the number written (never more than 3, but may be less than NELEM),
517    or -1 if an error occurred.  */
518
519 int
520 getloadavg (double loadavg[], int nelem)
521 {
522   int elem = 0;                 /* Return value.  */
523
524 #ifdef NO_GET_LOAD_AVG
525 #define LDAV_DONE
526   /* Set errno to zero to indicate that there was no particular error;
527      this function just can't work at all on this system.  */
528   errno = 0;
529   elem = -2;
530 #endif /* NO_GET_LOAD_AVG */
531
532 #if ! defined (LDAV_DONE) && defined (HAVE_KSTAT_H) && defined (HAVE_LIBKSTAT)
533 #define LDAV_DONE
534 /* getloadavg is best implemented using kstat (kernel stats), on
535    systems (like SunOS5) that support it, since you don't need special
536    privileges to use it.
537
538    Initial implementation courtesy Zlatko Calusic <zcalusic@carnet.hr>.
539    Integrated to XEmacs by Hrvoje Niksic <hniksic@srce.hr>.
540    Additional cleanup by Hrvoje Niksic, based on code published by
541    Casper Dik <Casper.Dik@Holland.Sun.Com>.  */
542   kstat_ctl_t *kc;
543   kstat_t *ksp;
544   static char *avestrings[] = { "avenrun_1min",
545                                 "avenrun_5min",
546                                 "avenrun_15min" };
547
548   if (nelem > countof (avestrings))
549     nelem = countof (avestrings);
550
551   kc = kstat_open ();
552   if (!kc)
553     return -1;
554   ksp = kstat_lookup (kc, "unix", 0, "system_misc");
555   if (!ksp)
556     {
557       kstat_close (kc);
558       return -1;
559     }
560   if (kstat_read (kc, ksp, 0) < 0)
561     {
562       kstat_close (kc);
563       return -1;
564     }
565   for (elem = 0; elem < nelem; elem++)
566     {
567       kstat_named_t *kn =
568         (kstat_named_t *) kstat_data_lookup (ksp, avestrings[elem]);
569       if (!kn)
570         {
571           kstat_close (kc);
572           return -1;
573         }
574       loadavg[elem] = (double)kn->value.ul / FSCALE;
575     }
576   kstat_close (kc);
577 #endif /* HAVE_KSTAT_H && HAVE_LIBKSTAT */
578
579 #if !defined (LDAV_DONE) && defined (HAVE_SYS_PSTAT_H)
580 #define LDAV_DONE
581   /* This is totally undocumented, and is not guaranteed to work, but
582      mayhap it might ....  If it does work, it will work only on HP-UX
583      8.0 or later.  -- Darryl Okahata <darrylo@sr.hp.com> */
584 #undef LOAD_AVE_TYPE            /* Make sure these don't exist. */
585 #undef LOAD_AVE_CVT
586 #undef LDAV_SYMBOL
587   struct pst_dynamic    procinfo;
588   union pstun           statbuf;
589
590   statbuf.pst_dynamic = &procinfo;
591   if (pstat (PSTAT_DYNAMIC, statbuf, sizeof (struct pst_dynamic), 0, 0) == -1)
592     return (-1);
593   loadavg[elem++] = procinfo.psd_avg_1_min;
594   loadavg[elem++] = procinfo.psd_avg_5_min;
595   loadavg[elem++] = procinfo.psd_avg_15_min;
596 #endif  /* HPUX */
597
598 #if !defined (LDAV_DONE) && defined (__linux__)
599 #define LDAV_DONE
600 #undef LOAD_AVE_TYPE
601
602 #ifndef LINUX_LDAV_FILE
603 #define LINUX_LDAV_FILE "/proc/loadavg"
604 #endif
605
606   char ldavgbuf[40];
607   double load_ave[3];
608   int fd, count;
609
610   fd = open (LINUX_LDAV_FILE, O_RDONLY);
611   if (fd == -1)
612     return -1;
613   count = read (fd, ldavgbuf, 40);
614   (void) close (fd);
615   if (count <= 0)
616     return -1;
617
618   count = sscanf (ldavgbuf, "%lf %lf %lf",
619                   &load_ave[0], &load_ave[1], &load_ave[2]);
620   if (count < 1)
621     return -1;
622
623   for (elem = 0; elem < nelem && elem < count; elem++)
624     loadavg[elem] = load_ave[elem];
625 #endif /* __linux__ */
626
627 #if !defined (LDAV_DONE) && defined (__NetBSD__) || defined (__OpenBSD__)
628 #define LDAV_DONE
629 #undef LOAD_AVE_TYPE
630
631 #ifndef NETBSD_LDAV_FILE
632 #define NETBSD_LDAV_FILE "/kern/loadavg"
633 #endif
634
635   unsigned long int load_ave[3], scale;
636   int count;
637   FILE *fp;
638
639   fp = fopen (NETBSD_LDAV_FILE, "r");
640   if (fp == NULL)
641     return -1;
642   count = fscanf (fp, "%lu %lu %lu %lu\n",
643                   &load_ave[0], &load_ave[1], &load_ave[2],
644                   &scale);
645   (void) fclose (fp);
646   if (count != 4)
647     return -1;
648
649   for (elem = 0; elem < nelem; elem++)
650     loadavg[elem] = (double) load_ave[elem] / (double) scale;
651 #endif /* __NetBSD__ or __OpenBSD__ */
652
653 #if !defined (LDAV_DONE) && defined (NeXT)
654 #define LDAV_DONE
655   /* The NeXT code was adapted from iscreen 3.2.  */
656
657   host_t host;
658   struct processor_set_basic_info info;
659   unsigned info_count;
660
661   /* We only know how to get the 1-minute average for this system,
662      so even if the caller asks for more than 1, we only return 1.  */
663
664   if (!getloadavg_initialized)
665     {
666       if (processor_set_default (host_self (), &default_set) == KERN_SUCCESS)
667         getloadavg_initialized = 1;
668     }
669
670   if (getloadavg_initialized)
671     {
672       info_count = PROCESSOR_SET_BASIC_INFO_COUNT;
673       if (processor_set_info (default_set, PROCESSOR_SET_BASIC_INFO, &host,
674                              (processor_set_info_t) &info, &info_count)
675           != KERN_SUCCESS)
676         getloadavg_initialized = 0;
677       else
678         {
679           if (nelem > 0)
680             loadavg[elem++] = (double) info.load_average / LOAD_SCALE;
681         }
682     }
683
684   if (!getloadavg_initialized)
685     return -1;
686 #endif /* NeXT */
687
688 #if !defined (LDAV_DONE) && defined (UMAX)
689 #define LDAV_DONE
690 /* UMAX 4.2, which runs on the Encore Multimax multiprocessor, does not
691    have a /dev/kmem.  Information about the workings of the running kernel
692    can be gathered with inq_stats system calls.
693    We only know how to get the 1-minute average for this system.  */
694
695   struct proc_summary proc_sum_data;
696   struct stat_descr proc_info;
697   double load;
698   REGISTER unsigned int i, j;
699
700   if (cpus == 0)
701     {
702       REGISTER unsigned int c, i;
703       struct cpu_config conf;
704       struct stat_descr desc;
705
706       desc.sd_next = 0;
707       desc.sd_subsys = SUBSYS_CPU;
708       desc.sd_type = CPUTYPE_CONFIG;
709       desc.sd_addr = (char *) &conf;
710       desc.sd_size = sizeof conf;
711
712       if (inq_stats (1, &desc))
713         return -1;
714
715       c = 0;
716       for (i = 0; i < conf.config_maxclass; ++i)
717         {
718           struct class_stats stats;
719           memset ((char *) &stats, 0, sizeof stats);
720
721           desc.sd_type = CPUTYPE_CLASS;
722           desc.sd_objid = i;
723           desc.sd_addr = (char *) &stats;
724           desc.sd_size = sizeof stats;
725
726           if (inq_stats (1, &desc))
727             return -1;
728
729           c += stats.class_numcpus;
730         }
731       cpus = c;
732       samples = cpus < 2 ? 3 : (2 * cpus / 3);
733     }
734
735   proc_info.sd_next = 0;
736   proc_info.sd_subsys = SUBSYS_PROC;
737   proc_info.sd_type = PROCTYPE_SUMMARY;
738   proc_info.sd_addr = (char *) &proc_sum_data;
739   proc_info.sd_size = sizeof (struct proc_summary);
740   proc_info.sd_sizeused = 0;
741
742   if (inq_stats (1, &proc_info) != 0)
743     return -1;
744
745   load = proc_sum_data.ps_nrunnable;
746   j = 0;
747   for (i = samples - 1; i > 0; --i)
748     {
749       load += proc_sum_data.ps_nrun[j];
750       if (j++ == PS_NRUNSIZE)
751         j = 0;
752     }
753
754   if (nelem > 0)
755     loadavg[elem++] = load / samples / cpus;
756 #endif /* UMAX */
757
758 #if !defined (LDAV_DONE) && defined (DGUX)
759 #define LDAV_DONE
760   /* This call can return -1 for an error, but with good args
761      it's not supposed to fail.  The first argument is for no
762      apparent reason of type `long int *'.  */
763   dg_sys_info ((long int *) &load_info,
764                DG_SYS_INFO_LOAD_INFO_TYPE,
765                DG_SYS_INFO_LOAD_VERSION_0);
766
767   if (nelem > 0)
768     loadavg[elem++] = load_info.one_minute;
769   if (nelem > 1)
770     loadavg[elem++] = load_info.five_minute;
771   if (nelem > 2)
772     loadavg[elem++] = load_info.fifteen_minute;
773 #endif /* DGUX */
774
775 #if !defined (LDAV_DONE) && defined (apollo)
776 #define LDAV_DONE
777 /* Apollo code from lisch@mentorg.com (Ray Lischner).
778
779    This system call is not documented.  The load average is obtained as
780    three long integers, for the load average over the past minute,
781    five minutes, and fifteen minutes.  Each value is a scaled integer,
782    with 16 bits of integer part and 16 bits of fraction part.
783
784    I'm not sure which operating system first supported this system call,
785    but I know that SR10.2 supports it.  */
786
787   extern void proc1_$get_loadav ();
788   unsigned long load_ave[3];
789
790   proc1_$get_loadav (load_ave);
791
792   if (nelem > 0)
793     loadavg[elem++] = load_ave[0] / 65536.0;
794   if (nelem > 1)
795     loadavg[elem++] = load_ave[1] / 65536.0;
796   if (nelem > 2)
797     loadavg[elem++] = load_ave[2] / 65536.0;
798 #endif /* apollo */
799
800 #if !defined (LDAV_DONE) && defined (OSF_MIPS)
801 #define LDAV_DONE
802
803   struct tbl_loadavg load_ave;
804   table (TBL_LOADAVG, 0, &load_ave, 1, sizeof (load_ave));
805   loadavg[elem++]
806     = (load_ave.tl_lscale == 0
807        ? load_ave.tl_avenrun.d[0]
808        : (load_ave.tl_avenrun.l[0] / (double) load_ave.tl_lscale));
809 #endif  /* OSF_MIPS */
810
811 #if !defined (LDAV_DONE) && (defined (MSDOS) || defined (WIN32))
812 #define LDAV_DONE
813
814   /* A faithful emulation is going to have to be saved for a rainy day.  */
815   for ( ; elem < nelem; elem++)
816     {
817       loadavg[elem] = 0.0;
818     }
819 #endif  /* MSDOS */
820
821 #if !defined (LDAV_DONE) && defined (OSF_ALPHA)
822 #define LDAV_DONE
823
824   struct tbl_loadavg load_ave;
825   table (TBL_LOADAVG, 0, &load_ave, 1, sizeof (load_ave));
826   for (elem = 0; elem < nelem; elem++)
827     loadavg[elem]
828       = (load_ave.tl_lscale == 0
829        ? load_ave.tl_avenrun.d[elem]
830        : (load_ave.tl_avenrun.l[elem] / (double) load_ave.tl_lscale));
831 #endif /* OSF_ALPHA */
832
833 #if !defined (LDAV_DONE) && defined(LOAD_AVE_TYPE)
834
835   /* UNIX-specific code -- read the average from /dev/kmem.  */
836
837 #define LDAV_PRIVILEGED         /* This code requires special installation.  */
838
839   LOAD_AVE_TYPE load_ave[3];
840
841   /* Get the address of LDAV_SYMBOL.  */
842   if (offset == 0)
843     {
844 #ifndef sgi
845 #ifndef NLIST_STRUCT
846       strcpy (nl[0].n_name, LDAV_SYMBOL);
847       strcpy (nl[1].n_name, "");
848 #else /* NLIST_STRUCT */
849 #ifdef NLIST_NAME_UNION
850       nl[0].n_un.n_name = LDAV_SYMBOL;
851       nl[1].n_un.n_name = 0;
852 #else /* not NLIST_NAME_UNION */
853       nl[0].n_name = (char *) LDAV_SYMBOL;
854       nl[1].n_name = 0;
855 #endif /* not NLIST_NAME_UNION */
856 #endif /* NLIST_STRUCT */
857
858 #ifndef SUNOS_5
859       if (
860 #if !(defined (_AIX) && !defined (ps2))
861           nlist (KERNEL_FILE, nl)
862 #else  /* _AIX */
863           knlist (nl, 1, sizeof (nl[0]))
864 #endif
865           >= 0)
866           /* Omit "&& nl[0].n_type != 0 " -- it breaks on Sun386i.  */
867           {
868 #ifdef FIXUP_KERNEL_SYMBOL_ADDR
869             FIXUP_KERNEL_SYMBOL_ADDR (nl);
870 #endif
871             offset = nl[0].n_value;
872           }
873 #endif /* !SUNOS_5 */
874 #else  /* sgi */
875           int ldav_off;
876
877           ldav_off = sysmp (MP_KERNADDR, MPKA_AVENRUN);
878           if (ldav_off != -1)
879           offset = (long) ldav_off & 0x7fffffff;
880 #endif /* sgi */
881         }
882
883   /* Make sure we have /dev/kmem open.  */
884   if (!getloadavg_initialized)
885     {
886 #ifndef SUNOS_5
887       channel = open ("/dev/kmem", 0);
888       if (channel >= 0)
889         {
890           /* Set the channel to close on exec, so it does not
891              litter any child's descriptor table.  */
892 #ifdef FD_SETFD
893 #ifndef FD_CLOEXEC
894 #define FD_CLOEXEC 1
895 #endif
896           (void) fcntl (channel, F_SETFD, FD_CLOEXEC);
897 #endif
898           getloadavg_initialized = 1;
899         }
900 #else /* SUNOS_5 */
901       /* We pass 0 for the kernel, corefile, and swapfile names
902          to use the currently running kernel.  */
903       kd = kvm_open (0, 0, 0, O_RDONLY, 0);
904       if (kd != 0)
905         {
906           /* nlist the currently running kernel.  */
907           kvm_nlist (kd, nl);
908           offset = nl[0].n_value;
909           getloadavg_initialized = 1;
910         }
911 #endif /* SUNOS_5 */
912     }
913
914   /* If we can, get the load average values.  */
915   if (offset && getloadavg_initialized)
916     {
917       /* Try to read the load.  */
918 #ifndef SUNOS_5
919       if (lseek (channel, offset, 0) == -1L
920           || read (channel, (char *) load_ave, sizeof (load_ave))
921           != sizeof (load_ave))
922         {
923           close (channel);
924           getloadavg_initialized = 0;
925         }
926 #else  /* SUNOS_5 */
927       if (kvm_read (kd, offset, (char *) load_ave, sizeof (load_ave))
928           != sizeof (load_ave))
929         {
930           kvm_close (kd);
931           getloadavg_initialized = 0;
932         }
933 #endif /* SUNOS_5 */
934     }
935
936   if (offset == 0 || !getloadavg_initialized)
937     return -1;
938
939   if (nelem > 0)
940     loadavg[elem++] = LDAV_CVT (load_ave[0]);
941   if (nelem > 1)
942     loadavg[elem++] = LDAV_CVT (load_ave[1]);
943   if (nelem > 2)
944     loadavg[elem++] = LDAV_CVT (load_ave[2]);
945
946 #define LDAV_DONE
947 #endif /* !LDAV_DONE && LOAD_AVE_TYPE */
948
949   return elem;
950 }
951
952 #endif /* ! HAVE_GETLOADAVG */
953 \f
954 #ifdef TEST
955 void
956 main (int argc, char **argv)
957 {
958   int naptime = 0;
959
960   if (argc > 1)
961     naptime = atoi (argv[1]);
962
963   while (1)
964     {
965       double avg[3];
966       int loads;
967
968       errno = 0;                /* Don't be misled if it doesn't set errno.  */
969       loads = getloadavg (avg, 3);
970       if (loads == -1)
971         {
972           perror ("Error getting load average");
973           exit (1);
974         }
975       if (loads > 0)
976         printf ("1-minute: %f  ", avg[0]);
977       if (loads > 1)
978         printf ("5-minute: %f  ", avg[1]);
979       if (loads > 2)
980         printf ("15-minute: %f  ", avg[2]);
981       if (loads > 0)
982         putchar ('\n');
983
984       if (naptime == 0)
985         break;
986       sleep (naptime);
987     }
988
989   exit (0);
990 }
991 #endif /* TEST */
992
993 #else
994
995 /* Emulate getloadavg.  */
996 int
997 getloadavg (double loadavg[], int nelem)
998 {
999   int i;
1000
1001   /* A faithful emulation is going to have to be saved for a rainy day.  */
1002   for (i = 0; i < nelem; i++) 
1003     {
1004       loadavg[i] = 0.0;
1005     }
1006   return i;
1007 }
1008
1009 #endif /*__GNUWIN32__*/
1010 #endif /* WINDOWSNT */