1/* 2 * ProFTPD: mod_load -- a module for refusing connections based on system load 3 * 4 * Copyright (c) 2001-2011 TJ Saunders 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. 19 * 20 * As a special exemption, TJ Saunders and other respective copyright holders 21 * give permission to link this program with OpenSSL, and distribute the 22 * resulting executable, without including the source code for OpenSSL in the 23 * source distribution. 24 * 25 * This is mod_load, contrib software for proftpd 1.2.x and above. 26 * For more information contact TJ Saunders <tj@castaglia.org>. 27 * 28 * Based on the getloadavg.c file included in GNU make-3.79, which carries 29 * the following copyright: 30 * 31 * Copyright (C) 1985, 86, 87, 88, 89, 91, 92, 93, 1994, 1995, 1997 32 * Free Software Foundation, Inc. 33 * 34 * $Id: mod_load.c.in,v 1.10 2011-05-23 20:58:46 castaglia Exp $ 35 * $Libraries: @MODULE_LIBS@ $ 36 */ 37 38#include "conf.h" 39#include "privs.h" 40#include "mod_load.h" 41 42#define MOD_LOAD_VERSION "mod_load/1.0.1" 43 44/* Make sure the version of proftpd is as necessary. */ 45#if PROFTPD_VERSION_NUMBER < 0x0001030402 46# error "ProFTPD 1.3.4rc2 or later required" 47#endif 48 49/* Support code. Most of this is from getloadavg.c 50 */ 51 52#if defined (unix) || defined (__unix) 53# include <sys/param.h> 54#endif 55 56#ifndef HAVE_GETLOADAVG 57# if !defined(LDAV_CVT) && defined(LOAD_AVE_CVT) 58# define LDAV_CVT(n) (LOAD_AVE_CVT (n) / 100.0) 59# endif 60 61# if !defined (BSD) && defined (ultrix) 62# define BSD 63# endif 64 65# ifdef NeXT 66# undef BSD 67# undef FSCALE 68# endif 69 70# ifdef __GNU__ 71# undef BSD 72# undef FSCALE 73# endif /* __GNU__ */ 74 75# if defined (HPUX) && !defined (hpux) 76# define hpux 77# endif 78 79# if defined (__hpux) && !defined (hpux) 80# define hpux 81# endif 82 83# if defined (__sun) && !defined (sun) 84# define sun 85# endif 86 87# if defined(hp300) && !defined(hpux) 88# define MORE_BSD 89# endif 90 91# if defined(ultrix) && defined(mips) 92# define decstation 93# endif 94 95# if defined (__SVR4) && !defined (SVR4) 96# define SVR4 97# endif 98 99# if (defined(sun) && defined(SVR4)) || defined (SOLARIS2) 100# define SUNOS_5 101# endif 102 103# if defined (__osf__) && (defined (__alpha) || defined (__alpha__)) 104# define OSF_ALPHA 105# include <sys/mbuf.h> 106# include <sys/socket.h> 107# include <net/route.h> 108# include <sys/table.h> 109# endif 110 111# if defined (__osf__) && (defined (mips) || defined (__mips__)) 112# define OSF_MIPS 113# include <sys/table.h> 114# endif 115 116# if !defined (tek4300) && defined (unix) && defined (m68k) && defined (mc68000) && defined (mc68020) && defined (_MACH_IND_SYS_TYPES) 117# define tek4300 118# endif 119 120# ifndef LOAD_AVE_TYPE 121 122# ifdef MORE_BSD 123# define LOAD_AVE_TYPE long 124# endif 125 126# ifdef sun 127# define LOAD_AVE_TYPE long 128# endif 129 130# ifdef decstation 131# define LOAD_AVE_TYPE long 132# endif 133 134# ifdef _SEQUENT_ 135# define LOAD_AVE_TYPE long 136# endif 137 138# ifdef sgi 139# define LOAD_AVE_TYPE long 140# endif 141 142# ifdef SVR4 143# define LOAD_AVE_TYPE long 144# endif 145 146# ifdef sony_news 147# define LOAD_AVE_TYPE long 148# endif 149 150# ifdef sequent 151# define LOAD_AVE_TYPE long 152# endif 153 154# ifdef OSF_ALPHA 155# define LOAD_AVE_TYPE long 156# endif 157 158# if defined (ardent) && defined (titan) 159# define LOAD_AVE_TYPE long 160# endif 161 162# ifdef tek4300 163# define LOAD_AVE_TYPE long 164# endif 165 166# if defined(alliant) && defined(i860) /* Alliant FX/2800 */ 167# define LOAD_AVE_TYPE long 168# endif 169 170# ifdef _AIX 171# define LOAD_AVE_TYPE long 172# endif 173 174# ifdef convex 175# define LOAD_AVE_TYPE double 176# ifndef LDAV_CVT 177# define LDAV_CVT(n) (n) 178# endif 179# endif 180 181# endif /* No LOAD_AVE_TYPE. */ 182 183# ifdef OSF_ALPHA 184# undef FSCALE 185# define FSCALE 1024.0 186# endif 187 188# if defined(alliant) && defined(i860) /* Alliant FX/2800 */ 189# undef FSCALE 190# define FSCALE 100.0 191# endif 192 193# ifndef FSCALE 194 195# ifdef MORE_BSD 196# define FSCALE 2048.0 197# endif 198 199# if defined(MIPS) || defined(SVR4) || defined(decstation) 200# define FSCALE 256 201# endif 202 203# if defined (sgi) || defined (sequent) 204# undef FSCALE 205# define FSCALE 1000.0 206# endif 207 208# if defined (ardent) && defined (titan) 209# define FSCALE 65536.0 210# endif 211 212# ifdef tek4300 213# define FSCALE 100.0 214# endif 215 216# ifdef _AIX 217# define FSCALE 65536.0 218# endif 219 220# endif /* Not FSCALE. */ 221 222# if !defined (LDAV_CVT) && defined (FSCALE) 223# define LDAV_CVT(n) (((double) (n)) / FSCALE) 224# endif 225 226# ifndef NLIST_STRUCT 227 228# ifdef MORE_BSD 229# define NLIST_STRUCT 230# endif 231 232# ifdef sun 233# define NLIST_STRUCT 234# endif 235 236# ifdef decstation 237# define NLIST_STRUCT 238# endif 239 240# ifdef hpux 241# define NLIST_STRUCT 242# endif 243 244# if defined (_SEQUENT_) || defined (sequent) 245# define NLIST_STRUCT 246# endif 247 248# ifdef sgi 249# define NLIST_STRUCT 250# endif 251 252# ifdef SVR4 253# define NLIST_STRUCT 254# endif 255 256# ifdef sony_news 257# define NLIST_STRUCT 258# endif 259 260# ifdef OSF_ALPHA 261# define NLIST_STRUCT 262# endif 263 264# if defined (ardent) && defined (titan) 265# define NLIST_STRUCT 266# endif 267 268# ifdef tek4300 269# define NLIST_STRUCT 270# endif 271 272# ifdef butterfly 273# define NLIST_STRUCT 274# endif 275 276# if defined(alliant) && defined(i860) /* Alliant FX/2800 */ 277# define NLIST_STRUCT 278# endif 279 280# ifdef _AIX 281# define NLIST_STRUCT 282# endif 283 284# endif /* defined (NLIST_STRUCT) */ 285 286# if defined(sgi) || (defined(mips) && !defined(BSD)) 287# define FIXUP_KERNEL_SYMBOL_ADDR(nl) ((nl)[0].n_value &= ~(1 << 31)) 288# endif 289 290# if !defined (KERNEL_FILE) && defined (sequent) 291# define KERNEL_FILE "/dynix" 292# endif 293 294# if !defined (KERNEL_FILE) && defined (hpux) 295# define KERNEL_FILE "/hp-ux" 296# endif 297 298# if !defined(KERNEL_FILE) && (defined(_SEQUENT_) || defined(MIPS) || defined(SVR4) || defined(ISC) || defined (sgi) || (defined (ardent) && defined (titan))) 299# define KERNEL_FILE "/unix" 300# endif 301 302# if !defined (LDAV_SYMBOL) && defined (alliant) 303# define LDAV_SYMBOL "_Loadavg" 304# endif 305 306# if !defined(LDAV_SYMBOL) && ((defined(hpux) && !defined(hp9000s300)) || defined(_SEQUENT_) || defined(SVR4) || defined(ISC) || defined(sgi) || (defined (ardent) && defined (titan)) || defined (_AIX)) 307# define LDAV_SYMBOL "avenrun" 308# endif 309 310# ifdef HAVE_UNISTD_H 311# include <unistd.h> 312# endif 313 314# include <stdio.h> 315 316# if !defined(LOAD_AVE_TYPE) && (defined(BSD) || defined(LDAV_CVT) || defined(KERNEL_FILE) || defined(LDAV_SYMBOL)) 317# define LOAD_AVE_TYPE double 318# endif 319 320# ifdef LOAD_AVE_TYPE 321 322# ifndef VMS 323# ifndef __linux__ 324# ifndef NLIST_STRUCT 325# include <a.out.h> 326# else /* NLIST_STRUCT */ 327# include <nlist.h> 328# endif /* NLIST_STRUCT */ 329 330# ifdef SUNOS_5 331# include <fcntl.h> 332# include <kvm.h> 333# include <kstat.h> 334# endif 335 336# if defined (hpux) && defined (HAVE_PSTAT_GETDYNAMIC) 337# include <sys/pstat.h> 338# endif 339 340# ifndef KERNEL_FILE 341# define KERNEL_FILE "/vmunix" 342# endif /* KERNEL_FILE */ 343 344# ifndef LDAV_SYMBOL 345# define LDAV_SYMBOL "_avenrun" 346# endif /* LDAV_SYMBOL */ 347# endif /* __linux__ */ 348 349# else /* VMS */ 350 351# ifndef eunice 352# include <iodef.h> 353# include <descrip.h> 354# else /* eunice */ 355# include <vms/iodef.h> 356# endif /* eunice */ 357# endif /* VMS */ 358 359# ifndef LDAV_CVT 360# define LDAV_CVT(n) ((double) (n)) 361# endif /* !LDAV_CVT */ 362 363# endif /* LOAD_AVE_TYPE */ 364 365# if defined(__GNU__) && !defined (NeXT) 366# define NeXT 367# define host_self mach_host_self 368# endif 369 370# ifdef NeXT 371# ifdef HAVE_MACH_MACH_H 372# include <mach/mach.h> 373# else 374# include <mach.h> 375# endif 376# endif /* NeXT */ 377 378# ifdef sgi 379# include <sys/sysmp.h> 380# endif /* sgi */ 381 382# ifdef UMAX 383# include <stdio.h> 384# include <signal.h> 385# include <sys/time.h> 386# include <sys/wait.h> 387# include <sys/syscall.h> 388 389# ifdef UMAX_43 390# include <machine/cpu.h> 391# include <inq_stats/statistics.h> 392# include <inq_stats/sysstats.h> 393# include <inq_stats/cpustats.h> 394# include <inq_stats/procstats.h> 395# else /* Not UMAX_43. */ 396# include <sys/sysdefs.h> 397# include <sys/statistics.h> 398# include <sys/sysstats.h> 399# include <sys/cpudefs.h> 400# include <sys/cpustats.h> 401# include <sys/procstats.h> 402# endif /* Not UMAX_43. */ 403# endif /* UMAX */ 404 405# ifdef DGUX 406# include <sys/dg_sys_info.h> 407# endif 408 409# if defined(HAVE_FCNTL_H) || defined(_POSIX_VERSION) 410# include <fcntl.h> 411# else 412# include <sys/file.h> 413# endif 414 415# ifdef NeXT 416static processor_set_t default_set; 417static int getloadavg_initialized; 418# endif /* NeXT */ 419 420# ifdef UMAX 421static unsigned int cpus = 0; 422static unsigned int samples; 423# endif /* UMAX */ 424 425# ifdef DGUX 426static struct dg_sys_info_load_info load_info; /* what-a-mouthful! */ 427# endif /* DGUX */ 428 429# if !defined(HAVE_LIBKSTAT) && defined(LOAD_AVE_TYPE) && !defined(__linux__) 430/* File descriptor open to /dev/kmem or VMS load ave driver. */ 431static int channel; 432/* Nonzero iff channel is valid. */ 433static int getloadavg_initialized; 434/* Offset in kmem to seek to read load average, or 0 means invalid. */ 435static long offset; 436 437# if !defined(VMS) && !defined(sgi) && !defined(__linux__) 438static struct nlist nl[2]; 439# endif /* Not VMS or sgi */ 440 441# ifdef SUNOS_5 442static kvm_t *kd; 443# endif /* SUNOS_5 */ 444 445# endif /* LOAD_AVE_TYPE && !HAVE_LIBKSTAT */ 446 447/* Put the 1 minute, 5 minute and 15 minute load averages into the first 448 * NELEM elements of LOADAVG. Return the number written (never more than 3, 449 * but may be less than NELEM), or -1 if an error occurred. 450 */ 451 452static int getloadavg(double *loadavg, int nelem) { 453 int elem = 0; /* Return value. */ 454 455# ifdef NO_GET_LOAD_AVG 456# define LDAV_DONE 457 /* Set errno to zero to indicate that there was no particular error; 458 this function just can't work at all on this system. */ 459 errno = 0; 460 elem = -1; 461# endif 462 463# if !defined (LDAV_DONE) && defined (HAVE_LIBKSTAT) 464/* Use libkstat because we don't have to be root. */ 465# define LDAV_DONE 466 kstat_ctl_t *kc; 467 kstat_t *ksp; 468 kstat_named_t *kn; 469 470 kc = kstat_open (); 471 if (kc == 0) 472 return -1; 473 ksp = kstat_lookup (kc, "unix", 0, "system_misc"); 474 if (ksp == 0 ) 475 return -1; 476 if (kstat_read (kc, ksp, 0) == -1) 477 return -1; 478 479 480 kn = kstat_data_lookup (ksp, "avenrun_1min"); 481 if (kn == 0) 482 { 483 /* Return -1 if no load average information is available. */ 484 nelem = 0; 485 elem = -1; 486 } 487 488 if (nelem >= 1) 489 loadavg[elem++] = (double) kn->value.ul/FSCALE; 490 491 if (nelem >= 2) 492 { 493 kn = kstat_data_lookup (ksp, "avenrun_5min"); 494 if (kn != 0) 495 { 496 loadavg[elem++] = (double) kn->value.ul/FSCALE; 497 498 if (nelem >= 3) 499 { 500 kn = kstat_data_lookup (ksp, "avenrun_15min"); 501 if (kn != 0) 502 loadavg[elem++] = (double) kn->value.ul/FSCALE; 503 } 504 } 505 } 506 507 kstat_close (kc); 508# endif /* HAVE_LIBKSTAT */ 509 510# if !defined (LDAV_DONE) && defined (hpux) && defined (HAVE_PSTAT_GETDYNAMIC) 511/* Use pstat_getdynamic() because we don't have to be root. */ 512# define LDAV_DONE 513# undef LOAD_AVE_TYPE 514 515 struct pst_dynamic dyn_info; 516 if (pstat_getdynamic (&dyn_info, sizeof (dyn_info), 0, 0) < 0) 517 return -1; 518 if (nelem > 0) 519 loadavg[elem++] = dyn_info.psd_avg_1_min; 520 if (nelem > 1) 521 loadavg[elem++] = dyn_info.psd_avg_5_min; 522 if (nelem > 2) 523 loadavg[elem++] = dyn_info.psd_avg_15_min; 524 525# endif /* hpux && HAVE_PSTAT_GETDYNAMIC */ 526 527# if !defined (LDAV_DONE) && defined (__linux__) 528# define LDAV_DONE 529# undef LOAD_AVE_TYPE 530 531# ifndef LINUX_LDAV_FILE 532# define LINUX_LDAV_FILE "/proc/loadavg" 533# endif 534 535 char ldavgbuf[40]; 536 double load_ave[3]; 537 int fd, count; 538 539 fd = open (LINUX_LDAV_FILE, O_RDONLY); 540 if (fd == -1) 541 return -1; 542 count = read (fd, ldavgbuf, 40); 543 (void) close (fd); 544 if (count <= 0) 545 return -1; 546 547 count = sscanf (ldavgbuf, "%lf %lf %lf", 548 &load_ave[0], &load_ave[1], &load_ave[2]); 549 if (count < 1) 550 return -1; 551 552 for (elem = 0; elem < nelem && elem < count; elem++) 553 loadavg[elem] = load_ave[elem]; 554 555 return elem; 556 557# endif /* __linux__ */ 558 559# if !defined (LDAV_DONE) && defined (__NetBSD__) 560# define LDAV_DONE 561# undef LOAD_AVE_TYPE 562 563# ifndef NETBSD_LDAV_FILE 564# define NETBSD_LDAV_FILE "/kern/loadavg" 565# endif 566 567 unsigned long int load_ave[3], scale; 568 int count; 569 FILE *fp; 570 571 fp = fopen (NETBSD_LDAV_FILE, "r"); 572 if (fp == NULL) 573 return -1; 574 count = fscanf (fp, "%lu %lu %lu %lu\n", 575 &load_ave[0], &load_ave[1], &load_ave[2], 576 &scale); 577 (void) fclose (fp); 578 if (count != 4) 579 return -1; 580 581 for (elem = 0; elem < nelem; elem++) 582 loadavg[elem] = (double) load_ave[elem] / (double) scale; 583 584 return elem; 585 586# endif /* __NetBSD__ */ 587 588# if !defined (LDAV_DONE) && defined (NeXT) 589# define LDAV_DONE 590 /* The NeXT code was adapted from iscreen 3.2. */ 591 592 host_t host; 593 struct processor_set_basic_info info; 594 unsigned info_count; 595 596 /* We only know how to get the 1-minute average for this system, 597 so even if the caller asks for more than 1, we only return 1. */ 598 599 if (!getloadavg_initialized) 600 { 601 if (processor_set_default (host_self (), &default_set) == KERN_SUCCESS) 602 getloadavg_initialized = 1; 603 } 604 605 if (getloadavg_initialized) 606 { 607 info_count = PROCESSOR_SET_BASIC_INFO_COUNT; 608 if (processor_set_info (default_set, PROCESSOR_SET_BASIC_INFO, &host, 609 (processor_set_info_t) &info, &info_count) 610 != KERN_SUCCESS) 611 getloadavg_initialized = 0; 612 else 613 { 614 if (nelem > 0) 615 loadavg[elem++] = (double) info.load_average / LOAD_SCALE; 616 } 617 } 618 619 if (!getloadavg_initialized) 620 return -1; 621# endif /* NeXT */ 622 623# if !defined (LDAV_DONE) && defined (UMAX) 624# define LDAV_DONE 625/* UMAX 4.2, which runs on the Encore Multimax multiprocessor, does not 626 have a /dev/kmem. Information about the workings of the running kernel 627 can be gathered with inq_stats system calls. 628 We only know how to get the 1-minute average for this system. */ 629 630 struct proc_summary proc_sum_data; 631 struct stat_descr proc_info; 632 double load; 633 register unsigned int i, j; 634 635 if (cpus == 0) 636 { 637 register unsigned int c, i; 638 struct cpu_config conf; 639 struct stat_descr desc; 640 641 desc.sd_next = 0; 642 desc.sd_subsys = SUBSYS_CPU; 643 desc.sd_type = CPUTYPE_CONFIG; 644 desc.sd_addr = (char *) &conf; 645 desc.sd_size = sizeof conf; 646 647 if (inq_stats (1, &desc)) 648 return -1; 649 650 c = 0; 651 for (i = 0; i < conf.config_maxclass; ++i) 652 { 653 struct class_stats stats; 654 bzero ((char *) &stats, sizeof stats); 655 656 desc.sd_type = CPUTYPE_CLASS; 657 desc.sd_objid = i; 658 desc.sd_addr = (char *) &stats; 659 desc.sd_size = sizeof stats; 660 661 if (inq_stats (1, &desc)) 662 return -1; 663 664 c += stats.class_numcpus; 665 } 666 cpus = c; 667 samples = cpus < 2 ? 3 : (2 * cpus / 3); 668 } 669 670 proc_info.sd_next = 0; 671 proc_info.sd_subsys = SUBSYS_PROC; 672 proc_info.sd_type = PROCTYPE_SUMMARY; 673 proc_info.sd_addr = (char *) &proc_sum_data; 674 proc_info.sd_size = sizeof (struct proc_summary); 675 proc_info.sd_sizeused = 0; 676 677 if (inq_stats (1, &proc_info) != 0) 678 return -1; 679 680 load = proc_sum_data.ps_nrunnable; 681 j = 0; 682 for (i = samples - 1; i > 0; --i) 683 { 684 load += proc_sum_data.ps_nrun[j]; 685 if (j++ == PS_NRUNSIZE) 686 j = 0; 687 } 688 689 if (nelem > 0) 690 loadavg[elem++] = load / samples / cpus; 691# endif /* UMAX */ 692 693# if !defined (LDAV_DONE) && defined (DGUX) 694# define LDAV_DONE 695 /* This call can return -1 for an error, but with good args 696 it's not supposed to fail. The first argument is for no 697 apparent reason of type `long int *'. */ 698 dg_sys_info ((long int *) &load_info, 699 DG_SYS_INFO_LOAD_INFO_TYPE, 700 DG_SYS_INFO_LOAD_VERSION_0); 701 702 if (nelem > 0) 703 loadavg[elem++] = load_info.one_minute; 704 if (nelem > 1) 705 loadavg[elem++] = load_info.five_minute; 706 if (nelem > 2) 707 loadavg[elem++] = load_info.fifteen_minute; 708# endif /* DGUX */ 709 710# if !defined (LDAV_DONE) && defined (apollo) 711# define LDAV_DONE 712/* Apollo code from lisch@mentorg.com (Ray Lischner). 713 714 This system call is not documented. The load average is obtained as 715 three long integers, for the load average over the past minute, 716 five minutes, and fifteen minutes. Each value is a scaled integer, 717 with 16 bits of integer part and 16 bits of fraction part. 718 719 I'm not sure which operating system first supported this system call, 720 but I know that SR10.2 supports it. */ 721 722 extern void proc1_$get_loadav (); 723 unsigned long load_ave[3]; 724 725 proc1_$get_loadav (load_ave); 726 727 if (nelem > 0) 728 loadavg[elem++] = load_ave[0] / 65536.0; 729 if (nelem > 1) 730 loadavg[elem++] = load_ave[1] / 65536.0; 731 if (nelem > 2) 732 loadavg[elem++] = load_ave[2] / 65536.0; 733# endif /* apollo */ 734 735# if !defined (LDAV_DONE) && defined (OSF_MIPS) 736# define LDAV_DONE 737 738 struct tbl_loadavg load_ave; 739 table (TBL_LOADAVG, 0, &load_ave, 1, sizeof (load_ave)); 740 loadavg[elem++] 741 = (load_ave.tl_lscale == 0 742 ? load_ave.tl_avenrun.d[0] 743 : (load_ave.tl_avenrun.l[0] / (double) load_ave.tl_lscale)); 744# endif /* OSF_MIPS */ 745 746# if !defined (LDAV_DONE) && (defined (__MSDOS__) || defined (WINDOWS32)) 747# define LDAV_DONE 748 749 /* A faithful emulation is going to have to be saved for a rainy day. */ 750 for ( ; elem < nelem; elem++) 751 { 752 loadavg[elem] = 0.0; 753 } 754# endif /* __MSDOS__ || WINDOWS32 */ 755 756# if !defined (LDAV_DONE) && defined (OSF_ALPHA) 757# define LDAV_DONE 758 759 struct tbl_loadavg load_ave; 760 table (TBL_LOADAVG, 0, &load_ave, 1, sizeof (load_ave)); 761 for (elem = 0; elem < nelem; elem++) 762 loadavg[elem] 763 = (load_ave.tl_lscale == 0 764 ? load_ave.tl_avenrun.d[elem] 765 : (load_ave.tl_avenrun.l[elem] / (double) load_ave.tl_lscale)); 766# endif /* OSF_ALPHA */ 767 768# if !defined (LDAV_DONE) && defined (VMS) 769 /* VMS specific code -- read from the Load Ave driver. */ 770 771 LOAD_AVE_TYPE load_ave[3]; 772 static int getloadavg_initialized = 0; 773# ifdef eunice 774 struct 775 { 776 int dsc$w_length; 777 char *dsc$a_pointer; 778 } descriptor; 779# endif 780 781 /* Ensure that there is a channel open to the load ave device. */ 782 if (!getloadavg_initialized) 783 { 784 /* Attempt to open the channel. */ 785# ifdef eunice 786 descriptor.dsc$w_length = 18; 787 descriptor.dsc$a_pointer = "$$VMS_LOAD_AVERAGE"; 788# else 789 $DESCRIPTOR (descriptor, "LAV0:"); 790# endif 791 if (sys$assign (&descriptor, &channel, 0, 0) & 1) 792 getloadavg_initialized = 1; 793 } 794 795 /* Read the load average vector. */ 796 if (getloadavg_initialized 797 && !(sys$qiow (0, channel, IO$_READVBLK, 0, 0, 0, 798 load_ave, 12, 0, 0, 0, 0) & 1)) 799 { 800 sys$dassgn (channel); 801 getloadavg_initialized = 0; 802 } 803 804 if (!getloadavg_initialized) 805 return -1; 806# endif /* VMS */ 807 808# if !defined (LDAV_DONE) && defined(LOAD_AVE_TYPE) && !defined(VMS) 809 810 /* UNIX-specific code -- read the average from /dev/kmem. */ 811 812# define LDAV_PRIVILEGED /* This code requires special installation. */ 813 814 LOAD_AVE_TYPE load_ave[3]; 815 816 /* Get the address of LDAV_SYMBOL. */ 817 if (offset == 0) 818 { 819# ifndef sgi 820# ifndef NLIST_STRUCT 821 strcpy (nl[0].n_name, LDAV_SYMBOL); 822 strcpy (nl[1].n_name, ""); 823# else /* NLIST_STRUCT */ 824# ifdef NLIST_NAME_UNION 825 nl[0].n_un.n_name = LDAV_SYMBOL; 826 nl[1].n_un.n_name = 0; 827# else /* not NLIST_NAME_UNION */ 828 nl[0].n_name = LDAV_SYMBOL; 829 nl[1].n_name = 0; 830# endif /* not NLIST_NAME_UNION */ 831# endif /* NLIST_STRUCT */ 832 833# ifndef SUNOS_5 834 if ( 835# if !(defined (_AIX) && !defined (ps2)) 836 nlist (KERNEL_FILE, nl) 837# else /* _AIX */ 838 knlist (nl, 1, sizeof (nl[0])) 839# endif 840 >= 0) 841 /* Omit "&& nl[0].n_type != 0 " -- it breaks on Sun386i. */ 842 { 843# ifdef FIXUP_KERNEL_SYMBOL_ADDR 844 FIXUP_KERNEL_SYMBOL_ADDR (nl); 845# endif 846 offset = nl[0].n_value; 847 } 848# endif /* !SUNOS_5 */ 849# else /* sgi */ 850 int ldav_off; 851 852 ldav_off = sysmp (MP_KERNADDR, MPKA_AVENRUN); 853 if (ldav_off != -1) 854 offset = (long) ldav_off & 0x7fffffff; 855# endif /* sgi */ 856 } 857 858 /* Make sure we have /dev/kmem open. */ 859 if (!getloadavg_initialized) 860 { 861# ifndef SUNOS_5 862 channel = open ("/dev/kmem", 0); 863 if (channel >= 0) 864 { 865 /* Set the channel to close on exec, so it does not 866 litter any child's descriptor table. */ 867# ifdef F_SETFD 868# ifndef FD_CLOEXEC 869# define FD_CLOEXEC 1 870# endif 871 (void) fcntl (channel, F_SETFD, FD_CLOEXEC); 872# endif 873 getloadavg_initialized = 1; 874 } 875# else /* SUNOS_5 */ 876 /* We pass 0 for the kernel, corefile, and swapfile names 877 to use the currently running kernel. */ 878 kd = kvm_open (0, 0, 0, O_RDONLY, 0); 879 if (kd != 0) 880 { 881 /* nlist the currently running kernel. */ 882 kvm_nlist (kd, nl); 883 offset = nl[0].n_value; 884 getloadavg_initialized = 1; 885 } 886# endif /* SUNOS_5 */ 887 } 888 889 /* If we can, get the load average values. */ 890 if (offset && getloadavg_initialized) 891 { 892 /* Try to read the load. */ 893# ifndef SUNOS_5 894 if (lseek (channel, offset, 0) == -1L 895 || read (channel, (char *) load_ave, sizeof (load_ave)) 896 != sizeof (load_ave)) 897 { 898 close (channel); 899 getloadavg_initialized = 0; 900 } 901# else /* SUNOS_5 */ 902 if (kvm_read (kd, offset, (char *) load_ave, sizeof (load_ave)) 903 != sizeof (load_ave)) 904 { 905 kvm_close (kd); 906 getloadavg_initialized = 0; 907 } 908# endif /* SUNOS_5 */ 909 } 910 911 if (offset == 0 || !getloadavg_initialized) 912 return -1; 913# endif /* LOAD_AVE_TYPE and not VMS */ 914 915# if !defined (LDAV_DONE) && defined (LOAD_AVE_TYPE) /* Including VMS. */ 916 if (nelem > 0) 917 loadavg[elem++] = LDAV_CVT (load_ave[0]); 918 if (nelem > 1) 919 loadavg[elem++] = LDAV_CVT (load_ave[1]); 920 if (nelem > 2) 921 loadavg[elem++] = LDAV_CVT (load_ave[2]); 922 923# define LDAV_DONE 924# endif /* !LDAV_DONE && LOAD_AVE_TYPE */ 925 926# ifdef LDAV_DONE 927 return elem; 928# else 929 /* Set errno to zero to indicate that there was no particular error; 930 this function just can't work at all on this system. */ 931 errno = 0; 932 return -1; 933# endif 934} 935 936#endif /* !HAVE_GETLOADAVG */ 937 938module load_module; 939 940static double load_get_system_load(void) { 941 int res; 942 double loadavg = -1.0; 943 944 /* It is necessary on some platforms (such as Solaris) to have root 945 * privs when doing this, as the information is determined by reading 946 * the image of the running kernel (yikes!) 947 */ 948 PRIVS_ROOT 949 res = getloadavg(&loadavg, 1); 950 PRIVS_RELINQUISH 951 952 /* Return the default value if we did not receive the expected number 953 * of elements from getloadavg(). 954 */ 955 if (res != 1) 956 return -1.0; 957 958 return loadavg; 959} 960 961/* Configuration handlers 962 */ 963 964/* usage: MaxLoad max [mesg] */ 965MODRET set_maxload(cmd_rec *cmd) { 966 double loadval = 0.0; 967 config_rec *c = NULL; 968 969 if (cmd->argc-1 < 1 || cmd->argc-1 > 2) 970 CONF_ERROR(cmd, "incorrect number of parameters"); 971 972 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); 973 974 if (strcasecmp(cmd->argv[1], "none") == 0) 975 loadval = -1.0; 976 else { 977 loadval = atof(cmd->argv[1]); 978 if (loadval < 0.0) 979 CONF_ERROR(cmd, "positive load limit required"); 980 } 981 982 c = add_config_param(cmd->argv[0], cmd->argc-1, NULL); 983 c->argv[0] = pcalloc(c->pool, sizeof(double)); 984 985 if (loadval < 0.0) 986 c->argv[0] = NULL; 987 988 else 989 *((double *) c->argv[0]) = loadval; 990 991 if (cmd->argc-1 == 2) { 992 c->argv[1] = pcalloc(c->pool, sizeof(char *)); 993 c->argv[1] = pstrdup(c->pool, cmd->argv[2]); 994 } 995 996 return PR_HANDLED(cmd); 997} 998 999/* Initialization functions 1000 */ 1001 1002static int load_sess_init(void) { 1003 config_rec *c = NULL; 1004 double max_load = 0.0, curr_load = 0.0; 1005 char curr_load_str[16], max_load_str[16]; 1006 1007 /* Lookup any configured load limit. */ 1008 c = find_config(main_server->conf, CONF_PARAM, "MaxLoad", FALSE); 1009 if (!c) 1010 return 0; 1011 1012 /* If the config_rec is present, but argv[0] is NULL, do nothing */ 1013 if (!c->argv[0]) 1014 return 0; 1015 max_load = *((double *) c->argv[0]); 1016 1017 curr_load = load_get_system_load(); 1018 if (curr_load < 0) { 1019 pr_log_pri(PR_LOG_NOTICE, 1020 "notice: unable to determine system load average: %s", strerror(errno)); 1021 return 0; 1022 } 1023 1024 pr_log_debug(DEBUG5, MOD_LOAD_VERSION ": current system load: %.2f", 1025 curr_load); 1026 1027 if (curr_load >= max_load) { 1028 pr_log_pri(PR_LOG_INFO, "MaxLoad (%.2f) reached: connection denied", 1029 max_load); 1030 1031 if (c->argc == 2) { 1032 pr_response_send(R_421, "%s", (const char *) c->argv[1]); 1033 1034 } else { 1035 pr_response_send(R_421, _("System busy, try again later")); 1036 } 1037 1038 pr_session_disconnect(&load_module, PR_SESS_DISCONNECT_MODULE_ACL, 1039 "MaxLoad"); 1040 } 1041 1042 /* Register some Variable entries for showing the system load. */ 1043 memset(curr_load_str, '\0', sizeof(curr_load_str)); 1044 snprintf(curr_load_str, sizeof(curr_load_str)-1, "%.2f", curr_load); 1045 if (pr_var_set(session.pool, "%{mod_load.curr_load}", 1046 "Current system load average", PR_VAR_TYPE_STR, 1047 curr_load_str, NULL, 0) < 0) 1048 pr_log_debug(DEBUG1, MOD_LOAD_VERSION 1049 ": error setting %%{mod_load.curr_load} variable: %s", strerror(errno)); 1050 1051 memset(max_load_str, '\0', sizeof(max_load_str)); 1052 snprintf(max_load_str, sizeof(max_load_str)-1, "%.2f", max_load); 1053 if (pr_var_set(session.pool, "%{mod_load.max_load}", 1054 "Maximum system load average", PR_VAR_TYPE_STR, 1055 max_load_str, NULL, 0) < 0) 1056 pr_log_debug(DEBUG1, MOD_LOAD_VERSION 1057 ": error setting %%{mod_load.max_load} variable: %s", strerror(errno)); 1058 1059 return 0; 1060} 1061 1062/* Module API tables 1063 */ 1064 1065static conftable load_conftab[] = { 1066 { "MaxLoad", set_maxload, NULL }, 1067 { NULL } 1068}; 1069 1070module load_module = { 1071 NULL, NULL, 1072 1073 /* Module API version 2.0 */ 1074 0x20, 1075 1076 /* Module name */ 1077 "load", 1078 1079 /* Module configuration handler table */ 1080 load_conftab, 1081 1082 /* Module command table */ 1083 NULL, 1084 1085 /* Module authentication handler table */ 1086 NULL, 1087 1088 /* Module initialization function */ 1089 NULL, 1090 1091 /* Session initialization function */ 1092 load_sess_init, 1093 1094 /* Module version */ 1095 MOD_LOAD_VERSION 1096}; 1097