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