(date): Interpret the date, L/M/N, as YYYY/MM/DD
[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 (__sun) && !defined (sun)
141 #define sun
142 #endif
143
144 #if defined(hp300) && !defined(hpux)
145 #define MORE_BSD
146 #endif
147
148 #if defined(ultrix) && defined(mips)
149 #define decstation
150 #endif
151
152 #if (defined(sun) && defined(SVR4)) || defined (SOLARIS2)
153 #define SUNOS_5
154 #endif
155
156 #if defined (__osf__) && (defined (__alpha) || defined (__alpha__))
157 #define OSF_ALPHA
158 #include <sys/table.h>
159 #endif
160
161 #if defined (__osf__) && (defined (mips) || defined (__mips__))
162 #define OSF_MIPS
163 #include <sys/table.h>
164 #endif
165
166 /* UTek's /bin/cc on the 4300 has no architecture specific cpp define by
167    default, but _MACH_IND_SYS_TYPES is defined in <sys/types.h>.  Combine
168    that with a couple of other things and we'll have a unique match.  */
169 #if !defined (tek4300) && defined (unix) && defined (m68k) && defined (mc68000) && defined (mc68020) && defined (_MACH_IND_SYS_TYPES)
170 #define tek4300                 /* Define by emacs, but not by other users.  */
171 #endif
172
173
174 /* VAX C can't handle multi-line #ifs, or lines longer than 256 chars.  */
175 #ifndef LOAD_AVE_TYPE
176
177 #ifdef MORE_BSD
178 #define LOAD_AVE_TYPE long
179 #endif
180
181 #ifdef sun
182 #define LOAD_AVE_TYPE long
183 #endif
184
185 #ifdef decstation
186 #define LOAD_AVE_TYPE long
187 #endif
188
189 #ifdef _SEQUENT_
190 #define LOAD_AVE_TYPE long
191 #endif
192
193 #ifdef sgi
194 #define LOAD_AVE_TYPE long
195 #endif
196
197 #ifdef SVR4
198 #define LOAD_AVE_TYPE long
199 #endif
200
201 #ifdef sony_news
202 #define LOAD_AVE_TYPE long
203 #endif
204
205 #ifdef sequent
206 #define LOAD_AVE_TYPE long
207 #endif
208
209 #ifdef OSF_ALPHA
210 #define LOAD_AVE_TYPE long
211 #endif
212
213 #if defined (ardent) && defined (titan)
214 #define LOAD_AVE_TYPE long
215 #endif
216
217 #ifdef tek4300
218 #define LOAD_AVE_TYPE long
219 #endif
220
221 #if defined(alliant) && defined(i860) /* Alliant FX/2800 */
222 #define LOAD_AVE_TYPE long
223 #endif
224
225 #ifdef _AIX
226 #define LOAD_AVE_TYPE long
227 #endif
228
229 #ifdef convex
230 #define LOAD_AVE_TYPE double
231 #ifndef LDAV_CVT
232 #define LDAV_CVT(n) (n)
233 #endif
234 #endif
235
236 #endif /* No LOAD_AVE_TYPE.  */
237
238 #ifdef OSF_ALPHA
239 /* <sys/param.h> defines an incorrect value for FSCALE on Alpha OSF/1,
240    according to ghazi@noc.rutgers.edu.  */
241 #undef FSCALE
242 #define FSCALE 1024.0
243 #endif
244
245 #if defined(alliant) && defined(i860) /* Alliant FX/2800 */
246 /* <sys/param.h> defines an incorrect value for FSCALE on an
247    Alliant FX/2800 Concentrix 2.2, according to ghazi@noc.rutgers.edu.  */
248 #undef FSCALE
249 #define FSCALE 100.0
250 #endif
251
252
253 #ifndef FSCALE
254
255 /* SunOS and some others define FSCALE in sys/param.h.  */
256
257 #ifdef MORE_BSD
258 #define FSCALE 2048.0
259 #endif
260
261 #if defined(MIPS) || defined(SVR4) || defined(decstation)
262 #define FSCALE 256
263 #endif
264
265 #if defined (sgi) || defined (sequent)
266 /* Sometimes both MIPS and sgi are defined, so FSCALE was just defined
267    above under #ifdef MIPS.  But we want the sgi value.  */
268 #undef FSCALE
269 #define FSCALE 1000.0
270 #endif
271
272 #if defined (ardent) && defined (titan)
273 #define FSCALE 65536.0
274 #endif
275
276 #ifdef tek4300
277 #define FSCALE 100.0
278 #endif
279
280 #ifdef _AIX
281 #define FSCALE 65536.0
282 #endif
283
284 #endif  /* Not FSCALE.  */
285
286 #if !defined (LDAV_CVT) && defined (FSCALE)
287 #define LDAV_CVT(n) (((double) (n)) / FSCALE)
288 #endif
289
290 /* VAX C can't handle multi-line #ifs, or lines longer that 256 characters.  */
291 #ifndef NLIST_STRUCT
292
293 #ifdef MORE_BSD
294 #define NLIST_STRUCT
295 #endif
296
297 #ifdef sun
298 #define NLIST_STRUCT
299 #endif
300
301 #ifdef decstation
302 #define NLIST_STRUCT
303 #endif
304
305 #ifdef hpux
306 #define NLIST_STRUCT
307 #endif
308
309 #if defined (_SEQUENT_) || defined (sequent)
310 #define NLIST_STRUCT
311 #endif
312
313 #ifdef sgi
314 #define NLIST_STRUCT
315 #endif
316
317 #ifdef SVR4
318 #define NLIST_STRUCT
319 #endif
320
321 #ifdef sony_news
322 #define NLIST_STRUCT
323 #endif
324
325 #ifdef OSF_ALPHA
326 #define NLIST_STRUCT
327 #endif
328
329 #if defined (ardent) && defined (titan)
330 #define NLIST_STRUCT
331 #endif
332
333 #ifdef tek4300
334 #define NLIST_STRUCT
335 #endif
336
337 #ifdef butterfly
338 #define NLIST_STRUCT
339 #endif
340
341 #if defined(alliant) && defined(i860) /* Alliant FX/2800 */
342 #define NLIST_STRUCT
343 #endif
344
345 #ifdef _AIX
346 #define NLIST_STRUCT
347 #endif
348
349 #endif /* defined (NLIST_STRUCT) */
350
351
352 #if defined(sgi) || (defined(mips) && !defined(BSD))
353 #define FIXUP_KERNEL_SYMBOL_ADDR(nl) ((nl)[0].n_value &= ~(1 << 31))
354 #endif
355
356
357 #if !defined (KERNEL_FILE) && defined (sequent)
358 #define KERNEL_FILE "/dynix"
359 #endif
360
361 #if !defined (KERNEL_FILE) && defined (hpux)
362 #define KERNEL_FILE "/hp-ux"
363 #endif
364
365 #if !defined(KERNEL_FILE) && (defined(_SEQUENT_) || defined(MIPS) || defined(SVR4) || defined(ISC) || defined (sgi) || defined(SVR4) || (defined (ardent) && defined (titan)))
366 #define KERNEL_FILE "/unix"
367 #endif
368
369
370 #if !defined (LDAV_SYMBOL) && defined (alliant)
371 #define LDAV_SYMBOL "_Loadavg"
372 #endif
373
374 #if !defined(LDAV_SYMBOL) && ((defined(hpux) && !defined(hp9000s300)) || defined(_SEQUENT_) || defined(SVR4) || defined(ISC) || defined(sgi) || (defined (ardent) && defined (titan)) || defined (_AIX))
375 #define LDAV_SYMBOL "avenrun"
376 #endif
377
378 #ifdef HAVE_UNISTD_H
379 #include <unistd.h>
380 #endif
381
382 #include <stdio.h>
383
384 /* LOAD_AVE_TYPE should only get defined if we're going to use the
385    nlist method.  */
386 #if !defined(LOAD_AVE_TYPE) && (defined(BSD) || defined(LDAV_CVT) || defined(KERNEL_FILE) || defined(LDAV_SYMBOL))
387 #define LOAD_AVE_TYPE double
388 #endif
389
390 #ifdef LOAD_AVE_TYPE
391
392 #ifndef VMS
393 #ifndef __linux__
394 #ifndef NLIST_STRUCT
395 #include <a.out.h>
396 #else /* NLIST_STRUCT */
397 #include <nlist.h>
398 #endif /* NLIST_STRUCT */
399
400 #ifdef SUNOS_5
401 #include <fcntl.h>
402 #include <kvm.h>
403 #include <kstat.h>
404 #endif
405
406 #ifndef KERNEL_FILE
407 #define KERNEL_FILE "/vmunix"
408 #endif /* KERNEL_FILE */
409
410 #ifndef LDAV_SYMBOL
411 #define LDAV_SYMBOL "_avenrun"
412 #endif /* LDAV_SYMBOL */
413 #endif /* __linux__ */
414
415 #else /* VMS */
416
417 #ifndef eunice
418 #include <iodef.h>
419 #include <descrip.h>
420 #else /* eunice */
421 #include <vms/iodef.h>
422 #endif /* eunice */
423 #endif /* VMS */
424
425 #ifndef LDAV_CVT
426 #define LDAV_CVT(n) ((double) (n))
427 #endif /* !LDAV_CVT */
428
429 #endif /* LOAD_AVE_TYPE */
430
431 #ifdef NeXT
432 #ifdef HAVE_MACH_MACH_H
433 #include <mach/mach.h>
434 #else
435 #include <mach.h>
436 #endif
437 #endif /* NeXT */
438
439 #ifdef sgi
440 #include <sys/sysmp.h>
441 #endif /* sgi */
442
443 #ifdef UMAX
444 #include <stdio.h>
445 #include <signal.h>
446 #include <sys/time.h>
447 #include <sys/wait.h>
448 #include <sys/syscall.h>
449
450 #ifdef UMAX_43
451 #include <machine/cpu.h>
452 #include <inq_stats/statistics.h>
453 #include <inq_stats/sysstats.h>
454 #include <inq_stats/cpustats.h>
455 #include <inq_stats/procstats.h>
456 #else /* Not UMAX_43.  */
457 #include <sys/sysdefs.h>
458 #include <sys/statistics.h>
459 #include <sys/sysstats.h>
460 #include <sys/cpudefs.h>
461 #include <sys/cpustats.h>
462 #include <sys/procstats.h>
463 #endif /* Not UMAX_43.  */
464 #endif /* UMAX */
465
466 #ifdef DGUX
467 #include <sys/dg_sys_info.h>
468 #endif
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 or VMS load ave driver.  */
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 #if !defined(VMS) && !defined(sgi) && !defined(__linux__)
501 static struct nlist nl[2];
502 #endif /* Not VMS or sgi */
503
504 #ifdef SUNOS_5
505 static kvm_t *kd;
506 #endif /* SUNOS_5 */
507
508 #endif /* LOAD_AVE_TYPE */
509 \f
510 /* Put the 1 minute, 5 minute and 15 minute load averages
511    into the first NELEM elements of LOADAVG.
512    Return the number written (never more than 3, but may be less than NELEM),
513    or -1 if an error occurred.  */
514
515 int
516 getloadavg (loadavg, nelem)
517      double loadavg[];
518      int nelem;
519 {
520   int elem = 0;                 /* Return value.  */
521
522 #ifdef NO_GET_LOAD_AVG
523 #define LDAV_DONE
524   /* Set errno to zero to indicate that there was no particular error;
525      this function just can't work at all on this system.  */
526   errno = 0;
527   elem = -1;
528 #endif
529
530 #if !defined (LDAV_DONE) && defined (SUNOS_5)
531 /* Use libkstat because we don't have to be root.  */
532 #define LDAV_DONE
533   kstat_ctl_t *kc;
534   kstat_t *ksp;
535   kstat_named_t *kn;
536
537   kc = kstat_open ();
538   if (kc == 0) return -1;
539   ksp = kstat_lookup (kc, "unix", 0, "system_misc");
540   if (ksp == 0 ) return -1;
541   if (kstat_read (kc, ksp, 0) == -1) return -1;
542
543
544   kn = kstat_data_lookup (ksp, "avenrun_1min");
545   if (kn == 0)
546     {
547       /* Return -1 if no load average information is available.  */
548       nelem = 0;
549       elem = -1;
550     }
551
552   if (nelem >= 1)
553     loadavg[elem++] = (double) kn->value.ul/FSCALE;
554
555   if (nelem >= 2)
556     {
557       kn = kstat_data_lookup (ksp, "avenrun_5min");
558       if (kn != 0)
559         {
560           loadavg[elem++] = (double) kn->value.ul/FSCALE;
561
562           if (nelem >= 3)
563             {
564               kn = kstat_data_lookup (ksp, "avenrun_15min");
565               if (kn != 0)
566                 loadavg[elem++] = (double) kn->value.ul/FSCALE;
567             }
568         }
569     }
570
571   kstat_close (kc);
572 #endif /* SUNOS_5 */
573
574 #if !defined (LDAV_DONE) && defined (__linux__)
575 #define LDAV_DONE
576 #undef LOAD_AVE_TYPE
577
578 #ifndef LINUX_LDAV_FILE
579 #define LINUX_LDAV_FILE "/proc/loadavg"
580 #endif
581
582   char ldavgbuf[40];
583   double load_ave[3];
584   int fd, count;
585
586   fd = open (LINUX_LDAV_FILE, O_RDONLY);
587   if (fd == -1)
588     return -1;
589   count = read (fd, ldavgbuf, 40);
590   (void) close (fd);
591   if (count <= 0)
592     return -1;
593
594   count = sscanf (ldavgbuf, "%lf %lf %lf",
595                   &load_ave[0], &load_ave[1], &load_ave[2]);
596   if (count < 1)
597     return -1;
598
599   for (elem = 0; elem < nelem && elem < count; elem++)
600     loadavg[elem] = load_ave[elem];
601
602   return elem;
603
604 #endif /* __linux__ */
605
606 #if !defined (LDAV_DONE) && defined (__NetBSD__)
607 #define LDAV_DONE
608 #undef LOAD_AVE_TYPE
609
610 #ifndef NETBSD_LDAV_FILE
611 #define NETBSD_LDAV_FILE "/kern/loadavg"
612 #endif
613
614   unsigned long int load_ave[3], scale;
615   int count;
616   FILE *fp;
617
618   fp = fopen (NETBSD_LDAV_FILE, "r");
619   if (fp == NULL)
620     return -1;
621   count = fscanf (fp, "%lu %lu %lu %lu\n",
622                   &load_ave[0], &load_ave[1], &load_ave[2],
623                   &scale);
624   (void) fclose (fp);
625   if (count != 4)
626     return -1;
627
628   for (elem = 0; elem < nelem; elem++)
629     loadavg[elem] = (double) load_ave[elem] / (double) scale;
630
631   return elem;
632
633 #endif /* __NetBSD__ */
634
635 #if !defined (LDAV_DONE) && defined (NeXT)
636 #define LDAV_DONE
637   /* The NeXT code was adapted from iscreen 3.2.  */
638
639   host_t host;
640   struct processor_set_basic_info info;
641   unsigned info_count;
642
643   /* We only know how to get the 1-minute average for this system,
644      so even if the caller asks for more than 1, we only return 1.  */
645
646   if (!getloadavg_initialized)
647     {
648       if (processor_set_default (host_self (), &default_set) == KERN_SUCCESS)
649         getloadavg_initialized = 1;
650     }
651
652   if (getloadavg_initialized)
653     {
654       info_count = PROCESSOR_SET_BASIC_INFO_COUNT;
655       if (processor_set_info (default_set, PROCESSOR_SET_BASIC_INFO, &host,
656                              (processor_set_info_t) &info, &info_count)
657           != KERN_SUCCESS)
658         getloadavg_initialized = 0;
659       else
660         {
661           if (nelem > 0)
662             loadavg[elem++] = (double) info.load_average / LOAD_SCALE;
663         }
664     }
665
666   if (!getloadavg_initialized)
667     return -1;
668 #endif /* NeXT */
669
670 #if !defined (LDAV_DONE) && defined (UMAX)
671 #define LDAV_DONE
672 /* UMAX 4.2, which runs on the Encore Multimax multiprocessor, does not
673    have a /dev/kmem.  Information about the workings of the running kernel
674    can be gathered with inq_stats system calls.
675    We only know how to get the 1-minute average for this system.  */
676
677   struct proc_summary proc_sum_data;
678   struct stat_descr proc_info;
679   double load;
680   register unsigned int i, j;
681
682   if (cpus == 0)
683     {
684       register unsigned int c, i;
685       struct cpu_config conf;
686       struct stat_descr desc;
687
688       desc.sd_next = 0;
689       desc.sd_subsys = SUBSYS_CPU;
690       desc.sd_type = CPUTYPE_CONFIG;
691       desc.sd_addr = (char *) &conf;
692       desc.sd_size = sizeof conf;
693
694       if (inq_stats (1, &desc))
695         return -1;
696
697       c = 0;
698       for (i = 0; i < conf.config_maxclass; ++i)
699         {
700           struct class_stats stats;
701           bzero ((char *) &stats, sizeof stats);
702
703           desc.sd_type = CPUTYPE_CLASS;
704           desc.sd_objid = i;
705           desc.sd_addr = (char *) &stats;
706           desc.sd_size = sizeof stats;
707
708           if (inq_stats (1, &desc))
709             return -1;
710
711           c += stats.class_numcpus;
712         }
713       cpus = c;
714       samples = cpus < 2 ? 3 : (2 * cpus / 3);
715     }
716
717   proc_info.sd_next = 0;
718   proc_info.sd_subsys = SUBSYS_PROC;
719   proc_info.sd_type = PROCTYPE_SUMMARY;
720   proc_info.sd_addr = (char *) &proc_sum_data;
721   proc_info.sd_size = sizeof (struct proc_summary);
722   proc_info.sd_sizeused = 0;
723
724   if (inq_stats (1, &proc_info) != 0)
725     return -1;
726
727   load = proc_sum_data.ps_nrunnable;
728   j = 0;
729   for (i = samples - 1; i > 0; --i)
730     {
731       load += proc_sum_data.ps_nrun[j];
732       if (j++ == PS_NRUNSIZE)
733         j = 0;
734     }
735
736   if (nelem > 0)
737     loadavg[elem++] = load / samples / cpus;
738 #endif /* UMAX */
739
740 #if !defined (LDAV_DONE) && defined (DGUX)
741 #define LDAV_DONE
742   /* This call can return -1 for an error, but with good args
743      it's not supposed to fail.  The first argument is for no
744      apparent reason of type `long int *'.  */
745   dg_sys_info ((long int *) &load_info,
746                DG_SYS_INFO_LOAD_INFO_TYPE,
747                DG_SYS_INFO_LOAD_VERSION_0);
748
749   if (nelem > 0)
750     loadavg[elem++] = load_info.one_minute;
751   if (nelem > 1)
752     loadavg[elem++] = load_info.five_minute;
753   if (nelem > 2)
754     loadavg[elem++] = load_info.fifteen_minute;
755 #endif /* DGUX */
756
757 #if !defined (LDAV_DONE) && defined (apollo)
758 #define LDAV_DONE
759 /* Apollo code from lisch@mentorg.com (Ray Lischner).
760
761    This system call is not documented.  The load average is obtained as
762    three long integers, for the load average over the past minute,
763    five minutes, and fifteen minutes.  Each value is a scaled integer,
764    with 16 bits of integer part and 16 bits of fraction part.
765
766    I'm not sure which operating system first supported this system call,
767    but I know that SR10.2 supports it.  */
768
769   extern void proc1_$get_loadav ();
770   unsigned long load_ave[3];
771
772   proc1_$get_loadav (load_ave);
773
774   if (nelem > 0)
775     loadavg[elem++] = load_ave[0] / 65536.0;
776   if (nelem > 1)
777     loadavg[elem++] = load_ave[1] / 65536.0;
778   if (nelem > 2)
779     loadavg[elem++] = load_ave[2] / 65536.0;
780 #endif /* apollo */
781
782 #if !defined (LDAV_DONE) && defined (OSF_MIPS)
783 #define LDAV_DONE
784
785   struct tbl_loadavg load_ave;
786   table (TBL_LOADAVG, 0, &load_ave, 1, sizeof (load_ave));
787   loadavg[elem++]
788     = (load_ave.tl_lscale == 0
789        ? load_ave.tl_avenrun.d[0]
790        : (load_ave.tl_avenrun.l[0] / (double) load_ave.tl_lscale));
791 #endif  /* OSF_MIPS */
792
793 #if !defined (LDAV_DONE) && (defined (MSDOS) || defined (WIN32))
794 #define LDAV_DONE
795
796   /* A faithful emulation is going to have to be saved for a rainy day.  */
797   for ( ; elem < nelem; elem++)
798     {
799       loadavg[elem] = 0.0;
800     }
801 #endif  /* MSDOS */
802
803 #if !defined (LDAV_DONE) && defined (OSF_ALPHA)
804 #define LDAV_DONE
805
806   struct tbl_loadavg load_ave;
807   table (TBL_LOADAVG, 0, &load_ave, 1, sizeof (load_ave));
808   for (elem = 0; elem < nelem; elem++)
809     loadavg[elem]
810       = (load_ave.tl_lscale == 0
811        ? load_ave.tl_avenrun.d[elem]
812        : (load_ave.tl_avenrun.l[elem] / (double) load_ave.tl_lscale));
813 #endif /* OSF_ALPHA */
814
815 #if !defined (LDAV_DONE) && defined (VMS)
816   /* VMS specific code -- read from the Load Ave driver.  */
817
818   LOAD_AVE_TYPE load_ave[3];
819   static int getloadavg_initialized = 0;
820 #ifdef eunice
821   struct
822   {
823     int dsc$w_length;
824     char *dsc$a_pointer;
825   } descriptor;
826 #endif
827
828   /* Ensure that there is a channel open to the load ave device.  */
829   if (!getloadavg_initialized)
830     {
831       /* Attempt to open the channel.  */
832 #ifdef eunice
833       descriptor.dsc$w_length = 18;
834       descriptor.dsc$a_pointer = "$$VMS_LOAD_AVERAGE";
835 #else
836       $DESCRIPTOR (descriptor, "LAV0:");
837 #endif
838       if (sys$assign (&descriptor, &channel, 0, 0) & 1)
839         getloadavg_initialized = 1;
840     }
841
842   /* Read the load average vector.  */
843   if (getloadavg_initialized
844       && !(sys$qiow (0, channel, IO$_READVBLK, 0, 0, 0,
845                      load_ave, 12, 0, 0, 0, 0) & 1))
846     {
847       sys$dassgn (channel);
848       getloadavg_initialized = 0;
849     }
850
851   if (!getloadavg_initialized)
852     return -1;
853 #endif /* VMS */
854
855 #if !defined (LDAV_DONE) && defined(LOAD_AVE_TYPE) && !defined(VMS)
856
857   /* UNIX-specific code -- read the average from /dev/kmem.  */
858
859 #define LDAV_PRIVILEGED         /* This code requires special installation.  */
860
861   LOAD_AVE_TYPE load_ave[3];
862
863   /* Get the address of LDAV_SYMBOL.  */
864   if (offset == 0)
865     {
866 #ifndef sgi
867 #ifndef NLIST_STRUCT
868       strcpy (nl[0].n_name, LDAV_SYMBOL);
869       strcpy (nl[1].n_name, "");
870 #else /* NLIST_STRUCT */
871 #ifdef NLIST_NAME_UNION
872       nl[0].n_un.n_name = LDAV_SYMBOL;
873       nl[1].n_un.n_name = 0;
874 #else /* not NLIST_NAME_UNION */
875       nl[0].n_name = LDAV_SYMBOL;
876       nl[1].n_name = 0;
877 #endif /* not NLIST_NAME_UNION */
878 #endif /* NLIST_STRUCT */
879
880 #ifndef SUNOS_5
881       if (
882 #if !(defined (_AIX) && !defined (ps2))
883           nlist (KERNEL_FILE, nl)
884 #else  /* _AIX */
885           knlist (nl, 1, sizeof (nl[0]))
886 #endif
887           >= 0)
888           /* Omit "&& nl[0].n_type != 0 " -- it breaks on Sun386i.  */
889           {
890 #ifdef FIXUP_KERNEL_SYMBOL_ADDR
891             FIXUP_KERNEL_SYMBOL_ADDR (nl);
892 #endif
893             offset = nl[0].n_value;
894           }
895 #endif /* !SUNOS_5 */
896 #else  /* sgi */
897           int ldav_off;
898
899           ldav_off = sysmp (MP_KERNADDR, MPKA_AVENRUN);
900           if (ldav_off != -1)
901           offset = (long) ldav_off & 0x7fffffff;
902 #endif /* sgi */
903         }
904
905   /* Make sure we have /dev/kmem open.  */
906   if (!getloadavg_initialized)
907     {
908 #ifndef SUNOS_5
909       channel = open ("/dev/kmem", 0);
910       if (channel >= 0)
911         {
912           /* Set the channel to close on exec, so it does not
913              litter any child's descriptor table.  */
914 #ifdef FD_SETFD
915 #ifndef FD_CLOEXEC
916 #define FD_CLOEXEC 1
917 #endif
918           (void) fcntl (channel, F_SETFD, FD_CLOEXEC);
919 #endif
920           getloadavg_initialized = 1;
921         }
922 #else /* SUNOS_5 */
923       /* We pass 0 for the kernel, corefile, and swapfile names
924          to use the currently running kernel.  */
925       kd = kvm_open (0, 0, 0, O_RDONLY, 0);
926       if (kd != 0)
927         {
928           /* nlist the currently running kernel.  */
929           kvm_nlist (kd, nl);
930           offset = nl[0].n_value;
931           getloadavg_initialized = 1;
932         }
933 #endif /* SUNOS_5 */
934     }
935
936   /* If we can, get the load average values.  */
937   if (offset && getloadavg_initialized)
938     {
939       /* Try to read the load.  */
940 #ifndef SUNOS_5
941       if (lseek (channel, offset, 0) == -1L
942           || read (channel, (char *) load_ave, sizeof (load_ave))
943           != sizeof (load_ave))
944         {
945           close (channel);
946           getloadavg_initialized = 0;
947         }
948 #else  /* SUNOS_5 */
949       if (kvm_read (kd, offset, (char *) load_ave, sizeof (load_ave))
950           != sizeof (load_ave))
951         {
952           kvm_close (kd);
953           getloadavg_initialized = 0;
954         }
955 #endif /* SUNOS_5 */
956     }
957
958   if (offset == 0 || !getloadavg_initialized)
959     return -1;
960 #endif /* LOAD_AVE_TYPE and not VMS */
961
962 #if !defined (LDAV_DONE) && defined (LOAD_AVE_TYPE) /* Including VMS.  */
963   if (nelem > 0)
964     loadavg[elem++] = LDAV_CVT (load_ave[0]);
965   if (nelem > 1)
966     loadavg[elem++] = LDAV_CVT (load_ave[1]);
967   if (nelem > 2)
968     loadavg[elem++] = LDAV_CVT (load_ave[2]);
969
970 #define LDAV_DONE
971 #endif /* !LDAV_DONE && LOAD_AVE_TYPE */
972
973 #ifdef LDAV_DONE
974   return elem;
975 #else
976   /* Set errno to zero to indicate that there was no particular error;
977      this function just can't work at all on this system.  */
978   errno = 0;
979   return -1;
980 #endif
981 }
982
983 #endif /* ! HAVE_GETLOADAVG */
984 \f
985 #ifdef TEST
986 void
987 main (argc, argv)
988      int argc;
989      char **argv;
990 {
991   int naptime = 0;
992
993   if (argc > 1)
994     naptime = atoi (argv[1]);
995
996   while (1)
997     {
998       double avg[3];
999       int loads;
1000
1001       errno = 0;                /* Don't be misled if it doesn't set errno.  */
1002       loads = getloadavg (avg, 3);
1003       if (loads == -1)
1004         {
1005           perror ("Error getting load average");
1006           exit (1);
1007         }
1008       if (loads > 0)
1009         printf ("1-minute: %f  ", avg[0]);
1010       if (loads > 1)
1011         printf ("5-minute: %f  ", avg[1]);
1012       if (loads > 2)
1013         printf ("15-minute: %f  ", avg[2]);
1014       if (loads > 0)
1015         putchar ('\n');
1016
1017       if (naptime == 0)
1018         break;
1019       sleep (naptime);
1020     }
1021
1022   exit (0);
1023 }
1024 #endif /* TEST */