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