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