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