pax_global_header00006660000000000000000000000064145161457410014522gustar00rootroot0000000000000052 comment=e79c0f55875185a8509266ed97440862c6230791 munin-2.0.75/000077500000000000000000000000001451614574100127435ustar00rootroot00000000000000munin-2.0.75/.codespell.exclude000066400000000000000000000001511451614574100163430ustar00rootroot00000000000000 'A program to fix permissions of munin directories and files', 'Create graphs from RRD files', munin-2.0.75/.flake8000066400000000000000000000001251451614574100141140ustar00rootroot00000000000000# settings for the "flake8" (python code style checks) [flake8] max-line-length = 99 munin-2.0.75/.gitignore000066400000000000000000000001351451614574100147320ustar00rootroot00000000000000*/Build */_build/ */blib/ build-*-stamp build/ .*.swp /sandbox/ MYMETA.* SPOOL-META /RELEASE munin-2.0.75/.perlcriticrc000066400000000000000000000014021451614574100154260ustar00rootroot00000000000000# note the policy causing each warning, so they can be turned off more easily verbose = [%p] %m at %f line %l, near '%r'\n # no warnings if non-existant policies are mentioned in the config. profile-strictness = quiet ### Turn off policies ########################################################## [-Modules::RequireVersionVar] [-ErrorHandling::RequireUseOfExceptions] [-BuiltinFunctions::RequireBlockGrep] [-BuiltinFunctions::RequireBlockMap] ### Configure policies ######################################################### [Variables::ProhibitPunctuationVars] severity = 5 allow = $_ $! [Miscellanea::RequireRcsKeywords] severity = 5 keywords = Id [CodeLayout::RequireTidyCode] perltidyrc = perltidyrc [CodeLayout::ProhibitTrailingWhitespace] severity = 5 munin-2.0.75/.travis.yml000066400000000000000000000037041451614574100150600ustar00rootroot00000000000000dist: xenial language: minimal branches: only: - stable-2.0 - master - travis-test addons: apt: packages: - codespell - devscripts - libpango1.0-dev - libfile-copy-recursive-perl - libfile-slurp-perl - libhtml-template-perl - libio-socket-inet6-perl - libio-stringy-perl - liblist-moreutils-perl - liblog-dispatch-perl - liblog-log4perl-perl - libmodule-build-perl - libnet-server-perl - libnet-snmp-perl - libnet-ssleay-perl - librrds-perl - libtest-deep-perl - libtest-differences-perl - libtest-exception-perl - libtest-longstring-perl - libtest-mockmodule-perl - libtest-mockobject-perl - python3-flake8 - shellcheck - dh-make-perl - libdevel-cover-perl - libhttp-tiny-perl - libio-socket-ssl-perl - libjson-pp-perl - libyaml-perl - libmodule-build-tiny-perl notifications: email: false irc: on_success: change # no need for spam on_failure: always channels: - "irc.oftc.net#munin" template: - "%{repository} (%{branch} - %{commit} : %{author}): %{message}" - "Build details: %{build_url}" matrix: # we don't need to continue any build when 1 test is failing. fast_finish: true env: - TEST_MEDIUM=1 before_install: - ( cd /tmp && wget https://cpan.metacpan.org/authors/id/M/MI/MIKIHOSHI/Devel-Cover-Report-Coveralls-0.13.tar.gz ) - ( cd /tmp && tar zxvf Devel-Cover-Report-Coveralls-0.13.tar.gz ) - ( cd /tmp && dh-make-perl make --build Devel-Cover-Report-Coveralls-0.13 ) - ( cd /tmp && sudo dpkg -i libdevel-cover-report-coveralls-perl*.deb ) - ( cd /tmp && sudo apt-get install -yf ) script: - make # the old travis environment requires an old flake8 invocation - PYTHON_LINT_CALL="python3 -m flake8.main" make lint - make test - make clean && dev_scripts/install && dev_scripts/run munin-node-configure --suggest munin-2.0.75/Announce-2.0000066400000000000000000000030501451614574100147270ustar00rootroot00000000000000Introducing Munin 2.0! The most important features: * Even better scalability through: - Full CGI integration. It is for graphing as well for html, if needed. It is also completely compatible with FastCGI, that only compiles once per run. - Binary state data files. This has enabled a really fast startup and storing of state files. Quite useful in munin limits that does need to access to the rrd files. - Complete integration with rrdcached. Starting with RRD 1.4, there is an update daemon. It enables batched updates, and therefore reduce the IO down. Even with 1000+ files to update every 5 min. - Large performance improvements on almost all munin's components * Complete IPv6 integration. - The master only require a new perl module (IO::Socket::INET6) - The node needs a IPv6-patched Net::Server * Native SSH transport - No need to have a hairy setup anymore. * Graph Zooming - The UI is still raw, but right to the point. * New look on HTML pages, new graph colours with better contrast * IRC ( #munin @ OFTC ) has become a real support channel, mostly aimed at trunk. Availability: * You can find Munin 2.0 as source tar ball at sourceforge (https://sourceforge.net/projects/munin/files/) * Debian Experimental. Hopefully in the next stable release * Expected in EPEL (Extra Packages for (Red Hat) Enterprise Linux) within not too long If you are a packager please contact us and we'll list your support/repository/whatever on our Wiki. Full details in http://munin-monitoring.org/browser/tags/2.0/ChangeLog munin-2.0.75/COPYING000066400000000000000000000032611451614574100140000ustar00rootroot00000000000000Munin - a network-wide graphing framework Copyright (C) 2004-2010 Lupe Christoph Copyright (C) 2004-2011 Jimmy Olsen Copyright (C) 2006-2013 Nicolai Langfeldt Copyright (C) 2007-2014 Matthias Schmitz Copyright (C) 2007-2017 Stig Sandbeck Mathisen Copyright (C) 2009-2009 Kjell-Magne Øierud Copyright (C) 2009-2011 Matthew Boyle Copyright (C) 2009-2011 Tom Feiner Copyright (C) 2009-2012 Erik I. Bolsø Copyright (C) 2010-2018 Steve Schnepp Copyright (C) 2011-2018 Kenyon Ralph Copyright (C) 2012-2015 Daniel Black Copyright (C) 2013-2018 Gabriele Pohl Copyright (C) 2015-2018 Quentin Stoeckel Copyright (C) 2016-2018 Lars Kruse ... and many more contributors This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. munin-2.0.75/ChangeLog000066400000000000000000006016061451614574100145260ustar00rootroot00000000000000-*- text -*- munin-2.0.75, 2023-10-25 Kim B. Heino (1): plugins/ipmi_: handle "0.000" as "na" in temperature upper critical/warning Steve Schnepp (1): cgi/graph: replace Date::Manip with Date::Parse munin-2.0.74, 2023-10-01 ------- Summary ------- Bugfix release. Closes: D#1033734 ------------------ Detailed Changelog ------------------ Holger Levsen (2): fix typo in comment ("stanard") munin-plugins-core: nginx_*: include ipv6 localhost in example configuration, thanks to Helmut Grohne. Kim B. Heino (2): plugins/cpuspeed: fix for Linux kernels with SMT disabled plugins/cpuspeed: fix shellcheck warning Steve Schnepp (1): cgi/graph: replace Date::Manip with Date::Parse munin-2.0.73, 2023-03-19 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Kai (1): doc: Switch to rewrite-repeat-if-not-file Steve Schnepp (2): p/acpi: fix the plugin to handle munin-node-c p/mysql_: fix failure due to unknown section munin-2.0.72, 2023-02-08 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Salvatore Bonaccorso (1): Import custom css file in style-new.css munin-2.0.71, 2022-09-19 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Steve Schnepp (1): fix the fix for Date::Manip munin-2.0.70, 2022-09-11 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Andreas Perhab (3): plugins/http_loadtime: enable saving cookies between requests UpdateWorker: prevent use of uninitialized value $first_epoch fix html_strategy to point to munin-cgi-html Lars Kruse (2): plugin fail2ban: allow configuration of warning/critical thresholds Plugin haproxy_: handle backends with special characters Olivier Mehani (2): [snmp__df_ram] Update title to match the memory plugin [snmp__cpuload] Update title to match the cpu plugin Pierre-Alain TORET (1): Plugin df_inode : Exclude msdosfs on FreeBSD as there's no concept of inode Sandro (3): Plugin bind9: stabilize order of values in graph Plugin bind9: Author's blog has moved to .no TLD Plugin bind9: Move 'Other' to bottom of list Steve Schnepp (2): Update munin-graph.in Update munin-cgi-graph.in munin-2.0.69, 2021-11-22 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Andreas Perhab (1): Lars Kruse (1): Fix loading of "local_address" configuration munin-2.0.68, 2021-11-14 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Andreas Perhab (1): plugins/postgres_: enable configuring warning/critical Christoph Moench-Tegeder (2): postgres_querylength: catch real statements only ntp_: do not mask required variable from Net::IP Guillaume Rousse (2): display locally-defined tresholds use dedicated munin SNMP plugin Kim B. Heino (2): plugins/mailman: add missing "graph_category" header plugins/postfix_mailqueue: don't run postconf if spooldir-override is defined Lars Kruse (11): Remove obsolete links to old bug tracker ("trac") Fix typo plugin.sh: remove conversation log regarding shell redirects Remove old "authors" file Fix typos munin-asyncd: use configured update rate if undefined Munin::Master::Node: unify configuration access munin-run: allow to pass additional arguments to the plugin (#1419) Fix homepage link fix(postgres_querylength_): fix wrong PostgreSQL version (for 9.4) docs: remove stale content from release checklist Sven Edge (1): Handle NVME drives in the Linux iostat plugin (2.0 branch) Ville Skyttä (1): Use `grep -E/-F` instead of `egrep` and `fgrep` wferi (1): Use the host-specific local_address configuration if present munin-2.0.67, 2021-02-22 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Lars Kruse (7): Lars Kruse (7): Template "overview": add missing closing "li" and unify indentation CDEF handling: fix access to "negative" field metadata CDEF handling: fix translation of fieldnames to RRD field names CDEF handling: fix access to "negative" field with long name or starting with a digit master: tolerate fields without content in "info" Plugin hddtemp_smartctl: avoid misinterpretation of serial numbers as partitions Plugin memcached_: implement suggest and improve autoconf Wiebe Cazemier (1): Make netstat human and high connection friendly munin-2.0.66, 2021-01-06 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Lars Kruse (7): unknown_limit: do not misinterpret zero as one unknown_limit: fix handling of value "1" SpoolReader: process spooled content lines separately async: fetch spooled data in order of timestamps async: use name squashing rules when determining the names of spooled files async: clarify handling of timestamp doc: describe "timeout_fetch_all_nodes" and "timeout_fetch_one_node" Matt Merhar (1): Make sensors_ temperature plugin work on musl libc Wiebe Cazemier (1): Symlink and custom name support for diskstat_ Younes Ichiche (2): fix reporting on URL in the munin-update log (stable-2.0) make it possible to configure global munin-update timeouts munin-2.0.65, 2020-10-28 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Hans van Kranenburg (1): Use configured plugin group as primary group John Hall (1): plugins/memory: add per-cpu counter JosefRypacek (1): Munin thresholds support for http_loadtime Kjetil Torgrim Homme (1): adjust_threshold: strings indexes are 1 based in AWK Lars Kruse (10): Plugin apt_all: prevent ambiguity between slash and hyphen in release names Fix utf8 encoding in plugins Fix special character for author name (Kristian Lyngstl) Plugin snmp__if_err_: clarify authors list Plugin nomadix_users_: clarify formatting of authors Plugin vserver_cpu_: clarify copyright statement Plugin snmp__uptime: clarify authors Unify spelling of author "Dagfinn Ilmari Mannsker" Unify more author names Fix template error in case of an empty host list munin-2.0.64, 2020-07-09 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Lars Kruse (8): Plugins snmp__if(_err)_: describe alias handling for configuration Plugin cpu: add support for hiding selected fields Plugins snmp__if*: clarify usage of interface names munin-node-configure: remove "--exitnoterror" argument LINESTACK: add support for undefined line width and restrict regex Plugins ntp_kernel_*: handle alternative implementations (e.g. ntpsec) Plugins ntp_kernel_*: force non-scientific notation for output Plugin smart_: avoid ambiguous variable name Mike Beattie (3): Allow '@' in plugin names Support SNMP network interfaces by names instead of index Allow overriding of network interface alias from SNMP munin-2.0.63, 2020-05-27 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Alexander (1): Remove unneeded forks in plugin.sh Lars Kruse (3): plugin.sh: fix shellcheck issues and tolerate unset optional arguments Plugins: force execution in a shell for "command -v" plugin.sh: improve documentation for "print_warning" and "print_critical" munin-2.0.62, 2020-05-27 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Lars Kruse (5): Move "munin-node-configure" test from Makefile to travis checks Fix formatting of Munin::Common::Daemon documentation Plugin proc: change to "manual" family Plugins: use "command -v" instead of "which" Plugins: hide output for boolean checks of "command -v" d0m84 (1): add start and stop log for munin-graph munin-2.0.61, 2020-04-30 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Lars Kruse (2): Plugins apache_*: tolerate URL without placeholder for port Plugins apache_*: fix handling of URL with port placeholder munin-2.0.60, 2020-04-22 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Hans de Graaff (1): Plugin mysql_innodb: pass argument to mysql_exec Lars Kruse (6): Remove logging from Munin::Common::Daemon Plugin ping_: use "ping" instead of "ping6" except for *BSD Plugin nginx_status: improve error message in case of missing dependency munin-run: skip systemd properties simulation in case of missing privileges Remove asterisk_* plugins Steve Schnepp (1): dists: remove all dists specific stuff Thomas Daniels (1): Fix typo in doc/reference/plugin.rst of stable-2.0 munin-2.0.59, 2020-03-24 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Christoph Moench-Tegeder (1): Lars Kruse (1): Fix syntax error in munin-asyncd munin-2.0.58, 2020-03-23 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Christoph Moench-Tegeder (1): Andreas Perhab (1): plugin ipmi_: add support for volts Klas Meder Boqvist (1): Plugin cpuspeed: ensure value is not in scientific notation Lars Kruse (28): Plugin ipmi_sensor_: fix "suggest" in case of missing "ipmitool" dev_scripts: allow override for sandbox directory Plugin proc (linux): announce availability of autoconf Plugin proc (linux): fix indentation Remove remaining occurrences of subversion revision markers ($Id$) Plugin ntp_: output "U" values in case of data retrieval problems emit_sd_notify_message: adjust log level for messages asyncd: prevent skipping of values due to variable collection periods master: do not block connection to munin-node in case of TCP timeout Fix test for munin_master_node devscripts/install: override users and groups with the current user Plugin mysql_: handle missing dependencies gracefully Plugin ntp_: handle missing dependencies gracefully Plugin ntp_states: move "autoconf" code to the top Plugin ntp_states: handle missing dependency gracefully Plugin varnish_: handle missing dependency gracefully Makefile: test "munin-node-configure --suggest" Plugin mbmon_: convert plugin documentation to perldoc header Plugin mbmon_: prepare for configurable arguments Plugin mbmon_: allow additional arguments for "mbmon" Plugin apt_all: improve formatting of error messages Plugin apt_all: enforce stable order of fields Plugin apt_all: include release names with slashes (e.g. buster/updates) Plugin apt_all: unify whitespace Plugin apt_all: apply default "options" for apt-get Plugin apt_all: use a proper name for the state file templates: remove superfluous whitespace after "Munin" in title templates: add missing hostname to category and comparison views of multigraph services Trygve Vea (1): Proc plugin: Allow monitoring of processes with empty cmdline munin-2.0.57, 2020-03-09 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Christoph Moench-Tegeder (1): Bas Couwenberg (1): Add support for multiple fail2ban instances. Christoph Moench-Tegeder (1): extend linux diskstat plugins for kernel 5.5 Kim B. Heino (2): head.tmpl: remove trailing white spaces head.tmpl: add missing Lars Kruse (9): munin-run: improve formatting of constant value munin-run: ignore DropInPaths property Fix spelling and perl style issues Ignore misinterpreted codespell errors Ignore the content of certificate files Remove superfluous trailing semicolons from shell-based plugins Implement "sd_notify" interface for munin-node munin-node: allow to force "foreground" mode via commandline arguments Implement "sd_notify" interface for munin-asyncd Rowan (1): Fixed broken link to multicpu1sec Steve Schnepp (2): 2.0.56 travis: using system perl install munin-2.0.56, 2020-02-07 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Christoph Moench-Tegeder (1): extend linux diskstat plugins for kernel 5.5 Kim B. Heino (2): head.tmpl: remove trailing white spaces head.tmpl: add missing munin-2.0.55, 2020-02-05 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Lars Kruse (3): munin-run: prepend new systemd-related messages with "#" munin-run: work around systemd's serialization of EnvironmentFile munin-run: emit a warning if execution failed Olivier Mehani (3): Expose NAME in all services HTML templates Put host name or service (NAME) first in munin-2.0.54, 2020-01-20 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Lars Kruse (2): munin-run: skip execution via "systemd-run" for systemd before v236 Disable "dashed" threshold lines for rrdtool version before 1.5.3 Steve Schnepp (1): munin-run: Use directly systemd-run munin-2.0.53, 2020-01-15 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Lars Kruse (6): Add missing "munindoc" to the node build process Respect custom color for warning thresholds Visualize warning thresholds as dashed line munin-run: allow multi-line environment values munin-run: remove duplicated copyright/license statement munin-run: apply systemd properties configured for "munin-node.service" Terry Burton (1): Plugin postgres_streaming_: Use modern WAL status function names munin-2.0.52, 2019-11-20 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Alexander Moisseev (1): snmp__if_: handle undefined values Christian Gut (1): Make date parsing work in Safari Lars Kruse (7): sample config: improve notification mail subject limits: do not redefine variable used in outer scope Plugin ntp_states: fix missing required output Plugin proc: count processes with a negative nice level Plugin sensors_: add support for voltage output with unit prefix (mV) Remove remaining traces of munin-gather Add minimal local plugin management tool ("munin-get") munin-2.0.51, 2019-10-18 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Lars Kruse (7): limits: add missing module import limits: prevent hiding of state changes due to subsequent fields limits: reduce code duplication for counting unknown values limits: send notification if the first emitted value of a plugin is unknown limits: mention worst state change in log message limits: use uppercase"OK" instead of "ok" in notifications limits: remove unused conditional munin-2.0.50, 2019-10-16 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Alexander Moisseev (1): snmp__df_ram: handle undef and zero values Kenyon Ralph (1): doc: fix example apache virtualhost RewriteRule Lars Kruse (20): Add comment regarding munin-async timing meminfo plugin: fix "phisical/physical" typo in descriptions Fix link anchors for problems page Avoid erroneous wildcard matching when parsing plugin configurations Remove trailing whitespace plugin bind9_rndc: fix documentation Plugin ejabberd_: remove "usersindays" feature Improve log output in case of broken numeric content Improve error message in case privilege dropping problems Fix some spelling errors Makefile: add lint checks for spelling Makefile: ignore spelling errors in multiple directories Fix perldoc header in Munin::Master::LimitsOld Fix perldoc header in munin-graph Move manpages for munin-node and munin-run to section 8 (admin) Add empty codespell exclusion file Plugin snmp__cpuload: use deterministic order for CPU fields Fix travis setup for new codespell linting Multiple snmp__* plugins: use deterministic order of fields Prevent duplicate timestamps in async spool data Nicolai Langfeldt (1): Don't "exit 1" Tobias Kronthaler (1): Fix spelling of "network" in port_ mafri (1): Fixed iteration at apt_all moisseev (1): snmp__processes: handle undefined values munin-2.0.49, 2019-05-09 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Lars Kruse (2): Fix the reversal of path manipulation Add image path manipulation for "problems" graphs munin-2.0.48, 2019-05-03 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Lars Kruse (10): munin-cgi-html: avoid accidental permanent path manipulation Plugins: use natural order for numbered items with multiple digits Ignore malformed lines when parsing "datafile" Plugin snmp__if: handle broken "zero" speed announcements Plugin diskstat_: accept block device information of newer kernel tests: verify name munging of remote plugins via async Plugin open_files: remove "max" field from graph Allow node-specific TLS settings on the master Add support for the Net::Server option "reverse_lookups" Matthew Gabeler-Lee (1): fix(nutups): make frequency chart aware of input, output, not polling Valentin Lorentz (1): Sanitise plugin names in munin-asyncd. Vincas Dargis (1): Remove stray comment in postgresql_connections_ Zenon Mousmoulas (3): Let the server use max timestamp rather than now for non-timestamped Do not fall back to current timestamp for spoolfetch Use a named constant rather than hardcoding -1 munin-2.0.47, 2019-02-28 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Steve Schnepp (2): fix t/munin_master_update.t warning t: remove munin_master_processmanager.t munin-2.0.46, 2019-02-28 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Lars Kruse (9): travis: switch to Ubuntu Xenial and add missing dependencies shellcheck: ignore interpreter warnings (SC2239) shellcheck: simplify inverted tests (SC2236) travis: switch to Linux virtualization infrastructure Unify whitespace formatting Plugin apt_all: handle configured list of "releases" properly Plugin apt_all: filter the stored state for wanted releases Plugin apt_all: improve readability of labels Plugin apt_all: escape release name for field name Steve Schnepp (1): Remove munin-sched lonepsycho (1): fix idle connections showing as waiting for lock munin-2.0.45, 2019-02-06 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Damian Lukowski (1): Graph idle kernel threads as available since Linux 4.2 Darafei Praliaskouski (1): Support Samsung SSD 970 EVO Lars Kruse (8): Disable another flaky munin-node proxyspooler test Do not misinterpret an original umask of zero as a failure munin-node: announce UTF-8 to locale-aware plugin interpreters Revert "munin-node: announce UTF-8 to locale-aware plugin interpreters" Force UTF-8 encoding for plugins related to Python3 Plugin cupsys_pages: handle weekly logrotated file (for redhat) PostgreSQL plugins: adjust version comparison for v10 or later Plugin postgres_connections_: fix backend_type in filter expression munin-2.0.44, 2018-12-19 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Kim B. Heino (2): plugins/mailman: fix to return values if there is no rotated logfile plugins/postfix_mailvolume: fix typo in queue ID expire Lars Kruse (18): Fix spelling issues Plugin ipmi_sensor_: fix python2/3 migration issue with "decode" Revert "p/ipmi_sensor_: fix fan thresholds" "lint" target: tolerate shellcheck's "SC2230" Plugin ipmi_sensor_: use explicit raw string for regular expression Plugin ipmi_sensor_: handle devices without assertions Avoid reliance on network access in tests Makefile: avoid duplicate processing of Makefile.config Makefile: handle test for user existence for multiple platforms Remove obsolete Makefile.config-maint Fix dependencies for configurable Makefile location Remove "get_fq_hostname" test Plugin memory (linux): use fixed colors for all fields Plugin mysql_: remove non-working (AREA|LINE)STACK backwards compatibility Plugin apt_all: automatically determine 'releases' Plugin postfix_mailstats: fixed various variable handling issues Respect custom color for warning thresholds Plugin smart_: ignore invalid threshold data Stefan Huehner (2): Split query as preparation for specific change applicable Fix issue with PostgreSQL 10. Only count lines related to client munin-2.0.43, 2018-11-16 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Christoph Moench-Tegeder (1): make Linux diskstats plugin comaptible with Linux 4.19+ Kim B. Heino (1): munin-asyncd: sync munin-cron and munin-asyncd runtimes to not overlap Lars Kruse (27): getversion: fix shellcheck issue getversion: treat all states unrelated to branches equally getversion: use commit tag if possible; update documentation Clarify conditional usage of the CGI workaround Plugin bind9_rndc: stabilize order of values in graph Plugin smart_: fix variable handling during first run Update master configuration test Revive "_extract_name_from_greeting" for tests Fix test for output of munin-node-configure Use unique CNs for test TLS certificates Remove trailing whitespace from openssl.cnf file Remove outdated "md5" hash from OpenSSL configuration for tests Recreate TLS certificate for tests Enable evaluation of test results for test-common and test-plugins Fix remaining broken master tests travis: add missing dependency travis: ignore result of "test-master" Use "Alien::RRDtool" for travis tests Disable the failing "proxyspooler" test for "test-node" munindoc: output reasonable error message if plugin is not found munindoc: prevent stderr output from perldoc Plugin apt_all: allow status update after "apt-get update" Plugin apt_all: fix "apt-get update" call Tests: allow to run tests as root Tests: allow to run in an autopkgtest environment lacking a FQDN Tests: skip the tests around the effective user ID for root Munin::Node::Service: accept zero (root) as defuser/defgroup RenWal (2): Narrowed down voltage regex Narrow down regex for all modes SeeSchloss (1): smartctl_exit_status should be a single number and not a range (fixes #1100) Steve Mokris (1): Only apply Perl 5.20 CGI workaround when needed munin-2.0.42, 2018-09-21 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Lars Kruse (5): Fix Module::Build instance parameters for master and node Retrieve version number for modules "Munin::Common" and "Munin::Plugins" Module description: set contact to mailing list address getversion: hide error message for detached HEAD getversion: prevent localized output format for "git branch" munin-2.0.41, 2018-09-19 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Alejandro Suarez (1): Update default warning threshold Christian Göttsche (6): give reasons why a plugin says no on more occasions [plugins] give reasons for autoconf no response [plugins] drop autoconf capability when no real autoconf is supported [plugins] overlook exim_mailstats [mnc] fix documentation so that --help works [munin-node-configure] print autoconf response on parse error Dmitry Marakasov (1): Fix CDEF variable substitution Kim B. Heino (6): ProcessManager: remove \n from output before it's logged plugins/squeezebox: add missing graph_vlabel plugins/bind9: use category "dns" as in other similar plugins, not "BIND" plugins/nginx_request: stats are for all ports (80+443), never for single port plugins/squid_traffic: allow negative numbers munin-asyncd: retry connection to node once in startup Lars Kruse (21): Plugin postgres_users: ignore undefined usename Makefile: fix "tar" target Makefile: increase compression level for 'tar' target Plugins: replace all absolute references to shells with placeholders Plugin http_loadtime: fix shellcheck issue Whitespace cleanup: remove trailing and unify some spaces/tabs Plugin cpuspeed: add support for 'intel_pstate' driver Plugin cpuspeed: minor cleanup; support capability DIRTYCONFIG Plugin cpuspeed: fix data type for "intel_pstate" driver Build.PL: define a 'module_name' and a proper version string Nagios command format: ensure non-empty plugin output dev_scripts: fix processing of configuration files munin-node: set default port if not mentioned in config file Plugin apc_nis: autoconf tests connection to socket Plugin acp_nis: unify whitespace Replace CGITMPDIR placeholder during build Plugin apt_all: simplify conditional expression for "update" operation Plugin apt_all: update the current status before calculating the summary munin-asyncd: improve messages in case of failed connections to munin-node Plugins apache_: add documentation for HTTPS connections Plugin acpi: prevent error message for missing thermal sys directory munin-2.0.40, 2018-08-15 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Christian Göttsche (15): [plugins] fix varnish autoconf errors [plugins] fix nestats_multi autoconf error [plugins] fix meminfo autoconf error [plugins] fix ejabberd_ autoconf error fix pgsql plugin driver to give a well formed autoconf reply fix warning on node list operation [plugins] give reasons for inactive plugins [plugins] return 0 in autoconf [munin-node-configure] expect autoconf to return 0 even in the no case [plugins] fix autoconf for lpstat overlook some plugins [munin-node-configure] do not error out on empty suggest output [plugins] quote variable [plugins] fix hddtemp_smartctl autoconf [plugins] overlook df and netstat linux plugins Hiroaki Abe (1): plugins/freebsd/{df,df_inode}: exclude cd9660 file systems. Lars Kruse (24): Makefile: add current directory to perl include path Plugin digitemp_: unify whitespace Plugin digitemp_: fix shellcheck issues; clarify error message Plugin digitemp_: reduce code duplication Plugin digitemp_: implement "autoconf" and "suggest" Plugin digitemp_: mention the configuration file Plugin hddtemp_smartctl: unify whitespace Plugin sensors_: enable 'auto' family Plugin smart_: unify state file access Plugin smart_: pythonize expressions Plugin smart_: simplify output parser Plugin smart_: remove hard disk list weirdness Plugin smart_: simplify 'SunOS' disk handling Plugin smart_: unify code for guessing of full path Plugin smart_: assemble environment configuration at the beginning Plugin smart_: use 'subprocess' for safe command execution Plugin smart_: improve exit code handling Plugin smart_: avoid global variables and clarify data structure Plugin smart_: update version information Revert "fix warning on node list operation" Plugin digitemp_: fix shellcheck issue postfix_mailsts plugin: add 'milter-reject' event Makefile: add targets "tar-signed" and "tar-upload" Update release checklist Mathieu Arnold (1): Fix when using more than one swap device. munin-2.0.39, 2018-07-24 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Hans de Graaff (1): Fix syntax error in make install sh code Lars Kruse (37): Merge pull request #985 from bunder2015/patch-1 Merge pull request #988 from graaff/make-install-bash-syntax-error plugin postfix_mailvolume: handle statefile upgrade from previous version plugin threads: tolerate SELinux policies and other read errors Makefile / dev_scripts: simplify variables Fix shellcheck issues in dev_scripts/ Plugin cpuspeed: fix documentation of 'scaleto100' configuration Plugins for Linux: fix shellcheck issues Plugin tcp: fix autoconf exit code Makefile: add lint check for Linux plugins Plugin cpu: configurable 'scaleto100' Plugins for Linux: fix more shellcheck issues Plugin snort_pkts: remove dependency on 'bc' Plugins: fix shellcheck issues for bash plugins Plugins: fix MUNIN_LIBDIR source quoting Plugin qmailqstat: fix misspelled configuration documentation Plugin foldingathome: improve path handling Plugin ipac-ng: return "U" in case of errors Plugin multiping: remove unusable symlink detection Plugins: fix more trivial shellcheck issues Plugins: a bit more complicated fixes for shellcheck issues Plugins: fix trivial quoting issues for non-Linux platforms 'make lint': add more platforms for shell checks and add python checks 'make lint': add python plugins travis: add 'make lint' for style checks Merge pull request #977 from sumpfralle/style-checks Makefile: improve robustness of "Defaults" variable substitution Makefile: unify usage of whitespace Makefile: prevent repeated substitution of "Defaults.pm" during "install" Makefile: remove obsolete 'build-common-prime' target Makefile: prevent repetitive rebuild of man pages Makefile: prevent repetitive substitutions for generated files Travis: add 'liblog-log4perl-perl' dependency Makefile: handle filenames with colons even with old versions of Make Travis: reduce set of perl versions for CI Plugin postfix_mailvolume: handle long queue IDs Plugins postfix_*: unify whitespace Steve Schnepp (1): Merge pull request #980 from zmousm/fix-asynd-fork-bomb Zenon Mousmoulas (1): Fix munin-asyncd fork bomb bunder2015 (1): Fix paths on nutups_ munin-2.0.38, 2018-06-29 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Dmitry Marakasov (5): Specify minimum value for tuples Specify minimal value for postgres scans Specify minimal value for bgwriter Speficy minimal value for PostgreSQL checkpoints Specify minimal value for postgresql buffer cache Jose-Marcio Martins da Cruz (1): Update ntp_states.in Kenyon Ralph (3): add Transport Domain environment variable for Munin::Plugin::SNMP add SNMP domain to tests munin-node-configure: allow setting SNMP domain Lars Kruse (55): Plugin postfix_mailvolume: calculate separate volume for delivered mails move debug plugin 'sincos' to debug plugin directory plugin smart_: fix flake8 warnings plugin threads (Linux): use "find" instead of globbing; handle zero threads plugins postgres_*: unify commas in arrays and hashes plugins postgres_*: unify end of plugin backport plugin changes from master Makefile: "build-man" depends on "infiles" dynazoom: cleanup whitespace dynazoom: add missing closing tags dynazoom: fix validation issues and mark as XHTML 1.1 dynazoom: cgi-based template adapted to cron-based template Template 'problemview': fix HTML validation problems Template 'serviceview': use double quotes for tag attributes plugin postfix_mailvolume: do not use hashs with save_state/restore_state dynazoom: temporarily remove doctype line plugin hddtemp_smartctl: handle missing 'smartctl' gracefully Java compilation: change source and target specification from 1.5 to 1.7 getversion: replace '$(...)' with backticks (for portability) getversion: unify whitespace getversion: improve shell style getversion: fix or override shellcheck warnings plugin quota_usage_: backport changes from master mysql_innodb: fix version string comparisons for v10.x (or later) memory (freebsd): add new "laundry" counter plugin bonding_err_: improve input parsing plugin bonding_err_: separate interface name parsing plugin bonding_err_: separate BONDINGIF assembly plugin bonding_err_: separate misconfiguration warning; exit with error plugin bonding_err_: rename variable 'if' to 'if_name' plugin bonding_err_: fix remaining shellcheck issues plugin iostat_ios: fix major block device number for filtering LVM plugin iostat_ios: clarify documentation (only low-level devices are tracked) plugin ipmi_sensor_: handle lines without values plugin ipmi_sensor_: indicate possible symlink names in case of error plugin ipmi_sensor_: unify whitespace plugin ipmi_sensor_: python3 compatibility plugin ipmi_sensor_: fix flake8 style issues plugin ipmi_sensor_: decoding for python3 plugin ipmi_: add hint for "power", support %-based fan speed Python plugin: Python3 compatibility plugin df_inode_ (FreeBSD): adjust filesystem exclusion to 'df' plugin plugin df_ (FreeBSD): ignore /sys via the filesystem type plugin df_inode (FreeBSD): exclude sysfs HTML: escape ampersand in URL plugin ntp_: reduce DNS timeout to 5 seconds plugin netstat: unify the two sets of netstat plugins Makefile: clarify plugin order comment Makefile: simplify HP-UX-specific plugin handling switch default python interpreter for plugins to python3 COPYING: remove obsolete reference to Bitstream Vera Mono update COPYING based on commits plugin samba: improve documentation formatting plugin samba: warn if run as non-root plugin samba: improve formatting, fix shellcheck issue Robert (1): Fix argument size too big in threads plugin when there are too many processes. Tom Hendrikx (1): Add a lower limit of 0 to postgres database size Tomohiro Hosaka (1): Do not set database=undef when paramdatabase. leeclemens (2): Graph system peer's stratum Add missing parens around $syspeer_stratum_value munin-2.0.37, 2018-03-28 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Steve Schnepp (1): p/munin_stats: fields are configurable dipohl (1): doc: add example graphs munin-2.0.36, 2018-03-25 ------- Summary ------- Bugfix release. Closes: #939, D#894017 ------------------ Detailed Changelog ------------------ Lars Kruse (7): Plugin acpi (Linux): various improvements Plugin ejabberd_: unify indentation Plugin ejabberd_: fixed shellcheck issues Plugin ejabberd_: implement configuration file parsing for yaml format Plugin ejabberd_: enable strict shell behaviour Plugin ejabberd_: defined reasonable defaults Plugin ejabberd_: use 'clean_fieldname' instead of custom function Steve Schnepp (1): p/munin_stats: should not emit graph.value on CGI Zenon Mousmoulas (1): Simplify spooled value regex dipohl (1): doc: add example plugin graphs (from master branch) y-ken (4): Add support for MySQL 5.5/5.6 mysql_innodb_insert_buf compatibility problem fixed for MySQL 5.5/5.6 fixed indent add verified version for mysql_ plugin munin-2.0.35, 2018-03-21 ------- Summary ------- Bugfix release. Closes: #710, #382, #301, #923, #586, #864, #881, #891, #857, #888, #310, T#1018, T#1037, T#1276 ------------------ Detailed Changelog ------------------ Adam Woodbeck (1): Added HP-UX support to processes plugin. Anders Ossowicki (1): DERIVE does not need a max to avoid false spikes. Andreas Teuchert (1): snmp__if_multi: Set warning to 75% of interface speed instead of 133%. Axel Huebl (4): Fix 1350, user agent, auto config Multi-URL and requisites support (with backwards compatible options) Remove Autoconf Review Comments Bart Coppens (1): Handle negative values in spooled data Bas Couwenberg (1): Add support for rndc options in bind9_rndc plugin. Bkefi Gbor (1): Correcting mysql_queries plugin random hash ordering. Chris Butler (1): Add PostgreSQL 10 support to postgres_xlog Daniel Black (13): add threads running to mysql plugin on connections graph mysql: don't suggest galera graphs if no galera mysql: add data from query response time plugin mysql: graph_period in vlabel and not title mysql: query response time total - useconds mysql: graphs are dynamic and therefore need caching mysql: freeze->nfreeze mysql: plugin_query_response_time - no need for plugin_map mysql: Add user statistics graphs mysql: change default database to information_schema mysql: and grants and examples mysql: user munin for unix_socket auth plugin mysql: add graph binlog_groupcommit Dmitry Marakasov (7): Whitespace consistency Don't hardcode graph period Remove duplicate label definition Use consistent Nginx spelling among nginx_ plugins Don't use unnecessary bold lines for nginx_* graphs Calculate diskspace/inode usage percent accurately Sort df output Ed Szynaka (1): Added autoconf to spamstats Gabriele (1): Fixed setting of warning limit and some minor issues Herwin Weststrate (1): Typo fix in comments of fw_conntrack Hiroshi Shirosaki (1): Fix Java committed memory bigger than Max Holger Levsen (1): fixup perms, broken since >2.999.4 InsanePrawn (1): Update smart_ Ivn Montoro (1): Add IPv6 support Jason Woods (2): Report GAUGE values that are not updated within the heartbeat as Unknown Further improve handling of unknown values and small refactor to make easier to follow Jean-Louis Dupond (1): haproxy_ng: add SessionRate and adjust title of current sessions value Jose-Marcio Martins da Cruz (1): Update meminfo.in K.Cima (3): Fix typo $CDEV -> $CDEF Add --lower-limit, --upper-limit and vlabel Add disk device names for Oracle VM (LDOMs) and Kernel Zones Kenyon Ralph (1): snmp__if_: handle noSuchInstance Kim B. Heino (5): apc_nis: add line frequency monitoring vlan_: fix syntax error plugins/squeezebox: match category with munin contrib repo plugins/squeezebox: wired devices are reported as strength 0, should be 100 plugins/apc_nis: add missing category Kjetil Torgrim Homme (1): reduce number of forked processes Lars Kruse (33): plugin smart_: typofix - https://github.com/vlajos/misspell_fixer plugin smart_: set executable permission plugin smart_: remove emacs mode hints plugin smart_: switch to python3 updated travis setup: follow most changes of master travis: import cpanfile from master getversion: backport from master plugin munin_stats: no warning if "graph" updates in CGI mode plugin proc (Linux): use 'clean_fieldname' plugin proc (Linux): suppress error messages for unreadable files Logger: add missing "Carp" import for "confess" postgres plugins: fixed database selection hddtemp_smartctl plugin: add more smartctl labels (190 / 194) varnish plugin: compatibility with Varnish 5.2 Plugin http_loadtime: improved quoting, modern command substitution Plugin http_loadtime: time calculation based on "date" instead of "time" Plugin http_loadtime: improve wget calls Plugin http_loadtime: fix redirection order, remove bashisms, switch to sh plugin 'ping_': improve shell quoting; fix shellcheck issues plugin df (generic): unify whitespace plugin df (generic): fix most shellcheck issues plugin df (generic): reduce code duplication plugin df (generic): support DIRTYCONFIG capability plugin df (generic): replace ambiguous device names with mountpoints plugin df_inode (generic): import changes of the "df" plugin iptables-related plugins: use "-w" for consistent results plugins df/df_inode: fix recently broken autoconf plugin memcached_: exitcode zero for "autoconf" response "no" plugin meminfo (Linux): fixed typo ("Hlocked"/"Mlocked") Munin::Plugin: "rotate" event without warning for "tail_open" Limits Notifications: prevent notification executable from writing to STDOUT Limits check: increase log level for limit state changes Plugin "if_" (FreeBSD): mention non-suitability for older systems and fix syntax Mark Saad (1): The 32bit Warning is not accurate . On modern FreeBSD systems netstat -i returns int64_t numbers Mathieu Arnold (2): Fix if_ and if_errcoll_ plugins. Add an if_packets_ to monitor packets going through an interface. Michael 'PoempelFox' Meier (1): split cache into cache + shmem in linux memory plugin Michael Niewiara (1): Update nfs plugins: Fixing autoconf to detect if service enabled Michael Pirogov (1): Fast & ugly fix for PG10 support. Michael Scherer (1): Add autoconfig support to mailman plugin Raphal Droz (1): Keep-up with modern Linux kernel netstat data. Rick Hansen (1): plugins/postgres: Fix connection stats for PG 9.2-9.4 Steve Schnepp (5): p/snmp__if_: support ifHighSpeed p/snmp__if_: accept interface num with leading 0 plugins: fix sum_cdef_tester plugin plugins: fix sum_cdef_tester p/ipmi_sensor_: fix fan thresholds Stig Sandbeck Mathisen (5): Do not sort smart_ plugin attributes Replace looped cat|perl and cat|egrep|awk with pure perl remove bashism Declare variable before use Exclude btrfs from df_inode plugin Tobias Kapp (1): Ignore current process when configuring labels for postgres_users plugin. Vadim Zeitlin (1): Add "ignoreexit" parameter to smart_ plugin. Viktor Szpe (3): Added graph_category Signal error in http_loadtime Typo in condition Vlad K (2): Fix netstat active connection openings string Annotate and fix netstat's active connections match. bovarysme (1): Fix the check_perms_if_paranoid method duritong (1): select correct avcstats location lr1980 (1): verbose level = MUNIN_DEBUG nii_kenichi (1): Typo corrected rantal (1): Added OldGen graphs, switched used and free bytes (used is on bottom of the graph, free is on top) shapirus (1): plugins/memory: draw the KSM-sharing value if present sumpfralle (1): plugin 'psu_': fix config fieldname munin-2.0.34, 2017-10-20 ------- Summary ------- Bugfix release. Closes: #804, DH:856536 ------------------ Detailed Changelog ------------------ Christian Gut (2): Add support for IBM i fix whitespace Stefan Agner (1): Update lighttpd.rst Steve Schnepp (1): m/Utils: always use munin_read_storable() pir1981 (1): Update nginx.rst munin-2.0.33, 2017-03-02 ------- Summary ------- Bugfix release. Closes: #804, DH:856536 ------------------ Detailed Changelog ------------------ Steve Schnepp (1): cgi: use Scalar::Util::looks_like_number munin-2.0.32, 2017-03-01 ------- Summary ------- Bugfix release. Closes: #802, DH:856455 ------------------ Detailed Changelog ------------------ Steve Schnepp (1): cgi: handle the empty string in CGI arguments munin-2.0.31, 2017-02-25 ------- Summary ------- Bugfix release. Closes: GH:607 ------------------ Detailed Changelog ------------------ Kenyon Ralph (1): ntp_kernel plugins: convert ntpq output to seconds munin-2.0.30.1, 2017-02-25 ------- Summary ------- Security release. Closes: GH:721, D:855705, CVE-2017-6188 ------------------ Detailed Changelog ------------------ Steve Schnepp (1): Fix wrong parameter expansion in CGI munin-2.0.30, 2017-01-18 ------- Summary ------- Bugfix release. Closes: GH:745, GH:771, GH:783 ------------------ Detailed Changelog ------------------ Kenyon Ralph (2): Revert "if_: check for non-empty and >0 before reporting speed (thanks to ssm)" plugins/linux/meminfo: correct typo Yu Watanabe (1): Revert "munin_stats plugin: only graph munin-graph if graph_strategy=cron" munin-2.0.29, 2016-12-31 ------- Summary ------- Bugfix release. Closes: D:847649, D:849383 ------------------ Detailed Changelog ------------------ Artur Smet (1): Add queries for PostgreSQL 9.4 Peter J. Holzer (1): p/postgres_querylength_: don't stack times Vincas Dargis (1): Update postgres_connections_ for PostgreSQL 9.6 munin-2.0.28, 2016-12-04 ------- Summary ------- Bugfix release. ------------------ Detailed Changelog ------------------ Dr. Nagy Elemr Kroly (1): Visible graph titles => Ctrl-F works in browsers. IWAI, Masaharu (1): autodetect the node encoding Kim B. Heino (3): if_: /sys/class/net/ reports speed 0 for some devices if_, if_err_: add more virtual devices like gre0 and bond0.99 to skip list if_: check for non-empty and >0 before reporting speed (thanks to ssm) Lars Kruse (1): plugin munin_stats: fix message typo Mark H. Wood (1): Replace trivial use of Netcat with our own gadget to test for an open port. Stig Sandbeck Mathisen (1): Add configuration for ssh master-node transport Tomohiro Hosaka (1): s/IMGWEEKSUM/IMGYEARSUM/ sstj (1): Fix "Use of uninitialized value in numeric eq (==)" warning munin-2.0.27, 2016-10-31 ------- Summary ------- Bugfix release. For Halloween =) Closes: D:767032, D:768553, D:825136, D:834194, GH:690, GH:714 ------------------ Detailed Changelog ------------------ Andreas Maus (1): slapd_bdb_cache - autoconf fails when database dir is valid Holger Levsen (1): munin_stats plugin: only graph munin-graph if graph_strategy=cron Kenyon Ralph (1): plugins/node.d/ntp_states: fix "outlier" state spelling for recent versions of NTP Steinar H. Gunderson (1): Update acpi plugin to use the /sys interface. dipohl (1): Return smartctl exit code and warning message munin-2.0.26, 2016-09-09 ------- Summary ------- Bugfix release. Closes: D:761190, GH:426 ------------------ Detailed Changelog ------------------ Bjrn Forsman (1): multips: reject 'autoconf' unless $names is set Gabriele (1): p/snmp_if: fix warning on receive Jason Woods (4): Fix fofields always having the same entries as ofields. fofields now contains only entries that had a state change to OK (fo = fixed ok) Fix ofields previous state detection not working correctly Fix broken limits - Inheritance of warning/critical now works correctly and does not break subsequent limits - Aliased graph fields now obey limits assigned to them Fix get_limit returning [undef, undef] instead of undef when no warning or critical defined Julien Pivotto (1): Fix #1468: backport http_loadtime from devel Ken-ichi Mito (1): fix https://github.com/munin-monitoring/munin/issues/426 (Numbers are crazy in diskstats plugin after reboot) Kenyon Ralph (1): fix typo in graph_title Steve Schnepp (2): p/apt_all: Be able to override /etc/apt.conf p/apt_all: add some comment about default options munin-2.0.25, 2014-11-24 ------- Summary ------- Bugfix release. Closes: GH:304, D:769415, D:770745, D:770746, D:770826 ------------------ Detailed Changelog ------------------ Christoph Biedl (1): p/irqstats: Improve generated labels Daniel Black (1): ip/iostat_ios: also support /dev/xvdX devices Holger Levsen (1): p/iostat_ios: also support /dev/vdX devices Picnic Pete (1): Issue warning if no data Steve Schnepp (5): m/Node: add [INFO] in a log m/limits: remove warning about uninitialized plugins/df_abs: fix the naming for regular devices p/pgsql: Detect "rc" postgresql versions. p/ping_: honor ping env variable munin-2.0.24, 2014-10-26 ------- Summary ------- Bugfix release. It reopens D:675318, as it wasn't really fixed anyway. Closes: D:696981 Reopens: D:675318 ------------------ Detailed Changelog ------------------ Steve Schnepp (3): m/update: fix "not a reference at /../Utils.pm" Revert "p/postfix_mail: fix incorrect delivered message count" p/postfix_mailstats: proper fix, but partial munin-2.0.23, 2014-10-17 ------- Summary ------- Bugfix release. It enable the use of Perl 5.20, which broke CGI. ( commit 9fabbe7 was forgotten in the previous release ) Closes: D:762796 ------------------ Detailed Changelog ------------------ Steve Schnepp (1): cgi/html: fix CGI for perl 5.20 munin-2.0.22, 2014-10-15 ------- Summary ------- Bugfix release. It enable the use of Perl 5.20, which broke CGI. Closes: #1224, D:675318, D:711465, D:750954, D:751190, D:753506, D:761841, D:762904 ------------------ Detailed Changelog ------------------ Chen-Yu Tsai (1): cgi/graph: Set RRDCACHED_ADDRESS during each request Daniel Black (1): Corrects the truncating of negative values in the diskstats plugin Gerald Turner (2): dhcpd3 plugin should support multiple ranges in a single subnet lower-case the dictionary keys in ipmi_sensor_ Joerg Jaspert (1): make proc plugin more careful in /proc/ Klaus S. Madsen (1): prevent diskstats plugin from reporting negative latency Rob Shortt (1): node: munin plugin config ignores leading * Steve Schnepp (15): master: add "use_default_node" travis: adding preliminary test for rrd travis: be root for rrd install travis: fix rrd tar extract travis: customized rrd build travis: be root to install rrdtools. travis: install the Perl deps in the Perl dir travis: add some whitespace travis: optimize for compile times node: Only spoolfetch send 5 samples per service asyncd: Use SyncDictFile for plugin_rates asyncd: just use the SpoolWriter META-DATA p/postfix_mail: fix incorrect delivered message count p/http_loadtime: fix escaping of user agent Stig Sandbeck Mathisen (3): Remove linux-specific plugin files_ (ticket:1661) Detect more postgresql versions. Fix POD errors tbear2500 (1): Generate correct rrd filename munin-2.0.21, 2014-04-22 ------- Summary ------- Bugfix release. It fixes the regression on nested groups. The offending commit is only reverted, so the bug is here again. Reopens: #1224 ------------------ Detailed Changelog ------------------ Steve Schnepp (1): Revert "Generate correct RRD filenames for nested graphs. Fixes #1224" munin-2.0.20, 2014-03-28 ------- Summary ------- Bugfix release. It fixes the "start should be less than end" bug. Closes: #1358, #1350, #1434, #1224 ------------------ Detailed Changelog ------------------ Alexander Shorin (2): Generate correct RRD filenames for nested graphs. Fix unreasonable die in iostat_io Axel Huebl (1): p/http_loadtime: renable autoconf Lars Thegler (1): plugins/Plugin.pm: log filename in tail_open() Matthias Schmitz (2): Use perl instead of GNU sed; fix spelling error Fix small typo (misspelled name) Steve Schnepp (11): p/Pgsql.pm: fix version check on windows master/Update: atomic write datafile with tmp+ren master: fix kill invocation master/graph: fix gfx error when plugin is too old travis: We want to support down to perl 5.8 travis: test perl 5.18 travis: test perl 5.12 travis: build the master branch travis: remove notify on successive successful travis: change the IRC template travis: failing fast cernst1980 (1): p/lpstat: fix jobsize munin-2.0.19, 2013-12-07 ------- Summary ------- Bugfix release, aimed at fixing 2 bugs for Debian 7.3. Thanks again to Christoph Biedl for both (reports & patchs) Closes: #1394, #1395 ------------------ Detailed Changelog ------------------ Steve Schnepp: master: fix failures on partial graph_order master: lazy create PNG in cron munin-2.0.18, 2013-11-12 ------- Summary ------- Bugfix + secfix release. Note that this release has 2 security fixes : * Avoid a node DoS on bad plugin (CVE-2013-6359) * Avoid an OOM in HTML generation on bad multigraph data (CVE-2013-6048) Closes: #910, #1397, D:728840, C:CVE-2013-6359, C:CVE-2013-6048 ------------------ Detailed Changelog ------------------ Kjetil Torgrim Homme: common: add missing keywords common: refactor the keywords to ease changes Matthias Schmitz: Substitute some @@ vars in generate files. Steve Schnepp: plugins/open_files: fix overrided used.warn/crit node/asyncd: fix a crash case node/asynd: avoid wake up each second master: fix unexpected "warning limit" lines master: avoid an endless loop in HTML generation master: validate multigraph argument master: don't abort node collection on bad plugin p/ipmi_sensor_: fix the environ() usage munin-2.0.17, 2013-07-19 ------- Summary ------- Bugfix release. Closes: #1305, D:710899, D:712793, D:717265 ------------------ Detailed Changelog ------------------ Steve Schnepp: p/df: also ignore devtmpfs plugins/df_abs: fix the multiple block device gitignore: ignoring output dir sandbox/ master: fix "graph_period hour" plugins: fix uninitialized $MUNIN::VERSION Stig Sandbeck Mathisen: Remove typo in the example rrdcached socket documentation munin-2.0.16, 2013-06-03 ------- Summary ------- Bugfix release. - Fixed a spurious WARNING in GFX cgi ------------------ Detailed Changelog ------------------ Kenyon Ralph: plugins/ntp_: fix parsing older ntpq output Steve Schnepp: master: fix warning in GraphOld.pm munin-2.0.15, 2013-06-01 ------- Summary ------- Bugfix release. - Fixes for ip_ handling Closes: D:710527, D:710529 ------------------ Detailed Changelog ------------------ Steve Schnepp: plugins/Plugin.pm: fix bad state file handling (fixed) master: adding ":" to the allowed chars in CGI dev_scripts: automate async install master: re-enable debugging output in GraphOld.pm asyncd: use the same rules as munin-update Stig Sandbeck Mathisen: Fix for munin-cgi-graph crash in trend and predict. munin-2.0.14, 2013-05-10 ------- Summary ------- Bugfix release. - Fixes for plugin.pm state handling - Fix the limits for ABSOLUTE ------------------ Detailed Changelog ------------------ Kenyon Ralph: plugins/node.d/apc_nis: improve maximum limits Steve Schnepp: master: fix limits computation for ABSOLUTE Revert "plugins/Plugin.pm: fix bad state file handling" munin-2.0.13, 2013-04-26 ------- Summary ------- Bugfix & perf release. - Fixes for munin-async - Enhanced perf for munin-html - Relaxed the timeout for munin-node 2.0.12 fixed the timeout code. But it had a nasty side-effect as the timeout setting is shared for global timeout and plugin timout. This version decouples both by introducing a new global_timeout munin-node.conf variable that defaults to 15 min. Closes: #775, #947, #1267, D:558800 ------------------ Detailed Changelog ------------------ Jens Holzkmper: different state files for cron job & as plugin Kenyon Ralph: ntp_: rewrite plugin to support IPv6 ntp_states: decrease timeouts so DNS queries don't cause node timeout ntp_states: don't use this silly plugin loading method node.d/ntp_states: improve compatibility with older perls Kim B. Heino: plugin/diskstats: fix negative stats, relax limits plugins/http_loadtime: more wget/time fixes plugins/lpstat: fix autodetection plugins/snmp__if_multi: prefer ifAlias for alias name plugins/postfix_mailstats: support proxy-rejected (amavis) log lines asyncd: add more signal handlers. Steve Schnepp: master: whitespace fix in Utils.pm master: avoid having 2 concurrent $htmlconfig master: undef the config pre-reload (frees mem) master: RAM usage for $cache master: remove weaken on storable::read master: remove auto_weaken() contrib: add a test for memory leaks contrib: complete test with Storable dump/load plugins: add doc about conntrack plugins: use $PATH for external commands in shell plugins/postfix_mailstats: using a per-minute graph plugins/snmp_if: replace 32b counters by 64b ones node: set the global timeout to 15 min node: add timeout documentation in sample conf master: add --debug to munin-graph master: fix end date when predicting plugins/Plugin.pm: fix a security issue on state plugins/Plugin.pm: fix bad state file handling p/http_loadtime: fix failure on FreeBSD p/http_loadtime: do not reuse $TMPDIR dev_scripts: run should only run executable files node: add a lock to spoolreader node: update doc to show negative args Stig Sandbeck Mathisen: Use a more efficient sort on the list of nodes munin-2.0.12, 2013-03-21 ------- Summary ------- Bugfix-only release. - Fixed TLS/SSL transport - Fixed extra logging Closes: #1251, #1258, D:699803, D:689291, D:687912 ------------------ Detailed Changelog ------------------ Bjrn Ruberg: plugins/bonding_err_: adjust for newer kernels Steve Schnepp: node: don't ignore timeout config option plugins/apt: fix plugin statedir for cron html: fix bug introduced by 1aec747c10 graph: remove all extra INFO messages plugins/proc: fix bug in selecting via uid dev_scripts: remove warnings graph: remote superfluous -T in cmdline master: fix TLS enabled updates munin-2.0.11.1, 2013-02-09 Ironically, asyncd is broken in .11, as a typo makes it uncompilable. This is a patched release, and I'll remove any .11 tgz as it's not-working. Sorry. munin-2.0.11, 2013-02-01 ------- Summary ------- Bugfix-only release. - Important async fixes - Many fixes Closes: D:698079, D:697907 ------------------ Detailed Changelog ------------------ Diego Elio Petteno: hddtemp*: avoid using the degrees symbol in vlabel. Update .gitignore. master: always initialize the default cgiurl_graph value. master: simplify logic for mapping images to their path. master: provide a default to graph_strategy. master: avoid repeated tests on graph_strategy. master: remove unused variable. pod: declare all POD-processed files as utf8 encoded. Marc Schutz: fail2ban: Handle the case where no jails are configured. Stefan Rubner: plugins/mysql_: fix check if InnoDB is disabled Steve Schnepp: plugins/slapd_: fix Net::LDAP call asyncd: fix races in writing to spool asyncd: typo in pod asyncd: try to reuse socket when asking node asyncd: add the hostname in $0 asyncd: fix timeout bug & adding some debug asyncd: ignore current plugin if failing cnx to node asyncd: close the datafile before rename asyncd: quickfix a sleeping dataloss bug Stig Sandbeck Mathisen: Fix spelling mistakes in POD sections munin-2.0.10, 2013-01-09 ------- Summary ------- Bugfix-only release. - The --force flag of munin-limits is back, sorry for the disruptive API change in the 2.0.x series. - Many fixes Closes: D#615957, D#671448 ------------------ Detailed Changelog ------------------ D. Johnson: templates: fix overview missing tag Diego Elio Petteno: mailman: allow user to configure paths instead of hardcoding them. asterisk_*: remove dependency on Net::Telnet. asterisk_channels: fix name of plugin in configuration examples. Gerald Turner: plugins/bind9_rndc: fix if named.stats is on tmpfs Holger Levsen: plugins/df: update exclude list doc to match code Magnus Hagander: Disable server-side prepared statements for pg plugins Michal Cihar: plugins/snmp__df: find all mount points Nicolai Langfeldt: getversion: Handle checked out tags better Steve Schnepp: fix plugins.history plugins/cpuspeed: fix if cpus are at maximum speed getversion: update doc getversion: use the commit SHA instead of tree SHA getversion: prefix the commit with "-g" magic getversion: apply the changes described in doc getversion: also fix the stable-* rules Stig Sandbeck Mathisen: munin-limits: Add --always-send, --force options munin-2.0.9, 2012-12-05 ------- Summary ------- Small bugfix-only release. - Many minor fixes ------------------ Detailed Changelog ------------------ Daniel Black: node: allow ipv6 localhost in sample conf Diego Elio Petteno: common: fix TLS options settings. master: fix config test, add missing configuration options and update defaults. master: fix and rename htmlold test to test HTMLConfig instead. master: remove test for function that has been deleted. master: check for timeout before checking for failure to connect. master: remove one more test that requires a big rewrite to make sense. master: fix tests so that they actually pass for master. node: fix export_service_environment test. node: add a temporary default spooldir to the proxyspooler test. master: make sure to always consider limits, for when always_send is configured. master: fix test when executing from a release tarball. Magnus Hagander: Fix PostgreSQL plugins to work with development versions Marc Schutz: New regexes for virus/spam count in amavis plugin. Sebastian Noack: Fixed broken exit codes in ejabberd plugin. Fixed order of arguments for 'ejabberdctl status-num-host' in ejabberd plugin. Steve Schnepp: master: remove date manip warning in cgi graph contrib: rename the net::server patch master: connect to AAAA and A address Stig Sandbeck Mathisen: Remove documentation disclaimer intrigeri: plugins: do not use the "read w/o variable" bashism. munin-2.0.8, 2012-11-08 ------- Summary ------- Small bugfix-only release. - Many minor fixes ------------------ Detailed Changelog ------------------ Andrea Piccinelli: http_loadtime: fixed stderr redirection with 'time' Anton Tolchanov: Fixed parsing of first element in custom graph_data_size Diego Elio Petteno: master: on limit checks, if one of the two values is 'U', make the final value 'U' as well. master: on limits, be more careful on checking what always_send is set to. master: munin-limits: remove --force parameter master: munin-limits: open the data file based on hostname, not alias. plugins: Remove all '%' characters from plugins' graph_titles. master: factor out the SSH command, and set parameters to disable interactions master: if there is no path present, don't send a remote command. master: ignore all the lines before the greeting. Juho Juopperi: Fix multiping to show packetloss correctly Kjetil Torgrim Homme: fix problem with SIGHUP causing exec of unqualified script name Marc Schutz: Update licenses to current address of FSF. Steve Schnepp: master: fixed graph_data_size custom parsing getversion: update to use the debian-auto one. master: add an ip6 example in the sample munin.conf gitignore: ignoring output dir sandbox/ Stig Sandbeck Mathisen: Update apache httpd configuration example for 2.0.7 munin-2.0.7, 2012-10-03 ------- Summary ------- - Fixed datafiles handling from munin-update to others (cgi & cron). We only use storable datafiles now as it is less error prone. - Many other minor fixes Closes: #1259, D#688528, D#497400, D#687495, D#686982, #1251, #1251, #1102, D#628533, #1102, D#628533, D#681938 ------------------ Detailed Changelog ------------------ Adrien "ze" Urban: master: fix new config reading for m-u 1st run master: fixed datafiles handling Diego Elio Petteno: docs: fix link names apc_nis: create only one socket before going for config. apc_nis: improve configuration. plugins: replace deprecated graph_vtitle with graph_vlabel. ejabberd_: allow overriding the ejabberd.cfg path and add /etc/jabber to search paths. fw_conntrack: count both ipv4 and ipv6 connections together Jon Whiteman: plugins: fix plugin vserver_cpu_ for 9+ cpu cores Kazunori Kojima: Makefile.config: fix bad interpreter Kenyon Ralph: node.d.linux/interrupts: encode as UTF-8 Steve Schnepp: resources: remove osoblete apache-cgi.conf sample node: remove File::Path as it's not needed redhat: move the redhat stuff outside main repo master: fix too many warnings in munin-graph.log master: remove useless log lines, send to debug plugins: fix import in ipmi_sensor_ master: fix typo in Utils.pm plugins: fix apt_all plugin statedir for cron asyncd: fix --spooldir help master: split rrd updates when using rrdcached plugins: use Storable::nstore() instead of store() plugins: ignore wrong state files. Thorsten Gunkel: plugins: auto detect SATA in hddtemp_smartctl Vincent Danjean: plugins: add by-id to smart_ munin-2.0.6, 2012-08-31 ------- Summary ------- graph cron is the default again. This enables simple install either to work out of the box or be upgraded from 1.4.x in a far easier way than with the CGI default. Only fixes, but many of them. * fixes for munin-graph (cron + cgi) * fixes for munin-update * fixes for links in templates * fixes for the various security bugs ------------------ Detailed Changelog ------------------ Adrien "ze" Urban: templates: fix multiple bad links (mostly with subgroups) graph: cosmetics for no-data to graph db/graphs: remove duplicate entries munin-cgi-graph: fixed memory leak doc: nginx - Authentication and group access doc: multimaster contrib: munin-mergedb.pl (tool for multimaster) doc: fix missing link to tips/multimaster munin-cgi-*: use MUNIN_CONFIG if available munin-cgi-html: update timestamp when running for a while Chen-Yu Tsai: update: write node/services with update=no to datafile as well Diego Elio Petteno: snmp__print_pages: fix snmpconf. http_loadtime: use bash instead of any sh; `time` is not a standard command proc: fix Perl syntax to not use deprecated defined(@array) proc: drop autoconf capability and handling code. varnish_: remove another deprecated defined(@array) Holger Levsen: munin-cgi-graph: ignore @ARGV to fix CVE-2012-3513 and Debian #684076 munin-cron: call munin-graph with --cron argument (Closes: D#685343) Rename cgi-bin to munin-cgi: the default for the Apache webserver is to setup a ScriptAlias for /cgi-bin. The ScriptAlias directive does not permit changing sublocations, thus making it impossible to impose further restrictions like access controls etc. This change was done in munin-cgi-graph, munin-graph, HTMLConfig.pm, static/dynazoom.html and static/zoom.js _and_ also needs to be done in the distribution specific webserver configuration file. Niall Donegan: Quick fix to http_loadtime Added back in the capacities magic marker Peter Palfrader: update: bugfix _node_read_fast() (Closes: D#686089, D#686090) update: remove the last line to fix parsing errors update: fix use of undefined variable async: fix retrieve of ip_ We probably also want to allow : in spool filenames Robin H. Johnson: mysql_: Multiple instance support. Steve Schnepp: adding a new sum+cdef tester plugin munin.conf.in: show the defaut value in the sample adding some comments for HTML cron vs cgi Only kill & log if the subprocess is still alive update/spoolfetch: disable _node_read_fast node: more secure state file handling (Closes: #1234, CVE-2012-3512, D#684075, D#679897) plugins: use runtime $ENV{MUNIN_PLUGSTATE} master: revert to cron GFX per default plugins: fix for uninitialized $MUNIN_VERSION master: fix the D::M:B warning in munin-graph async: allow any good-looking-enough file in spool (Closes: D#686093) plugin: revert removal of munin_graph field munin-2.0.5, 2012-08-14 Diego Elio Petteno: ipmi_: improve autoconf handling sensors_: add support for power monitoring. munin-cgi-html: apply patch from ticket #1200 master: fix images for plugins w/ sub-categories. snmp__print_pages: fix snmpconf: Malte S. Stretz: Don't exclude simfs per default. Steve Schnepp: fix: dbdir shall not revert to default on m-u runs munin-cgi-graph: refine the fix for not cached cgi munin-cgi-graph: don't ever die() in CGI munin-2.0.4, 2012-07-30 Summary: * fixes for munin-graph * fixes for df_inode plugin Detailed Changelog Diego Elio Petteno: master: fix relative paths when using cron-based page generation. munin-graph: do not output text meant for CGI. munin-graph: use INFO for further details during execution. munin-graph: use the same default (non-cgi) as the rest of the Master. Steve Schnepp: Makefile: add a comment about JCVALID Stig Sandbeck Mathisen: Ignore reiserfs in the df_inode plugin Make df_inode plugin more robust. Add missing regular expression anchors munin-2.0.3, 2012-07-24 A quite raw output of 'git shortlog' : Christian Ruppert: nginx_{request,status}: include Munin::Plugin to fix version identifier. Christoph Biedl: fix bug that disabled gfx CGI caching Dan McGee: Force usage of the DM5 Date::Manip backend Don't remove Defaults.pm when invoking build-common target Expand list of filesystems excluded in Linux df_inode plugin Daniel Kahn Gillmor: slony_lag_: allow >1 subscribers Diego Elio Petteno: ifx_concurrent_sessions_: allow ps and pgrep to be in /bin as well as /usr/bin build: only move adv files if building on HP-UX. build: install TTF files as non-executables config: respect LIBDIR override. build: only install Java plugin files if JCVALID=yes df: exclude cgroup_root filesystem type as well. Add build-doc-stamp to .gitignore. master: bring back checks for graph_strategy set to cron. Jeremy Olexa: config: make sure to translate correctly for C-locale only Steve Schnepp: fix munin-graph typo in opening $graph_fh port multigraph_complex to pure POSIX shell doc: add "supersampling" plugins doc: adding supersampling section doc: changing markup use cgiurl_graph config in dynazoom.html use environment var for rrdcached revert to the same naming than 1.4.x makefile: added a comment on LANG node: only use basename for $0 Makefile: move default rule to be the 1st one directly compile to output dir git: ignore more build assets Stig Sandbeck Mathisen: Add documentation for writing a munin plugin. Correct reStructuredText syntax warnings Remove references we do not have targets for yet Document the munin node Add information about authorized_key hardening restructure documentation, work in progress Whitespace cleanup in the doc tree Add munin-cgi-graph manpage Add munin-cgi-html man page Order the man pages alphabetically Use correct reference Set a max depth for the table of contents Rename the graph aggregation directory appropriately Write proper man pages, and ensure "make man" creates them Add better description on the main index pages Add a directory reference page Remove plugin/aggregate, we have a replacement in examples/graph/aggregate Clean up table of contents in reference/ Add "how to use plugins" page Add munin-check man page Add markup and links for the supersampling doc munin-2.0.2, 2012-06-29 Summary : * munin-graph can be called again from cron * workaround "root" field name bug * initial doc/ subdir. will be munin book * emits percent in log about errors * various bugfix munin-2.0.1, 2012-06-10 Well, first bugfix release. SCM has moved from svn to git. Commits by luke, mwest, steve.schnepp, ssm Summary : * remove .storable files if they are unreadable. * change version strings to match git workflow * fix perms on state dir (D: closes #675593) * swallow stderr in cmd and ssh transport * remove the graph field in munin timings plugin Major enhancements to munin-async : * rename of the process names, it is now munin-async(d). * implement automatic cleanup * implement metadata, for the spool to be self-descriptive munin-2.0.0, r4900, 2012-05-30 It is 2.0-rc7, code wise. Only edited the mandatory doc files (Changelog, UPGRADING, ...) Here is a very short summary : * CGI graphing is the way to go as munin-graph is no more * Native SSH transport * Native IPv6 transport * Full rrdcached support * Graph zooming * Arbitrary precision down to 1 sec (needs 2.0 plugins) * Asynchronous polling support (still experimental) Full summary is in Announce-2.0 and the detailed changelog is below, scattered in the various pre-2.0 versions. munin 2.0-rc7, r4882, 2012-05-23 Commits by kenyon, steve.schnepp, ssm Summary : * Fix regex when $serv has some "-" in it (like dm-2). Closes #1218 * munin-node: sets $PATH to a secure value. (Closes #863 and #1128) * munin-cgi-graph: Add & to the list of "good" chars, since it is used in $ENV{QUERY_STRING} (D: Closes #674148) * munin-limits: fix for UNKNOWN on DERIVE (Closes #1203, #1221) * munin-limits: fix --host option * munin-limits: fix on hosts with multiple levels of domains munin 2.0-rc6, r4839, 2012-05-07 Commits by kenyon, knan, matthias, runesk, steve.schnepp, ssm Summary : * hddtemp_smartctl: just use the device name as the labels * Update our website address in various files * Add explicit license for all plugins. (D: Closes #670428) Many bugfixes in munin-cgi-graph : * If url parameters are not valid, send HTTP 404 instead of 500 * Remove the use of tempfiles. (D: Closes #668778) * Move the generation of png via cgi under /var/lib/munin/cgi-tmp/ (D: Closes #668536) * Don't cache URL with parameters anymore, and don't keep uncached URLs (D: Closes #668667) * Validate the url chars. (D: Closes #668666) * Add a max setting for cgi image size. (D: Closes #670811) munin 2.0-rc5, r4795, 2012-04-11 Commits by kenyon, steve.schnepp Summary : * Adding the current action to the command line. Useful for debugging. * Adding a new URL param full_size_mode to enable predictible IMG sizes * Enable sparklines with the url param "only_graph" * Start RRD just before first update. To avoid a very costly 1st update. * Emit the hosts in a sorted order, instead of somewhat random. * Do not emit png list if file handle is not defined. (D: Closes #666759) * Add old option of --force-root, but with a new name. more explicit (D: Closes #601371) * We dont generate the png list when using cgi html. * Remove warning when asking "list" w/o a hostname (U: Closes #907952) * Many bug fixes (Closes #967, #1210) (D: Closes #583189, #568511, #475078, #666759) munin 2.0-rc4, r4770, 2012-03-24 Commits by holger Summary : * Fix important typo munin 2.0-rc3, r4760, 2012-03-23 Commits by kenyon, steve.schnepp Summary : * Fix issue that CGI HTML doesn't refresh itself * Fixed plugins to use the default draw style * Also remove \r when reading config/fetch output. (works around bad behavior from munin-node-win32) * Various plugins fixes (closes #798, #663965, #595697, #648891) munin 2.0-rc2, r4709, 2012-03-09 Commits by kenyon, steve.schnepp Summary : * Import rrdmove into contrib/ as rrdcopy * Revert the Munin::Common fix as it disturbs the deb build * Various plugins fixes by kenyon munin 2.0-rc1, r4638, 2012-02-07 Commits by kenyon, steve.schnepp Summary : * Fix Munin::Common (closes #1036) * Document the fact that Munin needs at least Log4Perl 1.18 (closes #969) * Colors can now also be specified by an index of the palette (closes #1186) * Some plugins don't follow LC_ALL spec, so setting LANG=C also. (closes #1014) * Rewrite of fw_conntrack and fw_forwarded_local (closes #843, #725) * Various fixes & typos munin 2.0-beta7, r4600, 2012-01-21 Commits by kenyon, mha, tv, holger, steve.schnepp Summary : * Spoolfetch uses _node_read_fast() now : 3MiB is now read in 2 s instead of 7 min. * Fix for the images when browsing in 'categories' view to be clickable. * Fixing the png generation, in order to have 1px per RRA sample. (closes #1184) * Fix for nested nodes * single_value is wrong for some multigraph, disabling it for now * new plugin : (haproxy_ng multigraph plugin for haproxy) munin 2.0-beta6, r4544, 2012-01-14 Commits by kenyon, mha, holger, steve.schnepp Summary : * Rewrote the timeout handling code. The default node timeout is now 1 min. * Various bugfixes since 2.0b5 munin 2.0-beta5, r4508, 2011-12-7 Commits by knuthaug, ssm, bldewolf, tv, kenyon, janl, ligne, kristian, holger, steve.schnepp Summary : * Resurrecting munin-graph in a quite raw form * Set max_process default to 16 * Small performance improvements * Memory leak fixes (use weak reference for upper nodes) * Spoolfetch enabled nodes may send nothing. * Various code cleanups * Various bugfixes since 2.0b3 munin 2.0-beta4, r4306, 2011-8-2 Commit by steve.schnepp Summary : * fix issue with cgi html munin 2.0-beta3, r4256:4298, 2011-8-1 Commits by janl, ligne, steve.schnepp Summary : * HTML/CSS enhancements * munin-async-* enhancements * IPv6 enabled munin-node * Small munin-html performance improvements * Small plugins improvements * Various bugfixes since 2.0b2 munin 2.0-beta2, r4199:4252, 2011-7-3 Commits by bldewolf, steve.schnepp Summary : * Full integration of Munin with rrdcached * Fixed multigraph to be able to have multiple levels * Using Storable instead of a binary DB * Create a node.d.debug subdir * Various other bugfixes since 2.0b1 munin 2.0-beta1, r4124:4199, 2011-5-29 Commits by bldewolf, feiner.tom, jo, jorne, kristian, ligne, mha, runesk, ssm, steve.schnepp Summary : * Avoid memory leaks in munin-cgi * Enabling the generation of RRD with a different step size (eg: 10s instead of 5 min) * Various other bugfixes since 2.0a2 munin 2.0-alpha2, r3856:4124, 2011-1-19 Commits by bldewolf, feiner.tom, janl, knan, ligne, mha, ssm, steve.schnepp, lupe, jorne Summary : * Huge performance boost * munin-graph is over, cgi graphing only from now on * Enabling CGI HTML with an reworked UI * use a binary DB to store spoolfetch timestamps (fixes growing file bug in 2.0a1) * documentation cleanup Enhancements : * Better node side timeout error message allowing things to be found out if needed * Properly label the bgbouncer queries-per-second graph. * Properly return undef when the SNMP client returns noSuchObject instead of setting an error. * munin-node: Insert a reset_timeout() call to enable the master to get in a word in edgeways after a slow plugin has executed * munin-html: Start using fork to save some wall-clock time. Option --fork us now active and max_html_jobs is recognized by munin-html as the maximum number of paralell processes. * Actually recognize max_html_jobs as a config parameter * Add the publication part * Clean up Perl warnings due to use of undefined variables in two functions in LimitsOld.pm. * looking at the Net::SNMP doco, -username cannot be '' (it must be between 1 and 32 characters). * The time of munin-graph is over, cgi from now on * since pollers are now being respawned if they fall over, the shutdown-spooler signal handlers need to be removed before starting any poller up. * Cleanup of munin-cgi-graph: - Make it log right - Make it fast (now <0.1s pr graph on reasonable hardware) - Remove $config deep copy, it is too expensive, we need to fix expand_specials instead. - BUG: Broke --pinpoint processing * Munin::Master::Utils: - Replace some often used REs with string matches for speed. Helped speed up munin-cgi-graph quite a bit. Introduced munin_dumpconfig to help debug the expand_specials issue - Introduce munin_find_node_by_fqn to do faster lookups of things to graph. * Munin::Master::GraphOld: - Restructure a bit to avoid double loading of $config (in cooperation with munin-cgi-graph) and make it possible to pass some graphing options (--only-fqn for example) in some new places. - Propper logging the right way - NOTE: All code relating to host and service filtering and looping should be removed soonish - we're only using the FQN to find our work now. * Replacing 'echo -n', which is implementation-defined - with printf, which is posix * munin-update: Add local URI scheme to enable running async collector directly on the master as this may somehow enable push-to master. Closes #981, thanks to jorne * ALWAYS dumps the RRD cmd used when an error occurs when RRD::graphing * Add some extra HTTP Response Headers * JMX refactoring by Joachim Sauer * add some external tools to ease a Storable datafile * Enabled multigraph capabilities for the munin-async-server * Re-work filtering in linux df plugin to be much more flexible. * Force lpstat to C locale instead of EN_us. Various bugfix (not exhaustive) : * ${graph_period} substitution should happens in graph_title & fieldname (closes #881, thx stavros) * bugfix for mkpath on 5.8.8 (closes #992) -- thx Matthew Kent * fixes #991 (munin-update constantly thinks config has changed), thx mkent * Better checks for undefs in munin-limits (#973) * IPMI plugin now graphs power usage as well (#954) * Fix swap usage numbers in the sunos memory plugin (#696) * Make exim_mailstats plugin behave properly after initialisation (#985) * Fixed a bug where all services in a category were colored when a service had a warning or critical state * Clean up field names in the bonding_err_ plugin on linux (#962) * Cosmetic cleanups in snmp__if_multi plugin (#1008) * The snmp__if_multi plugin now makes proper use of 64bit counters if available (#1006) * The linux plugin selinux_avcstat now uses integers instead of floating points (#1005) * freebsd/coretemp - rrd doesn't do celsius/fahrenheit * Fix autoconf in the nfs_client plugin for linux nodes (#1015). * Fix issues with plugin names with - in them. * Make linux df plugin not die when it encounters a mountpoint it can't access (#940) * snmp__df - avoid division by zero * gets it working for net-snmp snmpd on a ubuntu 10.04 lts desktop with fuse mounts * Fix up Solaris's if_ plugin. * Low max_processes leads to hosts being skipped (closes #990) -- thx Matthew Kent * Make the mysql_ plugin better at parsing innodb output (#956) * Change call to mkpath to call to Utils module, resolving an issue with old perl (ticket #992) * snort_* : merge fix from branch 1.4 to trunk New plugins : * add plugin : snmp__df_ram plugin * add plugin : PostgreSQL 9.0+ streaming replication lag * add plugin : linux/proc - for graphing information about groups of processes. Removed plugins : No one was disappointed :-) munin 2.0-alpha1, r3400:3856, 2010-7-14 Commits by bldewolf, feiner.tom, janl, knan, ligne, mha, ssm, steve.schnepp * Enhanced CGI (CGI::Fast, direct call to GraphOld.pm, cgitmpdir) * Ability to zoom in or out graphs * fixed comparison pages in CGI mode as (closes #831) * hostname checks are case in-sensitive now : every hostname is converted to lowercase before use (closes #450) * Implementation of dirtyconfig (fixes #836), upgraded df plugin as an example. * custom format, per field/plugin, for graph_data_size * Add update_rate with optional "aligned" suboption to align RRD updates to granularity (avoids fractional for integer gauges) * SSH native transport (closes #842) * handle the last line of multiline options (fixes #855) * modify how varnish_ autoconf checks for the varnishstat executable. (closes #873) * use STDERR if cgi cannot open logfile (closes #874) * Add comments to the extremely long regexes in the sensors plugin. Many thanks to Noga Gazit for the patch adding the comments! (attached in debian bug http://bugs.debian.org/573613 * Fix sensors plugin parsing of Voltage, which needs to allow for whitespace at the begnining of the line. Debian bug: http://bugs.debian.org/573613. Thanks to Arthur Liu for the patch! * load $config once when using FastCGI, use Storable::dclone to deepclone the $config, in order to make .sum work again (see #894) (thx Jani M.) * Remove iso-8859-1 degree character from the example output from sensors, as it is ran with LC_ALL=C * prevent a spike in the graph after Amavis is restarted. Thanks to claudius for the patch. (closes #889) * set Net::Server's syslog_ident parameter, so log lines have 'munin-node' as the process name, rather than the significantly less useful 'net_server'. closes #789 * fix warnings when running under certain versions of perl, caused by exporting an undefined variable to the environment. thanks to Aleksandar Lazic (al4711) for the patch. (closes #896) * fix bug where snmp__netstat plugin can massively undercount the number of connections. * add a $Munin::Plugin::SNMP::DEBUG variable, which is an alias for $Munin::Plugin::DEBUG, so that SNMP plugins can automatically have debugging turned on when MUNIN_DEBUG environment variable is enabled. * update snmp__rdp_users plugin to use M::P::SNMP, and document it. * node.d/df* minor spring cleaning * processes plugin - implement autoconf * Add --pidebug to munin-node-configure. If PIDEBUG isn't defined, Node/Service.pm complains about uninitialized values. * Fix TLS, resolving several bugs. * Uptime should not scale, as it makes it hard to read after 1000 days of uptime. Thanks to Wakko Warner for noticing this (http://bugs.debian.org/575180) * strip the carriage return on the end of the capabilities list, if it exists. needed when debugging using telnet. closes #902. * Add plugins to monitor prepared transactions in PostgreSQL * Add a authors file for the buggers that use svn-git * add an experimental test script, that checks the POD sections embedded in plugins (as used by munindoc). * remove dependency on perl-5.10. (munin tries to be 5.8 only) * fixes on mysql_isam_space_ * extend M::N::SNMPConfig to support SNMPv3 scanning. * munin-async-[client|server]. A proxy for async polling of the localhost's munin-node * defer JCVALID evaluation to runtime, since $(JC) can be redefined later in a specific Makefile.config. The core Makefile.config serving as a Makefile.default in this case. * delete some obsolete tests. * Remove Bashism - Thanks to Raphael Geissert for pointing this out (bugs.debian.org/581126) * Adding a pure debugging munin-node * enable vectorized multiple-time updates * upgraded README to new website * get rid of unused variables, and needless initialisations. * first draft of a perlcritic policy. * Always start RRD 10 years ago to be able to spoolfetch in them. Otherwise we can only rewrite 10s of history. * more tests for m-n-c internals. * start to turn Munin::Node::Service into a "proper" class, rather than a bunch of static methods. * Include the name of the database in the graph title for PostgreSQL wildcard graphs. * start to migrate munin-run to instance-based M::N::Service * update munin-node to use a M::N::Service instance. * add $Id$ keywords where missing. * allow # characters to be included in config files, as long as they're preceded by a backslash * avoid spamming the logfile with debug messages. * create a directory for storing spool files. * spoolfetch support for M::N::Server. * postfix_mailstats.in: $logfile was being redefined, causing problems during autoconf. thanks to Lupe Christoph for the patch. fixes #930. * Addition of munin-sched munin 1.4.4, r3276,3379, 2010-2-26 * Localization problem in 1.4.1 (closes #781) * Sort plugin configuration file order. This ensures some predictability regarding where a configuration setting comes from when multiple files specify the same configuration variables. For details, see debian bug http://bugs.debian.org/564098 * munin-cgi-graph*: cgi semaphore fix, hopefully cures #834 * Munin/Master/GraphOld.pm sum+colour fix (closes #731) plugin to connect to a separate munin-node (or other daemon speaking the munin-node protocol) and fetch values from there. * munin-update: Fix config line continuation hadling * Add patch from munin ticket #828, to suppress "occasional" unknown states to avoid alerts. Thanks to Steve Wilson for the patch! * proxy_plugin: New proxy plugin, that makes it possible for a single Munin * jmx/Uptime: decimal days, not integer - closes #815 * nvidia_: fix suggest * linux/vserver*: fix autoconf exit codes * linux/ip_: RE fix to be able to match IPv6 addresses * linux/selinux_avcstat: New plugin from Lars Strand showing selinux stats Taken from Muninexchange. Thanks to Lars! * linux/lpar_cpu: A plugin to measure IBM PPC hardware virtualization logical partition CPU usage (made for the platform once called OpenPower) * varnish_: backend_unhealthy is a DERIVE value, note that canonical upstream is the varnish project * Add simfs" to linux df* plugins ignore list * linux/buddyinfo multigraph plugin by Gbor Gombs * Remediate patches from Gentoo that never came upstream. Prevents possible output on stderr in plugin linux/iostat_ios and corrects linux/fw_forwarded_local to handle a missing /proc/net/ip_conntrack linux/fw_forwarded_local: need to dereference the _conntrack_file variable * linux/diskstats: Upstream update, closes #838 #835 #837 * linux/ip_: Better matching of IPv6 addresses. Use a single awk command instead of a mix of awk and grep. * mysql_: fix graph_base error (Closes #840) Patch from Gbor Gombs * mysql_: plugin suggests wrong values (closes #857) * linux/selinux_avcstat - exit 0 on autoconf * linux/tcp: Add contributed plugin from Tim Small * linux/df_abs: multiple filesystem excludes do not work in df_abs Thanks to Daniel Reichelt for the patch! * snort*: Add documentation and minor updates * multips_memory: correct printing out raw fieldname, instead of clean_fieldname, documentation updates * linux/df_abs: Added configuration option to df_abs to Enable/Disable the graph total (The default is on). Thanks to Daniel Reichelt for the patch, submitted at http://bugs.debian.org/567895 - adapted, tested and documented by Tom Feiner * snmp__if_multi: Add env.ifTypeOnly filtering, fix multiple buglets * mailman: Corrected incorrect use of $MUNIN_PLUGSTATE in mailman plugin * amavis: Change amavis mktempfile function to run with backticks instead of $(command) as it doesnt work correctly piping to sed * exim_mailstats: exim_mailstats doesnt count Completed properly. Thanks to Wakko Warner for noticing this in http://bugs.debian.org/569621 * Deprecated tomcat plugins: Fix in to work properly when only one connector is availabe http://bugs.debian.org/543523 * postfix_mailstats: Fix shell->perl syntax in perl plugin * apt: update description with a pointer to apt_all * jmx/GCCount - fix negative counts on jvm restart (closes #852) * ping_: fix obvious typo (closes #854) munin 1.4.3, r3232:3274, 2009-12-30 * linux/diskstats: New multigraph plugin to replace diskstat_ - more tidy and more efficient. Thanks to Michael Renner for the deveopment of this and porting to multigraph * linux/cpuspeed: Specify "average" for the speed, switch to printf to get numbers printed in full, E notation looses significant digits and there are whispers of a bug in the E notation handling in munin-update * Make munin-update --nofork not die when nodes are unavailable. Closes #800. * hddtemp_smartctl: Multiple enhancements: - document how to set type_ for sata drives - document need to run as a user with device access - Support some debugging - detect if smartctl fails - detect if no recognized temperature output is present * lpstat: Warn if the predefined $lpstat executable is not executable, Make lpstat path configurable and search for it in likely places if it's not set. Patch from Lupe - thanks! Closes #826. * dhcpd3: Only emit .max if the .max is valid. Document a bug from the trac - adresses but doe not close #829. Patches for a complete fix welcome. * More specific test for watermark feature (1.2.13 required) * Make 0755 default permissions for $DBDIR and make consistent in Makefile and munin-check * Fix issue where far too many mails would be sent by munin-limits (fixes #795) * munin_stats: Fix a typo in the "no"-message that made munin-node-configure reject it. * linux/yum: Enhance statefile error message. Update to use M::C::Defaults * linux/vserver_*: Add autoconfiguration (patch from #811 submitted by proppy, closes ticket). * munin-update: Make true check into defined check to avoid dereferencing undefs * Munin::Plugin: - Better documentation of new default state file name. - In need_multigraph, if run from a tty with "config" or no arguments advice to use munin-run and exit. * mailman: User contributed patch to make plugin work better the first time. Also replaced "exit $n" with "die" with good error messages. Untested but perl says syntax ok. * hddtemp_smartctl: Added chomp, as it won't work with a newline at the end. Thanks to Ingvar Hagelund for pointing this out * munin-update: Suppress warning "Error reading includedir directory" in case the includedir is empty (which is the case in new installations. This caused cron to send these messages all the time * Munin::Master::GraphOld: warn_min/warn_max typo fix munin 1.4.2, r3167:3231, 2009-12-16 * Munin::Plugin::Pgslq: Clean the fieldnames before returning them, so we don't generate invalid data with for example a dash in the database name. * jmx_: Add authentication support, fix exceptions in various memory related parts, refactor connection negotiation to one central connection class, add default connection info for jmx_, so we actually are a bit autoconfy * munin_update: Update to work correctly with 1.4, document and enhance a bit * munin_stats: Provide better graph_info and set some warning and critical levels at 4 minutes and 4m45s * Improove colors contrast in jmx plugins which have Max area, to make them more readable + fix some indentation * ntp_offset: make info into graph_info * Munin-(node|run): Make group name resolution lazy, enhance error messages and reinstate the group foo,(bar) syntax * munin-(node|run): Set umask to 0002 so that state files are group writable INSTALLERS: chmod g+w on the files in your state directory (/var/opt/munin/plugin-state in a default install) * multips_memory: New plugin graphing the sum of the memory usage of different processes matched by regular expression. * Perl modules should not have x bit set: Remove on *Old.pm * munin-update: Make I/O timeout fatal to terminate munin-update<->munin-node communication otherwise the two parties falls out of phase (or we need to introduce a RESET command or close and reopen node connection, but I don't want to do that, esp. not now). Handle the fallout of making it fatal. * linux/load and linux/cpu: Correct warning/critical documentation * postgres_querylength_: Fix query that can never have worked... * Switch fonts to DejaVuSans og DejaVuSansMono. Vera is now obsoleted by DejaVu in most contexts. INSTALLERS: On platforms with RRD 1.3 the fonts need not be removed and no paths need to be installed as long as the DejaVuSans font family is installed. * Reorder somewhat to get lines on top of areas * Patch from blueyed to make munin-*cgi-graph work * snmp__if_multi: Only set .max if the interface speed has been obtained * mysql_: always exit 0 on autoconf * dhcpd3: re-fix deprecated non-0 exit on autoconf * add freeradius plugins from Alan DeKok and adapt better for munin-trunk * linux/df_inode: Small pod correction and add debugfs to df_inode exclude list * linux/df: Added debugfs to df.in exclude list * Apache plugins: Exit with proper error of LWP::UserAgent not found. This can happen if someone configured the plugin manually, or if the plugin is installed when LWP::Useragent is installed and passes autoconf, but sometime later LWP::Useragent is removed. Without this patch the error message is confusing: Cant locate object method "new" via package "LWP::UserAgent" This also keeps autoconf output sane even if LWP::Useragent is not available * linux/ip_: Actually support ipv6. Munin does actually handle : in plugin names right * make rpm does not work and won't work. Remove makefile target and spec file. Might be added back later. * There are no new autoconfiguring plugins in this release. munin (1.4.1, r3116:3166, 2009-12-04) * munin-node: Introduce a select loop to read from plugin output/errors and avoid locking up * munin-node: Plugins that time out must not be added to service list * Looked into making sunos/memory use kstat but that's a hack of more than 30 minutes, so document dependence on top instead * hddtemp_smartctl: make it quiet on systems that lack smartctl * sunos/iostat plugin now works * linux/load: Correct documentation on how to set warning levels * sunos/load: Set more graph_ things, including graph_scale no to avoid "milliload" * Update exim_mailqueue plugin to use exiqgrep instead of exim, closing #780 * sunos/df*: Add handling of /cdrom and /media mount points (ignore them) * M::M::Utils: Remove the "*BUG*", it is wrong and worrisome * linux/df*: Reinstate the warning and critical levels. Document how to set them. * munin-graph: Correct ordering problem in command-line. AREA|LINE/STACK sequences must not be interrupted by HRULEs and such (broke graphing of old cpu plugins) * munin-graph: warning lines without range endpoint gets better detection. * linux/df: Reinstate the default filesystem filling limits. Document how to set them. * linux/if: * Munin::Plugin: Support default values for warning and critical levels. * munin-*cgi-graph: Untaint the whole hostname, not only the first occurance of something unholy * Munin-*cgi-graph: Let "." be legal in hostnames. * munin-graph: Recognize "max_graph_jobs" in the configuration instead of logging errors * Also add munin_cgi_graph_jobs to the legal keywords * make tar now produces munin-$VERSION instead of using underline between * linux/if_: This change prevents the linux if_ plugin, when run as non-root, from leaking stderr output from ethtool to the log. * postfix_mailstats: MUNIN_PLUGSTATE is an environment variable. Thanks to pkhamre for reporting this. * linux/fw_forwarded_local: check for readability with test rather than cat. * Fixes to Announce-1.4.0 munin (1.4.0, r3033:3115, 2009-11-27) * Give the docs a once-over and update ChangeLog * Update plugins.history * Mention Aleksey Studnev in jmx_ doc * Merge patch from debian bug http://bugs.debian.org/507069 where netstat plugin counts \"active connections rejected because of time stamp\" as active connections, because of a lax regex * amavis plugin: Minor pod fix * Add snmp__print_(pages|supplies) printer plugins * jmx_: nitpicks * Fix #739: Perl error message only appears when there are no plugins on any nodes, so check that and LOGCROAK in a helpful way * munin-update: Change LOGCROAK to WARN for munin includedir missing or empty * Munin-node-configure --snmp: modify host enumeration code so hostnames that are passed in aren't indiscriminately converted to IPs. only works with plain hostnames, not CIDR. * Remove Log::Log4perl TRACE level use since that requires a too new Log::Log4perl * Munin::Plugin::SNMP: Added support for env.timeout for SNMP sessions * Add and extend the UPGRADING doc * Remove reliance on the very newest Getopt::Long version, update docs accordingly. * linux/files_: fix slightly odd usage of awk. the warning and critical thresholds are static, so they can just be echoed along with the rest of the config. * Postgres plugins: Correct pod from Munin::Plugins::Pgsql => Munin::Plugin::Pgsql (singular plugin, instead of plural) * Munin::Plugin::Pgsql: Correct Pgsql.pm plugin configuration documentation section * Correct the generic crontab install command * Typo fixes in docs * Munin-node: Provide a different statefile for each master. Helps prevent plugins that rely on their state-file from reporting incorrect data when used on a plugin that is polled by multiple masters. It uses the master's IP address, so it won't work if they are both behind the same NAT gateway, for example. set_state_name() in Munin::Plugin is also modified to behave in a similar manner. Additionally exports $MUNIN_STATEFILE into each plugin' environment, so that all plugins can partake thereof. Should be useful to plugins written in languages other than Perl. * Update 1.4 documentation * Munin::Plugin: fix $Munin::Plugin::DEBUG. it wasn't being set (lexical variable $DEBUG was being set in its place). change from "use vars" to "our" for all the externally-accessible variables, to make it clearer what is going on. + fix indentation * munin-limits: Check if $children is undef before using it as a array reference. Fixes #762 * snmp__if_multi: Call "need_multigraph" function to avoid working under the wrong circumstances * dhcpd3 plugin: Critical and warning limits added. Some cleanup and minor fixes. * Munin::Plugin: straight port of the multigraph assertion sub from Plugin.sh. * netbsd/sensors_: according to <http://munin-monitoring.org/wiki/protocol-config>, graph_vtitle has been superseded by graph_vlabel. * a few more tests for Munin::Common::Config. * linux/fw_(packets|conntrack): check for readability not existence bail properly if /proc/net/snmp cannot be opened. * Fix #758: Enhance and fix error propagation, fix exception handling, fix sub-process waiting/reaping, and finaly print old config data for failed nodes in a working way (probably worked before, but I broke it when I retooled how old_service_configs is read) * Munin::Master::ProcessManager: Correct the join command, which resulted in not printing the firewalled host - now it does :) * Returning iostst* to family auto, and moving diskstat_ to manual, until we have multigraph support added to diskstat leaving diskstat in auto without multigraph creates 3 graphs per disk, and on multi disk machines this makes the main html quite heavy * Uncomment includedir /etc/munin/munin-conf.d in munin.conf.in, and make sure Makefile creates the directory at install time * add some tests for Munin::Common::Config. * Munin::Common::Config: populate the valid keyword hash directly, rather than through an intermediate array. * linux/diskstat_: Remove fake_munin_plugin, as we know we include Munin::Plugin , no need to fake it :) * munin-node.conf: only specify setsid once in the config file * squid_traffic: hit_kbytes_out needs to be converted to bits, as with the other data series. fixes Munin ticket #761 and Debian bug 557385 <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=557385>. thanks to Samuel Leon <leon36@gmail.com> for the patch. * Prepare announcement of 1.4.0 munin (1.4.0-beta, r2982:3032, 2009-11-21) * ejabberd_: Fix some documentation and other problems, move to generic plugin directory (not Linux specific software) * No .t files in the TAGS file! * munin-update: Make rrd files even if the DS label evaluates to false in perl (e.g., foo.label 0) * bind9: Parse query logs with view information. Closes #661 * linux/ip_: Avoid matching x.x.x.2 with x.x.x.24. Fix is "obviously" correct and has only been syntax checked * linux/ip_: Document that we won't support rule matching on the "any" firewall framework * linux/cps_ (LVS) plugin fixed by incurporating patch in #636. Not tested beyond a syntax/autoconf test * bind9_rndc: Parse BIND 9.6 stats output. * exim_mailqueue: Add documentation about user required to run plugin and modidy AWK script as the default action is to print the whole line, which was done... * Various fixees to various plugins * sunos/iostat: New plugin showhing I/O thruput on Solaris * exim_mailstats: count the number of received, rejected and completed emails using regexes rather than substr(). fix for ticket #698. also suppress stderr when fetching the log_file_path, remove some unnecessary code, improve error when the logfile is unreadable, and tidy up here and there. * dev_scripts/install: add install-plugins-prime to the targets run during a standard install. makes working on plugins rather easier, since a full, slow, salted-earth reinstall isn't required. * postfix_mailstats: remove processing of rotated logfiles, as decided in ticket #631. also includes a bit of tidying, and descriptive error messages when bailing rather than relying exclusively on exit status. i don't have a postfix server to hand, so this is not exactly well tested... * Plugin execution: remove undefined elements from the plugin command passed to exec. this fixes the underlying problem the yum plugin was encountering earlier. * linux/yum: change yum plugin so it ignores defined-but-empty arguments. makes fetch work again. * munin-graph: Re-introduce line continuation in the rrdtool graph command debug output * Make munin_graph immune to redundance in graph_order field * M::M::Config: Enhance a error message to include input line number * linux/fw_conntrack: Let there be a total.value even if the input is empty * linux/diskstat_: Document problem with multiple masters * overview template: Remove COMPARE links as we will not be able to make them correct in any reasonable timeframe. The datastructure we have now does not support that O:-) * munin.conf.pod: Removed the list of example required parameters configurations from munin.conf man page, as they are not required anymore, instead moved them to the explanation section for each parameter, showing the default * linux/iostat: Changed to manual, as they are replaced by the new diskstat_ plugin by Michael Renner * linux/threads: make a little less racy (and a tiny bit more efficient to boot). it's still not perfect, because even now there's a gap between the shell expanding the glob and grep getting round to reading the files, but at least it complains less. * log anything that plugins print to stderr. also sends a brief message back to the server explaining what went wrong. fix for ticket #582. * munin-graph: Stop trying to copy cdef when aliasing fields in graph_order -- 1.2 doesn't do that. * Changing munin.conf.pod, munin-cron.pod to munin.conf.pod.in munin-cron.pod.in. Adjusting Makefile accordingly. Closes #753 * munin-node: Do "next" on weird filenames not "return" so that we read all plugin config files * favicon for all templates. Refactored the finding of the root path and css name in the process. Added tests for get_root_path * modify how nested timeouts are handled, as discussed with janl. this fixes the bug whereby a connection to munin-node would be unexpectedly terminated if a plugin took more than 10 seconds to run, regardless of whether this was allowed by the plugin configuration. * Catch problems if someone forgot a "address <IP>" or "update no" - and make it plain in the error message. * Use_node_name should default to "no". Closes #757 * Clearify munin_set_var_loc error message so we know what value is set illegaly * munin-node: Improve error reporting during startup by sort of merging it with _run_service(). move the eval to the very top of the event loop so as to avoid interfering with the session timeout. * linux/fw_conntrack: Now works in 2.6, closes #532, thanks :-) * Added ejabberd contrib plugin, moved from debian patches munin (1.4.0-alpha2, r2899:2981, 2009-11-13) * set obsoleted tomcat_* plugins to family=manual * smart_ - fix autoconf yes and suggest empty * openvpn and misc ntp_* plugins: fix some nonzero autoconfs * mysql_: fix autoconf for missing Cache::Cache * zimbra_: fix autoconf when missing perl modules * Fix brokem munin-cgi-graph, which was : missing : --list-images param for munin-graph, a missing chomp($file), and a wrong path to the generated png. Also fix HTMLOld passing an array ref instead of an array * a bit more debug output for the node server -- note which node each plugin is associated with, and whether it requires multigraph. * report when plugins are being ignored due to errors when they were being configured. not very detailed, but better than nothing. also delete them from the list of plugins, so they're not included in the list. * Munin-node: fix logic in _suggested_links(): + never return any suggested links when the plugin shouldn't be installed. + correctly handle SNMP plugins whose only wildcard parameter is the host. (thanks to janl for spotting this was broken.) * Add proof of concept snort plugins * snmp__if_multi: Update to acutuall implementation of multigraph and some further testing and corrections * INSTALL: Update w.r.t. support for SNMP v3 * munin.conf: Correct "includedir" example * postfix_mailqueue: Fix autoconf for the negative case, pretty sure it still works for the positive case O:-) * Fix various autoconf problems with slapd and ifx plugins * mysql_* plugins: Now obsoleted by new mysql_ plugin. Set family=manual * Asterisk_* plugins: None of these support autoconf so remove magicmarker * HTML: Border on graphs in critical and warning state for nodeview. Comparison views (hopefully) to follow * M::M::Utils: End the pod section with =cut, so that the last line, # vim: syntax=perl ts=8 wont slip into the man page * M::M::ProcessManager: documentation typo fix * bind9_rndc: reinstate @@PERL@@ substitution marker, which broke in r2958 * Get the BIND logfile's size before rndc is run, rather than taking a guess at where it might have been. Fix for #746, thanks to guillomovitch for the patch. * Clean up a typo, add use strict, use warnings to all 3 apache_* plugins, add an info explanation for apache_accesses * HTML: munin-serviceview: add column header on 6th column in legend table * M::M::HTMLOld: removes underscore from top navigation element describing the plugin name for the service page emission. you know for the visual touch * HTML: added variable to set the text for the dropdown for each of the template emitting functions. * Refactoring templates into partials (with include) to avod a lot of duplication. There is some left. Better look on the legend table for service-biew and dropdown with 'other' links with config value. Default is 1 at the moment. Makefile adds install steps for new partial folder * M::M::UpdateWorker: Typo--; * linux/df_inode: vfat doesn't have any inodes either. * More documentation of plugins * Graphs: Fix cur: values for .stack and .sum. * Graphs: ADDNAN in expand_specials cdefs * Remove .label requirement on .stack fields. * Changed munin_stats to check for $Munin::Common::Defaults::MUNIN_LIBDIR/munin-update existance + executable in order to autoconf it as previously it checked for the existance of the munin master logs, which did not exist in a clean installation. Also added a check + extinfo message in case any of the log files are not readable * Fix copying of subhashes (#%#parent was broken in old version). Fixed various issues in a couple of programs. * M::M::ProcessManager: clarify what the numbers at the end of the log message mean. * M::P::SNMP: for some reason, Net::SNMP doesn't consider 'No Such Object' to be a real error. Handle better in get_single. * linux/ip_: Use env.hostname to replace ip-number in graph_title. * pm3users_ : we can do colour now * ipmi_sensor_ - fix autoconf for missing ipmitool * M::M::Config: Handle continued lines (ending in \) * M::M::GraphOld: Comment out utime call that interferes with rrd's --lazy option * M::M::Node: A missing .label isn't as fatal as all that. Insert a value and supply .extinfo to explain more * Make the linux/diskstat_ plugin work better w/o root access, document a bit better * Remove obsolete nagios specific munin-limits commands from crontabs all over. * M::M::Update: Load datafile relative to the configured dbdir, not the default dbdir * squeezebox_: Various enhancements * M::M::Node: More specific location of the missing label from the config output * M::M::LimitsOld: Forgot to actually make this into a exporter module. Thanks to Kevin Fenzi for pointing this out * mysql_ plugin: Update vlabels for concistency * Give the BIND plugins a graph_category as pointed out in email by Rado Rovny * Update mysql_ plugin from upstream * Get rid of munindoc.in from the installation (closes #742) * INSTALL: More modules, and a section on using CPAN-shell * Implement dropdown boxes for peers when there are more than "dropdownlimit" number of peers. Implemented for all views except overview, which is a special kind of, ehm, view. Also removes underscores from names for graph peers to clean them up a bit munin (1.4.0-alpha, r1560:2898, 2009-11-06) * squeezebox_: Changes to support several players * smart_: Exit 0 on autoconf as the doctor ordered * Make munin-graph recognize --help * Quite a bit of work on error messages and log messages * munin_stats plugin: Accept logdir setting from plugin-conf.d * processes and linux/proc_pri plugins: Add vlabel * Handle domain_order as group_order. * Sort peers properly (fixes #577) * Fix handling of group_order and node_order (partial fix for #579) * Munin-update: soften up the protocol error handling a bit * Make munin-html tolerate --nofork - even if it has no effect. Document. * mysql_ plugin: Fix "suggest" bug * Add multigraph_tester plugin to test (and show off) multigraph features * Add extinfo_tester plugin for developer aid * Make use_node_name work again (fixes #739) * One single instance of "includedir" in munin.conf is now obeyed to implement a "drop directory" for munin-master configuration. Example in munin.conf * Add python-plugin OO framework as python-plugin * Make M::M::Logger a bit more subtle about warnings when the logs are not open (so that they appear somewhere instead of nowhere) * Update plugin configuration for varnish_ * Make notifications work again * munin.conf: Set HTML and CGI dir consistently * Added new mysql_ by Kjell-Magne ierud plugin, document origin, and sentence the others to exile (family=contrib) * Fix "make/make install" problems, document the other * Change <img> framing a bit to allow coloured frames (once the templates are updated) * Add multigraph support to all programs in the munin-suite * mysql_innodb plugin: The "new way" of detecting InnoDB free space was changed in 5.1.24, add a case to compare correctly. * linux/bonding_err_: it's a counter not a gauge * Perltidy quite a bit * Retire fieldname.warn and fieldname.crit. The documentation has been saying .warning and .critical for a number of years. Mass fix plugins accordingly. * Service view: Add hopefuly helpful explanatory text about warn/crit/unknown states in the graph-info field. * Get the HTML templates and CSS validating. * Updated templates and CSS with stylish new elements, fonts and such. Thanks to Knut Haugen. * Switch to Log4perl on the master side. Node still has to use old logging to avoid node-installation-dependency-hell. * Ever more corrections to INSTALL * Basic modularisation of munin-limits * Move favicon.ico to templates, some of us need the HTMLDIR to be fully re-generatable from the templates directory (in order to be FHS complaint) * Commented some templates to make them easier to read and understand * munin.conf: include cgiurl_graph example * memcached_: New wildcard plugin * Refactoring of munin-update, munin-node and munin-node-configure by Kjell-Mangne ierud and Matthew Boyle. Actuall tests for testing the programs were added too! * templates: * html templates: The timestamp can't really be ISO8601 with the support of portable strftime % formats so remove the T, and cover the eventuallity that strftime does not support %z * Make templates @@ free - less processing - change Makefile and M::M::HTMLOld accordingly * Put INFO and DEBUG statements into the locking code to make it debugable * Fix bashisms in nutups_.in : function is useless * Introduced some actuall debugging output to munin-html * Cleaned up logging in munin-html and M::M::Utils * munin-master: make getopt::long dependency explicit at build-time * linux/df*: Ubuntu Karmic uses "none" as device a lot, so handle that * HACKING.pod: Revert "Note to do 'make build' before trying the install script." * testplugin: Testplugin for various failure modes - developer support * Node.pm: Sanitise plugin names * Node.pm: Fix the fieldname sanitiser a bit * Node.pm: Better error message for empty plugin list (links to wiki) * If ntp_offset can read no values from ntpq, replace values with 'U' to avoid master confusion. * New plugin for Squeezebox Duo: squeezebox_ * Add plugin for connected PostgreSQL users. * Cosmetic changes in munin-cron script to make it more failure obvious and and clearify the ordering * Minimal modularization of munin-html, munin-graph and munin-limits - now half suited to include in munin-fastcgi-graph if we want to * Turns out that the // (and //=) operator is new to perl so remove all use * Add plugin for PostgreSQL tuple access count * Add plugin for Slony (PostgreSQL replication) node lag time * Add plugins for PostgreSQL scans (squential vs index) * Support connecting to non-default database in pg plugins * Support multiple parameters to pg plugins, using colon to separate them. * Document max_graph_jobs and max_cgi_graph_jobs (see also the wiki). * Made munin-graph paralell (defaulting to 6 forked processes). Adapted from 1.2 patch by Kjetil Homme. Many many thanks! * check whether javac works before using it, skip java plugins if not * pgsql: Properly apply filters to config query as well for wildcard plugins, so we don't generate lots of nan fields in the graphs. * munin-html: Do not try to calculate png size, use geometry of the file in the filesystem instead. This will cause a propagation delay - image sizes _may_ be wrong, until munin-html runs again. * munin-graph: set m/a time of png to last modification time of the corresponding rrd file (since reverted) * Adapt patch in #3 to current munin-cgi-graph for non-fastcgi. Closes #3. Thanks to blueyed * Adapt patch in #3 to current munin-cgi-graph to obtain munin-fastcgi-graph * Install munin-fastcgi-graph * munin-update and family: Do not accpet hostnames outside [^-A-Za-z0-9]. Issue helpful error message if someone bumps into this. * Munin-node-configure: always do "exit 0" even when saying "no" because it _is_ a normal exit and we do read the stdout. Mass fix of plugins in trunk. * linux/cpuspeed: Documentation fixes and slight error-message enhancements * Changed the Max memory for the jmx_ plugins to default to a light color (just like apache_processes free slots graph) as currently Max got a dark blue color, making it hard to read graphs * Compile the java files with -target 1.5 and -source 1.5 so they will work correctly on sun-java-1.5 and on (without this, when compiled using openjdk/sun-1.6-jdk, they refuse to run on 1.5 jre * New vserver plugins adapted from Debian * New asterisk plugins adapted from Debian * ipmi_sensor_: Applied patch from http://bugs.debian.org/490093 so that the graph scale always starts from zero * Add ipmi_sensor_ wildcard plugin from Debian branch, and POD it * Massive PODing of plugins for use with munindoc by Stig Sandbeck Mathisen Many thanks! * New plugin: openvpn.in from debian, and POD it gently * Added the ability to customize the install location for the jmx java library. As most distributions wont want it in the default munin libdir * munin-node-configure needs pass the list of plugin names through to apply_wildcards() too. part of #718. thanks to knan for the report. * Munin-node: Clean up man-pages, and try and standardise common information across all three applications. * Report errors against plugins with inconsistent/unexpected magic markers. * Java plugins written by FIX. Adapted for inclusion in munin by Erik Bols, Tom Feiner and Ilya Kikoin. THANKS! * Applied patch by Katriel Traum for snmp__memory that converts the plugin to use Munin::Plugin::SNMP, and does some clean-ups * New plugin: nomadix_users_. Monitors a kind of enterprise wifi infrastructure. * snmp__if_multi: Updated with some known bugs/enhancement needs as well as better author/copyright info * linux/ip_: Better support for IPv6. Patch by Rune Skillingstad, thanks! Closes #563. * linux/if_* plugins: Even more virtual lan support. Patch by Rune Skillingstad. Thanks. Closes #562. * amavis: Fix use of $MUNIN_MKTEMP as pr. #502. Should probably provide a working function in plugin.sh or a better MUNIN_MKTEMP * apache_* plugins: Better error messages for diffetent kinds of LWP errors (for example missing Crypt::SSLeay). Patch by Tom Feiner, thanks! Closes Debian #542477 and Munin #710 * ntp_ plugin: Provide error-message in english if plugin invoked without recognized hostname in symlink * hddtemp_smartctl: Use --nocheck=standby if supported by smartctl. Thanks for patch from marxarelli. Closes #715 * note bug in amavis plugin in doc * linux/port_: Fix to allow more than 9999 connections. Patch from Olav Kolbu, thanks! Closes #583 * sendmail_mqilqueue: Correct queue count on sendmail with HOST_STATUS/hoststat enabled. Thanks to andy for this. Closes #630 * exim_mailqueue plugin now supports showing frozen messages and uses prettier colours. Thanks to micha for the patch that was adapted! Closes #107. * munin-node-configure: Some re-wording of the manual page, and remove some obsolete dependencies on the --shell option. * Port munin_stats plugin to use the logtail functionality from Munin::Plugin. Fixes #527. Thanks to janl for indirectly reminding me that I'd never got round to committing this :-) * postfix_mailqueue: use postconf to find default spool path. Update documentation and correct author (to "unknown"). * Munin-node-configure: document M::N::SNMPConfig properly. * zimbra_: fix lucene_cachehit race condition with zmstat writer * Add new fail2ban plugin * munin-check: Updated copyright to Matthias Schmitz * Linux: ip_conntrack_max at new location in newer kernels. * Add zimbra_ (groupware) plugin * Added linux/diskstat_ plugin, from Michael Renner http://blogs.amd.co.at/robe/2008/12/graphing-linux-disk-io-statistics-with-munin.html * Added logging for denied connection in munin-node. Closes #714 * linux/sensros_: Fix error when no sensors execuatble is installed. Merge from http://bugs.debian.org/491473 * Merge fix for apc_nis.in from http://bugs.debian.org/511781 - The config output by the apc_nis plugin includes: line_volt.max 200 Where the normal mains voltage is higher (most of the world), this has the effect of excluding the quantity from the generated graphs. The value should be increased to at least 300. * tomcat_: all new plugin from laxis@magex.hu, deprecate tomcat_* * linux/iostat: Added stdout/stderr redirection, merge from debian bug http://bugs.debian.org/512407 * allow for : in plugin names to support IPv6, Merge from debian bug http://bugs.debian.org/499391 * Fixed typo in Munin::Plugin documentation, merged from debian bug: http://bugs.debian.org/495003 * linux/if_*: Added support for msh interfaces * linux: Changed df.in and df_inode.in to have nagios compatible graph title, nagios doesnt like special chars in graph titles. (The same patch from debian bug http://bugs.debian.org/472239) * add minimal ruby infrastructure * Added NetApp plugins from Guillaume Blairon * Add mysql_innodb plugin * Fix (now old) tomcat plugins * freebsd/df*, include nullfs in filesystem list. Patch from Cezary Morga. * linux/if_*: Fix vlan support * Make boolean parsing a bit more robust (case insensitive) * Fix cap protocol on server and node * - is also allowed in host names * Remove datafield length limit of 19 characters (the understanding of this limit was based on obsolete documentation) * Document Munin::Master::Logger better * Make Munin::Master::Utils capable of logging * Make the pg plugins work properly when DBD::Pg is not present (by saying they can't work). * Add slapd_* (OpenLDAP) plugins by Bjrn Ruberg * Add PGSQL plugin framework by Magnus Hagander, and some plugins * linux/iostat_ios: check if $ARGV[0] is defined to avoid "use of uninitialized value" warnings. Thanks for the report to nicklock! (Ticket #640) * postfix_mailstats: Don't fall back to a logfile we have already determined not to exist. Thanks to Ulrik Haugen (Deb:#532876)! * freebsd/netstat: Match reset$ instead of reset. Closes #708 with regards to trunk. * Add "make tags" target to make navigation easier. * linux/port_: Fix #500 better as suggested by Kolbu. Thanks! * linux/if_*: Recognize venet|veth interfaces in suggest code. Thanks to "blueyed" for patch! Closes #591 * Patch to allow master side overrides to work much better. Thanks for patch from "mg"! Closes #588 * plugins/hddtemp_smartctl: Apply autoconf patch from "qha" that only autoconfigures the plugin if the first drive is autodetectable - or configured. Thanks! * linux/cpuspeed: reversed Hz and % in one case, exclude "cpuidle", only graph cpu[0-9]* * Documentation: mod_expiry => mod_expires * linux/cpu: As per a mailing list thread, change Linux cpu plugin to not set max for any fields because recent kernels are inaccurate to the point where a single field can edge just slightly over the calculated max if it is using all of the processor time (for example, the idle state can trigger this). Without removing the max, the affected fields have gaps, creating confusing graphs. * Fix some brainos in apc_envunit_ plugin, make it more like autoconf'igurable * Fix sunos/df* plugins quite a bit * plugin.sh: Document print_warning better * Add a generic crontab for the munin user * Fixed longstanding bug in munin-node-configure which reversed list of installed and new wildcard names for wildcard plugins - this has made auto-reconfiguration of whildcard plugins impossible, and downright dangerous if you used the --remove-also option * Fix sunos if_ and if_err_ to suggest correct interface names * Rename sunos if_errcoll_ to if_err_ to get in line with the other architectures * The "setseid" keyword in munin-node.conf should be spelled "setsid" in order to be understood by Net::Server. Since log_file is also set, setsid is redundant, but it is now correctly redundant instead of just ignored as garbage :) * Update allow/deny examples in munin-node.conf.in. Net::Server can use CIDR if Net::CIDR is installed. Keep "allow" as default, so as not to need additional dependencies on the default install. * linux/df: make tmpfs'es indexed by mountpoint rather than device. * Do "list $node" rather than "list $fetchdomain" when collecting node data. (closes #699). * Make ./getversion work on Solaris 10 and other systems with old-style /bin/sh (closes #700) * Remove special_(sum|stack) from allowed keywords list * Make graph_args --title "Foo bar" work as expected (closes #683) * Remove depreciated options special_stack and special_sum (replaced by stack and sum ages ago) * Make "make install" behave slightly better and document problem with NFS * Add varnish_ plugin. Canonical version is to be found in varnish_ repo at all times. * Detect rejects from postfix/cleanup, and not just rejects from postfix/smtpd Thanks to Cedric Knight for patch. * sendmail_*: Suppress error output from which(1) when the mailstats binary is missing. * Add missing documentation and config file option (see #567). * munindoc has been left out after r2126, which is wrong. * Fix bashisms in several plugins thanks to Raphael Geissert for the bug and the patch. (Closes Debian bug #530147) * Various if_ plugins: fix typo found by Peter Schwindt, Debian #523765 * synced redhat specfile to upstream so make rpm works again * Updated redhat's Makefile.config to be in sync with dist * ISO8859-1 specific (C) symbol removed * Corrected typo that left out man page for munin-update. * Fixed regression where MUNIN_* variables would not be exported * smart_: Fix brainfart wrt PLUGSTATE vs. STATEDIR and nico's email address * New version of smart_ plugin * Moved perl build functionality to Module::Build. * Sanitise fieldnames * Added the no-fixes test. Fixed failing test * Fixed Perl::Critic issues * More robust and consistent use of boolean configuration options * Removed deprecated configuration option: use_default_name * Removed unused cli option from docs * Unshift DEFs instead of pushing to fix a CDEF argument-ordering issue * Fix scope-issue+typo with $STATS * Use /opt as default target and move Makefile.config-dist to Makefile.config * Make [plugin] user-name evaluation lazy so that configuration for unused plugins will not cause error messages. * Added capability negotiation to munin-update * Added tls README * Add a general man page hub for munin as requested in bugs.debian.org/517952 * Add -w to munin-limits and fix warnings * Moved plugins to a separate folder * Moved docs into doc/ * Add env.upsname and env.upsc settings to the nut_* node plugins * Added python oo plugin class by Morten Siebuhr as a contribution * nvidia_ plugin: Adapt some more to mainline plugin niceties * Applied patch from #669. Make munin-run use Munin::Node::Defaults. Thanks to Matthew Boyle <mlb@decisionsoft.com> for the patch * Fixed getversion to handle git-svn usage * linux/quota_usage: Use clean_fieldname as needed. Bug discovered by ert256 Closes #645. * linux/sensors: Recognize negative temperatures. Fix by cnu. Thanks! Closes #647 * http_loadtime uses pipes and mktemp. Patch by ekohl. Thanks! * hddtemp_smartctl: Only check if device is standby for sd? and hd? devices (it fails on raid devices). Patch by anonymous trac user. Thanks! * munin-graph: warn -> warning in two places - that will make graphs more usefull - we hope - as noted by stevew@purdue.edu * Fix warn lines where warn is given as range (foo:bar). Patch by stevew@purdue.edu. Thanks! * Fix get_[gu]id to correctly recognize numbers and strings. Patch by ligne. Thanks! * Ignore Emacs and Joe temporary files * Protect graph rendering by semaphore too - as suggested by Snide in a patch. Closes #657. * More typo fixes from linge. Closes #659, #658. Thanks! * Apply patch from #660, fix typos in error messages in munin-node. Many thanks to ligne * postfix_mailstats: More explicit error messages. Patch by kuriyama. Thanks! * munin-cgi-graph: Check returnvalue of sem* functions in the correct manner * apache_processes: Graph free process slots, and provide explicit colours to make it prettier. Patch by TTimo - thanks a lot! * linux/df_abs: need bash to compute correctly * Fix #619 - error in comment * Move munin-node-configure-snmp to @@LIBDIR@@ - it should not be called directly * Align snmp__if_ and snmp__if_err_ and remove filter on what kinds of interfaces they catch. * snmp__if_multi: Structure the multigraph namespace somewhat and add some helpfull comments for the future programmer. * snmp__if_multi: A first multigraph plugin - now we just have to make munin understand it... * Port snmp__if_err_ to Munin::Plugin::SNMP - it still needs aligning with snmp__if_ so that interface names will match and so on. * Port snmp__swap to Munin::Plugin::SNMP - also change base to 1024 as this is memory which is accounted for in powers of 2 * Port snmp__winload to Munin::Plugin::SNMP * Transplant get_by_regex to Munin::Plugin::SNMP and change snmp__netstat to match * Port snmp__netstat to Munin::Plugin::SNMP * node.d/smart_ documentation contributed by Gabriele Pohl. Thanks! * Extend SNMP plugin documentation with a "MIB INFORMATION" header. * Start a separate program to test Munin::Plugin functions * snmp__if_ plugin can now use 64 bit byte counters if they are available. Added extensive texts in documentation and graph_info about the problems with 32 bit counters and made it explicit if 64 bit counters are available. * Add option --pidebug for plugin debugging to munin-run and munin-node. Exports setting through the environment variable MUNIN_DEBUG. Also introduce these in the usage texts. Far from all plugins support this setting. * Make Munin::Plugin::scaleNumber and use it in snmp__if_ plugin * Port snmp__if_ to Munin::Plugin::SNMP, document, and refine * Fix a typo in Munin::Plugin::SNMP - now verified with authenticated SNMPv3 * Extend Munin::Plugin::SNMP to support SNMP v3 * Install Munin::Plugin::SNMP, a general generic interface for taking away the boring bits in SNMP plugins. Many thanks for Ilmari for his work on this module. * Port snmp__uptime to use Munin::Plugin::SNMP and document it nicely. * Do not kluge lines with rrd 1.3 - the lines in 1.3 are nice and precise * Apply RRD 1.3 patch from 1.2 series - Many thanks to matthias! * Start using Log::Log4perl * Add test-case plugins in conjunction with munin-gather work (note: munin-gather is not completed and is semi abandoned at the time of 1.4.0 release) * dhcp3 plugin: Document and credit to Rune N. Skillingstad * Remove --force-root. It's silly and causes naught but problems. Added text in the root error message about how to su to munin. * Add a script to compute n'th percentile. Needs some munin-update extentions to work. * munin_get_loc: Fix premature exit - patch by Joe Damato * This may fix the double legend headers bug * Clearify the autoconf "no" from linux/swap * Remove max field from samba config - patch by Kozik * linux/iostat: Introduce environment variable SHOW_NUMBERED to show sda<n> in environments where this is wanted * Update postfix_mailqueue plugin to pull warning/critical values from the environment. * linux/iostat_ios reformed to use seconds (avoiding milli-milli-seconds), added graph_info and munin-doc documentation * Introduce SSPOOLDIR for munin-gather's use * Slightly update docs of apache plugins * Add haproxy_ plugin contributed by Jimmy Olsen * Add found nginx plugins - after some cosmetic work * Fixup debug output in munin-limits (Brian De Wolf) * Add pipe and pipe_command in keyword list in Munin.pm (Brian De Wolf) * Fill message structure in munin-limits correctly (Brian De Wolf) * Enhanced DEBUG consistency in munin-graph (Patch by Brian De Wolf, thanks) * freebsd/netstat: Correct absolute path to netstat program * freebsd/memory and vmstat: hardcode absolute paths for awk, bc, netstat, ps and vmstat * Lets munin-run print out its usage if its called without a plugin parameter. This avoids a lot of "Use of uninitialized value $ARGV[0] in regexp compilation at /usr/sbin/munin-run line .." messages and closes Debian #416478 * linux/df: let make insert the right configuration directory in the documentation * Make fix for OS X 10.5 by George Barnett - thanks * Received hpux df plugins with munindoc - thanks again * hp-ux: Added df plugins based on the bdf command - contributed by Chris Gardner * linux/iostat_ios: Bugfix by Philipp Niemann to get it working. Thanks! * Node/plugin.sh: Patch by Philipp Niemann to limit field names to 19 characters (removed as of release of 1.4 as munin actually handles this) * Munin-html: Patch to relative path calculation for graph borrowing by Phillip Niemann. * Makefile: Correctly locate magic markers - patch by Philipp Niemann * Munin-html: Added borrowing of whole groups, through group_order. * Plugin.pm/Plugin.sh: Fix up the thresholds functions * munin-check: ignore lost+found directories * Added new option "group_order" to order groups/graphs at any level. * sh-mode, not shell-mode for plugin.sh.in * Update FSF address in copyright notices * Added plugin to graph haproxy backend usage. * Munin::Plugin support for max value settings, patch by Brian De Wolf. Thanks a lot! * munin-check: Replace "munin" by "@@USER@@", patch from Pedro Melo, thanks * freebsd/systat, new plugin showing interrupts and context switches * freebsd/iostat: New plugin * node.d/dhcpd3: Parse "include" statements, patch by Walter Huf * smart_: Update Nico Stranskys email address * SuSE cron.d/munin mentioned munin-nagios, a obsoleted part of munin. * Clearify the purpose of DESTDIR * plugin qmail-qstat now uses a single pipe to generate all output * new plugin: qmailqstat contributed by Nils Breunese * Add an eval to $DF to expand the string to arguments. * And a changelog for 1.3.4 too * Update RELEASE and plugins.history for 1.3.4 munin (1.3.4, r1277:1560, 8/3/2008) * New acpi thermal_zone plugin * Bugfix linux/if_ plugin (--path=>-p, quoting, ethtool output tolerance) * linux/cpuspeed: $(< ) isn't in POSIX it seems * linux/cpu: "steal" is yet another value in /proc/stat on newer kernels. * We must have a cron job for munin-limits * Added detection of setr* at build time. The OSes without these functions will work anyway (AIX, Darwin) * Added some darwin(osx) plugins. * Better handling of # in plugins and munin.conf (it can now be escaped). Solves #38. * Fix bug in node.d/ntp_ where jitter is plotted with the wrong values. Fixes #520. * Munin.pm: Bugfix in an error message, as well as added low-level reason for dieing. Solves #468. Detaint pid before kill(). Fixes #508. * munin-limits: Added support for "unknown" Resloves #29, #43. * munin-update: Fixed timeout bug (timeout not working where intended). Closes #168. * freebsd/if_: Disable DNS lookups which are not used (fixes #509) * Documentation clearification as requested in #462 * List requirements at start of install document * full SSL support (#490, #489, #8). Many thanks to Brian De. Wolf for this patch! * As pr #510 make df plugins sensitive to warning and critical environment variables. Also Solaris plugins issued ".warn" which is just wrong. Two plugins used awk in the inner loop, these were not fixed. * munin-node.conf: Add documentation of host directive * linux/ip_: Fix #439, support ipv6 in linux (not tested, I have no IPv6) * postfix_mailvolume: Fix #473 - config did too much work * linux/netstat: Address #493 - SuSE feature regarding special class of rejections based on timestamps. We do not count them, but will no longer be confused by them. * snmp__cpuload: Fix #506, what to do when no load value is returned? * ping_: If invoked as ping6_ uses ping6 for pinging * surfboard: Fixes and typos. * linux/port_: Fix #500, report zero open ports as 0 not blank * Integrate a load of snmp_ plugins from Lars Strand * snmp__if_: Fix #453, snmp__if_ now supports SNMPv3 and heavy authentication. Also always use $name to name the interface. * hddtemp_smartctl: now works with recent kernels and 3ware controllers * apt_all: apt_all lacked some graph metadata, fixes #478 * ircu: more robust and more useful (fixes #517, thanks to Zhenech) * Clearify apache-cgi foo * linux/irqstats: Fix unitialized message (#497) * munin-cgi-graph: Path error in unlocking (#507), - SysV semaphore locking, thanks to Fox for patch (closes #499) * loggrep: Support foreign hostnames * INSTALL: Fix filename typo in INSTALL (closes #513) * Add postgresql plugins from #63, muninexchange and openproject.hu - make the support for servernames, port and so on uniform * ntp plugins: Make ntp_ plugin manual, it is no longer recommended, the new ntp_offset is - Interprete magical ip-numbers used by ntpd for local clocks - stops spurious DNS lookups of these (closes #150) * It appears that some versions of Net::SSLeay needs a "" in print_errs("") calls (closes #154) * Add munin_stats plugin written by Rodolphe Quideville - and patch to munin-limits to provide timing information by same. Closes #485. Problem: plugin can take more than 10 seconds to run with long log files, needs to use log-tail and a state file * Make netstat plugins --logarithmic, and autoconfigure * Make iostat* plugins autoconfigure * Make netstat from darwin generic and elliminate identical plugins from other BSD OSes * Add postgres_commits_ and postgres_queries_ contributed by Moses Moore based on work done by Vajtsz and others * exim_mailstats did not have human readable error messages, it tried signaling by coded exit statuses * ntp_*: Implement option to supress showing delay graph (suggested by Hvard Eidnes, fixes #350) * Generic processes plugin submitted by Lars Stand. Thanks!! (#441) * Fix typo in munin-limits (oks -> ok) closes debian #387283 * Enhanced doc of cgi mode in munin.conf - closes debian #307963 * linux/interrupts: Replace sed|egrep|awk pipeline with a single awk script * linux/cpuspeed: Report the average frequency, Use the default line style. * hddtemp_smartctl: Fixes to avoid disk-spin-up (fixes 137) * linux/interrupts: Remove & to fix a new pango problem ("&" is illegal entity). Fixes #537 * linux/nfsd4: Add nfs4 server plugin * Postgres plugins: Make graph_category consistent (fixes #534) * linux/ip_: ip_ plugin: add support for alternate chain names * munin_graph: Set category to manual, munin_stats superseeds them * Postgres plugins enhanced by Tim Retout - thanks (closes #541) (Use standard pgsql env variables for DB connection - fixes #544) * colour_tester: Plugin to experiment with the colour palette * hddtemp_smartctl: Insert comment about SCSI vs. SATA on hddtemp plugin * Multiple documentation updates from Brian de. Wolf * Debian packaging: Set correct log file for the postfix_mailvolume plugin (Debian bug #461302) * linux/df: Update list of file systems we don't want to graph. (Debian bug #385291) * munin-cron: Filter "attempt to put segment in horiz list twice" better, closes #538 * munin-update: Negative magnitude broken in scientific format interpretation (thanks Matthias) * New functions in Munin::Plugin (state retention, tail functions) * Some preparations for same in plugin.sh * munin-limits: Remove unused code for opening the wrong log file. closes #553 and debian #385358 * munin-limits: Fix redirect handling. Fixes #552 and debian #385358 * Quoting fixes in postgres plugins - fixes #546 - thanks to diocles * snmp__if*: Probe switches for interface alias and use it if one is assigned - fixes #551 - thanks to bart for patch * hp-ux: Start of HP-UX plugins from Raph Grothe * Fix perl warning relating to strangely empty environment value * HP-UX munin-node init.d script and rc.config.d file by Ralph Grothe * munin-node: Added a newline on TLS logging * Added tls_ca_certificate to @legal * squid_objectsize: New plugin to show mean object size in Squid. Useful for tuning the caching size. Thanks to Bjrn * Munin-master programs: Remove locks as we're done with them. Removes ambiguity around left over lock files. * Introducing the munindoc command and example documentation * Munin::Plugin - fix doc and testcase, test better * irqstats: Now work with 2.6.24 kernels (fixes debian #463721), also make "MIS" and "ERR" interrupts be reported on SMP systems * Sensors plugin for nvidia graphics cards * munin_stats: Slight change to remove this error: "Useless use of a constant in void context at /etc/munin/plugins/munin_stats line 35." * munin-graph: Add "Munin $VERSION" watermark to graphs - everyone uses munin but not everyone knows * Munin.pm: Fixed locking bug, lock was not created, and previously there was no error message * Introduced "palette" keyword for configuration along with the named palettes "old" and "default". The names should be self descriptive * Munin-master programs Stronger warnings about about running as root. Standardized across all server components * Copyright on main programs now extended to 2008. * Add Informix plugin for concurrent session counting contributed by Ralph Grothe * Introduce apache configuration to control html and graph expiry. Document in INSTALL, and remove old example now obsoleted by doc * Fix graph expiery in munin-cgi-graph as well * Munin-graph: Hum, that expiery thing has been fixed quite often. Go with the one I liked the best * freebsd/if_*: FreeBSD if_ and if_errcoll_ from downstream maintainer. Removes more interfaces * hddtemp_smartctl: Recognize ATA disks on FreeBSD (patch from downstream) * New FreeBSD plugin: coretemp, graphs Intel Core (and newer) temperature measured by MSR * Munin-update: Disable tls by default - fixes #452 * munindoc'ify apache plugins, document how to do basic authentication * freebsd/dev_cpu_: Add freebsd plugin for cpu-temp and speed (requires Core Duo or newer CPUs) * Document users plugin a bit * linux/sensors: Fix regexp, there may be _no_ space after : * Plugin.pm: Remove comma from quoted word list * munin-check: add the script munin-check for checking permissions of the munin-dirs - Thanks Matthias! * Implement multilevel-groups. * Enabled plugins to send values on the form <epoch>:<value> (enabeling backdating of data) * Rename munin_set_var -> munin_set for consistency purposes. * Fetch services directly after config, to make use of OS caches. * Bugfix: .graph was ignored in some cases in munin-html, ending in trying to show nonexistent graphs. * postfix_mailvolume: multiple fixes for problems found in sibling exim_mailstats plugin: - Use english to communicate errors (not exit statuses) - Discontinue use of rotated log files to elliminate associated bugs - Give reasons for not autoconfiguring plugin * munin-limits: Open log on demand. * Munin.pm Fix false red/yellow markers in html output. * Munin::Plugin: Security fix, do not open symlinks for writing * postfix_mailvolume: Fix bug introduced in previous cleanup to stop startup spike. * munin_graph: Changed munin_graph plugin to graph total time used by munin-graph, since we don't draw graphs grouped by domain anymore. * Correct graph_category on some plugins * style.css: Fixes too wide link issue. * Add getversion command to add SVN revision to unversioned svn checkouts * Remove redundant munin_graph plugin - see munin_stats * linux/if_ plugin: Clearify the 32 bit issue with fast interfaces * linux/irqstats plugin: Incorporate magic markers in pod * node.d/multips plugin: Document, use clean_fieldname to remove bugs, add usefull .info information about processes watched. * Various fixes to amavis plugin * Fix wrong chmod for plugin-state directory munin (1.3.3, r910-r1236) It's very likely that the entries for 1.3.3 contain some duplicates. In part because it's been going on for more than a year, in part because a large part has been constructed from the svn commit log. * linux/cpuspeed: Report the average frequency. * linux/interrupts: replace sed|egrep|awk pipeline with a single awk script. * Linux plugins: Use @@GOODSH@@ (posix-shell) there too * linux/vlan: Better autoconf messages * Solaris plugins: bring them @@GOODSH@@ and some elementary civilization * sunos memory: Make autoconf'able * Bring the goodness og @@GOODSH@@ to the generic plugins * node.d/df and df_inode plugins: Rewrite like the last 20 years of shell enhancments happened * munin monitoring plugins: Make them auto-installing * ntp_ and ntp_states plugins: Insert graph_category, make them "auto" and invent category "time" as well * ntp_kernel_* plugins: Not ntpdc's were created equal, autodetect better * munin-cgi-graph: Patch to limit number of concurrent rrdgraph processes * munin-html: Do not generate height/width attributes when in cgi mode * munin-html: Log runtime - like with munin-graph etc. * users plugin: Make vehicle for *STACK draw types * munin-graph: Support draw types AREASTACK and LINESTACK which is not sensitive to order like LINE/STACK and AREA/STACK * INSTALL: Document what to do first when working with a svn checkout * munin-graph: Simplify colour processing * Makefile: Remove build before rebuilding it * users plugin: Further fixing. Make testbed for field.colour and graph_printf * munin-graph/Munin.pm: Support graph_printf * users plugin: Move to platform independent directory * users plugin: change familiy to auto, make generic * linux/memory: Further adjustments w.r.t. correct handling of vmalloc_used * Fix typo "contigious" * munin-node-simple: Fix @@ variable typos * SunOS df plugins: use @@GOODSH@@ * Define GOODSH and BASH in Makefile.config to be used by shell plugins * exim_* plugins: Problems with "which exim" on Solaris corrected * Put "host" thing in munin-node.conf template config * munin-node docs: Mention two most importand Net::Server config options. * Linux df* plugins: Configurable filesystem-type exclution. cds by defaut * plugin.sh: Utility functions for shell plugins * Munin::Plugin: Utility functions for perl plugins * Sample plugins: Convert to use plugin.sh and Munin::Plugin * Makefile: Install utility files * Munin-run/node: Put most @@ config params from install time into plugin environment. This means that plugins can access @@PLUGSTATE@@ as $MUNIN_PLUGSTATE, and so on. * df* plugins on all architectures: Make fieldnames comply to charset restrictions * Makefile: Install platform-specific plugins after generic plugins so that the former override the latter ones * Munin-graph: Installed kluge for rrd 1.2 to make lines narrower to compensate for crayon-effect. * Munin-update: Add code to interpret Scientific notation (3E-20) for RRD so plugins don't have to * Bring SunOS df plugin names into line with other platforms * Make SunOS df plugins auto/autoconf * Linux/memory plugin: Remove warning on overcommit * Partial audit of correct magicmarkers in generic and linux plugins * Multiple plugins: Correct autoconf/suggest action slightly * lpstat: New plugin for print queue depths * Munin-node-configure: Make more robust, make it present plugin errors and exit with error if there are any. Write testcases and node-monkeywrench target in Makefile to test the error handling. * munin-run: Make --debug messages consistent wrt STDERR/STDOUT and prefix with # to make them obvious to users and munin-node-configure * Makefile: Create plugin state directory with correct ownership and permissions * port_ plugin did not use @@PLUGSTATE@@ for plugin state * ipmi plugin: Incorrect handeling of 'autoconf' method * YUM plugin did not autoconf correctly * install-node-plugins now installs all plugins in the distribution - no matter if they existed already - how else do we get bugfixed plugins installed? * Inform about ruthless installation practices in INSTALL document * Add maintainer version of Makefile.config - and stop it from being distributed as the actual Makefile.config by accident * Add favicon.ico file and install * Add other convinience features for the maintainer to Makefile * linux/fw_forwarded_local: Removed arbitrary max settings (trac #149) * Contributed plugins for netbsd * New ntpdc based ntp plugins to show kernel params * New plugin: Multiping shows multiple ping results in one graph (trac #109) * munin-node: Fix depreciated environment setting messages (trac #377) * Document what graph_strategy cgi does (wiki docs, trac #98) * Add a possibly helpful readme file to dists/sunos * Contrib: (updated) hack to generate pages of each-service-on-every-host * Munin.pm: Fix uninitialized value problem in lock reading code. * linux/irqstats plugin: Fix bug related to different /proc/interupts format on Sparc hardware (trac #436) * Added init script for Solaris in resources directory * Add uptime plugin for solaris (trac #419) * Squid plugins: Add timeout to http connection call * Correct years of (C) in snmp__winmem * nutups_: new plugin for ups checking through "nut". * munin-html: Split on /\s+/ instead of / / to ensure more robust handling of user input * Fix rrd 1.2 font size problem causing line wrap (#104) * Fold in contributed hack to generate single service comparative pages * Node: Clear up some operator presidence causing problems in perl 5.8.7 * Update copyright years and put copyright info into the new README file * Install a .htaccess file by default and document it * Move surfboard plugin, not Linux specific. I suspect this plugin is redundant and snmp__if_ can do the same job * Bugfix to support notify_alias at service level * Put configuration-file snippets into resource directory and use install @@ substitutions in them * Makefile: Better dependencies * Add digitemp and yum plugins * Make munin-node-configure-snmp die gracefully with a potentially useful error message if Net::SNMP->session() fails. Patch helpfully provided by Cyril Bouthors <cyb@debian.org>, thanks! * New AIX load plugin * Plugins from Lars Strand: linux/threads linux/proc_pri * New version of linux/if_ plugin that also handles mii-tool dependent interfaces and handles absence of tools better. * linux/memory: warn -> warning * postfix/mailstats: sort keys instead of keys, prettyer that way * Windows SNMP plugins: Memory and cpuload plugins seems pretty broken reinstate other versions. * New SNMP lugins: snmp__rdp_users and snmp__winload * Change global headers limit to 16 or longer labels - 20 was too much even with rrd 1.0 * linux/memory: Saner values on 64 bit machines - ticket #119 * SNMP based uptime grapher - ticket #141 * Linux/load: Make warn/crit levels configurable by environment (ticket #44) * Aix plugins: Better graph_categories * New plugin: qmailscan by David Obando, lists viruses found by name * New plugins: freebsd/uptime, linux/quota_usage, snmp: cmc_tc_sensor r1054 | janl | 2006-08-25 01:31:44 +0200 (fre, 25 aug 2006) | 2 lines * Linux/apt: Better counting: Debian #314610, Munin #92 * New plugins for Tomcat from runesk * Applied patch to allow _aggregated graph * spamstats plugin: Check if logfile exists before reading * Delete obsolete plugins: i2c* * ACPI plugin is linux specific * linux/cpu: Various fixes relating to 1000Hz machines, closing #228. * Applied field.colour patch by munin@iambitter.org, ticket #54 * New plugin: IPMI plugin added * apc_envunit_ plugin: Remove hard coded defaults that overrode configuration. * freebsd/vmstat: Avoid calling sysctl through $PATH. That variable is changed on process restart. * Get rid of undefined variable warnings in mailscanner - patch by dz@426.ch (trac #227) * cups plugin: No @ in field names (#66, #70) * sendmail_mailqueue plugins: Follow symlinks (#102) * hddtemp_smartctl: different attribute ID for temperature (#81) * ps_ plugin: Counted grep as well as the process * New plugin: cpuspeed for linux. * Linux/multips: wrong category of multips (#103) * Munin-node/run: Set LC_ALL to C to avoid locale changes to program output * Linux/cpu plugin: Scale to >10 CPUs (trac #37) * Linux/df: Rewritten df plugins in perl. The old plugins were extremely slow on busy system due to massive forking * sendmail_mqueue: (#65) wrong seek call * munin-update: (#39) incorrect error handling when opening STATS handle * New plugin: Linux/nfs4_client: should be folded with nfs_client when we get multigraph * node/munin-run: Inserted missing \n * linux/nfs_client: Fixed input processing inefficiency * linux/nfsd: Ditto * linux/fw_forwarded_local: Ditto * ps_ plugin: pgrep? /usr/bin/pgrep! * sendmail_mailqueue plugin: Replaced a $mspqueue too many * Synced most rpm specfile changes from Fedora package into unstable tree * A RedHat specific sendmail plugin config file * Add testing framework and some tests for munin-run. * Updated to final 1.2.4 version [merged change r98r984 from /branches/1.2-stable * Change copyright notice in programs to mention the GNU *General* Public License, Expand on "no warranty". Mention COPYING and http://www.fsf.org/licensing/licenses/gpl.txt. Extend copyright to 2005. * Fix generic/mysql_isam_space_ to pass user-specified mysqlshow options first. * Fix generic/ntp_ to report seconds instead of milliseconds. * Fix handling of 'update no' on services in munin-update. * Log warning about nodes without addresses in munin-update. * SNMP plugins no longer report host_name when checking localhost. * Remade the horizontal logo * Added two svg versions of the logo: original and horizontal * Merged debian packaging changes from 1.2 [changes r615:920 from /branches/1.2-stable/dists/debian and r855:891 from /branches/debian/sarge/dists/debian] * Added support for summing values from log lines. * Correct munin-limits pod. * Tuned logging. * Added support for huge rrd files. * Changed paths in Makefile wrt move from cvs to svn. * Make it possible to run plugins with group root. * Plugins: "exists $ARGV[0]" does not work with perl < 5.6.0. use "defined $ARGV[0]". * Dists: Brought dists.freebsd in line with the FreeBSD ports. * Dists: Merged changes from Debian Etch package. See dists/debian/changelog. * Server: filter a trivial error message from RRDtool (Deb#326061) * Server: A patch from trunk that makes munin-limits eat stdout/err from the contact.foo.command option in munin.conf. (Deb#301196) * Server: Fix thinko in category_order in munin-html.in. * Node: Prevent list command from polluting the node list. * Plugins: Fix tempfile creation by using the best method available on a given platform. (amavis, courier_, perdition) * Plugins: Fix to support the Linux cciss driver in the iostat plugin. * Plugins: squid_requests: Put the CDEF back that computes misses. * Plugins: postfix_mailvolume: Fix bad exit code test for `which postconf`. * Plugins: hddtemp_smartctl: Fix bugs on Solaris. Revert back to using -A (attributes) rather than -a (all). That can be specified through the args_$dev mechanism. THIS CAN BREAK EXISTING SETUPS! * Plugins: samba: Make location of smbstatus program configurable. Streamline computation of foo.value. Correct typos and a thinko. Add configurable "ignoreipcshare". Make awk program a lot more complicated to deal with variations of smbstatus output. Quote error message with parentheseis. * Plugins: smart_: Allow to query several drives on the same 3ware card. Correct a bug when '-i' was not listed in smartargs. Don't fail if no value was obtained for hard drive model. Minor enhancements and minor bug fixes from Nicolas STRANSKY <Nico@neo-lan.net>. smartctl exit code is a value composed of bits. Allow more than one to be turned on simultaneously by using the exit code itself rather than the log2 of it. * Plugins: perdition: Add missing backquote on mktempfile line. Support IMAPS and POP3S protocols. * Plugins (FreeBSD): if_, if_errcoll_: Shift field numbers in awk for interfaces that have no MAC address. * Plugins (Solaris): fs_df, fs_inodes: Allow '-' in mountpoints. * Plugins (Solaris): processes: Replaced numerous calls to various programs with a single awk program. * Plugins (Solaris): netstat: "exists $ARGV[0]" does not work with perl < 5.6.0. use "defined $ARGV[0]". * Plugins (Solaris): memory: Rewrite the value computation as a single nawk program. Support scale factor "K" (gives values < 1). * Plugins (Linux): port_: Correct offset error in TCP6 code. * Plugins (Linux): if_: Simplify awk program greatly, suggested by Nicolai Langfeldt <janl@linpro.no> * Plugins: Added plugin generic/digitemp_. * Plugins: Added plugin linux/yum. * Node: use Sys::Hostname::hostname()+gethostbyname() instead of Net::Domain::hostfqdn() in order to determine the fully-qualified host name of the host. The latter appears to be brain-damaged, see <http://bugs.debian.org/275024> for background (Deb#307462, Trac#89). munin (1.3.2) * Main/node: Implemented TLS support. * Main: fix the file vs. pipe check in munin-limits. * Main: place the munin-limits lock file in rundir, not dbdir. * Main: Yet another rewrite of munin-limits open call (SF#1115434). * Main: Make sure all rrd-tunes are correct after an upgrade (Deb#296454, Deb#296645). * Main: Better handling of broken connections (Deb#298108). * Main: HTML cleanup (Deb#296676). * Main: Re-enable width/height attributes to img tags. * Main: Escape regexps more properly (Deb#296575). * Main: Correct expires-headers in CGI output (SF#1159742). * Main: Redirect stdout/stderr from munin-limits contact commands, to the munin-limits log file (Deb#301196). * Node: munin-node didn't treat default_plugin_user properly (Deb#295366). * Node: munin-node-configure now properly respect user plugins. * Plugins: sort drives in generic/hddtemp_smartcl (SF#1174847). * Plugins: linux/if* now treats ra* interfaces as wireless. * Plugins: minor bugfix in generic/bind9. * Plugins: generic/sendmail_mailqueue handles bigger queues (fix by Mickey Everts). * Plugins: Made contrib-plugins generic/amavis and generic/mailstats more secure. * Plugins: Minor bugfix in linux/if_, with info fields. * Plugins: Added warning note in linux/if_ output. * Plugins: generic/smart_ exit value no longer triggers criticals. * Plugins: linux/df* now ignores bind mounts. * Plugins: Added plugin generic/snmp__cpuload. * Plugins: generic/amavis now has configurable logfile location (Deb#296533). * Plugins: linux/irqstats should no longer fail on some systems (Deb#296452). * Plugins: modified graph_args of generic/apache_processes, to work around an rrdtool bug (Deb#296528). * Plugins: All plugins using logtail now properly detect its format (Deb#297628). * Plugins: sunos/memory repaired (SF#1143610). * Plugins: Made linux/if_ work with more versions of iwlist (SF#1150954). * Plugins: generic/mailman now handles regular mailman (Deb#297904). * Plugins: New contrib plugin generic/mbmon_ from Arne Schwabe. * Plugins: Made linux/df work properly with tmpfs and devmapper (Deb#298442). * Plugins: Thanks to Stephen Gran, generic/exim_mailstats now graphs rejects (Deb#295799). * Plugins: Only run 'exim -bpr' once in generic/exim_mailqueue_alt, and use only awk to process the data. * Plugins: Make linux/sensors_ handle multiline output better (Deb#300690). * Plugins: generic/postfix_mailstats now treats reject-lines better (Deb#302220). * Docs: Added man page for munin-node-configure-snmp. * Docs: Brushed up the comments in the default munin.conf a bit (Deb#294060). * Docs: Fixed erronous cgiurl docs (SF#1159722). * Installation: Create CIGdir if nonexistent. munin (1.3.1) * Main: Fix bug with calculation of "Avg:" field when using graph_sums. * Main: Make munin-limits work properly with perl <5.8 (SF#1109039). * Main: Bugfix in munin-cgi-graph with hostnames including - (SF#1111510). * Main: Added category_order, to complete the *_order options. * Main/Node: Added support for TLS. * Plugin: linux/users now has proper hashbang. * Plugin: Bugfix in generic/sendmail_mail* autoconf. * Plugin: Changed default log for generic/postfix_mailstats from syslog to mail.log. Downgraded it from auto to manual. (Deb#291720). * Plugin: Made generic/amavis autodetect logtail format (Deb#284638, Deb#288395). * Plugins: generic/named probes for more log files before giving up (Deb#291849). * Plugins: New wildcard plugin generic/courier_ by Micah Anderson (Deb#291854). * Plugins: New plugin generic/perdition by Micah Anderson (Deb#291855). * Plugins: Plugin generic/squid_cache now handles multiple caches (Deb#288579). * Plugins: Improved graph_title of generic/postfix_* (Deb#292083). * Plugins: Turn on graph_scale for generic/postfix_mailvolume. * Plugins: Make generic/postfix_mailstats catch more formats (Deb#292110). * Plugins: Added plugin generic/hddtemp_smartctl, made by Lupe Christoph. Made it the default hddtemp plugin. * Plugin: Added madwifi support to linux/if_* plugins. * Installation: Make the single python plugin use @@PYTHON@@ again. * Docs: Documented minimum requirements for use of graph_sums (SF#1109040). munin (1.3.0) * Main: Allow dots in PNG paths (patch by Jacques Caruso). * Main: Properly size table below graphs in service-view. * Main: Fix bug which lead to some graphs failing with STACK error. * Main: Added limit message option "strtrunc". * Main: "contacts" can now be set to "none". * Main: Bugfix with graphs using both "graph_sums" and data aliases. * Main: Tables in service-view now sorted according to graph_order. * Main: Tables in service-view now containt the correct "Type" when using data aliases. * Plugins: generic/sendmail_mail{stats,traffic} updated. * Plugins: Made generic/samba more portable (fixes by Nicolas Stransky). * Plugins: Fixed typo in generic/loggrep breaking implicit labeling. * Plugins: Bugfix in generic/amavis. munin (1.1.9) * Main: Optimised munin-cgi-graph a bit. * Main: Internal name (for use in munin.conf) added to the table in service view. * Main: Added option cgiurl_graph. * Main: Bugfix when using long labels and CGI graphing. * Node: Fix bug when encountering strange environment (Deb#285173). * Node: Remove hardcoding of default user/group to run the plugin as (SF#1083251). * Packaging: Expand man-page macros properly on man-page generation (Deb#286399). * Plugin: Added plugin generic/dhcpd3, created by Rune N. Skillingstad. * Plugin: Patch generic/bind9 to report "unnamed" as "other" (Nicolas Stransky). * Plugin: linux/apt* has been forced to LANG=C, to get predictable output. * Plugin: Removed hardcoded host_name in linux/cps_. * Plugin: Added plugin linux/users, created by Michael Kaiser. * Plugin: Cleaned up generic/samba a bit (SF#1087961). munin (1.1.8) * Munin-limits: Log less noise. * Munin-limits: Notify correctly in all situations. * Munin-graph: Treat CDEFs in fields with long names properly. * Munin-graph: Log an illegal STACK in the first field better (SF#1081903). * Munin-run: Now behaves properly when running invalid plugins (SF#1074242). * Munin-node: host_name in plugin-conf.d now overrides plugin output. * Munin-graph: Added "graph_period" option, to make "graph_sums" usable. * Munin-update: Fix bug when setting min to 0. * Munin-node-configure: Fix bug with underscore in wildcard plugins. * Plugin: New version of generic/smart_ by plugin creator (Nicolas Stransky) (SF#1072459). * Plugin: generic/uptime was re-classified as linux/uptime (SF#1074576). * Plugin: Renamed generic/folding@home to generic/foldingathome (SF#1074241). * Plugin: Modify generic/ping_ so it's compatible with Solaris ping (SF#1074545). * Plugin: Fixed broken autoconf in generic/sendmail_mailtraffic (SF#1074528). * Plugin: Patched sunos/cpu to work on Solaris 9 (SF#1077899). * Plugin: Major improvements to sunos/io_ops,bytes,busy, by Lupe Christoph (SF#1077898). * Plugin: Portability enhancementes to sunos/fs_df (SF#1077903). * Plugin: linux/fw_forwarded_local now initialises properly (Deb#284673). * Plugin: Added more sensible autoconf to generic/sybase_space. * Plugin: Added more sensible autoconf to generic/munin_graph,munin_update. * Plugin: Added two new plugins contributed by Jacques Caruso, generic/exim_mailqueue_alt and generic/mailscanner. * Plugin: New version of generic/pm3users_ by plugin creator Jacques Caruso. * Plugin: generic/bind9 now handles syslog format as well (by xavier). * Plugin: Two new contrib plugins generic/foldingathome_*, by xavier. * Plugin: generic/named a bit more portable (by Will Froning). * Plugin: Sort fields in df*-plugins alphabetically. * Plugin: Added plugin generic/bind9_rndc, by Facq Laurent. * Plugin: Two network ups tool plugins (generic/nut_*) contributed by xavier. * Plugin: Added plugin generic/mhttping by Greg Connor. * Plugin: linux/cps_ plugin now groks high numbers. munin (1.1.7) * Allow floating point values in warning/critical limits. * Bugfix in munin-html, when using groups/host names with more than one dot. * Updated RPM package creation with regards to the 1.2 series. * Created RPM package creation for SuSE with regards to the 1.2 series. * Added --stdout-option to all programs (SF#1073148). * Log updates of nonexisting fields better (SF#1073172). * Force plugin linux/apt to use C locale (SF#1072470). munin (1.1.6) * Failsafe entry for @@PYTHON@@. * Bugfix in munin-limits (it didn't work properly). * Added separate makefile target to take care of SNMP-only items. munin (1.1.5) - Munin main package: * Treat long field names properly. Sadly, this will lead to data loss for fields that earlier had their field names truncated. :-( * Small interface improvements in the HTML output. * Changes of min and max values now causes corresponding changes in the RRD files. * Added new option "graph_sums" which creates summarised graphs. * Munin-update adapts to field type changes (loss-free conversion from COUNTER->DERIVE et al.) * Added new config option "local_address", to specify which local address outgoing connections (from munin-update) should be used. * Added "comparison" views, to view a whole hostgroup at once. * Bugfix in munin-limits. * Draw min/max ranges in all graphs with only one visible field. * Replaced notification system. Munin can now send warning/critical messages to whatever process/file that is needed. * Removed the munin-nagios program, as it is no longer needed. * Use column headers for min/max/cur/avg if label is too long. * Minor template modifications, so the output HTML validates correctly (SF#1039850). * Added new field option "line", which draws HRULEs. - Munin-node: * The node now changes dir to /, to make sure it's in a directory readable by all users. * Added per-plugin timeouts, contributed by Chan Wilson (SF#881044). - Plugins * SNMP plugin "df" properly strips the label and serial number from Windows drive labels. * SNMP plugins now honour the "host" environment variable if they can't deduce the hostname from $0. * Use @@PERL@@ in all perl shebang lines. * Added new SNMP plugins for sensors, reading info from System-Informant on Windows boxes. * New wildcard plugin generic/ping_ to graph ping times. * Replace "rpc" field with "total" in NFS and NFSD plugins. * Added new plugin linux/apt_all, contributed by xavier. * Added new plugins generic/courier_mta*, contributed by Rune N. Skillingstad. * Plugin generic/acpi now autodetects even if the acpi version does not contain the acpi_available program. * Added plugin linux/irqstast, showing individual interrupt rates. * Fixed typo in linux/fw_forwarded_local (Deb#275535). * Fixed typo in linux/fw_packets (Deb#275537). * Added SNMP plugins for temperature and fan info provided by the Fujitsu Siemens ServerView agents. * Adapt linux/apt plugin to work properly with Debian testing/unstable (patch from Rune N. Skillingstad). * Added new plugin generic/apc_nis to monitor APC UPS, contributed by xavier. * Made graph_title a parameter for generic/exim_mailqueue (patch by Torstein T. Svendsen, SF#1060834). * Fixed typo in generic/sendmail_mailstats (patch by Lupe Christoph, SF#1058128). * Applied patch from Torstein T. Svendsen to generic/exim_mailstats, to handle logfiles with timestamps in the name (SF#1055214). * Applied patch from Nicolas Stransky to generic/hddtemp, to fetch temp more elegantly (SF#1052845). * Added new plugin linux/hddtemp_smartctl, made by Peter Gervai (SF#1032727). * Fixed linux/if_(err_) braindamage affecting hosts with vlans or multi-digit interface numbers. * Added wildcard plugin generic/smart_, contributed by Nicolas Stransky. * Added plugin generic/loggrep for generic log grepping. * Fixed bug in generic/sendmail_mailqueue, when queue is empty. * Fixed bug in generic/hddtemp2, patch by arturaz (SF#1037002). * Added new plugin linux/forks, to graph forks per second. * The linux/iostat plugin now ignores devices without traffic (Deb#267195). * Changed a lot of plugins so they use DERIVE instead of COUNTER. * Upped generic/ntp_ to auto family. * Upped generic/sendmail_mailstats and generic/sendmail_mailtraffic to auto family. * Fixed linux/sensors_ plugin to report warning and critical values for temperatures and voltages if 'sensors' reports them. - Installation * Changed variable name of ARCH to OSTYPE, to avoid name crash on newer FreeBSDs (SF#1068238). * Added variable PYTHON, as we now have our first Python plugin. munin (1.1.4) * Better error handling when fetching data from RRD files. * Fixed bug in munin-graph when choosing colours (Deb#267185). * Added plugin linux/df_abs * Sanitise incoming fieldnames a bit better. * Small layout improvements. * Added new options "graph_height" and "graph_width", to beter control size. * Added support for SNMP probing. * SNMP plugin "if" now accepts PPP interfaces as well as ethernet interfaces. * SNMP plugin "df" now understands Windows SNMP daemon. munin (1.1.3) * Applied patch from Jacques Caruso to make HTML output standards compliant. * Munin-graph: fixed bug when combining graph_order aliases with "normal" fields, as reported by Jacques Caruso. * Plugin bugfixes from Jacques Caruso, in linux/iostat_ios and generic/ipacng. * Munin-nagios: Only state number of OKs when something is in warning or critical state. * Munin-graph: Added 'graph_sources' option. This is (in effect) the same as graph_order, but with a default of 'graph no'. * Applied patch from Matthieu Lochegnies to munin-graph, and fixed the same problem elsewhere in the code (Deb#250982, SF#924561). * Force LANG/LC_ALL=C in generic/hddtemp2, to remove problems in parsing of hddtemp output (Deb#253497). * Force LANG/LC_ALL=C in linux/sensors_, to remove problems in parsing of sensors output (SF#972749, SF#972748, Deb#255312). * linux/sensors_temp now understand temp lines without hyst or max settings (Deb#256380). * Made linux/sensors_volt work with negative voltages (Deb#256734). * Made generic/hddtemp2 understand environment variables with quotes (Deb#265022). * Added plugin generic/uptime contributed by Nicolas Salles (SF#998665). * Added plugin generic/hddtempd contributed by Stein Magnus Jodal (SF#958762). munin (1.1.2) * The server programs now open the log file at an earlier point. * Munin-limits added to distro. * Warnings and criticals now show up in nodeview and overview. * Makefile no longer stopping when complaining about htmldoc errors * Added contributed plugin generic/cupsys_pages, contributed by Rune N. Skillingstad. * Minor documentation bugfix. * Added automatic "graph no" to negative field when using negatives. * Added options to munin-graph to skip locking and stats generation. * Added --cron option to munin-graph. This is used internally for special case checking when run from cron. * Added dist-directory for Solaris packages. * Created munin-cgi-graph, which creates dynamic graphs. * Added munin.conf option "graph_strategy (cgi|cron)" which defaults to cron. * Bugfix in cdef calculations. munin (1.1.1) * Added a table in the service view page, with information about each field. * Added plugin options "graph_info" and "<field>.info", which can be used to describe the graph/fields in the above mentioned table. * Bugfix in the linux/df_inode plugin, regarding filesystems withouth inodes. * Added warning and critical statuses to the info table in the service view. * Added "info"-fields to linux/cpu and linux/load plugins, to demonstrate how it works. * Linux/ip_ wildcard plugin contributed by Mathy Vanvoorden (SF#954851). * Added a definition file (definitions.html) to the server distribution. * Use "sed 1d" instead of "tail +2" in df plugins (patch by Olivier Delhomme). * Tuned cdef-code to make it work properly after clean-up. * Added "graph_category" option, to categorise plugins. * Set categories to most of the plugins. munin (1.1.0) * Bug regarding logo namechange from logo.gif to logo.png, when installing. * Allow/deny in munin-node can now be configured per plugin, in addition to "globally" for the whole node. * "Upped" som plugins from contrib/manual to manual or auto. * Code cleanups. munin (1.0.0pre4) * Munin-update now properly ignores node with "update no". * The generic/apache_* plugins now have defined max values. * New plugins generic/{sendmail*,amavis,apc_envunit} contributed by Xavier Redon. * Turned off scaling of values for cpu-graphs (no more nano-percentages). * New plugin linux/iostat_ios to graph the number of I/O operations. Contributed by Per A. Buer. * Added user configuration for generic/postfix* (SF#895680). * Fixed links in HTML templates (Deb#236792). * Fixed broken autoconf in apache-* plugins (Deb#236144). * Fixed bug in mysql-plugins (Deb#233762). * Fixed a problem in the node when running as a non-root user and using sudo to run the plugins (Deb#236694). * Clarified the vlabels in the apache-plugins (Deb#238594). * Patched temp and voltage parts of linux-pugin sensors_* with better regexp (Deb#245289, SF#906868). * Changed default ARCH variable in Makefile.config, to support older tr-s (SF#898814). * Make "graph_scale no" affect y-axis as well as numbers below the graph (Deb#236834). * New SNMP plugins: df, if_err, processes, fc_if (fibre-channel), fc_if_err, users, load. * Cleaned up Solaris plugins (SF#944389, ++) * New logo by Bianca Pfingsten, sponsored by Mediahaus Biering Grafischer Betrieb GmbH. (Thanks :-) * Made solaris plugin fs_df work without GNU df (SF#944389). * New solaris plugin temperature, by Richard van den Berg. * Two new linux firewall plugins; fw_packets (by S. Banerian), fw_conntrack (by Nicolai Langfeldt), and fw_forwarded_local (by Xavier). * Modified sunos/cpu, linux/cpu and freebsd/cpu to take "scaleto100"- parameter. * Added patch to contrib-plugin linux/nfsd, to graph rpc count (Alexandre Dupouy). * Added plugin linux/nfs_client, contributed by Alexandre Dupouy. * Added plugin ipac-ng, contributed by Arturas Slajus. * Added plugin hddtemp2, contributred by Andrew Radke, modified by Lupe Christoph. * Added Folding@home plugin, contributed by Xavier. * Fixed problem with sunos/memory, when memory was reported in gigabytes (SF#930964) * New plugin (pm3users) and a bunch of patches from Jacques Caruso. munin (1.0.0pre3) * Munin-graph had a perl 5.005_03 compatabilty problem, which slipped through to version pre2. * Added mkdir to install-man target. (SF#888545) * Added better logging of plugin failures in the node. (SF#881045) * Make install: Fix problems with systems that do not have getent. (SF#881046) * Remove dependency on pgrep (use process groups instead). (SF#881049) * Documentation updates. * Make the iostat plugin work properly. * Munin-graph now escapes ':' in labels properly. * Fixed bug in munin-graph where it caused a flood of cron-mail. * Linux/iostat now shows only disks also on machines without devfs. * Generic/apache-plugins have been modified to properly to report the correct autoconf value. Also, bugfixes in _processes and _volume. * Added new wildcard plugin linux/sensors_ that replaces the i2c plugins (SF#890952). * Made client timeouts configurable (not per plugin). * Improved timeout-handling in node (Deb#224480). munin (1.0.0pre2) * Plugin mysql_queries bugfix from Dagfinn I. Mannsker (SF#876443, SF#865125). * FreeBSD-plugin load bugfix from Robert Lindgren (SF#865928). * Fixed Node/run bug when changing groups. * Added support for multiple groups to run the plugin as. * Added support for optional groups. * Munin-html: Added support for domain_order. * Fixed pod typos patched by Lupe Christoph (SF#884092) * Made Munin compatible with perl 5.005_03 (patch by Lupe Christoph) (SF#884622) * Removed sunos/io_-plugin (SF#882357) * Bugfix, apache_processes now takes port numbers into account. (SF#882263) * Changed wildcard plugin ps_ so it can use "env.regex" in plugin-conf.d/ (SF#882131) * Made plugins apache_* compatible with older versions of LWP::UserAgent (SF#881411). * Bugfix in plugin mysql_queries - insertions were no longer graphed. (SF#881483). * Disabled plugins df and df_inode on Solaris (SF#882274). * Make vmstat plugin more portable (SF#882352). * Moved generic netstat to linux-dir, as it is too spesific. Added Solaris version of the plugin as well. (SF#882354) * Fixed bad debug output (forgotten linebreaks) in munin-node-configure (SF#882385). * Generic plugins now use printf instead of echo -n, as this is more portable (SF#885564) * Added a new plugin generic/multips to count several procs in one graph. (SF#885579) * More timeouts in munin-update (Deb#222674). * Added max value to generic/vmstat plugin (Deb#225489). * Bugfix in install-doc Makefile target (SF#884074). * Code tidying in munin-graph (SF#884625). * Added generic plugins ntp_ and ntp_states to manual family (SF#887000). * Bugfix; munin-html wrote bad html, as reported by Adam Heath. (Deb#230322) * Rewrite of linux/iostat by Mike Fedyk (Deb##223373,224113). * Linux/cpu now graphs all values on a 2.6 kernel (Deb#227946). * Added new plugins linux/nfsd and acpi, contributed by Alexandre Dupouy. munin (1.0.0pre1) * Renamed from LRRD to Munin. lrrd (0.9.9r6) * Added BSDish install-sh to make the installation work on both BSD and SysV variants (SF#840744). * Plugin linux/iostat modified. Now runs on 2.6, and now "mirrors" i/o like eth* et al. (Deb#224113, Deb#223373) * Lrrd-client: Don't try to change uid/gid if not running as root. (Deb#224300) * Lrrd-graph: Only log graphing errors (== less cron-mail). * Plugin linux/memory has been improved greatly by Mike Fedyk (Deb#223346) * Some plugins fail more gracefully when using uninstalled perl modules. * Lrrd-client-configure now prints the reason for suggesting to not use a plugin, as long as the plugins gives one. * Lrrd-client: Do a fake clean of the environment because of the taint checking. * Lrrd-client: added configuration option "ignore_file", which takes regex for files to ignore (e.g. rpmnew/save) (Deb#224265). * New plugin: Alexandre Dupouy contributed "hddtemp". * New plugin: Nicolai Langfeldt contributed "bind9". * Lrrd-server: dots supported in plugin names. * New plugin: snmp wildcard plugin for interface graphing. lrrd (0.9.9r5) * lrrd-nagios: enable/disable notifications. * Plugin: mysq_isam_space_ bugfix when printing "value". * lrrd-client/lrrd-run: Two bugfixes. * LRRD.pm: Change \1 to $1 to make lrrd-update shut up. * lrrd-update: Fix bug with timeout handling of children. lrrd (0.9.9r4) * Plugin: changes from Mike Fedyk (Deb#222838, Deb#222841). * lrrd-cron: Check if files are executable before runinng (Deb#221691). * lrrd-update: Added max_processes variable. * lrrd-update: No more zombies. * lrrd-update: Better handling of dying children and timeouts. (Deb#222674) lrrd (0.9.9r3) * New plugins (contrib): postfix_mailqueue and named contributed by Nicolai Langfeldt. * New plugins (contrib): postfix_mailstats and postfix_mailvolume. * LRRD.pm: make sure only legal configuration lines are written to datafile. * lrrd-html: Minor bugfixes (fixes by Chan Wilson). * lrrd-graph: Bugfix when aliasing fields (reported by Chan Wilson). lrrd (0.9.9r2) * LRRD.pm: minor bugfix - no noise about "extinfo". * lrrd-graph: A couple of minor bugfixes. lrrd (0.9.9) * New plugin: sybase_space. Keeps track of sybase database space usage. * New plugin: psu_ wildcard plugin, by Andras Korn (Deb#214210) * Lrrd-client: addressed uid/gid-issues from Deb#214277 * Lrrd-update: client port number now configurable (Deb#214114) * Squid-plugins: now rewritten to use new config format (Deb#214186) * Lrrd-update: each node now has its own lockfile. * Squid-plugins: Modified them to autoconf. Prettier graphs. Now auto. * Lrrd-client: Don't mess with environment. Not my problem. * Lrrd-client: host_name can now be set in the plugin configuration. * Lrrd-client: plugin configuration format changed slightly. * Lrrd-client: added parameter (conffile and cli) to turn paranoia on/off. Default is now off. (Deb#214186, Deb#216401) * New plugin for linux: entropy, graphs available entropy. * Lrrd-client-configure: Remove "use" of Config::General (Deb#216176) * SunOS-plugins: Enhancement submitted by Lupe Christoph. (SF#825937) * SunOS-portability: Various bugfixes reported by Lupe Cristoph. (SF#825909, SF#825911, SF#825926, SF#825936, SF#826670, SF#826356) * FreeBSD-plugins: Bugfixing patches by Lupe Cristoph applied. (SF#826352, SF#837014) * Lrrd-server: New format for server.conf. * Mysql-plugins: Make mysqladmin/mysqlshow locations configurable. (SF#825974) * Plugins: New plugins by Lupe Cristoph (SF#834529, SF#826002, SF#835208) * Lrrd-server: Only require installed Config::General if using old config format. * Mysql-queries-plugin: rewritten by Per A. Buer. * Lrrd-nagios: Be nice to Nagios, don't DOS it with thousands of services. * Lrrd-client-configure: Check what to do with new plugins when upgrading. * Plugins: contributions by Finn-Arne Johansen (SF#839210, SF#839212, SF#839209, SF#842703, SF#845692) * Lrrd-update: More graceful handling of dead hosts. (SF#841345) * Plugins: graph_noscale redefined to graph_scale. * New plugin surfboard contributed by Andrew Ryder. lrrd (0.9.8) jo: * Plugin bugfix: Applied suggestion from Antti Salmela considering plugins df and df_inode (SF#800984) * Lrrd-update now forks off one process for each host, doing the data downloading in parallel. * Network timeout made configurable via both configuration file, and command-line parameter. default increased to 180 seconds. * Command-line option and configuration file option for not forking added. * Added command-line option to show log lines on stdout as well. * Lrrd-update bugfix: Move socket and pidfile to proper place. (Deb#203173) * Lrrd-update bugfix: Now handles corrupt datafile properly. (Deb#202191) * Plugin bugfix: mysql_isam_space now behaves properly when MySQL is down. (Applied patch from Nicolas Francois resolving Deb#202637) * Lrrd-client now runs with perl -T (taint checking). * Lrrd-html bugfix. (Deb#205999) * Lrrd-update bugfix, patch from Torstein Svendsen, alarm/sigalrm was forgotten on a network read. (Deb#209329) * Lrrd-update bugfix. Move all possible sigalrm-calls into eval-blocks, to avoid hangs and segfaults. _Really_ fixes bug Deb#209329. * Lrrd-client now drops privileges when running a plugin. The plugin is run as user nobody, group nogroup/nobody as default. This can be overridden in .../client.d/<plugin>.auth. * Mysql-plugins can now use a config file. (Deb#202643) * Bugfix in mysq_isam_space_ plugin. (Deb#202639) * Plugins for FreeBSD 4.8 contributed by Patrick Domack (SF#802895) * Plugin "swap" on Linux modified so it works on 2.6 as well as 2.4. * Lrrd-client now uses conf/auth-file in .../lrrd/client-conf.d/. Passes config options to clients via environment. * Lrrd-graph bugfix: Cur/Min shown as 0 when using special_*. * Lrrd-update now has "use_default_name"-option, for use when fetching data from a client which may change name. * Lrrd-plugin "memory" ported to perl. (Deb#205019) * Added "--version" option. * Added "graph_total" option, for summarising stacks * Lrrd-graph now understands "graph_noscale true/yes/1", which drops scaling of Cur/Min/Avg/Max numbers. * New client program "lrrd-run" to run a plugin as the correct user. * Lrrd-client minor bugfix; should now get ARGV after HUP. * Lrrd-client-configure completely rewritten. lrrd (0.9.7) tore: * Applied a patch from Don Armstrong which makes the server ignore invalid pidfiles. * Massive changes in the Makefile to allow for more flexible installs. * Hardcode as little as possible. Use @@foo@@ in the scripts, which will be replaced with whatever 'foo' is set to in Makefile.config. * Added some AIX plugins contributed by Mike Discenza. * Merged the client and server changelogs into this one. * Improved vlabel and title in the port_ wildcard plugin. lrrd (0.9.6) tore: * First upload to the Debian archive, had to create an .orig.tar.gz. lrrd (0.9.5-2) * Bugfix: Resolved running-as-root-issue. (#695163) * Bugfix: Resolved lrrd-nagios --force issue (#701671) * Bugfix: Resolved lrrd-nagios --removeok --host issue (#701671) * Bugfix: Resolved lrrd-html logo.gif and style.css-issue (#693939) lrrd (0.9.5-1) * Small bugfixes * Bugfix: plugins lrrd_* now treats hostnames with dashes correctly (#727074) lrrd (0.9.3-2) * lrrd-nagios now has a longer timeout. * lrrd-html now creates much prettier html-pages. * Pod-documentation for /usr/lib/server/lrrd-*, /usr/sbin/lrrd-cron, and /etc/lrrd/server.conf * lrrd-graph now understands <field>_special_sum and _stack. * lrrd-graph now has extended syntax on graph_order-lines. * Field type represented in filename of rrd-file. * All fields in the .rrd-files now have the same name. * lrrd-graph now reuses colours when there are a lot of fields. * lrrd-nagios now reuses send_nsca handles. * lrrd-update can now handle .extinfo-fields. * lrrd-nagios can now handle .extinfo-fields. * Bugfixes, continuing Tore's movment of files * Parameterization of lrrd-* * Installation script for tarball. lrrd (0.9.3-1) * New configtype * Timeouts on server lrrd (0.9.2-4) * Added new node- and service-level option "update", which will replace "fetch_data". * Added new field-level option "graph", which will replace "skipdraw". * Some plugins now have config-files: exim_mail* * ps-plugin made faster (use pgrep instead of ps). * Bugfix in 'iostat'-plugin. * Added 'suggest' and 'autoconf'-parameters to the plugins. * Removed startup check for lrrd-client on '<plugin> config'-output. * "apt"-plugin rewritten for perl. It now uses .extinfo-fields. * Install script for tarball. * lrrd-client now understands --config --help --debug (then latter for later use). * Created pod-documentation for lrrd-client and client.conf. lrrd (0.9.2-3) * Bugfix; lrrd-nagios didn't treat 0-limits properly. lrrd (0.9.2-2) * Added graph_vlabel-option. * lrrd-nagios: bugfix when specifying only a minimum warning or critical value. * Proper timeouting also done when 'config'-command issued. * Some clients now use 'graph_vlabel'-option. * Comment changes in some plugins * Bugfix in plugin: 'iostat config' lrrd (0.9.2-1) * Lazy-option to lrrd-graph. Graphs will only be drawn when needed. * Cleaned up the timeout code a bit. lrrd (0.9.1-5) * Attempt to set exim-variables in the exim_mail*-plugins at install-time, in an attempt to avoid timeouts on high-load systems. * Properly reap plugins that timeout (and their children). lrrd (0.9.1-4) * Bugfix. Numbers with decimals (e.g. load) and "U" are now allowed. * Fixed bug in cronjob for apt lrrd (0.9.1-3) * Fixed small bug in lrrd-graph that caused some glitches in some graphs * Cronjob for apt lrrd (0.9.1-2) * Fixed a bug in lrrd-update that caused rrds not to be updated * Small bug in apt-client lrrd (0.9.1-1) * All networkservices converted from bytes (B) to bits (b) * More bugfixes * Added cronjob to go with apt-service lrrd (0.9-12) * Bugfixes in templates * More logging * More intelligence in lrrd-nagios * Cronjob to remove .ok files once a day lrrd (0.9-11) * Last small fixes with warning and critical-nagiosLast small fixes with warning and critical-nagios * Skipped a version to sync with server * Included warning and critical on som of the services lrrd (0.9-10) * Fixed logging and logrotate lrrd (0.9-9) * More Nagios Support, now extracted into lrrd-nagios * Fixed logging + logrotate lrrd (0.9-8) * Nagios-support * Do not depend on libnet-io on woody (included in perl5.6) lrrd (0.9-7) * Fixing string under 19 characters lrrd (0.9-6) * We have to substr the other way * Upping version number lrrd (0.9-5) * Bugfixes. New configfile * Bugfixes in df-services added -P for posix standard lrrd (0.9-4) * Total rewrite of client * Bugfixes. Memory service gargled * Bugfixes. mailstat service gargled lrrd (0.9-3) * Adapt to new version of client * Bugfixes to clients and a new configfile lrrd (0.9-2) * Fixed host_name lrrd (0.9-1) * The new version lrrd (0.7-1) * Total rewrite. Much prettier. Perl er brukt. * Bugfix in postinst (make_html when upgrading). * Lots of new lrrd-d-modules. Tuning and fixing. * Debconf is out. Added searching for old-name config-files. lrrd (0.6-8) * Renamed mrtgd til lrrd. * Daemon now runs lrrd.d-scripts properly. * Added modules: load memory lrrd (0.6-7) * make_graph has proper colours. * make_html creates a nicer index.html. * Debconf in use. * Minor bugfixes: init.d-restart lrrd (0.6-5) * make_html makes sure there's an index.html. * Bugfix. Config-file now read earlier. lrrd (0.6-4) * Several bugfixes. * Cron-entry now uses logfiles. * Added config-file. lrrd (0.6-3) * Added debian-dependencies. lrrd (0.6-2) * Added monitoring for apache httpd daemons. * The daemon now backgrounds itself. * The init.d-script now "stop"s. lrrd (0.6-1) * Slightly tested. Going out. lrrd (0.5-4) * Now ready for production environments. lrrd (0.5-3) * Quite a few changes. Beginning to get ready for release. lrrd (0.5-2) * New test. lrrd (0.5-1) * Initial Release. ��������������������������������������������������������������������������������������������������������������������������munin-2.0.75/Checklist������������������������������������������������������������������������������0000664�0000000�0000000�00000001501�14516145741�0014574�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Git release The tag used here is "2.0.1", exchange this with a current version. * Update ChangeLog new="2.0.1"; old=$(git describe --abbrev=0) header=$(printf '\nmunin-%s, %s\n%s' "$new" "$(date +%Y-%m-%d)" "$(sed -n 4,14p ChangeLog)") shortlog=$(git log --pretty=short --no-merges "${old}.." | git shortlog) printf "%s\n\n%s\n\n" "$header" "$shortlog" | sed -i "1r /dev/stdin" ChangeLog * Commit the updated changelog and tag this release git commit -m "$new" -- ChangeLog git tag -s "$new" -m "$new" # Make tarball from tag git checkout 2.0.1 make tar OR make tar-signed # Propaganda * Broadcast the good news: 1. Upload the release tar and signature: make tar-upload 2. Update our website 3. Send an email to the ML 4. Update the version in the topic of the IRC channel �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/HACKING.pod����������������������������������������������������������������������������0000664�0000000�0000000�00000005062�14516145741�0014516�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# this is in pod format (try `perldoc HACKING.pod`) =pod =head1 NAME HACKING.pod - contributing to Munin =head1 SYNOPSIS This is the guide for Munin internals contributors (developers, testers, documenters.) If you are looking for more information on how to I<use> Munin you probably want L<http://munin-monitoring.org/wiki/Documentation> instead. The munin code is marked by years on the battle front. You will therefore find code that deviates from the guidelines defined here. However, all new code should be made to comply. These guidelines are an RFC for the time being and are of course negotiable. =head1 GETTING STARTED In the dev_script directory you will find scripts that are meant to be useful for developing. Most of them are tools for creating and using munin in a sandbox. =head2 THE SANDBOX =over =item B<install> To make a clean rebuild of the sandbox ./dev_scripts/install 1 To just install the latest changes ./dev_scripts/install =item B<enable/disable tls> To test TLS, you can enable a paranoid TLS configuration by running: ./dev_scripts/enable_tls And disable it with: ./dev_scripts/disable_tls =item B<start/stop munin-node> ./dev_scripts/start_munin-node [munin-node params ...] And ./dev_scripts/stop_munin-node To do both: ./dev_scripts/restart_munin-node [munin-node params ...] =item B<query_munin_node> Use this command to query the munin-node directly: ./dev_scripts/query_munin_node list =item B<run> To run Munin master programs (munin-update, munin-html, munin-cron, etc) use the run command. ./dev_scripts/run CMD [CMD args ...] =back =head1 PUNCTUATION VARIABLES Don't use punctuation variables (see Perl Best Practices page 79.) use English qw(-no_match_vars); We'll add an exception for $_, and $! as they should be fairly widely recognized. =head1 FORMATTING =head2 perltidy FIX =head1 TESTS AND COVERAGE Use test-driven devolopment. In node, server, or common: perl Build.PL ./Build test =head1 EXCEPTION HANDLING Currently there is no unified approach to handling exceptions. Use L<Carp>? use Carp; croak("Foo happened!"); confess("Foo happened!"); # With stack trace Exceptions are caught with an eval: eval { # Exceptionally scary code }; if ($EVAL_ERROR) { # Handle exception } =head1 DOCUMENTATION The API documentation is embedded as POD in the code. See L<perlpod> for more on POD. More is on L<http://munin-monitoring.org/wiki/Documentation> =head2 WRITING The POD should be defined all in one place. For plugins you place it at the top, else at the bottom. =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/INSTALL��������������������������������������������������������������������������������0000664�0000000�0000000�00000024163�14516145741�0014002�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Munin installation requirements =============================== 2.0 now requires IPv6 *libraries*, but IPv6 doesn't need to be configured. If you plan to really *use* IPv6, please read the note at the end. On all hosts: - A reasonable Perl 5 (ie: at least 5.8) - GNU Make - do not attempt to use any other make (build time only) - Module::Build - this is part of perl 5.10, for earlier Perls it must be installed (build time only) - The perl module Time::HiRes - For TLS(SSL) to work you'll need Net::SSLeay everywhere Server: - A web server. Any web server supporting simple file service and CGI or Fast CGI should work. Apache HTTPD should be suitable. Also reported to be working is nginx and lighttpd, please contribute your configs. - RRD with Perl support - this means that RRDs.pm must be available and "perl -MRRDs -e ':;'" must run without errors. - Perl modules for server: - Time::HiRes - Storable - Digest::MD5, - HTML::Template - Text::Balanced - Params::Validate - TimeDate - Net::SSLeay if you want to use SSL/TLS - Getopt::Long - File::Copy::Recursive - CGI::Fast - IO::Socket::INET6 - Log::Log4perl 1.18 or later (which depends on - IPC::Shareable - Log::Dispatch - Log::Dispatch::FileRotate - MIME::Lite - Mail::Sender - Mail::Sendmail - MailTools) - Developers/packagers: For testing - Test::MockModule - Test::MockObject - Test::Pod::Coverage - Test::Perl::Critic 1.096 or later - Test::Exception - Directory::Scratch (err, wherefrom?) - As well as all the modules needed on a node - most servers are also nodes. Node: - Perl modules: Net::Server, Net::Server::Fork, Time::HiRes - Perl module Net::SNMP for SNMP autoconfiguration and plugins - For SNMPv3: Perl modules Crypt::DES, Digest::SHA1, Digest::HMAC - Net::SSLeay if you want to use SSL/TLS - Perl modules for plugins: Depends on the plugins you want to use, but not many. - If you want to use Java JMX plugins to monitor a Java app: Sun Java 5+ compiler (a basic JDK Standard Edition from java.sun.com or your package system). If you're missing some Perl modules and they are not in your package system it's recommended to use the cpan shell (as root) to install the needed ones. There is a short cpan shell section at the bottom of this file if you do not know how to use it. Please note that BSD and Linux OSes such as Red Hat Enterprise Linux (and CentOS), Fedora, Debian, Ubuntu, SuSE and others will have quite a few of these packages available to install with their standard install tools so there is no need to install from the source distribution unless you want a Munin release that is not packaged. Installing ========== NOTE!!!! If you're using NFS please note that the "make install" process is slightly problematic in that it (Module::Build actually) writes files under $CWD. Since "make install" is usually run by root and root usually cannot write files on a NFS volume, this will fail. If you use NFS please install munin from /var/tmp, /tmp or some such to work around this. There are make targets for node, master, documentation and man files. Generally you want to install everything on the master, and just the node and plugiuns on the nodes. To install both master and node: - Review/edit Makefile.config to suit your needs. - Create the user "munin" and the group "munin" if these are not made automatically. The user needs no shell and no privileges. On most Linux systems the munin user's shell is the nologin shell (it has different paths on different systems - but the user still needs to be able to run cron jobs. - make (do _NOT_ do "make install" directly, there is a bug somewhere that will result in a very defective Munin::Common::Defaults to be installed). - make install - For graphing you have to use CGI now. You usually want to use FastCGI graphing unless you have a quite small munin and a quite fast server. More information is available at http://munin-monitoring.org/wiki/CgiHowto2 In your HTMLDIR you will now find a .htaccess file with two main features: * Password protection. Users/passwords are kept in CONFDIR/munin-httpasswd. Use htpasswd to create/modify users in normal Apache fashion. * Munin page expiry to refresh contents. This requires mod_expires to be enabled. NOTE: Both these features require the relevant AllowOverride statement in Apache. AuthConfig and Indexes, respectively. If you already have a .htaccess file in HTMLDIR it will not be overwritten. If you prefer that munin be openly available make the .htaccess file empty to avoid overwriting it later. - Review CONFDIR/munin.conf to set up some nodes. At least one node needs to have at least one functional plugin for HTML generation. - Create a cron-entry to run "munin-cron" as the user "munin" every 5 minutes. See build/resources for generic or build/dists for some OS/distribution-specific scripts. To install a node: - Edit Makefile.config to suit your needs. - Create the user and group "munin". The use can have a nologin shell. - make - make install-common-prime install-node-prime \ install-plugins-prime NOTE: This overwrites any existing plugins. - Decide which plugins to use. The quick auto-plug-and-play solution: munin-node-configure --shell --families=contrib,auto | sh -x - Review CONFDIR/munin-node.conf. Ensure that your munin-master can access it. - Start the node agent (as root) SBINDIR/munin-node. Restart it it it was already started. The node only discovers new plugins when it is restarted. You probably want to use an init-script instead and you might find a good one under build/dists or in the build/resources directory (maybe you need to edit the init script, check the given paths in the script you might use). If you want to use SSH to contact the node from the master (rather than over tcp port 4949) please refer to http://munin-monitoring.org/wiki/Native_ssh For further build alternatives, see Makefile. Notes about node plugins ======================== "make install-node-plugins" installs the plugins in LIBDIR (defined in Makefile.config). Put the ones you want to use (or better yet, create softlinks) in CONFDIR/plugins/ . An easy way to do this, is the program "munin-node-configure", using the parameter "--shell". It will then suggest commands to do this. Example to show plugins it would enable: munin-node-configure --suggest To enable those: munin-node-configure --shell | sh -x You can also just run munin-node-configure --shell and paste the commands you want into a shell. Some of the plugins (mysql, postgresql, snmp, ...) require some configuration to get running. Some example configuration files (plugins.conf) is found under the build/dists directory. Whenever the installed plugins changes, the node needs to be restarted. Also restart the node when you change it's configuration. Many OSes and releases thereof have different ways of gathering data. A lot of OSes still have none. If you create plugins for an OS/system which is not already in the package, please send us a copy of them, so we can add them for others to use. We'd also be happy if you sent us any new plugins on systems already in the package. Using CPAN shell ================ If your OS does not provide all the needed perl packages they can be intalled by a perl installation tool called CPAN-Shell. There is ample documentation about it on the web, but here is a brief tour. As root execute: # perl -MCPAN -e shell The first time you run this you are interviewed about various things. Answer the questions, you can probably answer blank on any you do not understand. You will then be presented with a CPAN prompt (cpan>) . From this prompt you type: cpan> install Time::HiRes You could also do it one at a time like this: # perl -MCPAN -e 'install Time::HiRes' Do the same for all modules needed. E.g., install Time:HiRes install Storable install Digest::MD5 install Text::Balanced and so on. If you need to install Munin on a host with no Internet access you can use CPAN shell on a host _with_ Internet access and use the "get" command to retrieve the needed modules. One problem: The dependencies of modules will change over time so the list above may not be correct 6 months after it was last updated. SO: If you want to make sure you get all the needed modules you can do a full install of munin on a Internet connected system and then transfer all the modules to the non-connected system after. All the modules that the CPAN shell retrieved can be found like this: # cd ~/.cpan/sources # find . -name '*.tar.gz ./authors/id/G/GA/GAAS/Digest-MD5-2.39.tar.gz ... Now just make sure the sources directory is empty before you begin. IPv6 ==== The basic problem with munin and ipv6 is that we use Net::Server and there has been no releases of Net::Server supporting IPv6 (pr. 2011-07-22). So we've found out that Debian has patches for IPv6 support and they're even claiming to be complete - and our testing agrees. We (janl) have submitted a new version of Net::Server to CPAN based on this. If you use Debian (or derivates) with Net::Server 0.99-2 or later or if you find Net::Server 0.99.6.1 or more recent in your CPAN or Distribution repository is probably supports IPv6 out of the box. In the mean time look in the contrib directory for our Net::Server.patch. You can apply this directly to your installed Net::Server perl modules - this is a bit dirty, and you may have to jigger the directory paths a bit, but it will work. If you use CPAN anyway then get Net::Server and apply the patch provided. If your Net::Server understands IPv6 and you have "host *" in your munin-node.conf file your munin will be listening on a dual ipv4/ipv6 socket and you can connect to it with "telnet ::1 4949" to test. You probably need to correct your allow list like so: allow \:\:1$ to actually allow this. If you keep an eye on munin-node.log then the cause of any problems should be obvious. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/Makefile�������������������������������������������������������������������������������0000664�0000000�0000000�00000050173�14516145741�0014411�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- makefile -*- # # Gnu make only. Seriously. # # Defaults/paths. Allows $(CONFIG) to be overridden by # make command line DEFAULTS = Makefile.config CONFIG = Makefile.config include $(DEFAULTS) ifneq ($(DEFAULTS),$(CONFIG)) include $(CONFIG) endif ifeq ($(JCVALID),yes) JAVA_BUILD=build-plugins-java JAVA_INSTALL=install-plugins-java JAVA_PLUGINS=plugins/node.d.java/* endif RELEASE := $(shell $(CURDIR)/getversion) INSTALL_PLUGINS ?= "auto manual contrib snmpauto" INSTALL := ./install-sh DIR := $(shell /bin/pwd | sed 's/^.*\///') INFILES := $(shell find . -name '*.in' | sed 's/\.\/\(.*\)\.in$$/build\/\1/') INFILES_MASTER := $(shell find master -name '*.in' | sed 's/\(.*\)\.in$$/build\/\1/') CLASSFILES := $(shell find plugins/javalib -name '*.java' | sed 's/\(.*\)\.java$$/build\/\1.class/') PLUGINS := $(wildcard plugins/node.d.$(OSTYPE)/* plugins/node.d/* $(JAVA_PLUGINS)) MANCENTER := "Munin Documentation" MAN8 := master/_bin/munin-update master/_bin/munin-limits master/_bin/munin-html master/_bin/munin-graph node/_bin/munin-get PODMAN8 := build/master/doc/munin-cron master/doc/munin master/doc/munin-check PODMAN5 := build/master/doc/munin.conf node/doc/munin-node.conf PYTHON_LINT_CALL ?= python3 -m flake8 CONFVAR_SUBSTITUTION_FILES = \ master/blib/libdoc/Munin\:\:Master\:\:HTMLOld.3pm \ master/blib/lib/Munin/Master/HTMLOld.pm \ node/blib/sbin/munin-node-configure \ node/blib/sbin/munin-node \ node/blib/sbin/munin-run \ build/doc/munin-node.conf.5 MAKEFILES := Makefile $(DEFAULTS) $(CONFIG) # TODO: remove this fallback code for "make v3.x" (up to Debian Wheezy / Ubuntu Trusty) as soon as # the CI supports a more modern distribution # (see https://docs.travis-ci.com/user/reference/overview/) # Make v3.x failed to handle colon escaping properly - thus we remove complicated filenames from # targets and dependencies. This may affect corner cases of dependency handling for the one file # above containing a colon. But in this case it is just about substituting paths in a # documentation file - thus we can live with this rare risk of incorrectness. ifeq ($(firstword $(subst ., ,$(MAKE_VERSION))),3) # weed out all complicated filenames containing a colon CONFVAR_SUBSTITUTION_DEP_FILES = $(shell printf '%s\n' $(CONFVAR_SUBSTITUTION_FILES) | grep -v ":") else CONFVAR_SUBSTITUTION_DEP_FILES = $(CONFVAR_SUBSTITUTION_FILES) endif .PHONY: install install-pre install-master-prime install-node-prime install-node-pre install-common-prime install-doc install-man \ build build-common build-common-pre build-doc \ substitute-build-defaults-inline substitute-confvar-inline \ source_dist \ test lint clean \ clean-% test-% build-% install-% \ tags \ infiles .SECONDARY: node/Build master/Build plugins/Build .SUFFIXES: .java .class # This HAS to be the 1st rule default: build .java.class: $(JC) -sourcepath plugins/javalib -d build/plugins/javalib $(JFLAGS) plugins/javalib/$(subst plugins/javalib/,,$*.java) uninstall: echo "Uninstall is not implemented yet" # This removes the installed config so that the next install-pass installs # a new config. Target _only_ suitable for maintainers. unconfig: rm -f $(HTMLDIR)/.htaccess rm -f $(CONFDIR)/munin.conf tags: -rm -f TAGS find master common -type f | grep -Ev '/(build/|_build/|blib/|\.svn/)' | grep -v '\.t$$' | grep -Fv '~' | xargs etags -l perl -a ###################################################################### install: install-master-prime install-common-prime install-node-prime install-plugins-prime $(JAVA_INSTALL) install-man install-async-prime install-pre: $(MAKEFILES) @$(CHECKUSER) mkdir -p $(LOGDIR) mkdir -p $(STATEDIR) mkdir -p $(SPOOLDIR) mkdir -p $(CONFDIR) $(CHOWN) $(USER) $(LOGDIR) $(STATEDIR) $(SPOOLDIR) install-master-prime: $(INFILES_MASTER) install-pre install-master mkdir -p $(CONFDIR)/templates mkdir -p $(CONFDIR)/static mkdir -p $(CONFDIR)/templates/partial mkdir -p $(CONFDIR)/munin-conf.d mkdir -p $(LIBDIR) mkdir -p $(BINDIR) mkdir -p $(PERLLIB) mkdir -p $(PERLLIB)/Munin/Master mkdir -p $(HTMLDIR) mkdir -p $(DBDIR) mkdir -p $(DBDIR)/cgi-tmp mkdir -p $(CGIDIR) $(CHOWN) $(USER) $(HTMLDIR) $(DBDIR) $(CHMOD) 0755 $(DBDIR) $(CHOWN) $(CGIUSER) $(DBDIR)/cgi-tmp $(CHMOD) 0755 $(DBDIR)/cgi-tmp for p in master/www/*.tmpl ; do \ $(INSTALL) -m 0644 "$$p" $(CONFDIR)/templates/ ; \ done for p in master/static/* ; do \ $(INSTALL) -m 0644 "$$p" $(CONFDIR)/static/ ; \ done for p in master/www/partial/*.tmpl; do \ $(INSTALL) -m 0644 "$$p" $(CONFDIR)/templates/partial/ ; \ done $(INSTALL) -m 0644 master/DejaVuSansMono.ttf $(LIBDIR)/ $(INSTALL) -m 0644 master/DejaVuSans.ttf $(LIBDIR)/ test -f $(HTMLDIR)/.htaccess || $(INSTALL) -m 0644 build/master/www/munin-htaccess $(HTMLDIR)/.htaccess test -f "$(CONFDIR)/munin.conf" || $(INSTALL) -m 0644 build/master/munin.conf $(CONFDIR)/ $(INSTALL) -m 0755 build/master/_bin/munin-cron $(BINDIR)/ $(INSTALL) -m 0755 build/master/_bin/munin-check $(BINDIR)/ $(INSTALL) -m 0755 build/master/_bin/munin-update $(LIBDIR)/ $(INSTALL) -m 0755 build/master/_bin/munin-html $(LIBDIR)/ $(INSTALL) -m 0755 build/master/_bin/munin-graph $(LIBDIR)/ $(INSTALL) -m 0755 build/master/_bin/munin-limits $(LIBDIR)/ $(INSTALL) -m 0755 build/master/_bin/munin-datafile2storable $(LIBDIR)/ $(INSTALL) -m 0755 build/master/_bin/munin-storable2datafile $(LIBDIR)/ $(INSTALL) -m 0755 build/master/_bin/munin-cgi-graph $(CGIDIR)/munin-cgi-graph $(INSTALL) -m 0755 build/master/_bin/munin-cgi-html $(CGIDIR)/munin-cgi-html install-node-plugins: install-plugins-prime install-plugins-prime: install-plugins build $(PLUGINS) $(MAKEFILES) @$(CHECKGROUP) mkdir -p $(CONFDIR)/plugins mkdir -p $(CONFDIR)/plugin-conf.d mkdir -p $(LIBDIR)/plugins mkdir -p $(PLUGSTATE) $(CHOWN) root:root $(PLUGSTATE) $(CHMOD) 0755 $(PLUGSTATE) $(CHMOD) 0755 $(CONFDIR)/plugin-conf.d @# Process the OS specific plugins at the end. Otherwise they would be overridden by the @# generic ones. for p in build/plugins/node.d/* build/plugins/node.d.$(OSTYPE)/* ; do \ if test -f "$$p"; then \ echo Installing $$p; \ $(INSTALL) -m 0755 $$p $(LIBDIR)/plugins/; \ fi \ done @# Some HP-UX plugins need *.adv support files in LIBDIR if [ "$(OSTYPE)" = "hp-ux" ]; then mv $(LIBDIR)/plugins/*.adv $(LIBDIR); fi $(INSTALL) -m 0644 build/plugins/plugins.history $(LIBDIR)/plugins/ $(INSTALL) -m 0644 build/plugins/plugin.sh $(LIBDIR)/plugins/ install-plugins-java: build-plugins-java mkdir -p $(JAVALIBDIR) $(INSTALL) -m 0644 build/plugins/javalib/munin-jmx-plugins.jar $(JAVALIBDIR)/ mkdir -p $(LIBDIR)/plugins for p in build/plugins/node.d.java/*; do \ if test -f "$$p"; then \ echo Installing $$p; \ $(INSTALL) -m 0755 $$p $(LIBDIR)/plugins/; \ fi \ done #TODO: # configure plugins. Or not. Better done under the direction of the installer # or the packager. install-async-prime: install-async install-async: mkdir -p $(LIBDIR) $(INSTALL) -m 0755 build/node/_bin/munin-async $(LIBDIR)/ $(INSTALL) -m 0755 build/node/_bin/munin-asyncd $(LIBDIR)/ install-node-prime: install-node-pre install-node install-node-pre: build/node/munin-node.conf install-pre test -f "$(CONFDIR)/munin-node.conf" || $(INSTALL) -m 0644 build/node/munin-node.conf $(CONFDIR)/ install-common-prime: build-common install-common install-man: build-man $(MAKEFILES) mkdir -p $(MANDIR)/man1 $(MANDIR)/man5 $(MANDIR)/man8 $(INSTALL) -m 0644 build/doc/munin-node.conf.5 $(MANDIR)/man5/ $(INSTALL) -m 0644 build/doc/munin.conf.5 $(MANDIR)/man5/ $(INSTALL) -m 0644 build/doc/munin-update.8 $(MANDIR)/man8/ $(INSTALL) -m 0644 build/doc/munin-limits.8 $(MANDIR)/man8/ $(INSTALL) -m 0644 build/doc/munin-graph.8 $(MANDIR)/man8/ $(INSTALL) -m 0644 build/doc/munin-html.8 $(MANDIR)/man8/ $(INSTALL) -m 0644 build/doc/munin-cron.8 $(MANDIR)/man8/ $(INSTALL) -m 0644 build/doc/munin-check.8 $(MANDIR)/man8/ $(INSTALL) -m 0644 build/doc/munin.8 $(MANDIR)/man8/ install-doc: build-doc mkdir -p $(DOCDIR)/resources $(INSTALL) -m 0644 README $(DOCDIR)/ $(INSTALL) -m 0644 COPYING $(DOCDIR)/ $(INSTALL) -m 0644 build/resources/* $(DOCDIR)/resources ###################################################################### # Dummy rule to enable parallel building infiles: $(INFILES) build: infiles build-master build-common build-node build-plugins $(JAVA_BUILD) build-man build-confvar-substitution-stamp build/%: %.in @echo "$< -> $@" @mkdir -p build/`dirname $<` @sed -e 's|@@PREFIX@@|$(PREFIX)|g' \ -e 's|@@CONFDIR@@|$(CONFDIR)|g' \ -e 's|@@BINDIR@@|$(BINDIR)|g' \ -e 's|@@SBINDIR@@|$(SBINDIR)|g' \ -e 's|@@DOCDIR@@|$(DOCDIR)|g' \ -e 's|@@LIBDIR@@|$(LIBDIR)|g' \ -e 's|@@MANDIR@@|$(MANDIR)|g' \ -e 's|@@LOGDIR@@|$(LOGDIR)|g' \ -e 's|@@HTMLDIR@@|$(HTMLDIR)|g' \ -e 's|@@DBDIR@@|$(DBDIR)|g' \ -e 's|@@STATEDIR@@|$(STATEDIR)|g' \ -e 's|@@SPOOLDIR@@|$(SPOOLDIR)|g' \ -e 's|@@PERL@@|$(PERL)|g' \ -e 's|@@PERLLIB@@|$(PERLLIB)|g' \ -e 's|@@PYTHON@@|$(PYTHON)|g' \ -e 's|@@RUBY@@|$(RUBY)|g' \ -e 's|@@JAVARUN@@|$(JAVARUN)|g' \ -e 's|@@JAVALIBDIR@@|$(JAVALIBDIR)|g' \ -e 's|@@OSTYPE@@|$(OSTYPE)|g' \ -e 's|@@HOSTNAME@@|$(HOSTNAME)|g' \ -e 's|@@MKTEMP@@|$(MKTEMP)|g' \ -e 's|@@VERSION@@|$(VERSION)|g' \ -e 's|@@PLUGSTATE@@|$(PLUGSTATE)|g' \ -e 's|@@CGIDIR@@|$(CGIDIR)|g' \ -e 's|@@CGITMPDIR@@|$(CGITMPDIR)|g' \ -e 's|@@USER@@|$(USER)|g' \ -e 's|@@GROUP@@|$(GROUP)|g' \ -e 's|@@PLUGINUSER@@|$(PLUGINUSER)|g' \ -e 's|@@GOODSH@@|$(GOODSH)|g' \ -e 's|@@BASH@@|$(BASH)|g' \ -e 's|@@HASSETR@@|$(HASSETR)|g' \ $< > $@; build-confvar-substitution-stamp: $(CONFVAR_SUBSTITUTION_DEP_FILES) $(MAKE) substitute-confvar-inline touch build-confvar-substitution-stamp $(CONFVAR_SUBSTITUTION_DEP_FILES): build-master build-node build-man # the build process depends on the substituted script (see node/Build.PL) build-node: build/node/_bin/munin-get substitute-confvar-inline: perl -p -i -e 's|\@\@PREFIX\@\@|$(PREFIX)|g;' \ -e 's|\@\@CONFDIR\@\@|$(CONFDIR)|g;' \ -e 's|\@\@BINDIR\@\@|$(BINDIR)|g;' \ -e 's|\@\@SBINDIR\@\@|$(SBINDIR)|g;' \ -e 's|\@\@DOCDIR\@\@|$(DOCDIR)|g;' \ -e 's|\@\@LIBDIR\@\@|$(LIBDIR)|g;' \ -e 's|\@\@MANDIR\@\@|$(MANDIR)|g;' \ -e 's|\@\@LOGDIR\@\@|$(LOGDIR)|g;' \ -e 's|\@\@HTMLDIR\@\@|$(HTMLDIR)|g;' \ -e 's|\@\@DBDIR\@\@|$(DBDIR)|g;' \ -e 's|\@\@STATEDIR\@\@|$(STATEDIR)|g;' \ -e 's|\@\@SPOOLDIR\@\@|$(SPOOLDIR)|g;' \ -e 's|\@\@PERL\@\@|$(PERL)|g;' \ -e 's|\@\@PERLLIB\@\@|$(PERLLIB)|g;' \ -e 's|\@\@PYTHON\@\@|$(PYTHON)|g;' \ -e 's|\@\@RUBY\@\@|$(RUBY)|g;' \ -e 's|\@\@JAVARUN\@\@|$(JAVARUN)|g;' \ -e 's|\@\@JAVALIBDIR\@\@|$(JAVALIBDIR)|g;' \ -e 's|\@\@OSTYPE\@\@|$(OSTYPE)|g;' \ -e 's|\@\@HOSTNAME\@\@|$(HOSTNAME)|g;' \ -e 's|\@\@MKTEMP\@\@|$(MKTEMP)|g;' \ -e 's|\@\@VERSION\@\@|$(VERSION)|g;' \ -e 's|\@\@PLUGSTATE\@\@|$(PLUGSTATE)|g;' \ -e 's|\@\@CGIDIR\@\@|$(CGIDIR)|g;' \ -e 's|\@\@USER\@\@|$(USER)|g;' \ -e 's|\@\@GROUP\@\@|$(GROUP)|g;' \ -e 's|\@\@PLUGINUSER\@\@|$(PLUGINUSER)|g;' \ -e 's|\@\@GOODSH\@\@|$(GOODSH)|g;' \ -e 's|\@\@BASH\@\@|$(BASH)|g;' \ -e 's|\@\@HASSETR\@\@|$(HASSETR)|g;' \ $(CONFVAR_SUBSTITUTION_FILES) build-common-pre: common/Build cd common && $(PERL) Build code # The target needs an update, if the latest substitution stamp is older then the generated # Defaults.pm. This can happen, if: # * the generated Defaults.pm is missing # * or "build-common-pre" caused an update of its source file (thus regenerated Defaults.pm) build-common-defaults-stamp: common/blib/lib/Munin/Common/Defaults.pm $(MAKE) substitute-build-defaults-inline @# We need the stamp file, due to the inline nature of this build step. Otherwise it would @# be run again during "install" - which would mess up the paths substituted in that step. touch build-common-defaults-stamp # The "build-common-defaults-stamp" needs a way to generate the (non-substituted) defaults file # during its first run. Afterwards its content is sustituted due to the absence of the # "build-common-defaults-stamp" file. common/blib/lib/Munin/Common/Defaults.pm: build-common-pre substitute-build-defaults-inline: rm -f common/blib/lib/Munin/Common/Defaults.pm $(PERL) -pe 's{(PREFIX\s+=\s).*}{\1q{$(PREFIX)};}x; \ s{(CONFDIR\s+=\s).*}{\1q{$(CONFDIR)};}x; \ s{(BINDIR\s+=\s).*}{\1q{$(BINDIR)};}x; \ s{(SBINDIR\s+=\s).*}{\1q{$(SBINDIR)};}x; \ s{(DOCDIR\s+=\s).*}{\1q{$(DOCDIR)};}x; \ s{(LIBDIR\s+=\s).*}{\1q{$(LIBDIR)};}x; \ s{(MANDIR\s+=\s).*}{\1q{$(MANDIR)};}x; \ s{(LOGDIR\s+=\s).*}{\1q{$(LOGDIR)};}x; \ s{(HTMLDIR\s+=\s).*}{\1q{$(HTMLDIR)};}x; \ s{(DBDIR\s+=\s).*}{\1q{$(DBDIR)};}x; \ s{(STATEDIR\s+=\s).*}{\1q{$(STATEDIR)};}x; \ s{(SPOOLDIR\s+=\s).*}{\1q{$(SPOOLDIR)};}x; \ s{(PERL\s+=\s).*}{\1q{$(PERL)};}x; \ s{(PERLLIB\s+=\s).*}{\1q{$(PERLLIB)};}x; \ s{(PYTHON\s+=\s).*}{\1q{$(PYTHON)};}x; \ s{(RUBY\s+=\s).*}{\1q{$(RUBY)};}x; \ s{(OSTYPE\s+=\s).*}{\1q{$(OSTYPE)};}x; \ s{(HOSTNAME\s+=\s).*}{\1q{$(HOSTNAME)};}x; \ s{(MKTEMP\s+=\s).*}{\1q{$(MKTEMP)};}x; \ s{(VERSION\s+=\s).*}{\1q{$(VERSION)};}x; \ s{(PLUGSTATE\s+=\s).*}{\1q{$(PLUGSTATE)};}x; \ s{(CGIDIR\s+=\s).*}{\1q{$(CGIDIR)};}x; \ s{(USER\s+=\s).*}{\1q{$(USER)};}x; \ s{(GROUP\s+=\s).*}{\1q{$(GROUP)};}x; \ s{(PLUGINUSER\s+=\s).*}{\1q{$(PLUGINUSER)};}x; \ s{(GOODSH\s+=\s).*}{\1q{$(GOODSH)};}x; \ s{(BASH\s+=\s).*}{\1q{$(BASH)};}x; \ s{(HASSETR\s+=\s).*}{\1q{$(HASSETR)};}x;' \ common/lib/Munin/Common/Defaults.pm >common/blib/lib/Munin/Common/Defaults.pm build-doc: build-doc-stamp $(MAKEFILES) build-doc-stamp: touch build-doc-stamp mkdir -p build/doc build-man: build-man-stamp $(MAKEFILES) build-man-stamp: $(INFILES) mkdir -p build/doc for f in $(MAN8); do \ pod2man --section=8 --release=$(RELEASE) --center=$(MANCENTER) build/"$$f" > build/doc/`basename $$f`.8; \ done for f in $(PODMAN8); do \ pod2man --section=8 --release=$(RELEASE) --center=$(MANCENTER) "$$f".pod > build/doc/`basename $$f .pod`.8; \ done for f in $(PODMAN5); do \ pod2man --section=5 --release=$(RELEASE) --center=$(MANCENTER) "$$f".pod > build/doc/`basename $$f .pod`.5; \ done touch build-man-stamp build-plugins-java: build/plugins/javalib/munin-jmx-plugins.jar build/plugins/javalib/munin-jmx-plugins.jar: $(CLASSFILES) cd build/plugins/javalib && $(JAR) cf munin-jmx-plugins.jar org/munin/plugin/jmx build-java-stamp: mkdir -p build/plugins/javalib touch build-java-stamp build/%.class: %.class build-java-stamp @echo "Compiling $*" ###################################################################### # DIST RULES .PHONY: tar tar: munin-$(RELEASE).tar.gz.sha256sum .PHONY: tar-signed tar-signed: munin-$(RELEASE).tar.gz.asc munin-$(RELEASE).tar.gz: @# prevent the RELEASE file from misleading the "getversion" script rm -f RELEASE tempdir=$$(mktemp -d) \ && mkdir -p "$$tempdir/munin-$(RELEASE)/" \ && echo $(RELEASE) > "$$tempdir/munin-$(RELEASE)/RELEASE" \ && git archive --prefix=munin-$(RELEASE)/ --format=tar --output "$$tempdir/export.tar" HEAD \ && tar --append --file "$$tempdir/export.tar" --owner=root --group=root -C "$$tempdir" "munin-$(RELEASE)/RELEASE" \ && gzip -9 <"$$tempdir/export.tar" >"munin-$(RELEASE).tar.gz" \ && rm -rf "$$tempdir" munin-$(RELEASE).tar.gz.sha256sum: munin-$(RELEASE).tar.gz sha256sum "$<" >"$@" munin-$(RELEASE).tar.gz.asc: munin-$(RELEASE).tar.gz gpg --armor --detach-sign --sign "$<" .PHONY: tar-upload tar-upload: tar tar-signed @if [ -z "$(UPLOAD_DIR)" ]; then echo "You need to set UPLOAD_DIR (e.g. '/srv/www/downloads.munin-monitoring.org/munin/stable')" >&2; false; fi @if [ -z "$(UPLOAD_HOST)" ]; then echo "You need to set UPLOAD_HOST" >&2; false; fi { \ echo "mkdir $(UPLOAD_DIR)/$(VERSION)"; \ echo "put munin-$(VERSION).tar.gz* $(UPLOAD_DIR)/$(VERSION)/"; \ } | sftp -b - "$(UPLOAD_HOST)" suse-pre: (! grep MAINTAINER Makefile.config) @for file in `find dists/suse/ -type f -name '*.in'`; do \ destname=`echo $$file | sed 's/.in$$//'`; \ echo Generating $$destname..; \ sed -e 's|@@VERSION@@|$(VERSION)|g' \ $$file > $$destname; \ done -cp dists/tarball/plugins.conf . # (cd ..; ln -s munin munin-$(VERSION)) suse: suse-pre tar -C .. --dereference --exclude .svn -cvzf ../munin_$(RELEASE).tar.gz munin-$(VERSION)/ (cd ..; rpmbuild -tb munin-$(RELEASE).tar.gz) suse-src: suse-pre tar -C .. --dereference --exclude .svn -cvzf ../munin_$(RELEASE).tar.gz munin-$(VERSION)/ (cd ..; rpmbuild -ts munin-$(RELEASE).tar.gz) source_dist: clean (! grep MAINTAINER Makefile.config) (cd .. && ln -s $(DIR) munin-$(VERSION)) tar -C .. --dereference --exclude .svn -cvzf ../munin_$(RELEASE).tar.gz munin-$(VERSION)/ (cd .. && rm munin-$(VERSION)) ###################################################################### ifeq ($(MAKELEVEL),0) clean: clean-node clean-master clean-plugins clean-common else clean: endif -rm -rf build -rm -f build-stamp -rm -f build-doc-stamp -rm -f build-man-stamp -rm -f build-java-stamp -rm -f build-confvar-substitution-stamp -rm -f build-common-defaults-stamp -rm -rf t/install -rm -f dists/redhat/munin.spec -rm -f dists/suse/munin.spec ###################################################################### test: test-common test-master test-node test-plugins ifeq ($(MAKELEVEL),0) # Re-exec make with the test config old-test: t/*.t $(MAKE) $@ CONFIG=t/Makefile.config else test_plugins = id_default id_root env old-test: t/*.t t/install $(addprefix $(CONFDIR)/plugins/,$(test_plugins)) @for test in t/*.t; do \ echo -n "$$test: "; \ PERL5LIB=$(PERLLIB) $(PERL) $$test;\ done endif node-monkeywrench: install-node rm -rf $(CONFDIR)/plugins rm -rf $(LIBDIR)/plugins mkdir -p $(LIBDIR)/plugins mkdir -p $(CONFDIR)/plugins cp monkeywrench/plugin-break*_ $(LIBDIR)/plugins/ $(SBINDIR)/munin-node-configure --suggest echo 'Done?' t/install: $(MAKE) clean install-node-prime install-node-plugins CONFIG=t/Makefile.config INSTALL_PLUGINS=test ###################################################################### # This builds */Build from Build.PL %/Build: %/Build.PL cd $* && $(PERL) -I. Build.PL build-%: %/Build cd $* && $(PERL) Build build-common: build-common-defaults-stamp # BUG: the Build script writes files under PWD when it does "install" # can't seem to find a way to persuade it to write elsewhere. install-%: %/Build cd $* && $(PERL) Build install \ --install_path lib=$(PERLLIB) \ --install_path bin=$(BINDIR) \ --install_path script=$(BINDIR) \ --install_path sbin=$(SBINDIR) \ --install_path bindoc=$(MANDIR)/man1 \ --install_path libdoc=$(MANDIR)/man3 test-%: %/Build cd $* && $(PERL) Build test --verbose=0 .PHONY: lint lint-plugins lint-spelling lint: lint-plugins lint-spelling lint-plugins: @# SC1008: ignore our weird shebang (substituted later) @# SC1090: ignore sourcing of files with variable in path @# SC2009: do not complain about "ps ... | grep" calls (may be platform specific) @# SC2126: tolerate "grep | wc -l" (simple and widespread) instead of "grep -c" @# SC2230: do not complain about "which" (instead of "command -v") # TODO: fix the remaining shellcheck issues for the missing platforms: # aix, darwin, netbsd, sunos # (these require tests with their specific shell implementations) find plugins/node.d/ \ plugins/node.d.cygwin/ \ plugins/node.d.debug/ \ plugins/node.d.linux/ -type f -print0 \ | xargs -0 grep -l --null "@@GOODSH@@" \ | xargs -0 shellcheck --exclude=SC1008,SC1090,SC2009,SC2126,SC2230,SC2239 --shell dash find plugins/ -type f -print0 \ | xargs -0 grep -l --null "@@BASH@@" \ | xargs -0 shellcheck --exclude=SC1008,SC1090,SC2009,SC2126,SC2230,SC2239 --shell bash find plugins/ -type f -print0 \ | xargs -0 grep -l --null "@@PYTHON@@" \ | xargs -0 $(PYTHON_LINT_CALL) # TODO: perl plugins currently fail with perlcritic lint-spelling: # codespell misdetections may be ignored by adding the full line of text to the file .codespell.exclude find . -type f -print0 \ | grep --null-data -vE '^\./(\.git|\.pc|doc/_build|blib|.*/blib|build|sandbox|web/static/js|contrib/plugin-gallery/www/static/js)/' \ | grep --null-data -vE '\.(svg|png|gif|ico|css|woff|woff2|ttf|eot|pem)$$' \ | grep --null-data -vE '^\./(contrib|master|node|resources|plugins)/' \ | xargs -0 -r codespell --exclude-file=.codespell.exclude clean-%: %/Build build-common-defaults-stamp cd $* && $(PERL) Build realclean �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/Makefile.config������������������������������������������������������������������������0000664�0000000�0000000�00000014055�14516145741�0015654�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- makefile -*- # # This is the Makefile.config file to use for a "clean" distribution. # # This file specifies where Munin will look for things after you've # run 'make' in the source directory. Modify it to suit your needs. # DESTDIR is meant only for use when making Munin packages. Unless # you're doing packaging do NOT set it. # DESTDIR is empty during building, and optionally set to point to # a shadow tree during make install. # Note: There is a need for a VARDIR, with DBDIR, PLUGSTATE, HTMLDIR # and LOGDIR as subdirectories. Today, DBDIR contains configured # domains, as well as PLUGSTATE, and we risk collisions. # # the base of the Munin installation. # PREFIX = $(DESTDIR)/opt/munin # Where Munin keeps its configurations (server.conf, client.conf, ++) CONFDIR = $(DESTDIR)/etc/opt/munin # Server only - where to put munin-cron BINDIR = $(PREFIX)/bin # Client only - where to put munin-node, munin-node-configure, and munin-run SBINDIR = $(PREFIX)/sbin # Where to put text and html documentation DOCDIR = $(PREFIX)/doc # Where to put man pages MANDIR = $(PREFIX)/man # Where to put internal binaries and plugin repository LIBDIR = $(PREFIX)/lib # Server only - Output directory HTMLDIR = $(PREFIX)/www/docs CGIDIR = $(PREFIX)/www/cgi # Where to put internal data for master (RRD, internal files, ...) DBDIR = $(DESTDIR)/var/opt/munin # Where to put internal data for node (plugin state, ...) DBDIRNODE = $(DESTDIR)/var/opt/munin-node # Client only - Where the spool files are written. Must be writable by # group "munin", and should be preserved between reboots SPOOLDIR = $(DBDIR)/spool # Client only - Where plugins should put their states. Must be writable by # group "munin", and should be preserved between reboots PLUGSTATE = $(DBDIRNODE)/plugin-state # Where Munin should place its logs. LOGDIR = $(PREFIX)/log/munin # Location of PID files and other statefiles. On the server, must be # writable by the user "munin". STATEDIR = $(DESTDIR)/var/run/munin # The perl interpreter to use PERL := $(shell which perl) # The python interpreter to use (used by some plugins) PYTHON := /usr/bin/env python3 # The ruby interpreter to use (used by some plugins) RUBY := /usr/bin/env ruby # The java runtime to use (used by some plugins) JAVARUN := /usr/bin/java # The java library dir to use (used by some plugins) # this is needed in order to be able to install # java libraries in a custom location. Many distributions # enforce a spesific location for java libraries. JAVALIBDIR = $(LIBDIR) # A modern (posix) shell. We're not looking for arrays, but $() and # other modern stuff is expected. On a posix-system the expression # below will find the right shell. Most Unixes released the last 10 # years are POSIX compliant enough for this to work (he said bravely). # # On Linux /bin/sh, SunOS/Solaris /usr/xpg4/bin/sh or /bin/ksh # In general: bash or ksh will work # GOODSH := $(shell PATH=`getconf PATH 2>/dev/null || echo $(PATH)` LANG=C sh -c 'type sh | sed "s/.* //"') # Path of bash for bash specific plugins BASH := /bin/bash # Server only - Where to install the perl libraries PERLLIB = $(DESTDIR)$(shell $(PERL) -V:sitelib | cut -d"'" -f2) # Client only - Install plugins for this architecture # the LANG=C makes tr work as expected, not regarding any locale it # isn't done globally to enable users to have as much localized # errors as possible OSTYPE := $(shell uname | LANG=C tr '[A-Z]' '[a-z]' | cut -f 1 -d _) # How to figure out the hostname. (Only used in default configuration # files) HOSTNAME := $(shell hostname) # What is the safest way to create a tempfile. # Default is to figure it out by testing various methods. # Replace this with a known platform-specific method MKTEMP := $(shell ./test-mktemp) # Munin version number. VERSION := $(shell ./getversion) # User to run munin as USER := munin GROUP := munin # Default user to run the plugins as PLUGINUSER := nobody # Default user to run the cgi as CGIUSER := nobody # Which command to use to check if the USER and GROUP to run Munin as, exists. ifneq ($(shell which getent),) # "getent" works on most modern OS CHECKUSER_COMMAND := getent passwd $(USER) CHECKGROUP_COMMAND := getent group $(GROUP) else ifeq ($(OSTYPE),darwin) # This should work for OSX 10.5 (Leopard) or later CHECKUSER_COMMAND := dscl . -read /Users/$(USER) CHECKGROUP_COMMAND := dscl . -read /Groups/$(GROUP) else ifeq ($(OSTYPE),cygwin) CHECKUSER_COMMAND := id $(USER) CHECKGROUP_COMMAND := grep ^$(GROUP): /etc/group else ifeq ($(OSTYPE),hp-ux) CHECKUSER_COMMAND := pwget -n $(USER) CHECKGROUP_COMMAND := grget -n $(GROUP) else $(warning Missing test for user existence on this platform. Skipping this check and hoping for the best ...) CHECKUSER_COMMAND := true CHECKGROUP_COMMAND := true endif endif endif endif CHECKUSER := $(shell $(CHECKUSER_COMMAND) >/dev/null 2>/dev/null || (echo "echo User $(USER) nonexistent. Create the user and retry; exit 2")) CHECKGROUP := $(shell $(CHECKGROUP_COMMAND) >/dev/null 2>/dev/null || (echo "echo Group $(GROUP) nonexistent. Create the group and retry; exit 2")) CHOWN := chown CHMOD := chmod CHGRP := chgrp # Java compiler stuff - only needed on the buildhost JC := javac JFLAGS := -g -source 1.7 -target 1.7 -Xlint JAR := jar # Check if the java compiler works # Note that we defer JCVALID evaluation to runtime, # since $(JC) can be redefined later in a specific Makefile.config # The core Makefile.config is then used as a Makefile.default JCVALID = $(shell $(JC) -version >/dev/null 2>/dev/null && echo "yes") # Check whether setruid functionality can be used HASSETR := $(shell perl -e 'use Config; my @vars=("d_setruid", "d_setreuid", "d_setresuid"); foreach my $$var (@vars) { if ($$Config{$$var} eq "define") { print "1\n"; exit 0; } } print "0\n"; exit 0;' ) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/README���������������������������������������������������������������������������������0000664�0000000�0000000�00000000436�14516145741�0013626�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������This is Munin. Munin is distributed under the GNU GPL version 2. Munin is copyrighted 2002-2012 by its various authors as identified in the source files. Munin is homed at http://munin-monitoring.org/. After you have completed the INSTALL all the documentation can be found there. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/README.HP-UX���������������������������������������������������������������������������0000664�0000000�0000000�00000001027�14516145741�0014463�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������There is a somewhat ragged set of contributed HP-UX specific plugins requiring various licenses. We hope for further contributions to make the HP-UX support more complete. Please note that Makefile.config needs editing almost at the very end to work, the sections are commented. You will also need GNU Make which you can find here: Europe: http://hpux.asknet.de/hppd/hpux/Gnu/make-3.81/ US and other places: http://hpux.cs.utah.edu/hppd/hpux/Gnu/make-3.81/ Many thanks to Ralph Grothe for the contributions towards HP-UX support. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/README.OSX�����������������������������������������������������������������������������0000664�0000000�0000000�00000001764�14516145741�0014303�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Here's a howto on adding the users in the first place: bash-3.2# dscl . create /Groups/munin PrimaryGroupId 4949 bash-3.2# dscl . create /Users/munin UniqueId 4949 bash-3.2# dscl . create /Users/munin PrimaryGroupId 4949 bash-3.2# dscl . create /Users/munin UserShell /bin/false bash-3.2# dscl . create /Users/munin NFSHomeDirectory /nohome bash-3.2# dscl . create /Users/munin RealName Munin bash-3.2# dscl . create /Groups/munin GroupMembership munin bash-3.2# dscl . -read /Users/munin dsAttrTypeNative:PrimaryGroupId: 4949 dsAttrTypeNative:UniqueId: 4949 AppleMetaNodeLocation: /Local/Default GeneratedUID: 2A56D7FE-00D0-4B81-BAE7-F842E675031D NFSHomeDirectory: /nohome RealName: Munin RecordName: munin RecordType: dsRecTypeStandard:Users UserShell: /bin/false bash-3.2# dscl . -read /Groups/munin dsAttrTypeNative:PrimaryGroupId: 4949 AppleMetaNodeLocation: /Local/Default GeneratedUID: F53ACCA3-2CB9-440F-84A6-E2FADC44CB9E GroupMembership: munin RecordName: munin RecordType: dsRecTypeStandard:Groups ������������munin-2.0.75/UPGRADING������������������������������������������������������������������������������0000664�0000000�0000000�00000005276�14516145741�0014220�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Upgrading to 2.0 ================ Upgrading from the Munin 1.4 series to 2.0 should be quite easy. But there are some things to take note of if you want a very smooth transition. The most important change is the CGI requirement. * Munin graphs can now generated on-demand by the web server. It is mandatory for the new zooming feature. The cron "munin-graph" is still the default, as it really ease new install and upgrading for small setups. Going the CGI road is recommended way if you experience performance issues with the cron setup. * Munin html pages may be generated on-demand by the web server. The default is still to run "munin-html" from cron after every update. If you install from SVN or tar ball please make sure you read the INSTALL file VERY carefully. If you install from a packaged (DEB, RPM, ...) version, please make sure you read the specific INSTALL file VERY carefully, as there might be some particularities. From Munin 1.4 ------------------------- Based on comparisons of test Munin installations on Linux we see identical rrd file names, and rrd files are created and structured the same way as before. This ensures that your data history is preserved. Upgrading to 2.0 should therefore prove to be straightforward and cause no data loss. BUT, as hard as we tried, there might be a few exceptions, so YMMV. In terms of ordering I would upgrade the master first. If you do not use a packaging system you may have to look around for old .pm files and purge them to get 2.0 work properly. Do NOT purge the RRD files or the configuration. After the master is upgraded upgrade the nodes one by one. Please see notes about changes in plugin and data-field names in the sections below before you start upgrading the nodes. And as one said in IRC, an old munin proverb is : "Wait two munin fetch rounds before you can expect to see results." It is specially true when upgrading as the config files should auto-update themselves, but it might take up to 2 iteration of munin-cron to be really sorted out. From Munin 1.2 ---------------- If you still have munin-nodes running 1.2, there is no need to rush as polling 1.2 nodes from a 2.0 master is well supported. Upgrade master straight from 1.2 isn't supported, but should work mostly well as 2.0 is highly 1.4-compatible. But be sure to read UPGRADING-1.4 to look at specific issues from 1.2. Rollback ---------- Thanks to the highly 1.4-compatible status of 2.0, and our PnP-way of doing things, rollbacking the install is very easy : just reinstall 1.4 and remove all the *.storable files from /var/lib/munin. But don't touch the .rrd as they ARE your history. Then wait again for 2 munin-cron runs, and the rollback should be complete. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/UPGRADING-1.4��������������������������������������������������������������������������0000664�0000000�0000000�00000010436�14516145741�0014512�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Upgrading to 1.4 ================ Upgrading from the Munin 1.2 series to 1.4 should be quite easy. But there are some things to take note of if you want a very smooth transition. If you install from SVN or tar ball please make sure you read the INSTALL file VERY carefully. There are new package dependencies and some bugs in the install process are noted there. From Munin 1.2 in general ------------------------- Based on comparisons of a few (very few) Munin installations on Linux we see identical rrd file names, and rrd files are created and structured the same way as before. This ensures that your data history is preserved. Upgrading to 1.4 should therefore prove to be straightforward and cause no data loss. BUT! There are a few exceptions noted below. In terms of ordering I would upgrade the master first. If you do not use a packaging system you may have to look around for old .pm files and purge them to get 1.4 to work properly. Do NOT purge the RRD files or the configuration. After the master is upgraded upgrade the nodes one by one. Please see notes about changes in plugin and data-field names in the sections below before you start upgrading the nodes. Changes in default paths ------------------------ If you do not use a packaging system please review Makefile.config and after doing "make" the example munin.conf and note changes in default paths, which you may or may not like, and if you do not keep the new default htmldir you may want to edit Makefile.config, or alternatively take a look at the .htaccess file installed in the default htmldir to restrict public access to your munin (as described in the INSTALL file) From Munin 1.2.6 ---------------- If you have munin-nodes running 1.2.6 then some of the plugins use short (truncated) field names. Especially the Linux df plugin will be using truncated fieldnames for some disk devices. This is due to a basing a bug fix on obsolete documentation. The device names were limited to 19 characters, only the 19 last characters of the device name was used. This truncation is done by the plugin on the node. When the plugin/node is upgraded the truncation stops. There is no really reliable way to fix this or migrate automatically. What you can do is install 1.4.0 on one node at a time, and for each upgrade examine your rrd file names. You should be able to identify rrd files for the upgraded node that have nearly identical names. Personally I would use a graphical file browser for this. If you find two files that should be one then the one with the longest filename should be newest, and the one with the short file name should not have been updated since the node upgrade. Rename the file with the short name so that it has the long name. On Solaris ---------- Some plugin renamings to get the name aligned with the other platforms: - if_errcoll_ is now called if_err_ - fs_df is now called df - fs_inodes is now called df_inodes You can simply rename the rrd files according to this (or you can rename the plugins, but that will be a uphill battle, they'll be renamed back when you upgrade the next time). Note that the plugins will change names as you upgrade and auto-configure your nodes, not when you upgrade the munin-master. Users of Munin 1.2 in combination with NMSes -------------------------------------------- There are two issues that have been reported. Users of Munin in combination with Nagios have reported that because some graphs have changed their "graph_title" setting this dis-associates the tests from the right nagios checks. Also some plugins do not have default warning and critical levels set any more (e.g. "load") because the opinions on what is "normal" load differs widely. This means that the plugins will not send warning and critical events to contacts any more. The warning and critical levels should be settable for all of these plugins, please use the command "munindoc <plugin>" on the host where the plugin is installed to see how to set these levels. Too audit the differences in warning and critical levels you can make a copy of the munin file called "datafile" before upgrading (or get one from backup), and use grep -E to get a listing of the settings prior to update: grep -E '(warning|critical)' datafile.old and again on the post-upgrade datafile to compare the lists so you can specify the ones you need. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/��������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14516145741�0014233�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/Build.PL������������������������������������������������������������������������0000664�0000000�0000000�00000000757�14516145741�0015540�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use Module::Build; my $version = `../getversion`; chomp($version); my $build = Module::Build->new( dist_name => 'Munin::Common', dist_version => $version, dist_author => 'The Munin Team <munin-users@lists.sourceforge.net>', dist_abstract => 'Shared libraries for Munin Node and Munin Master', license => 'gpl', requires => {}, build_requires => {}, recommends => { 'Net::SSLeay' => 0, }, ); $build->create_build_script; �����������������munin-2.0.75/common/lib/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14516145741�0015001�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/lib/Munin/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14516145741�0016067�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/lib/Munin/Common/���������������������������������������������������������������0000775�0000000�0000000�00000000000�14516145741�0017317�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/lib/Munin/Common/Config.pm������������������������������������������������������0000664�0000000�0000000�00000007062�14516145741�0021067�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Munin::Common::Config; use warnings; use strict; use Carp; use English qw(-no_match_vars); # Functions here are unable to log as they don't know if they're used # by the node or the master which use divergent logging facilities. # In fact, the list in %legal is only used by the master. my %legal = map { $_ => 1 } qw( address always_send category_order cdef cdef_name cgitmpdir cgiurl cgiurl_graph colour command compare contact contacts create_args critical dbdir domain_order draw dropdownlimit extinfo fetch_data filename fork graph graph graphable graph_args graph_args_after graph_category graph_data_size graph_future graph_height graph_info graph_noscale graph_order graph_period graph_printf graph_scale graph_sources graph_strategy graph_sums graph_title graph_total graph_vlabel graph_vtitle graph_width group_order host_name htaccess htmldir html_rename html_strategy includedir info label line local_address logdir max max_cgi_graph_jobs max_graph_jobs max_html_jobs max_messages max_processes max_size_x max_size_y min munin_cgi_graph_jobs nagios ncsa ncsa_config ncsa_server negative node_order notify_alias nsca nsca_config nsca_server num_messages num_unknowns ok onlynullcdef palette pipe pipe_command port predict process realname realservname rrdcached_socket rundir service_order skipdraw ssh_command ssh_options stack state staticdir sum text timeout timeout_fetch_one_node timeout_fetch_all_nodes tls tls_ca_certificate tls_certificate tls_match tls_pem tls_private_key tls_verify_certificate tls_verify_depth tmpldir trend type unknown unknown_limit update update_rate use_default_name use_node_name use_default_node version warn warning worker_start_delay ); my %bools = map { $_ => 1} qw(yes no true false on off 1 0); sub cl_is_keyword { # Class-less version of is_keyword for legacy code. my ($word) = @_; return defined $legal{$word}; } sub is_keyword { my ($self, $word) = @_; return defined $legal{$word}; } sub parse_config_from_file { my ($self, $config_file) = @_; $config_file ||= $self->{config_file}; open my $file, '<', $config_file or croak "ERROR: Cannot open '$config_file': $OS_ERROR"; # Note, parse_config is provided by node or master specific config class eval { $self->parse_config($file); }; if ($EVAL_ERROR) { croak "ERROR: Failed to parse config file '$config_file': $EVAL_ERROR"; } close $file or croak "Cannot close '$config_file': $OS_ERROR"; } sub _trim { # Trim leading and trailing whitespace. my $class = shift; chomp $_[0]; $_[0] =~ s/^\s+//; $_[0] =~ s/\s+$//; return; } # allows # characters to get through as long as they're escaped # with a backslash sub _strip_comment { my $class = shift; $_[0] =~ s/(?<!\\)#.*//; $_[0] =~ s/\\#/#/g; return; } sub _looks_like_a_bool { my ($class, $str) = @_; return $bools{lc $str}; } sub _parse_bool { my ($class, $str) = @_; croak "Parse exception: '$str' is not a boolean." unless $class->_looks_like_a_bool($str); return $str =~ m{\A no|false|off|0 \z}xi ? 0 : 1; } 1; __END__ =head1 NAME Munin::Common::Config - Abstract base class for common config code. =head1 SYNOPSIS Don't use it directly. See L<Munin::Master::Config> and L<Munin::Node::Config>. =head1 METHODS =over =item B<parse_config_from_file> $config->parse_config_from_file($file_name); Parses the configuration in $file_name. =back ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/lib/Munin/Common/Daemon.pm������������������������������������������������������0000664�0000000�0000000�00000004001�14516145741�0021053�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Munin::Common::Daemon; use warnings; use strict; use English; use IO::Socket; sub emit_sd_notify_message { eval { my $socket_path = $ENV{NOTIFY_SOCKET}; if (defined $socket_path) { # Prevent children from talking to the socket provided solely for us. delete $ENV{NOTIFY_SOCKET}; # A socket path starting with "@" is interpreted as a Linux abstract namespace socket. # This can be indicated by the socket path starting with a null byte. # See "Address format: abstract" in "man 7 unix". $socket_path =~ s/^@/\0/; my $socket = IO::Socket::UNIX->new(Type => SOCK_DGRAM, Peer => $socket_path); if (defined $socket) { print($socket "READY=1\n"); close($socket); } } } } 1; __END__ =head1 NAME Munin::Common::Daemon - utilities for daemons. =head1 SYNOPSIS The following daemon-related features are supported: =over =item sd_notify: signal readiness of the daemon =back =head1 SUBROUTINES =head2 emit_sd_notify_message Example: emit_sd_notify_message(); Send a "ready" signal according to the C<sd_notify> interface: =over =item 1. check whether the environment variable "NOTIFY_SOCKET" is defined =item 2. remove this variable from the environment (this interface is not propagated to children) =item 3. send the string "READY=1" to the socket =back The function returns silently, if something fails. The function should be called as soon as the service is ready to accept requests. Calling this function is always safe - independent of the caller supporting the C<sd_notify> interface or not. Examples for callers supporting the C<sd_notify> interface: =over =item systemd: see C<Type=Notify> in L<systemd.exec/5> =item start-stop-daemon: see C<--notify-await> in L<start-stop-daemon/8> =back See the L<specification of "sd_notify"|https://www.freedesktop.org/software/systemd/man/sd_notify.html> for further details of this interface. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/lib/Munin/Common/Defaults.pm����������������������������������������������������0000664�0000000�0000000�00000004460�14516145741�0021430�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use warnings; use strict; # If you change the class path take a look in get_defaults too, please! package Munin::Common::Defaults; use English qw(-no_match_vars); use File::Basename qw(dirname); # This file's package variables are changed during the build process. # This variable makes only sense in development environment my $COMPONENT_ROOT = dirname(__FILE__) . '/../../..'; our $DROPDOWNLIMIT = 1; our $MUNIN_PREFIX = ''; our $MUNIN_CONFDIR = "$COMPONENT_ROOT/t/config/"; our $MUNIN_BINDIR = ''; our $MUNIN_SBINDIR = ''; our $MUNIN_DOCDIR = ''; our $MUNIN_LIBDIR = ''; our $MUNIN_HTMLDIR = ''; our $MUNIN_CGIDIR = ''; our $MUNIN_CGITMPDIR = ''; our $MUNIN_DBDIR = ''; our $MUNIN_PLUGSTATE = ''; our $MUNIN_SPOOLDIR = ''; our $MUNIN_MANDIR = ''; our $MUNIN_LOGDIR = "$COMPONENT_ROOT/log/"; our $MUNIN_STATEDIR = ''; our $MUNIN_USER = getpwuid $UID; our $MUNIN_GROUP = getgrgid $GID; our $MUNIN_PLUGINUSER = getpwuid $UID; our $MUNIN_VERSION = 'svn'; our $MUNIN_PERL = '/usr/bin/perl'; our $MUNIN_PERLLIB = ''; our $MUNIN_GOODSH = ''; our $MUNIN_BASH = ''; our $MUNIN_PYTHON = ''; our $MUNIN_RUBY = ''; our $MUNIN_OSTYPE = ''; our $MUNIN_HOSTNAME = ''; our $MUNIN_MKTEMP = ''; our $MUNIN_HASSETR = ''; sub get_defaults { my ($class) = @_; ## no critic no strict 'refs'; my $defaults = {}; for my $g (keys %{"Munin::Common::Defaults::"}) { next unless $g =~ /MUNIN_/; $defaults->{$g} = ${*$g{'SCALAR'}}; } ## use critic return $defaults; } sub export_to_environment { my ($class) = @_; my %defaults = %{$class->get_defaults()}; while (my ($k, $v) = each %defaults) { $ENV{$k} = $v; } return } 1; __END__ =head1 NAME Munin::Common::Defaults - Default values defined by installation scripts. =head1 PACKAGE VARIABLES See L<http://munin-monitoring.org/wiki/MuninInstallProcedure> for more information on the variables provided by this package. =head1 METHODS =over =item B<get_defaults> \%defaults = $class->get_defaults() Returns all the package variables as key value pairs in a hash. =item B<export_to_environment> $class = $class->export_to_environment() Export all the package variables to the environment. =back ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/lib/Munin/Common/DictFile.pm����������������������������������������������������0000664�0000000�0000000�00000002630�14516145741�0021341�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Munin::Common::DictFile; require Tie::Hash; our @ISA = qw(Tie::StdHash); our $DEBUG_ENABLED; # The method invoked by the command tie %hash, classname. # Associates a new hash instance with the specified class. # LIST would represent additional arguments (along the lines # of AnyDBM_File and compatriots) needed to complete the association. sub TIEHASH { my $classname = shift; my ($filename) = @_; my $self = { __filename__ => $filename, }; # Read the whole file if it exists if (-e $filename) { open(FILE, $filename) or die "Cannot open tied file '$filename' - $!"; while(my $line = <FILE>) { chomp($line); next unless $line =~ m/^'(.*)':\t'(.*)'$/; # Found a valid line, store it $self->{ _unescape($1) } = _unescape($2); } close(FILE); } return bless($self, $classname); } # Write everything down sub DESTROY { my $self = shift; my $tmp_filename = $self->{__filename__} . ".tmp.$$"; open(FILE, "> $tmp_filename") or die "Cannot open temp file '$tmp_filename' - $!"; foreach my $key (keys %$self) { print FILE "'" . _escape($key) . "':\t'" . _escape($self->{$key}) . "'\n"; } close (FILE); rename $tmp_filename, $self->{__filename__}; } sub DEBUG { print STDOUT "[DEBUG] @_" . "\n" if $DEBUG_ENABLED; } sub _escape { my $string = shift; $string =~ s/'/''/g; return $string; } sub _unescape { my $string = shift; $string =~ s/''/'/g; return $string; } 1; ��������������������������������������������������������������������������������������������������������munin-2.0.75/common/lib/Munin/Common/SyncDictFile.pm������������������������������������������������0000664�0000000�0000000�00000007722�14516145741�0022205�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Munin::Common::SyncDictFile; require Tie::Hash; our @ISA = qw(Tie::Hash); our $DEBUG_ENABLED; use IO::File; # The method invoked by the command tie %hash, classname. # Associates a new hash instance with the specified class. # LIST would represent additional arguments (along the lines # of AnyDBM_File and compatriots) needed to complete the association. sub TIEHASH { my $classname = shift; my ($filename) = @_; my $self = { filename => $filename, }; new IO::File($filename, O_CREAT) unless (-f $filename); return bless($self, $classname); } # Store datum value into key for the tied hash this. sub STORE { my ($self, $key, $value) = @_; DEBUG("STORE($key, $value)"); $key = escape_key($key); use IO::File; my $fh = _lock_write($self->{filename}, "r"); my $fh_tmp = _lock_write($self->{filename} . ".tmp"); # Read the whole file, writing it to $fh_tmp while(my $line = <$fh>) { chomp($line); DEBUG("read line $line"); # Print the read line, but ignore the key we are currently storing print $fh_tmp "$line\n" unless $line =~ m/^$key:/; } # Print the stored key at the end DEBUG("Print the stored $key:$value"); print $fh_tmp "$key:$value\n"; # close (therefore flush data) before rename $fh_tmp = undef; # overwrite atomically # XXX - any locked process will have an old version rename $self->{filename} . ".tmp", $self->{filename}; } # Retrieve the datum in key for the tied hash this. sub FETCH { my ($self, $key) = @_; DEBUG("FETCH($key)"); $key = escape_key($key); my $fh = _lock_read($self->{filename}); # Read the whole file while(my $line = <$fh>) { chomp($line); next unless $line =~ m/^$key:(.*)/; # Found return $1; } } # Return the first key in the hash. sub FIRSTKEY { my ($self) = @_; DEBUG("FIRSTKEY()"); my $fh = _lock_read($self->{filename}); # Read the file to find a key while(my $line = <$fh>) { chomp($line); next unless $line =~ m/^(\w+):/; # Found return $1; } } # Return the next key in the hash. sub NEXTKEY { my ($self, $lastkey) = @_; DEBUG("NEXTKEY($lastkey)"); $key = escape_key($key); my $fh = _lock_read($self->{filename}); # Read the file to find a key while(my $line = <$fh>) { chomp($line); next unless $line =~ m/^$key:(.*)/; # Found, read another line my $new_line = <$fh>; chomp($new_line); if ($new_line =~ m/^(\w+):/) { return $1; } else { # EOF return undef; } } } # Verify that key exists with the tied hash this. sub EXISTS { my ($self, $key) = @_; DEBUG("EXISTS($key)"); $key = escape_key($key); my $fh = _lock_read($self->{filename}); # Read the whole file while(my $line = <$fh>) { chomp($line); next unless $line =~ m/^$key:(.*)/; # Found return 1; } # Not found return 0; } # Delete the key key from the tied hash this. sub DELETE { my ($self, $key) = @_; DEBUG("DELETE($key)"); $key = escape_key($key); $self->_lock_write(); } # Clear all values from the tied hash this. sub CLEAR { my ($self) = @_; DEBUG("CLEAR()"); my $fh = $self->_lock_write(); } sub SCALAR { my ($self) = @_; DEBUG("SCALAR()"); my $fh = _lock_read($self->{filename}); # Read the file to read the number of lines my $nb_lines = 0; while(my $line = <$fh>) { $nb_lines ++; } return $nb_lines; } sub _lock_read { my ($filename) = @_; use Fcntl qw(:flock); use IO::File; my $fh = IO::File->new($filename, "r") or die "Cannot open tied file '$filename' - $!"; flock($fh, LOCK_SH) or die "Cannot lock tied file '$filename' - $!"; return $fh; } sub _lock_write { my ($filename, $mode) = @_; $mode ||= "a+"; use Fcntl qw(:flock); use IO::File; my $fh = IO::File->new($filename, $mode) or die "Cannot open tied file '$filename' - $!"; flock($fh, LOCK_EX) or die "Cannot lock tied file '$filename' - $!"; return $fh; } sub DEBUG { print STDOUT "[DEBUG] @_" . "\n" if $DEBUG_ENABLED; } # XXX - collision if there is a ____ # But should not happen often anyway sub escape_key { my $key = shift; $key =~ s/:/____/g; return $key; } 1; ����������������������������������������������munin-2.0.75/common/lib/Munin/Common/TLS.pm���������������������������������������������������������0000664�0000000�0000000�00000031372�14516145741�0020325�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Munin::Common::TLS; use warnings; use strict; use Carp; use English qw(-no_match_vars); sub new { my ($class, $args) = @_; my $self = { logger => $args->{logger}, read_fd => $args->{read_fd}, read_func => $args->{read_func}, write_fd => $args->{write_fd}, write_func => $args->{write_func}, }; for my $key (keys %$self) { croak "Required argument missing: $key" unless defined $self->{$key}; } $self = { %$self, DEBUG => $args->{DEBUG} || 0, tls_ca_cert => $args->{tls_ca_cert} || '', tls_cert => $args->{tls_cert} || '', tls_paranoia => $args->{tls_paranoia}|| 0, tls_priv => $args->{tls_priv} || '', tls_vdepth => $args->{tls_vdepth} || 0, tls_verify => $args->{tls_verify} || 0, tls_match => $args->{tls_match} || '', }; for my $args_key (keys %$args) { croak "Unrecognized argument: $args_key" unless exists $self->{$args_key}; } $self = { %$self, tls_context => undef, tls_session => undef, private_key_loaded => 0, }; return bless $self, $class; } sub _start_tls { my $self = shift; my %tls_verified = ( level => 0, cert => "", verified => 0, required_depth => $self->{tls_vdepth}, verify => $self->{tls_verify}, ); $self->{logger}("[TLS] Enabling TLS.") if $self->{DEBUG}; $self->_load_net_ssleay() or return 0; $self->_initialize_net_ssleay(); $self->{tls_context} = $self->_creat_tls_context(); $self->_load_private_key() or return 0; $self->_load_certificate(); $self->_load_ca_certificate(); $self->_initial_communication() or return 0; $self->_set_peer_requirements(\%tls_verified); if (! ($self->{tls_session} = Net::SSLeay::new($self->{tls_context}))) { $self->{logger}("[ERROR] Could not create TLS: $!"); return 0; } $self->_log_cipher_list() if $self->{DEBUG}; $self->_set_ssleay_file_descriptors(); $self->_accept_or_connect(\%tls_verified); return $self->{tls_session}; } sub _load_net_ssleay { my ($self) = @_; eval { require Net::SSLeay; }; if ($@) { $self->{logger}("[ERROR] TLS enabled but Net::SSLeay unavailable."); return 0; } return 1; } sub _initialize_net_ssleay { my ($self) = @_; Net::SSLeay::load_error_strings(); Net::SSLeay::SSLeay_add_ssl_algorithms(); Net::SSLeay::randomize(); } sub _creat_tls_context { my ($self) = @_; my $ctx = Net::SSLeay::CTX_new(); if (!$ctx) { $self->{logger}("[ERROR] Could not create SSL_CTX"); return 0; } # Tune a few things... Net::SSLeay::CTX_set_options($ctx, Net::SSLeay::OP_ALL()); if (my $errno = Net::SSLeay::ERR_get_error()) { $self->{logger}("[ERROR] Could not set SSL_CTX options: " + Net::SSLeay::ERR_error_string($errno)); return 0; } return $ctx; } sub _load_private_key { my ($self) = @_; if (defined $self->{tls_priv} and length $self->{tls_priv}) { if (-e $self->{tls_priv} or $self->{tls_paranoia} eq "paranoid") { if (Net::SSLeay::CTX_use_PrivateKey_file($self->{tls_context}, $self->{tls_priv}, &Net::SSLeay::FILETYPE_PEM)) { $self->{private_key_loaded} = 1; } else { if ($self->{tls_paranoia} eq "paranoid") { $self->{logger}("[ERROR] Problem occurred when trying to read file with private key \"$self->{tls_priv}\": $!"); return 0; } else { $self->{logger}("[ERROR] Problem occurred when trying to read file with private key \"$self->{tls_priv}\": $!. Continuing without private key."); } } } else { $self->{logger}("[WARNING] No key file \"$self->{tls_priv}\". Continuing without private key."); } } return 1; } sub _load_certificate { my ($self) = @_; if ($self->{tls_cert} && -e $self->{tls_cert}) { if (defined $self->{tls_cert} and length $self->{tls_cert}) { if (!Net::SSLeay::CTX_use_certificate_file($self->{tls_context}, $self->{tls_cert}, &Net::SSLeay::FILETYPE_PEM)) { $self->{logger}("[WARNING] Problem occurred when trying to read file with certificate \"$self->{tls_cert}\": $!. Continuing without certificate."); } } } else { $self->{logger}("[WARNING] No certificate file \"$self->{tls_cert}\". Continuing without certificate."); } return 1; } sub _load_ca_certificate { my ($self) = @_; if ($self->{tls_ca_cert} && -e $self->{tls_ca_cert}) { if(!Net::SSLeay::CTX_load_verify_locations($self->{tls_context}, $self->{tls_ca_cert}, '')) { $self->{logger}("[WARNING] Problem occurred when trying to read file with the CA's certificate \"$self->{tls_ca_cert}\": ".&Net::SSLeay::print_errs("").". Continuing without CA's certificate."); } } return 1; } sub _set_peer_requirements { my ($self, $tls_verified) = @_; $self->{tls_vdepth} = 5 if !defined $self->{tls_vdepth}; Net::SSLeay::CTX_set_verify_depth ($self->{tls_context}, $self->{tls_vdepth}); my $err = &Net::SSLeay::print_errs(""); if (defined $err and length $err) { $self->{logger}("[WARNING] in set_verify_depth: $err"); } Net::SSLeay::CTX_set_verify ($self->{tls_context}, $self->{tls_verify} ? &Net::SSLeay::VERIFY_PEER : &Net::SSLeay::VERIFY_NONE, $self->_tls_verify_callback($tls_verified)); $err = &Net::SSLeay::print_errs(""); if (defined $err and length $err) { $self->{logger}("[WARNING] in set_verify: $err"); } return 1; } sub _tls_verify_callback { my ($self, $tls_verified) = @_; return sub { my ($ok, $subj_cert, $issuer_cert, $depth, $errorcode, $arg, $chain) = @_; $tls_verified->{"level"}++; if ($ok) { $tls_verified->{"verified"} = 1; $self->{logger}("[TLS] Verified certificate.") if $self->{DEBUG}; return 1; # accept } if (!($tls_verified->{"verify"})) { $self->{logger}("[TLS] Certificate failed verification, but we aren't verifying.") if $self->{DEBUG}; $tls_verified->{"verified"} = 1; return 1; } if ($tls_verified->{"level"} > $tls_verified->{"required_depth"}) { $self->{logger}("[TLS] Certificate verification failed at depth ".$tls_verified->{"level"}."."); $tls_verified->{"verified"} = 0; return 0; } return 0; # Verification failed } } sub _log_cipher_list { my ($self) = @_; my $i = 0; my $p = ''; my $cipher_list = 'Cipher list: '; $p=Net::SSLeay::get_cipher_list($self->{tls_session},$i); $cipher_list .= $p if $p; do { $i++; $cipher_list .= ', ' . $p if $p; $p=Net::SSLeay::get_cipher_list($self->{tls_session},$i); } while $p; $cipher_list .= '\n'; $self->{logger}("[TLS] Available cipher list: $cipher_list.") if $self->{DEBUG}; } sub _set_ssleay_file_descriptors { my ($self) = @_; Net::SSLeay::set_rfd($self->{tls_session}, $self->{read_fd}); my $err = &Net::SSLeay::print_errs(""); if (defined $err and length $err) { $self->{logger}("[TLS] Warning in set_rfd: $err"); } Net::SSLeay::set_wfd($self->{tls_session}, $self->{write_fd}); $err = &Net::SSLeay::print_errs(""); if (defined $err and length $err) { $self->{logger}("[TLS] Warning in set_wfd: $err"); } } sub _accept_or_connect { my ($self, $tls_verified) = @_; $self->{logger}("[TLS] Accept/Connect: $self->{private_key_loaded}, " . $self->_use_key_if_present()) if $self->{DEBUG}; my $res; if ($self->_use_key_if_present()) { $res = Net::SSLeay::accept($self->{tls_session}); } else { $res = Net::SSLeay::connect($self->{tls_session}); } $self->{logger}("[TLS] Done Accept/Connect") if $self->{DEBUG}; my $err = &Net::SSLeay::print_errs(""); if (defined $err and length $err) { $self->{logger}("[ERROR] Could not enable TLS: " . $err); Net::SSLeay::free ($self->{tls_session}); Net::SSLeay::CTX_free ($self->{tls_context}); $self->{tls_session} = undef; } elsif (!$tls_verified->{"verified"} and $self->{tls_paranoia} eq "paranoid") { $self->{logger}("[ERROR] Could not verify CA: " . Net::SSLeay::dump_peer_certificate($self->{tls_session})); $self->_on_unverified_cert(); Net::SSLeay::free ($self->{tls_session}); Net::SSLeay::CTX_free ($self->{tls_context}); $self->{tls_session} = undef; } elsif ($self->{"tls_match"} and Net::SSLeay::dump_peer_certificate($self->{tls_session}) !~ /$self->{tls_match}/) { $self->{logger}("[ERROR] Could not match pattern \"" . $self->{tls_match} . "\" in dump of certificate."); $self->_on_unmatched_cert(); Net::SSLeay::free ($self->{tls_session}); Net::SSLeay::CTX_free ($self->{tls_context}); $self->{tls_session} = undef; } else { $self->{logger}("[TLS] TLS enabled.") if $self->{DEBUG}; $self->{logger}("[TLS] Cipher `" . Net::SSLeay::get_cipher($self->{tls_session}) . "'.") if $self->{DEBUG}; $self->{logger}("[TLS] client cert: " . Net::SSLeay::dump_peer_certificate($self->{tls_session})) if $self->{DEBUG}; } } # Abstract method sub _initial_communication { my ($self) = @_; croak "Abstract method called '_initial_communication', " . "needs to be defined in child" if ref $self eq __PACKAGE__; } # Abstract method sub _use_key_if_present { my ($self) = @_; croak "Abstract method called '_use_key_if_present', " . "needs to be defined in child" if ref $self eq __PACKAGE__; } # Redefine in sub class if needed sub _on_unverified_cert {} # Redefine in sub class if needed sub _on_unmatched_cert {} sub read { my ($self) = @_; croak "Tried to do an encrypted read, but a TLS session is not started" unless $self->session_started(); my $read = Net::SSLeay::read($self->{tls_session}); my $err = &Net::SSLeay::print_errs(""); if (defined $err and length $err) { $self->{logger}("[TLS] Warning in read: $err"); return; } undef $read if($read eq ''); # returning '' signals EOF $self->{logger}("DEBUG: < $read") if $self->{DEBUG} && defined $read; return $read; } sub write { my ($self, $text) = @_; croak "Tried to do an encrypted write, but a TLS session is not started" unless $self->session_started(); $self->{logger}("DEBUG: > $text") if $self->{DEBUG}; Net::SSLeay::write($self->{tls_session}, $text); my $err = &Net::SSLeay::print_errs(""); if (defined $err and length $err) { $self->{logger}("[TLS] Warning in write: $err"); return 0; } return 1; } sub session_started { my ($self) = @_; return defined $self->{tls_session}; } 1; __END__ =head1 NAME Munin::Node::TLS - Abstract base class implementing the STARTTLS protocol =head1 SYNOPSIS Should not be called directly. See synopsis for L<Munin::Common::TLSServer> and L<Munin::Common::TLSClient>. =head1 METHODS =over =item B<new> my $tls = Munin::Common::TLSFoo->new({ # Substitute Foo with Client or Server # Mandatory attributes: logger => \&a_logger_func, read_fd => fileno($socket), read_func => \&a_socket_read_func, write_fd => fileno($socket), write_func => \&a_socket_read_func, # Optional attributes DEFAULTS DEBUG => 0, # 0 tls_ca_cert => "path/to/ca/cert.pem", # '' tls_cert => "path/to/cert.pem", # '' tls_paranoia => 1, # 0 tls_priv => "path/to/priv_key.pem", # '' tls_vdepth => 5, # 0 tls_verify => 1, # 0 }); Constructor. Should not be called directly. This documents the attributes that are in common for L<Munin::Common::TLSServer> and L<Munin::Common::TLSClient>. =item B<read> my $msg = $tls->read(); Encrypted read. =item B<write> $tls->write($msg); Encrypted write. =item B<session_started> my $bool = $tls->session_started(); Returns true if the TLS object is ready to read/write encrypted data. =back ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/lib/Munin/Common/TLSClient.pm���������������������������������������������������0000664�0000000�0000000�00000002602�14516145741�0021456�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Munin::Common::TLSClient; use base qw(Munin::Common::TLS); use warnings; use strict; use Carp; use English qw(-no_match_vars); sub new { my ($class, $args) = @_; my $self = $class->SUPER::new($args); $self->{remote_key} = 0; return $self; } sub start_tls { my ($self) = @_; $self->SUPER::_start_tls(); } sub _initial_communication { my ($self) = @_; $self->{write_func}("STARTTLS\n"); my $tlsresponse = $self->{read_func}(); if (!defined $tlsresponse) { $self->{logger}("[ERROR] Bad TLS response \"\"."); return 0 } if ($tlsresponse =~ /^TLS OK/) { $self->{remote_key} = 1; } elsif ($tlsresponse !~ /^TLS MAYBE/i) { $self->{logger}("[ERROR] Bad TLS response \"$tlsresponse\"."); return 0; } return 1; } sub _use_key_if_present { my ($self) = @_; return !$self->{remote_key}; } sub _on_unverified_cert { my ($self) = @_; $self->write("quit\n"); } 1; __END__ =head1 NAME Munin::Node::TLSClient - Implements the client side of the STARTTLS protocol =head1 SYNOPSIS $tls = Munin::Node::TLSClient->new(...); $tls->start_tls(); =head1 METHODS =over =item B<new> $tls = Munin::Node::TLSClient->new(...); See L<Munin::Node::TLS> for documentation for constructor arguments. =item B<start_tls> $tls->start_tls(); Begin a STARTTLS request =back ������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/lib/Munin/Common/TLSServer.pm���������������������������������������������������0000664�0000000�0000000�00000002052�14516145741�0021505�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Munin::Common::TLSServer; use base qw(Munin::Common::TLS); use warnings; use strict; use Carp; use English qw(-no_match_vars); sub new { my ($class, $args) = @_; return $class->SUPER::new($args); } sub start_tls { my ($self) = @_; $self->SUPER::_start_tls(); } sub _initial_communication { my ($self) = @_; if ($self->{private_key_loaded}) { $self->{write_func}("TLS OK\n"); } else { $self->{write_func}("TLS MAYBE\n"); } return 1; } sub _use_key_if_present { my ($self) = @_; return $self->{private_key_loaded}; } 1; __END__ =head1 NAME Munin::Node::TLSServer - Implements the server side of the STARTTLS protocol =head1 SYNOPSIS # After receiving a STARTTLS request: $tls = Munin::Node::TLSServer->new(...); $tls->start_tls(); =head1 METHODS =over =item B<new> $tls = Munin::Node::TLSServer->new(...); See L<Munin::Node::TLS> for documentation for constructor arguments. =item B<start_tls> $tls->start_tls(); Process a STARTTLS request =back ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/lib/Munin/Common/Timeout.pm�����������������������������������������������������0000664�0000000�0000000�00000005735�14516145741�0021315�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use warnings; use strict; package Munin::Common::Timeout; use base qw(Exporter); use Carp; use English qw(-no_match_vars); BEGIN { our @EXPORT = qw( &do_with_timeout ); } # This represents the current ALRM signal setting my $current_timeout_epoch; # This sub always uses absolute epoch time reference. # This is in order to cope with eventual stealed time... # ... and to avoid complex timing computations # # $timeout is relative seconds, $timeout_epoch is absolute. sub do_with_timeout { my ($timeout, $block) = @_; croak 'Argument exception: $timeout' unless $timeout && $timeout =~ /^\d+$/; croak 'Argument exception: $block' unless ref $block eq 'CODE'; my $new_timeout_epoch = time + $timeout; # Nested timeouts cannot extend the global timeout, # and we always leave 5s for outer loop to finish itself if ($current_timeout_epoch && $new_timeout_epoch > $current_timeout_epoch - 5) { $new_timeout_epoch = $current_timeout_epoch - 5; } if ($new_timeout_epoch <= time) { # Yey ! Time's up already, don't do anything, just : "farewell !" return undef; } # Ok, going under new timeout setting my $old_current_timeout_epoch = $current_timeout_epoch; $current_timeout_epoch = $new_timeout_epoch; my $ret_value; eval { local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required alarm ($new_timeout_epoch - time); $ret_value = $block->(); }; my $err = $EVAL_ERROR; # Restore the old $current_timeout_epoch... $current_timeout_epoch = $old_current_timeout_epoch; # ... and restart the old alarm if needed if ($current_timeout_epoch) { my $timeleft = $current_timeout_epoch - time; if ($timeleft <= 0) { # no timeleft : directly raise alarm die "alarm\n"; } alarm ($timeleft); } else { # Remove the alarm alarm (0); } # And handle the return code if ($err) { return undef if $err eq "alarm\n"; die $err; # Propagate any other exceptions } return $ret_value; } 1; __END__ =head1 NAME Munin::Common::Timeout - Run code with a timeout. May nest. =head1 SYNOPSIS use Munin::Common::Timeout; do_with_timeout(50, sub { # ... do_with_timeout(5, sub { # ... # ... }); # ... }); =head1 DESCRIPTION See also L<Time::Out>, L<Sys::AlarmCall> =head1 SUBROUTINES =over =item B<do_with_timeout> my $finished_with_no_timeout = do_with_timeout($seconds, $code_ref) or die "Timed out!"; Executes $block with a timeout of $seconds. Returns the return value of the $block if it completed within the timeout. If the timeout is reached and the code is still running, it halts it and returns undef. NB: every $code_ref should return something defined, otherwise the caller doesn't know if a timeout occurred. Calls to do_with_timeout() can be nested. Any exceptions raised by $block are propagated. =back =cut # vim: ts=4 : sw=4 : et �����������������������������������munin-2.0.75/common/t/������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14516145741�0014476�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/critic.t����������������������������������������������������������������������0000664�0000000�0000000�00000000460�14516145741�0016140�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use FindBin; use lib "$FindBin::Bin/../lib"; plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD}; eval 'use Test::Perl::Critic'; plan ( skip_all => "Test::Perl::Critic required for testing coding standard" ) if $@; all_critic_ok(); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/munin_common_config.t���������������������������������������������������������0000664�0000000�0000000�00000006526�14516145741�0020717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use warnings; use strict; use Test::More 'no_plan'; use_ok('Munin::Common::Config'); # cl_is_keyword { ok( Munin::Common::Config::cl_is_keyword('logdir'), 'valid keyword'); ok(! Munin::Common::Config::cl_is_keyword('fnord'), 'invalid keyword'); } # is_keyword { ok( Munin::Common::Config->is_keyword('logdir'), 'valid keyword'); ok(! Munin::Common::Config->is_keyword('fnord'), 'invalid keyword'); } # parse_config_from_file # _trim { # input parameter has to be a variable, as it gets modified directly by _trim my $s; Munin::Common::Config->_trim($s = ''); is($s, '', 'empty line'); Munin::Common::Config->_trim($s = ' '); is($s, '', 'only whitespace'); Munin::Common::Config->_trim($s = ' leading whitespace'); is($s, 'leading whitespace'); Munin::Common::Config->_trim($s = 'trailing whitespace '); is($s, 'trailing whitespace'); Munin::Common::Config->_trim($s = ' both leading and trailing whitespace '); is($s, 'both leading and trailing whitespace'); } # _strip_comment { # input parameter has to be a variable, as it gets modified directly by _strip_comment my $s; Munin::Common::Config->_strip_comment($s = ''); is($s, '', 'empty line'); Munin::Common::Config->_strip_comment($s = 'line without comment'); is($s, 'line without comment'); Munin::Common::Config->_strip_comment($s = 'line with a simple comment ## here is the comment!'); is($s, 'line with a simple comment '); Munin::Common::Config->_strip_comment($s = 'line with an escaped \# '); is($s, 'line with an escaped # '); Munin::Common::Config->_strip_comment($s = 'line with a comment including an escaped hash # escaped \# '); is($s, 'line with a comment including an escaped hash '); Munin::Common::Config->_strip_comment($s = 'line with a comment including two \# \## escaped \# '); is($s, 'line with a comment including two # #'); } # _looks_like_a_bool { ok( Munin::Common::Config->_looks_like_a_bool('yes'), 'yes'); ok( Munin::Common::Config->_looks_like_a_bool('Yes'), 'Yes'); ok( Munin::Common::Config->_looks_like_a_bool('no'), 'no'); ok( Munin::Common::Config->_looks_like_a_bool('NO'), 'NO'); ok( Munin::Common::Config->_looks_like_a_bool('1'), 'the number 1'); ok( Munin::Common::Config->_looks_like_a_bool('0'), 'the number 0'); ok( Munin::Common::Config->_looks_like_a_bool('true'), 'true'); ok( Munin::Common::Config->_looks_like_a_bool('false'), 'false'); ok( Munin::Common::Config->_looks_like_a_bool('on'), 'on'); ok( Munin::Common::Config->_looks_like_a_bool('off'), 'off'); ok(! Munin::Common::Config->_looks_like_a_bool('falsch'), 'not a boolean'); ok(! Munin::Common::Config->_looks_like_a_bool('yes!'), 'not a boolean either'); } # _parse_bool { ok( Munin::Common::Config->_parse_bool('yes'), 'yes'); ok( Munin::Common::Config->_parse_bool('Yes'), 'Yes'); ok( Munin::Common::Config->_parse_bool('1'), 'the number 1'); ok( Munin::Common::Config->_parse_bool('true'), 'true'); ok( Munin::Common::Config->_parse_bool('on'), 'on'); ok(! Munin::Common::Config->_parse_bool('no'), 'no'); ok(! Munin::Common::Config->_parse_bool('NO'), 'NO'); ok(! Munin::Common::Config->_parse_bool('false'), 'false'); ok(! Munin::Common::Config->_parse_bool('off'), 'off'); eval { Munin::Common::Config->_parse_bool('falsch') }; like($@, qr/falsch/, 'exception on bad boolean'); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/munin_common_defaults.t�������������������������������������������������������0000664�0000000�0000000�00000000210�14516145741�0021241�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use warnings; use strict; use FindBin; use lib "$FindBin::Bin/../lib"; use Test::More tests => 1; use_ok('Munin::Common::Defaults'); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/munin_common_timeout.t��������������������������������������������������������0000664�0000000�0000000�00000002255�14516145741�0021133�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use warnings; use strict; use English qw(-no_match_vars); use Test::More tests => 6; use_ok('Munin::Common::Timeout'); BEGIN { if (Time::HiRes->can('alarm')){ Time::HiRes->import('alarm') ; } } # These test could have been made to run faster by using # Time::HiRes::alarm. Adding this to the module: # # BEGIN { # if (Time::HiRes->can('alarm')){ # Time::HiRes->import('alarm') ; # } # } # # However Time::HiRes::alarm is not compatible with nested alarms # (alarm(0) does not return time remaining) # # :( ok(do_with_timeout(1, sub {1}), "No timeout"); ok(!do_with_timeout(1, sub { for (;;) {}; return 1; }), "Timeout"); eval { do_with_timeout(1, sub {die "Test"}); }; like($EVAL_ERROR, qr/^Test/, "Exception gets propagated"); ###################################################################### { my ($stat1, $stat2); $stat1 = do_with_timeout(20, sub { $stat2 = do_with_timeout(1, sub { for (;;) {} }); return 1; }); ok(!$stat2, "Inner timed out"); ok($stat1, "Outer didn't time out during evaluation of inner"); } ###################################################################### ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/munin_common_tls.t������������������������������������������������������������0000664�0000000�0000000�00000005721�14516145741�0020250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use warnings; use strict; use Test::More tests => 4; use_ok('Munin::Common::TLS'); use_ok('Munin::Common::TLSClient'); use_ok('Munin::Common::TLSServer'); use Data::Dumper; use English qw(-no_match_vars); use FindBin; use IO::Handle; use Socket; sub do_server { my ($socket) = @_; #print $socket "Parent Pid $$ is sending this\n"; #chomp(my $line = <$socket>); #print "Parent Pid $$ just read this: `$line'\n"; chomp(my $line = <$socket>); die "Expected STARTTLS '$line'" unless $line eq 'STARTTLS'; my $tls = Munin::Common::TLSServer->new({ DEBUG => 1, logger => sub { print "LOG SERVER: ", @_, "\n" }, read_fd => fileno($socket), read_func => sub { print "Server reading ...\n"; my $line = <$socket>; print "Server done. ($line)\n"; return $line; }, tls_ca_cert => "$FindBin::Bin/tls/CA/ca_cert.pem", tls_cert => "$FindBin::Bin/tls/node_cert.pem", tls_paranoia => 1, tls_priv => "$FindBin::Bin/tls/node_key.pem", tls_vdepth => 5, tls_verify => 1, write_fd => fileno($socket), write_func => sub { print $socket @_ }, }); my $tls_session = $tls->start_tls(); die "STARTTLS failed, missing Net::SSLeay?" unless $tls_session; $line = $tls->read(); $tls->write($line); } sub do_client { my ($socket) = @_; #chomp(my $line = <$socket>); #print "Child Pid $$ just read this: `$line'\n"; #print $socket "Child Pid $$ is sending this\n"; my $tls = Munin::Common::TLSClient->new({ DEBUG => 1, logger => sub { print "LOG CLIENT: ", @_, "\n" }, read_fd => fileno($socket), read_func => sub { print "Client reading ...\n"; my $line = <$socket>; print "Client done. ($line)\n"; return $line; }, tls_ca_cert => "$FindBin::Bin/tls/CA/ca_cert.pem", tls_cert => "$FindBin::Bin/tls/master_cert.pem", tls_paranoia => 1, tls_priv => "$FindBin::Bin/tls/master_key.pem", tls_vdepth => 5, tls_verify => 1, write_fd => fileno($socket), write_func => sub { print $socket @_ }, }); my $tls_session = $tls->start_tls(); die "STARTTLS failed, missing Net::SSLeay?" unless $tls_session; my $req_msg = "ping\n"; $tls->write($req_msg); my $res_msg = $tls->read(); return $req_msg eq $res_msg; } socketpair(CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; CHILD->autoflush(1); PARENT->autoflush(1); my $pid; if ($pid = fork) { close PARENT; do_server(\*CHILD); close CHILD; waitpid($pid,0); my $child_exit_status = $CHILD_ERROR >> 8; # FIX becomes 1 for some reason ... is($CHILD_ERROR, 256, "Status OK"); # FIX Want to test $child_exit_status == 0 } else { die "cannot fork: $!" unless defined $pid; close CHILD; my $stat = do_client(\*PARENT); close PARENT; exit $stat ? 0 : 1; } �����������������������������������������������munin-2.0.75/common/t/no-fixes.t��������������������������������������������������������������������0000664�0000000�0000000�00000002521�14516145741�0016413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# vim: ts=4 : sw=4 : et use warnings; use strict; # Check for comments and POD flagged with FIX. # Are there any modules that already does this? # Perl::Critic::Policy::Bangs::ProhibitFlagComments doesn't consider # POD. Make a Perl::Critic::Policy out of this? use English qw(-no_match_vars); use File::Find; use FindBin; use Test::More; use Pod::Simple::TextContent; if ($ENV{TEST_POD}) { plan tests => 1; } else { plan skip_all => 'set TEST_POD to enable this test' } my $count = 0; find(\&fixes, "$FindBin::Bin/../lib"); is($count, 0, "Should not find any FIX comments"); sub fixes { my $file = $File::Find::name; # skip SVN stuff if ( -d and m{\.svn}) { $File::Find::prune = 1; return; } return unless -f $file; # # Check comments # open my $F, '<', $file or warn "Couldn't open $file: $!" && return; while (<$F>) { if (m{#\s*FIX}) { printf "Found a FIX comment at %s: %d\n", $file, $.; $count++; } } close $F; # # Check POD # my $pod_parser = Pod::Simple::TextContent->new; my $pod = ""; $pod_parser->output_string(\$pod); $pod_parser->parse_file($file); my $pod_count = scalar(grep {/FIX/} split /\n/, $pod); printf "Found %d FIX(es) in POD in %s\n", $pod_count,$file if $pod_count; $count += $pod_count; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/pod-coverage.t����������������������������������������������������������������0000664�0000000�0000000�00000000465�14516145741�0017243�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use FindBin; use lib "$FindBin::Bin/../lib"; plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD}; eval 'use Test::Pod::Coverage'; plan ( skip_all => "Test::Pod::Coverage required for testing POD coverage" ) if $@; all_pod_coverage_ok(); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/pod.t�������������������������������������������������������������������������0000664�0000000�0000000�00000000440�14516145741�0015443�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use Test::More; use FindBin; use lib "$FindBin::Bin/../lib"; plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD}; eval 'use Test::Pod'; plan ( skip_all => "Test::Pod required for testing for POD errors" ) if $@; all_pod_files_ok(); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/tls/��������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14516145741�0015300�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/tls/CA/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14516145741�0015563�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/tls/CA/ca_cert.pem������������������������������������������������������������0000664�0000000�0000000�00000003270�14516145741�0017670�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������-----BEGIN CERTIFICATE----- MIIEyDCCA7CgAwIBAgIUWtSxMOW82eJPsbsxT8CoZ6I8fGkwDQYJKoZIhvcNAQEL BQAwgZYxCzAJBgNVBAYTAk1VMRowGAYDVQQIDBFQcm92aW5jZSBvZiBNdW5pbjET MBEGA1UEBwwKTXVuaW4gVG93bjETMBEGA1UECgwKTXVuaW4gSW5jLjELMAkGA1UE CwwCQ0ExEjAQBgNVBAMMCTEyNy4wLjAuMTEgMB4GCSqGSIb3DQEJARYRbXVuaW5A ZXhhbXBsZS5vcmcwHhcNMTgxMTAxMDM0OTE4WhcNMjgxMDI5MDM0OTE4WjCBljEL MAkGA1UEBhMCTVUxGjAYBgNVBAgMEVByb3ZpbmNlIG9mIE11bmluMRMwEQYDVQQH DApNdW5pbiBUb3duMRMwEQYDVQQKDApNdW5pbiBJbmMuMQswCQYDVQQLDAJDQTES MBAGA1UEAwwJMTI3LjAuMC4xMSAwHgYJKoZIhvcNAQkBFhFtdW5pbkBleGFtcGxl Lm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALxwztS2TLsqVV5J zTg5GsBxO7ajuNJ1xANa1+Fv6rnjo3fE4hu66HcmjOMv1odk/dXte2w9HFctQ7Bg XBtUZea3xOH4pPYLiz+YsrdDCi5WwQUIsCxKvD0qVW7mQhsjadGzsN2kFSWXMBR4 vs8zPo2gAGdH1zhtY+rxdu6v8jgqwEKSzwk5Hd+t8CSOSIXVACPPm42dxNhT1sv1 s5fdG+Jew5qDKpSiLeikbOHBnkD8jyrVcO2YvYAzZKL6UhXUVswJQbCpIJRPuiol 4HRIL2Sp/eNUeEA9geh5QhrcT7wD0nTKPltGnHQQuZf1duj6niE9nQv7K6rfJwdQ qrUbERMCAwEAAaOCAQowggEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFCAhB/IB dM2Lo+7kqJy2rvAbOSnMMIHWBgNVHSMEgc4wgcuAFCAhB/IBdM2Lo+7kqJy2rvAb OSnMoYGcpIGZMIGWMQswCQYDVQQGEwJNVTEaMBgGA1UECAwRUHJvdmluY2Ugb2Yg TXVuaW4xEzARBgNVBAcMCk11bmluIFRvd24xEzARBgNVBAoMCk11bmluIEluYy4x CzAJBgNVBAsMAkNBMRIwEAYDVQQDDAkxMjcuMC4wLjExIDAeBgkqhkiG9w0BCQEW EW11bmluQGV4YW1wbGUub3JnghRa1LEw5bzZ4k+xuzFPwKhnojx8aTANBgkqhkiG 9w0BAQsFAAOCAQEAPdomAFKx4UyxcK/Y/abModSuN36GdnIZP5eyZmWtDYQkTw74 21TyZx6iYHOq/TUtF4zzWz6QHUXGbJbggOrMkLz9G3haRcFW3kv14+fch4ywkmd0 i1tIhY1Pqyq8oMccrtSfBWOj31faH6GpdRejmhUZWYvmpCHGRofzuEjlFWdipuUs KLo7HsEEOF4UqM/T9eUJCloEmt1/rwegPZWo6GyPEVkZ09zhlsmY8/olDKJ09Asb r6qYflpWkDAyjFFgnEY+Ef3cf8IC7kPai0FRMOTlPY9miUB9uf7Z59PNRcVszBr6 Sm47SMD03pY46BnXr9G6KHB/WD6nrFC6+LiP8g== -----END CERTIFICATE----- ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/tls/Makefile������������������������������������������������������������������0000664�0000000�0000000�00000002043�14516145741�0016737�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������SUBJECT_CA = /C=MU/ST=Province of Munin/L=Munin Town/O=Munin Inc./OU=CA/CN=127.0.0.1/emailAddress=munin@example.org/ SUBJECT_MASTER = /C=MU/ST=Province of Munin/L=Munin Town/O=Munin Inc./OU=Master/CN=127.0.0.1/emailAddress=munin@example.org/ SUBJECT_NODE = /C=MU/ST=Province of Munin/L=Munin Town/O=Munin Inc./OU=Node/CN=127.0.0.1/emailAddress=munin@example.org/ .PHONY: all clean all: mkdir CA mkdir CA/newcerts CA/private touch CA/index.txt echo '01' > CA/serial openssl req -new -nodes -x509 -extensions v3_ca -subj "$(SUBJECT_CA)" -keyout CA/private/ca_key.pem -out CA/ca_cert.pem -days 3650 -config ./openssl.cnf openssl req -new -nodes -subj "$(SUBJECT_NODE)" -keyout node_key.pem -out node_req.pem -config ./openssl.cnf openssl req -new -nodes -subj "$(SUBJECT_MASTER)" -keyout master_key.pem -out master_req.pem -config ./openssl.cnf yes | openssl ca -out node_cert.pem -in node_req.pem -config ./openssl.cnf -days 3650 yes | openssl ca -out master_cert.pem -in master_req.pem -config ./openssl.cnf -days 3650 clean: rm -r CA rm *.pem ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/tls/README��������������������������������������������������������������������0000664�0000000�0000000�00000001737�14516145741�0016170�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ SETTING UP A TESTBED FOR IMPLEMENTING TLS USING OPENSSL http://www.debian-administration.org/articles/284 * Configuration See openssl.cnf * Establish a CA (Self signe root certificate) ** Create database mkdir CA cd CA mkdir newcerts private echo '01' > serial touch index.txt ** Create key and certificate. Don't need to encrypt the key since this is just for testing. openssl req -new -nodes -x509 -extensions v3_ca -keyout CA/private/ca_key.pem -out CA/ca_cert.pem -days 3650 -config ./openssl.cnf * Create and sign certificates for each peer ** Create keys and certificate requests openssl req -new -nodes -keyout node_key.pem -out node_req.pem -config ./openssl.cnf openssl req -new -nodes -keyout master_key.pem -out master_req.pem -config ./openssl.cnf ** Sign openssl ca -out node_cert.pem -in node_req.pem -config ./openssl.cnf -days 3650 openssl ca -out master_cert.pem -in master_req.pem -config ./openssl.cnf -days 3650 ���������������������������������munin-2.0.75/common/t/tls/master_cert.pem�����������������������������������������������������������0000664�0000000�0000000�00000007647�14516145741�0020331�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Certificate: Data: Version: 1 (0x0) Serial Number: 2 (0x2) Signature Algorithm: sha256WithRSAEncryption Issuer: C=MU, ST=Province of Munin, L=Munin Town, O=Munin Inc., OU=CA, CN=127.0.0.1/emailAddress=munin@example.org Validity Not Before: Nov 1 03:49:21 2018 GMT Not After : Oct 29 03:49:21 2028 GMT Subject: C=MU, ST=Province of Munin, O=Munin Inc., OU=Master, CN=127.0.0.1 Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:cb:62:78:95:15:55:0f:16:a5:6a:3a:99:83:4d: b0:38:2f:4c:58:22:76:cf:48:a3:36:ef:47:2c:86: 8b:bb:cc:33:41:40:cf:14:90:b3:05:d0:38:02:10: 95:75:62:d6:75:ec:9c:fe:1e:cf:fe:8a:42:ee:a7: fa:80:63:3e:d5:fc:1d:e9:a8:b8:d3:96:9f:67:4c: be:56:7e:3f:1b:ca:90:58:14:80:ea:ec:eb:5d:e9: 5f:21:4b:a5:e7:ef:fb:43:b4:70:7f:d0:02:ac:31: 91:ef:8e:26:96:6c:aa:fa:ba:95:6a:a7:25:8b:8f: 6c:fc:8a:eb:bb:b0:c4:0d:cc:65:96:78:1e:ce:10: ab:09:50:ac:7a:96:9b:cf:a6:89:46:6c:be:94:4e: 6c:3c:42:57:02:63:94:16:30:f4:99:dc:9f:f3:e6: f6:30:50:29:04:bd:e9:79:12:46:4b:f1:54:f3:c7: c7:6a:e9:fd:bd:12:40:ff:01:e8:f8:9c:5a:11:50: 3d:06:ce:1c:12:fb:a2:dd:c6:b4:ff:31:29:7d:02: ce:10:0b:9b:7b:33:02:0d:1c:a0:30:24:06:7b:e7: 4f:ae:04:39:bc:5e:10:20:8f:18:d3:2d:02:3c:e9: 45:87:a5:ac:28:5e:80:12:be:43:37:fe:42:5d:29: 7b:b5 Exponent: 65537 (0x10001) Signature Algorithm: sha256WithRSAEncryption 41:d8:09:20:d2:98:40:8c:f3:40:a5:c9:fd:3b:24:f9:6b:ed: 88:1c:a8:04:36:9d:ac:ee:34:70:a7:e6:bf:8a:23:66:f2:ce: b5:8d:a2:59:72:b5:8b:de:bd:63:ce:c2:85:24:51:28:b9:1c: 6b:74:b7:35:94:84:c8:17:b6:2a:95:02:e2:63:a5:9a:fb:13: c1:a8:e2:e2:73:cc:28:31:fc:9c:ec:b1:7e:a5:22:55:22:56: 16:c3:71:1b:13:bc:e2:4e:4b:d9:ef:2f:13:e2:12:7b:d0:0a: 70:05:57:aa:f1:5b:d4:22:9d:38:20:12:93:30:6d:e6:65:24: 2d:7f:34:5f:64:6e:ed:a0:72:8d:a0:ec:a5:9f:8f:8d:b5:8d: 57:01:6a:e9:8e:48:71:2d:4e:5a:cc:6e:fb:2f:5c:49:d3:b7: 1f:ea:a3:65:cc:a5:ce:d6:5e:18:70:74:a4:7a:e7:dd:b7:38: 51:7b:20:2f:8f:29:c5:86:af:e4:26:da:da:9d:16:5e:a2:45: 0d:10:31:25:bd:64:d8:28:23:c9:dc:44:3a:61:7a:98:e0:b9: 1c:a9:06:6c:9c:19:cb:6c:00:38:00:e6:3a:00:d0:4a:60:ab: 70:5e:5f:ae:ed:96:84:83:e1:b0:11:f5:c2:95:e4:f3:53:71: 47:23:75:e6 -----BEGIN CERTIFICATE----- MIIDbjCCAlYCAQIwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNVBAYTAk1VMRowGAYD VQQIDBFQcm92aW5jZSBvZiBNdW5pbjETMBEGA1UEBwwKTXVuaW4gVG93bjETMBEG A1UECgwKTXVuaW4gSW5jLjELMAkGA1UECwwCQ0ExEjAQBgNVBAMMCTEyNy4wLjAu MTEgMB4GCSqGSIb3DQEJARYRbXVuaW5AZXhhbXBsZS5vcmcwHhcNMTgxMTAxMDM0 OTIxWhcNMjgxMDI5MDM0OTIxWjBjMQswCQYDVQQGEwJNVTEaMBgGA1UECAwRUHJv dmluY2Ugb2YgTXVuaW4xEzARBgNVBAoMCk11bmluIEluYy4xDzANBgNVBAsMBk1h c3RlcjESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAy2J4lRVVDxalajqZg02wOC9MWCJ2z0ijNu9HLIaLu8wzQUDPFJCz BdA4AhCVdWLWdeyc/h7P/opC7qf6gGM+1fwd6ai405afZ0y+Vn4/G8qQWBSA6uzr XelfIUul5+/7Q7Rwf9ACrDGR744mlmyq+rqVaqcli49s/Irru7DEDcxllngezhCr CVCsepabz6aJRmy+lE5sPEJXAmOUFjD0mdyf8+b2MFApBL3peRJGS/FU88fHaun9 vRJA/wHo+JxaEVA9Bs4cEvui3ca0/zEpfQLOEAubezMCDRygMCQGe+dPrgQ5vF4Q II8Y0y0CPOlFh6WsKF6AEr5DN/5CXSl7tQIDAQABMA0GCSqGSIb3DQEBCwUAA4IB AQBB2Akg0phAjPNApcn9OyT5a+2IHKgENp2s7jRwp+a/iiNm8s61jaJZcrWL3r1j zsKFJFEouRxrdLc1lITIF7YqlQLiY6Wa+xPBqOLic8woMfyc7LF+pSJVIlYWw3Eb E7ziTkvZ7y8T4hJ70ApwBVeq8VvUIp04IBKTMG3mZSQtfzRfZG7toHKNoOyln4+N tY1XAWrpjkhxLU5azG77L1xJ07cf6qNlzKXO1l4YcHSkeufdtzhReyAvjynFhq/k JtranRZeokUNEDElvWTYKCPJ3EQ6YXqY4LkcqQZsnBnLbAA4AOY6ANBKYKtwXl+u 7ZaEg+GwEfXCleTzU3FHI3Xm -----END CERTIFICATE----- �����������������������������������������������������������������������������������������munin-2.0.75/common/t/tls/master_key.pem������������������������������������������������������������0000664�0000000�0000000�00000003250�14516145741�0020146�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDLYniVFVUPFqVq OpmDTbA4L0xYInbPSKM270cshou7zDNBQM8UkLMF0DgCEJV1YtZ17Jz+Hs/+ikLu p/qAYz7V/B3pqLjTlp9nTL5Wfj8bypBYFIDq7Otd6V8hS6Xn7/tDtHB/0AKsMZHv jiaWbKr6upVqpyWLj2z8iuu7sMQNzGWWeB7OEKsJUKx6lpvPpolGbL6UTmw8QlcC Y5QWMPSZ3J/z5vYwUCkEvel5EkZL8VTzx8dq6f29EkD/Aej4nFoRUD0GzhwS+6Ld xrT/MSl9As4QC5t7MwINHKAwJAZ750+uBDm8XhAgjxjTLQI86UWHpawoXoASvkM3 /kJdKXu1AgMBAAECggEBAMA8xg7cB/boB9fdpaZHMy4X/sg7YyNP2/2bsyFx1vfd 7gBaeqFMl/oBsj65YBuBHYZ+7HEBvMUx+EvN+GeL5yOpVri5YmGUN/0Su8HTOGFT WJX4Y1YqM9ojMGivMRSGb2+ySLd2bGBHxlGwgeaitgWhgIM7k7BTE8eso15XmSgT /5jR+6E5c4uvfmRcKshVKjUfpzb4bVhyk3LMH2uFyTFzBunLHV4hbyU318+0aJpr fHgJ1zYMymDWEfwZesvRcXfz1R0xu0MSHiAblH5QjChdQ9P02VZ4TcljurMtwOZf YkKyTpBLYj/a0NMFOde3QE42gwzLURUU0Dt7JPMZGWECgYEA7mGqb4dwemgpmwgU BkmBvl+l0gD2+wncMjI+PUqmH6HxL9SB+oJyUcwDulZZZsPXGkgPnAYs70VfKoqh AvAE6wYJ0OddTIAYdE43A2oOqvY9aycHL52mgAU4Uz68SNUQ0dKViPvctJS5MjPy JcA5iao2dh95TZPwDInBAu0IeIkCgYEA2mqkUIMi6YvpDoOBbSQutXW4KjQzN7+u BBfL1QixaF5QjA1Z2mS12gEcBpALJDrupW25OOl6BKulTk+7YVsuuf426ovL6P+7 nVdJnf5288aNqQdfjvvY3vGD5p80OOef/9ezzOwOTy4idd7JVzuJyH8o+YsNmVkp RJ52kycvxs0CgYAnib9xHC/Fvb0Y37AiktKYNtkUCeDtJmeFU9wK4nTHTl7opgCh Aj3f30zG/Ud6ygTFcSpVoJiXXxQ3xCKMkC7gpHHAWn+ZDq3/rHFneZHRPWJWgXSp qyZQmY0MEZ29HsnZY2o1/EBRG1HUR9Vj+YwRj0/VK8c83Mq16as/xvzesQKBgQCf n8BmdvBU7sNsr/8u3Hqmc1ocvfmQjK2IoDt/fdLqC8OiFz/LtD6TDKyGv07lM4yl qgLr7PYX47nz+aFgDVug4oEP+QsVRZC/9MMAjLAyiLGPDqxvuu9MQAW9zixESkNM nz/wS7RJedYFfsR21DRmK0iu10khAWB/na/a65CnIQKBgDg5aUgPYjo5V9rQU5Xy 8Qm1V/WP424bM2C9tpOc6vlShlgeie7gUKB4St4JnjPE7GYOt80LxianfmG9v56S fKfOMm6k9WaaU8Pd+9SJSGTAaIYHN8VRBH2xwAh7rsHSGuK3dnNBKv4BEzmb/FFQ P8sJupQvqEvhNRuo7oBgbk/1 -----END PRIVATE KEY----- ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/tls/node_cert.pem�������������������������������������������������������������0000664�0000000�0000000�00000007645�14516145741�0017761�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Certificate: Data: Version: 1 (0x0) Serial Number: 1 (0x1) Signature Algorithm: sha256WithRSAEncryption Issuer: C=MU, ST=Province of Munin, L=Munin Town, O=Munin Inc., OU=CA, CN=127.0.0.1/emailAddress=munin@example.org Validity Not Before: Nov 1 03:49:21 2018 GMT Not After : Oct 29 03:49:21 2028 GMT Subject: C=MU, ST=Province of Munin, O=Munin Inc., OU=Node, CN=127.0.0.1 Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:9e:bf:2a:53:9b:3f:c8:26:24:80:02:15:9f:5d: 4b:96:2b:4d:81:16:48:fe:8b:a3:80:2e:c0:6b:07: 9d:cf:f5:1c:83:59:92:35:c2:df:bb:e8:f8:b3:1a: a8:0a:ed:4d:05:52:03:47:af:e4:e7:12:c2:23:94: e6:14:8e:87:cf:b3:3e:26:54:5a:b3:3d:5a:d8:45: 07:6f:d0:94:7a:e9:d7:ac:d5:e1:9a:0e:1b:4c:12: a0:c1:75:b6:13:3c:75:5b:08:51:3d:e4:3c:dd:a8: 02:16:a4:ee:2a:a6:fc:da:e6:31:ba:54:ac:26:96: b9:eb:f0:77:a9:99:1a:d8:a5:9e:ea:69:15:95:f9: e9:9c:0d:ff:d6:7f:7c:32:d4:31:dd:21:19:1f:45: e4:36:8c:8e:14:6c:9e:92:56:b2:27:f8:bd:aa:1a: 90:c5:c0:40:8f:27:30:19:ce:6d:51:1b:62:e4:3d: 19:8b:73:2a:1e:b5:fe:14:66:b7:12:8c:bc:41:a1: a6:00:d3:2a:9c:fc:1b:b9:70:94:b4:53:3b:0c:d1: 49:e4:3e:b7:2f:8f:0e:ed:bd:7b:a2:1c:d8:f7:6b: 26:5b:ff:4a:a3:83:02:3b:6e:9d:22:12:d2:8e:0d: 37:f4:1f:5b:44:d5:df:35:f7:1b:39:02:73:4f:ea: 11:75 Exponent: 65537 (0x10001) Signature Algorithm: sha256WithRSAEncryption 7c:18:5e:6e:a0:c3:7b:d5:bd:b4:16:40:74:77:0d:ce:a4:7a: 1a:d3:58:1e:15:9d:25:53:6c:a6:58:a4:71:fb:53:ea:52:d4: 56:a3:f3:2d:b7:f8:2f:af:22:bc:1f:b1:1b:cb:35:d8:d4:c6: ac:89:f4:d2:f5:17:80:86:e4:5c:0e:4a:ec:a4:e8:fe:93:21: 77:2e:cd:bd:8c:1e:6e:eb:65:82:35:42:53:3e:26:e4:f8:7c: 14:08:d0:a3:3f:c1:1c:69:a2:2e:84:55:32:f9:96:7f:b9:b9: cb:e8:7a:77:58:55:f4:26:e5:63:10:58:88:35:34:f9:ca:8d: 1d:24:f9:fa:36:c8:ce:f0:99:8b:6f:b8:1d:11:01:97:95:8f: 83:6a:ac:25:3a:01:e8:16:29:82:11:15:7b:c9:56:6e:21:b2: 58:fe:57:c8:3a:3b:d2:94:49:c5:a7:f1:c2:69:5a:fb:03:3b: f9:c8:8a:23:2d:58:23:4e:11:d1:8f:46:80:ec:e8:a4:d8:0c: 39:bc:e4:d6:ea:5f:4c:dd:d3:4d:3b:ed:02:c3:45:2e:f4:0f: 07:4c:ff:21:46:d2:dc:fe:50:7b:65:2f:94:34:7c:80:e3:f3: ff:3e:59:46:4d:8b:1f:70:da:25:17:73:93:ca:d3:87:1d:fa: 97:69:77:31 -----BEGIN CERTIFICATE----- MIIDbDCCAlQCAQEwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNVBAYTAk1VMRowGAYD VQQIDBFQcm92aW5jZSBvZiBNdW5pbjETMBEGA1UEBwwKTXVuaW4gVG93bjETMBEG A1UECgwKTXVuaW4gSW5jLjELMAkGA1UECwwCQ0ExEjAQBgNVBAMMCTEyNy4wLjAu MTEgMB4GCSqGSIb3DQEJARYRbXVuaW5AZXhhbXBsZS5vcmcwHhcNMTgxMTAxMDM0 OTIxWhcNMjgxMDI5MDM0OTIxWjBhMQswCQYDVQQGEwJNVTEaMBgGA1UECAwRUHJv dmluY2Ugb2YgTXVuaW4xEzARBgNVBAoMCk11bmluIEluYy4xDTALBgNVBAsMBE5v ZGUxEjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBAJ6/KlObP8gmJIACFZ9dS5YrTYEWSP6Lo4AuwGsHnc/1HINZkjXC37vo +LMaqArtTQVSA0ev5OcSwiOU5hSOh8+zPiZUWrM9WthFB2/QlHrp16zV4ZoOG0wS oMF1thM8dVsIUT3kPN2oAhak7iqm/NrmMbpUrCaWuevwd6mZGtilnuppFZX56ZwN /9Z/fDLUMd0hGR9F5DaMjhRsnpJWsif4vaoakMXAQI8nMBnObVEbYuQ9GYtzKh61 /hRmtxKMvEGhpgDTKpz8G7lwlLRTOwzRSeQ+ty+PDu29e6Ic2PdrJlv/SqODAjtu nSIS0o4NN/QfW0TV3zX3GzkCc0/qEXUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA fBhebqDDe9W9tBZAdHcNzqR6GtNYHhWdJVNsplikcftT6lLUVqPzLbf4L68ivB+x G8s12NTGrIn00vUXgIbkXA5K7KTo/pMhdy7NvYwebutlgjVCUz4m5Ph8FAjQoz/B HGmiLoRVMvmWf7m5y+h6d1hV9CblYxBYiDU0+cqNHST5+jbIzvCZi2+4HREBl5WP g2qsJToB6BYpghEVe8lWbiGyWP5XyDo70pRJxafxwmla+wM7+ciKIy1YI04R0Y9G gOzopNgMObzk1upfTN3TTTvtAsNFLvQPB0z/IUbS3P5Qe2UvlDR8gOPz/z5ZRk2L H3DaJRdzk8rThx36l2l3MQ== -----END CERTIFICATE----- �������������������������������������������������������������������������������������������munin-2.0.75/common/t/tls/node_key.pem��������������������������������������������������������������0000664�0000000�0000000�00000003250�14516145741�0017600�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCevypTmz/IJiSA AhWfXUuWK02BFkj+i6OALsBrB53P9RyDWZI1wt+76PizGqgK7U0FUgNHr+TnEsIj lOYUjofPsz4mVFqzPVrYRQdv0JR66des1eGaDhtMEqDBdbYTPHVbCFE95DzdqAIW pO4qpvza5jG6VKwmlrnr8HepmRrYpZ7qaRWV+emcDf/Wf3wy1DHdIRkfReQ2jI4U bJ6SVrIn+L2qGpDFwECPJzAZzm1RG2LkPRmLcyoetf4UZrcSjLxBoaYA0yqc/Bu5 cJS0UzsM0UnkPrcvjw7tvXuiHNj3ayZb/0qjgwI7bp0iEtKODTf0H1tE1d819xs5 AnNP6hF1AgMBAAECggEAXHWlVnrXBGFK4w/TvjVdoAJqquPq8e8s9KzGP9B41zXB PLO/1/Y/z7eamt7Mv0iFkrK43FkTbo7balQASjrV2WBhs3twNHNo5QwXj8WhpZFr n+wW8V6EAaPb47IGsV+GTtYiU3ULCmMfjEbVvRvzrB9lGUzV08f4U/aEC9Dz9Phf HBIBPfrSswxCpC3R+waccDDBr0TrSgCBaFPGdkm3Sk8Jk9LwflmQMxqiedS1uT1h 83I+TTyGYHtaVCzGF1pJaxVI5z2+X/8gbpoA+zgIVnzayqgy+tnGW1Fo4SFcHqBT 9GV1i56c9qYfMHOaAppmFVlueQJEt2s25wUERctWpQKBgQDN0VSPyYab2L0zo7AI vZAdJvx292C0DU8zT8DUYiLbKkkcNBkbQb5KB096WnQtBfwvKagt6yCRfD2JX5ZM CeICKlSz0f7O/jbgQf5ff4fTel0IyQlA4Ae7L1+6pZqPapPbh1doeAS5sL5/XziM TQ7i5Zl7xHrIxqhjfNEpkTbpewKBgQDFc8YKCLPe9oqXTooQFGyqoHj6LITQ/MTo KMKS4MY45Qe5Nc5aJolaeb1XmKI8mukLY7Bb611Zf4KxB3kaLydmCmfYttYXPaXc w31zdLqkEcPw5dt+pY5+wnBRCrZIShYp0oUyb0h99WPvtZhPsguBmOzEkjleTjQw 0oUBX+WlzwKBgQC3xYJgxMtpWlRNyIlEtKX6MbZZLzbsbbnbEgoWWO3QjWgsQ6Rr Pv9+sBpaIGv9S4vKPMLLBVfWL33urCfSCzz/O/bXfNis0DYFdUeA06levbJWRtL5 V0/v5jPuvzFLfxk4ehck0408lED0D/y44ZE6LVInPd94aFEwS+Gi4OOMAwKBgQDE QlglHxNSWjHVRT6bzwFX89trpxZ499XsgJ8uCPdeE+BCasXp+4XpBTp7N/6CSOO8 62CfbOnDjKdluOzZZuc9WLhxwwI35ZHYRgOK4AqoC/R7nK04S+y/+BpVo1Uds24m MdctE0m10VR9Lj3vcSRYs50bodAd+ZOMt6N2g1ArJwKBgBuLxgdH1OztXiB27WuP VNHioNLw230vI0p4OhyVxCLHmosX9DyQWTt0l+7Xc/zmIIlzNv7i2XlSEesVIv+f VodXMNEjyFdD5mdiBEc5lCrByd2RH5fqIaWa1BBbhza50vHW2toXof+9Xuj0hiux y7b0H/NLY88/7vRCiXGr5WDD -----END PRIVATE KEY----- ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/common/t/tls/openssl.cnf���������������������������������������������������������������0000664�0000000�0000000�00000001707�14516145741�0017460�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # OpenSSL configuration file. # # Establish working directory. dir = . [ ca ] default_ca = CA_default [ CA_default ] serial = $dir/CA/serial database = $dir/CA/index.txt new_certs_dir = $dir/CA/newcerts certificate = $dir/CA/ca_cert.pem private_key = $dir/CA/private/ca_key.pem default_md = default email_in_dn = no policy = policy_match [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ req ] default_bits = 2048 string_mask = utf8only distinguished_name = req_distinguished_name prompt = no req_extensions = v3_req [ req_distinguished_name ] [ v3_ca ] basicConstraints = CA:TRUE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always [ v3_req ] basicConstraints = CA:FALSE subjectKeyIdentifier = hash ���������������������������������������������������������munin-2.0.75/contrib/�������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14516145741�0014403�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/contrib/Net-Server.patch���������������������������������������������������������������0000664�0000000�0000000�00000017243�14516145741�0017425�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������This patch file has been copied off the Debian. Their Net::Server package can be found at http://packages.qa.debian.org/libn/libnet-server-perl.html diff -ur lib.orig/Net/Server/Proto/SSLEAY.pm lib/Net/Server/Proto/SSLEAY.pm --- lib.orig/Net/Server/Proto/SSLEAY.pm 2010-07-09 18:44:48.000000000 +0200 +++ lib/Net/Server/Proto/SSLEAY.pm 2011-04-06 16:32:19.835579843 +0200 @@ -23,7 +23,7 @@ use strict; use vars qw($VERSION $AUTOLOAD @ISA); -use IO::Socket::INET; +use IO::Socket::INET6; use Fcntl (); use Errno (); use Socket (); @@ -38,7 +38,7 @@ } $VERSION = $Net::Server::VERSION; # done until separated -@ISA = qw(IO::Socket::INET); +@ISA = qw(IO::Socket::INET6); sub object { my $type = shift; @@ -48,9 +48,12 @@ my $prop = $server->{'server'}; my $host; - if ($port =~ m/^([\w\.\-\*\/]+):(\w+)$/) { # allow for things like "domain.com:80" + if ($port =~ m/^([\w\.\-\*\/]+):(\w+)$/) { # allow for things like "domain.com:80" (IPv4) ($host, $port) = ($1, $2); } + elsif( $port =~ m/^\[([\:\w\.\-\*\/]+)\]:(\w+)$/ ){ # allow for things like "[::1]:80" (IPv6) + ($host,$port) = ($1,$2); + } elsif ($port =~ /^(\w+)$/) { # allow for things like "80" ($host, $port) = ($default_host, $1); } diff -ur lib.orig/Net/Server/Proto/SSL.pm lib/Net/Server/Proto/SSL.pm --- lib.orig/Net/Server/Proto/SSL.pm 2010-05-05 05:13:03.000000000 +0200 +++ lib/Net/Server/Proto/SSL.pm 2011-04-05 14:39:39.788076698 +0200 @@ -39,10 +39,14 @@ my $prop = $server->{server}; my $host; - ### allow for things like "domain.com:80" + ### allow for things like "domain.com:80" (IPv4) if( $port =~ m/^([\w\.\-\*\/]+):(\w+)$/ ){ ($host,$port) = ($1,$2); + ### allow for things like "[::1]:80" (IPv6) + }elsif( $port =~ m/^\[([\:\w\.\-\*\/]+)\]:(\w+)$/ ){ + ($host,$port) = ($1,$2); + ### allow for things like "80" }elsif( $port =~ /^(\w+)$/ ){ ($host,$port) = ($default_host,$1); diff -ur lib.orig/Net/Server/Proto/TCP.pm lib/Net/Server/Proto/TCP.pm --- lib.orig/Net/Server/Proto/TCP.pm 2010-05-05 06:41:08.000000000 +0200 +++ lib/Net/Server/Proto/TCP.pm 2011-04-05 14:29:26.123577536 +0200 @@ -23,10 +23,10 @@ use strict; use vars qw($VERSION $AUTOLOAD @ISA); -use IO::Socket (); +use IO::Socket::INET6 (); $VERSION = $Net::Server::VERSION; # done until separated -@ISA = qw(IO::Socket::INET); +@ISA = qw(IO::Socket::INET6); sub object { my $type = shift; @@ -35,10 +35,14 @@ my ($default_host,$port,$server) = @_; my $host; - ### allow for things like "domain.com:80" + ### allow for things like "domain.com:80" (IPv4) if( $port =~ m/^([\w\.\-\*\/]+):(\w+)$/ ){ ($host,$port) = ($1,$2); + ### allow for things like "[::1]:80" (IPv6) + }elsif( $port =~ m/^\[([\:\w\.\-\*\/]+)\]:(\w+)$/ ){ + ($host,$port) = ($1,$2); + ### allow for things like "80" }elsif( $port =~ /^(\w+)$/ ){ ($host,$port) = ($default_host,$1); diff -ur lib.orig/Net/Server.pm lib/Net/Server.pm --- lib.orig/Net/Server.pm 2010-07-09 16:55:31.000000000 +0200 +++ lib/Net/Server.pm 2011-04-06 16:33:57.739576765 +0200 @@ -25,7 +25,8 @@ use strict; use vars qw($VERSION); -use Socket qw(inet_aton inet_ntoa AF_INET AF_UNIX SOCK_DGRAM SOCK_STREAM); +use Socket qw(unpack_sockaddr_in sockaddr_family AF_INET AF_INET6 AF_UNIX SOCK_DGRAM SOCK_STREAM); +use Socket6 qw(inet_ntop inet_pton unpack_sockaddr_in6); use IO::Socket (); use IO::Select (); use POSIX (); @@ -356,7 +357,7 @@ push @{ $prop->{host} }, (($prop->{host}->[-1]) x (@{ $prop->{port} } - @{ $prop->{host}})); # augment hosts with as many as port foreach my $host (@{ $prop->{host} }) { $host = '*' if ! defined $host || ! length $host;; - $host = ($host =~ /^([\w\.\-\*\/]+)$/) ? $1 : $self->fatal("Unsecure host \"$host\""); + $host = ($host =~ /^([\[\]\:\w\.\-\*\/]+)$/) ? $1 : $self->fatal("Unsecure host \"$host\""); } $prop->{proto} = [] if ! defined $prop->{proto}; @@ -757,12 +758,14 @@ ### read information about this connection my $sockname = getsockname( $sock ); if( $sockname ){ + $prop->{sockfamily} = sockaddr_family( $sockname ); ($prop->{sockport}, $prop->{sockaddr}) - = Socket::unpack_sockaddr_in( $sockname ); - $prop->{sockaddr} = inet_ntoa( $prop->{sockaddr} ); + = ($prop->{sockfamily} == AF_INET6) ? unpack_sockaddr_in6( $sockname ) : unpack_sockaddr_in( $sockname ); + $prop->{sockaddr} = inet_ntop( $prop->{sockfamily}, $prop->{sockaddr} ); }else{ ### does this only happen from command line? + $prop->{sockfamily} = AF_INET; $prop->{sockaddr} = $ENV{'REMOTE_HOST'} || '0.0.0.0'; $prop->{sockhost} = 'inet.test'; $prop->{sockport} = 0; @@ -773,17 +776,17 @@ if( $prop->{udp_true} ){ $proto_type = 'UDP'; ($prop->{peerport} ,$prop->{peeraddr}) - = Socket::sockaddr_in( $prop->{udp_peer} ); + = ($prop->{sockfamily} == AF_INET6) ? unpack_sockaddr_in6( $prop->{udp_peer} ) : unpack_sockaddr_in( $prop->{udp_peer} ); }elsif( $prop->{peername} = getpeername( $sock ) ){ ($prop->{peerport}, $prop->{peeraddr}) - = Socket::unpack_sockaddr_in( $prop->{peername} ); + = ($prop->{sockfamily} == AF_INET6) ? unpack_sockaddr_in6( $prop->{peername} ) : unpack_sockaddr_in( $prop->{peername} ); } if( $prop->{peername} || $prop->{udp_true} ){ - $prop->{peeraddr} = inet_ntoa( $prop->{peeraddr} ); + $prop->{peeraddr} = inet_ntop( $prop->{sockfamily}, $prop->{peeraddr} ); if( defined $prop->{reverse_lookups} ){ - $prop->{peerhost} = gethostbyaddr( inet_aton($prop->{peeraddr}), AF_INET ); + $prop->{peerhost} = gethostbyaddr( inet_pton($prop->{sockfamily}, $prop->{peeraddr}), $prop->{sockfamily} ); } $prop->{peerhost} = '' unless defined $prop->{peerhost}; @@ -803,7 +806,6 @@ ### user customizable hook sub post_accept_hook {} - ### perform basic allow/deny service sub allow_deny { my $self = shift; @@ -1145,7 +1147,7 @@ or $self->fatal("Can't dup socket [$!]"); ### hold on to the socket copy until exec - $prop->{_HUP}->[$i] = IO::Socket::INET->new; + $prop->{_HUP}->[$i] = IO::Socket::INET6->new(); $prop->{_HUP}->[$i]->fdopen($fd, 'w') or $self->fatal("Can't open to file descriptor [$!]"); diff -ur lib.orig/Net/Server.pm lib/Net/Server.pm --- lib.orig/Net/Server.pm 2011-04-07 11:44:54.973953140 +0200 +++ lib/Net/Server.pm 2011-04-07 14:11:28.637453856 +0200 @@ -824,25 +824,29 @@ && $#{ $prop->{cidr_allow} } == -1 && $#{ $prop->{cidr_deny} } == -1; + ### work around Net::CIDR::cidrlookup() croaking, + ### if first parameter is an IPv4 address in IPv6 notation. + my $peeraddr = ($prop->{peeraddr} =~ /^\s*::ffff:([0-9.]+\s*)$/) ? $1 : $prop->{peeraddr}; + ### if the addr or host matches a deny, reject it immediately foreach ( @{ $prop->{deny} } ){ return 0 if $prop->{peerhost} =~ /^$_$/ && defined($prop->{reverse_lookups}); - return 0 if $prop->{peeraddr} =~ /^$_$/; + return 0 if $peeraddr =~ /^$_$/; } if ($#{ $prop->{cidr_deny} } != -1) { require Net::CIDR; - return 0 if Net::CIDR::cidrlookup($prop->{peeraddr}, @{ $prop->{cidr_deny} }); + return 0 if Net::CIDR::cidrlookup($peeraddr, @{ $prop->{cidr_deny} }); } ### if the addr or host isn't blocked yet, allow it if it is allowed foreach ( @{ $prop->{allow} } ){ return 1 if $prop->{peerhost} =~ /^$_$/ && defined($prop->{reverse_lookups}); - return 1 if $prop->{peeraddr} =~ /^$_$/; + return 1 if $peeraddr =~ /^$_$/; } if ($#{ $prop->{cidr_allow} } != -1) { require Net::CIDR; - return 1 if Net::CIDR::cidrlookup($prop->{peeraddr}, @{ $prop->{cidr_allow} }); + return 1 if Net::CIDR::cidrlookup($peeraddr, @{ $prop->{cidr_allow} }); } return 0; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/contrib/dump_db������������������������������������������������������������������������0000775�0000000�0000000�00000000621�14516145741�0015742�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#! /usr/bin/perl # Dump a binary dictionary to clear text hash # (c) GPL - Steve Schnepp <steve.schnepp@pwkf.org> use strict; use warnings; use Fcntl; # For O_RDWR, O_CREAT, etc. use DB_File; while (my $db_file = shift) { my %hash; tie(%hash, 'DB_File', $db_file, O_RDONLY) or die "$!"; for my $key (sort keys %hash) { my $value = $hash{$key}; print "$key\t$value\n"; } untie(%hash); } ���������������������������������������������������������������������������������������������������������������munin-2.0.75/contrib/dump_storable������������������������������������������������������������������0000775�0000000�0000000�00000000437�14516145741�0017175�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#! /usr/bin/perl # Dump a Perl storable file to clear text # (c) GPL - Steve Schnepp <steve.schnepp@pwkf.org> use strict; use warnings; use Storable; use Data::Dumper; $Data::Dumper::Indent = 1; while (my $file = shift) { my $ref = Storable::retrieve($file); print Dumper($ref); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/contrib/epoch2localtime����������������������������������������������������������������0000775�0000000�0000000�00000000204�14516145741�0017377�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#! /usr/bin/perl # Convert epoch to human readable time while (my $epoch = shift) { print "" . localtime($epoch) . "\n"; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/contrib/import_db����������������������������������������������������������������������0000775�0000000�0000000�00000000760�14516145741�0016313�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#! /usr/bin/perl # Import clear text hash to a binary dictionary # (c) GPL - Steve Schnepp <steve.schnepp@pwkf.org> use strict; use warnings; use Fcntl; # For O_RDWR, O_CREAT, etc. use DB_File; my $db_file = shift; unless ($db_file) { print STDERR "usage: $0 db_file [ textfile1 ... textfileN ]\n"; exit 1; }; my %hash; tie(%hash, 'DB_File', $db_file, O_RDWR | O_CREAT) or die "$!"; while(<>) { chomp; my ($key, $value) = split(/\s+/, $_, 2); $hash{$key} = $value; } untie(%hash); ����������������munin-2.0.75/contrib/install_rrd.sh�����������������������������������������������������������������0000664�0000000�0000000�00000001222�14516145741�0017251�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#! /bin/sh # Be pedantic set -x set -e # We want to optimize for *compile time* export CFLAGS="-O0 -pipe" # install deps sudo apt-get install libpango1.0-dev libxml2-dev # Download a fixed version wget http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.4.8.tar.gz tar -xzvf rrdtool-1.4.8.tar.gz cd rrdtool-1.4.8 ./configure \ --disable-dependency-tracking \ --disable-rrdcgi \ --disable-mmap \ --disable-pthread \ --enable-perl \ --enable-perl-site-install \ --disable-ruby \ --disable-lua \ --disable-tcl \ --disable-python \ --disable-libdbi \ --disable-libwrap \ # Leave at the end make sudo make install # Test the install perl -MRRDs -e '' ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������munin-2.0.75/contrib/munin-every-services-all-boxes-build-html.in�����������������������������������0000664�0000000�0000000�00000005572�14516145741�0024766�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w =head1 munin-every-services-all-boxes-build-html This is a contributed program and is not installed by default or tested in the release process. Install it in @@LIBDIR@@. This script will generate a page, by services, with all hosts included. handy if you want to watch memory usage on all of your infrastructure. =head1 installation add in munin-cron: find @@HTMLDIR@@ -name "*.png" | @@LIBDIR@@/munin-every-services-all-boxes-build-html [ -x @@LIBDIR@@/munin-html ] && nice find @@HTMLDIR@@ -mtime -30 -name "*.png" | @@LIBDIR@@/munin-every-services-all-boxes-build-html $@; optional for find : -mtime -30 ; will ignore old files mkdir @@LIBDIR@@/every-services-all-boxes chown @@USER@@.@@GROUP@@ @@HTMLDIR@@/every-services-all-boxes in @@CONFDIR@@/templates/munin-overview.tmpl: <div class="box"> <a href=every-services-all-boxes>every services on all boxes</a> =head1 TODO use the proprer template system in munin, and munin-html to generate the page, not just guessing it from the files =head1 License LGPL-2.1 =head1 Authors / credits 8D Technologies =cut use strict; my $munin_www='@@HTMLDIR@@'; my %host; my %plugin; my %domain; my $htmlpluginfileindex="$munin_www/every-services-all-boxes/index.html"; open (IPF,">$htmlpluginfileindex") || die "cant open $htmlpluginfileindex : $!"; print IPF "<html><head><title>every services for all boxes\n"; while (<>) { if (m|$munin_www/([\.\w]+)/([\.-\w]+)-([\.\w]+)-(\w+).png|) { $domain{$1}=1; $host{$2}=$1; $plugin{$3}=1; } } my @prefered_plugins=("memory","swap","hddtemp_smartctl","sensors_temp"); foreach (@prefered_plugins,sort keys %plugin) { my $plugin=$_; my $htmlpluginfile="$munin_www/every-services-all-boxes/munin-$plugin-all-boxes.html"; open (PF,">$htmlpluginfile") || die "cant open $htmlpluginfile : $!"; print PF "$plugin all boxes
\n"; #print "plugin : $_\n"; print IPF qq|$plugin
|; foreach (keys %domain) { #print "domain : $_\n"; my $domain=$_; foreach (sort keys %host) { my $host=$_; if ($host{$_} eq $domain) { #print "host in domain : $host\n"; # test existence here if ( -f "$munin_www/$domain/$host-$plugin-week.png" ) { print PF qq|\n|; print PF "\n"; print PF qq|\n|; print PF qq|\n|; print PF "\n"; } } } } print PF "
$host $plugin
$plugin$plugin
\n"; close PF; } munin-2.0.75/contrib/munin-mergedb.pl000066400000000000000000000101171451614574100174710ustar00rootroot00000000000000#!/usr/bin/perl # -*- cperl -*- # # Merge munin db (datafile{,.storable} / limits) for multi-update masters # environment # # (c) GPL - Adrien "ze" Urban use warnings; use strict; use Storable; use Munin::Master::Utils; # Exemple of config (munin-merge.conf): # # what to merge ? # merge_datafile yes # merge_limits yes # # destination is the directory having this file # # # source directories to merge from (option should be used multiple times) # merge_source_dbdir /nfs/munin/db/updatehost1 # merge_source_dbdir /nfs/munin/db/updatehost2 # merge_source_dbdir /nfs/munin/db/updatehost3 # merge_source_dbdir /nfs/munin/db/updatehost4 my $configfile_name = 'munin-merge.conf'; my $config_type = { 'merge_source_dbdir' => 'ARRAY', 'merge_datafile' => 'BOOL', 'merge_limits' => 'BOOL', }; my $config = { 'merge_dbdir' => undef, 'merge_source_dbdir' => [], 'merge_datafile' => 0, 'merge_limits' => 0, }; sub usage() { print STDERR <{'merge_dbdir'}; my $file = $dbdir . "/" . $configfile_name; open FILE, "<", $file or die "open: $!\n"; while () { chomp; next if (/^[[:space:]]*#/); # comment next if (/^[[:space:]]*$/); # empty line unless (/^[[:space:]]*([^[:space:]]+)[[:space:]]+([^[:space:]]+)[[:space:]]*$/) { die "$.: Unrecogized line format\n"; } my ($key, $value) = ($1, $2); if (not defined $config_type->{$key}) { die "$.: $key: unrecognized option\n"; } if ('ARRAY' eq $config_type->{$key}) { push @{$config->{$key}}, $value; } elsif ('BOOL' eq $config_type->{$key}) { if ($value =~ /(yes|y|1|true)/i) { $config->{$key} = 1; } elsif ($value =~ /(no?|0|false)/i) { $config->{$key} = 0; } else { die "$.: unrecognized boolean: $value\n"; } } else { die "INTERNAL ERROR: $config_type->{$key}: " . "type not implemented\n"; } } close FILE; } sub check_sources() { if (0 == scalar(@{$config->{'merge_source_dbdir'}})) { die "No source dbdir. " . "Should I produce a result from thin air?\n"; } # no datafile, means it's not really a munin dbdir for my $srcdir (@{$config->{'merge_source_dbdir'}}) { unless (-f "$srcdir/datafile") { die "$srcdir: datafile not found"; } } } sub merge_plaintext($) { my $name = shift; my $data = []; my $version = undef; for my $srcdir (@{$config->{'merge_source_dbdir'}}) { my $srcfile = "$srcdir/$name"; unless (-f $srcfile) { die "$srcdir: $name not found"; } open FILE, "<", $srcfile or die "open: $srcfile: $!\n"; my $ver = ; if (defined $version) { die "$srcfile: versions differs: $version vs $ver\n" if ($ver ne $version); } else { $version = $ver; } push @$data, ; close FILE; #print $name, " ", scalar(@$data), " ", $srcdir, "\n"; } my $dstfile = $config->{'merge_dbdir'} . "/" . $name; my $dsttmp = $dstfile . ".tmp.$$"; open FILE, ">", $dsttmp or die "open: $dsttmp: $!\n"; print FILE $version, @$data; close FILE; rename $dsttmp, $dstfile or die "mv $dsttmp $dstfile: $!\n"; } sub merge_datafile_storable() { my $name = 'datafile.storable'; my $data = undef; for my $srcdir (@{$config->{'merge_source_dbdir'}}) { my $srcfile = "$srcdir/$name"; unless (-f $srcfile) { die "$srcdir: $name not found"; } my $info = retrieve($srcfile); if (defined $data) { $data = munin_overwrite($data, $info); } else { $data = $info; } } my $dstfile = $config->{'merge_dbdir'} . "/" . $name; my $dsttmp = $dstfile . ".tmp.$$"; Storable::nstore($data, $dsttmp); rename $dsttmp, $dstfile or die "mv $dsttmp $dstfile: $!\n"; } sub merge_datafile() { merge_plaintext('datafile'); merge_datafile_storable(); } sub merge_limits() { merge_plaintext('limits'); } usage unless (1 == scalar(@ARGV)); $config->{'merge_dbdir'} = shift @ARGV; load_config; check_sources; merge_datafile if ($config->{'merge_datafile'}); merge_limits if ($config->{'merge_limits'}); exit 0; # vim: syntax=perl ts=8 munin-2.0.75/contrib/munin-node-debug000077500000000000000000000140071451614574100174700ustar00rootroot00000000000000#! /usr/bin/perl # Munin node that presents as many virtual hosts, plugins & fields # as needed to be able to : # - emulate a HUGE network to stress the munin-master server # - serve as a basis for protocol debugging # # Copyright (C) 2010 Steve Schnepp under the GPLv2 use strict; use warnings; use IO::Socket; use IO::Select; use Getopt::Long; # No buffering $| = 1; my $nb_servers = 3; my $nb_plugins = 30; my $fields_per_plugin = 5; my $starting_port = 24949; my $spoolfetch; my $starting_epoch = -3600; # 1 hour from now my $dump_config; my $is_debug; my $help; my $update_rate = 10; my $spoolfetch_rate = 5; my $listen = "localhost"; my $arg_ret = GetOptions( "nb-plugins|n=i" => \$nb_plugins, "nb-fields|f=i" => \$fields_per_plugin, "nb-servers|s=i" => \$nb_servers, "update-rate=i" => \$update_rate, "startint-port|p=i" => \$starting_port, "listen=s" => \$listen, "update-rate=i" => \$update_rate, "spoolfetch" => \$spoolfetch, "spoolfetch-rate=i" => \$spoolfetch_rate, "starting-epoch=i" => \$starting_epoch, "dump|d" => \$dump_config, "help|h" => \$help, "debug" => \$is_debug, ); if ($help) { print qq{Usage: $0 [options] Options: --help View this message. -s --nb-servers Number of servers [3] -p --start-port Starting TCP listening port [24949] -n --nb-plugins Number of plugins per server [30] -f --nb-fields Number of fields per plugins [5] --update-rate Update rate of plugins (in seconds) [10] --listen Which IP to bind [localhost]. Use '' or '*' to bind to every interface. --spoolfetch Be spoolfetch capable --starting-epoch Starting epoch: no data will available before. Can be relative to now if negative [-3600] -d --dump Only dump a generated munin.conf part [no] --debug Print debug informations [no] }; exit 0; } # Convert relatives starting_epoch to absolute $starting_epoch = time + $starting_epoch if ($starting_epoch < 0); if ($dump_config) { for (my $i = 0; $i < $nb_servers; $i++) { my $port = $starting_port + $i; print "[host$port.debug.lan]\n"; print " address 127.0.0.1\n"; print " port $port\n"; print "\n"; } # Only dump config exit; } # start the servers my @servers; for (my $i = 0; $i < $nb_servers; $i ++) { my $port = $starting_port + $i; # LocalAddr == * doesn't work, it has to be empty my $localaddr = ($listen eq '*') ? '' : $listen; debug("starting server on port $listen:$port"); my $server = IO::Socket::INET->new( "LocalPort" => $port, "LocalAddr" => $localaddr, "Listen" => 5, "ReuseAddr" => 1, "Proto" => "tcp", ) or die($!); push @servers, $server; } # Ignoring SIG_CHILD debug("Ignoring SIG_CHILD"); $SIG{CHLD} = 'IGNORE'; my $select = IO::Select->new(@servers); while (my @ready = $select->can_read()) { foreach my $ready_fh (@ready) { my $client = $ready_fh->accept(); if (! fork()) { debug("[$$] Serving new client"); service($client); # Exit the child debug("[$$] Finished"); exit; } } } sub service { my $client = shift; my $hostname = "host".$client->sockport().".debug.lan"; print $client "# munin node at $hostname\n"; while (my $line = <$client>) { chomp($line); debug("[$$] client of $hostname asked : $line"); if ($line =~ m/^list /) { for (my $i = 0; $i < $nb_plugins; $i ++) { print $client "debug_plugin_$i "; } print $client "\n"; } elsif ($line =~ m/^cap (\w+)/) { my @caps = "multigraph"; push @caps, "spool" if $spoolfetch; print $client "cap @caps\n"; } elsif ($line =~ m/^config (\w+)/) { my $plugin_number = get_plugin_number($1); my $plugin_name = "debug_plugin_$plugin_number"; print $client "graph_title Debug plugin $plugin_number\n"; print $client "update_rate $update_rate\n"; for (my $i = 0; $i < $fields_per_plugin; $i ++) { print $client "field_". $plugin_number . "_$i.label field $i of plugin $plugin_name on $hostname\n"; print $client "field_". $plugin_number . "_$i.type GAUGE\n"; } print $client ".\n"; } elsif ($line =~ m/^fetch (\w+)/) { my $plugin_number = get_plugin_number($1); for (my $i = 0; $i < $fields_per_plugin; $i ++) { my $value = sin( (time / 3600) * $plugin_number + $i); print $client "field_". $plugin_number . "_$i.value $value\n"; } print $client ".\n"; } elsif ($line =~ m/^spoolfetch (\d+)/) { my $timestamp = $1; # Cannot start before $starting_epoch print "asked $timestamp, " if $is_debug; $timestamp = ($timestamp < $starting_epoch) ? $starting_epoch : $timestamp; print "starting at $starting_epoch, using $timestamp, \n" if $is_debug; # Only send something every $spoolfetch_rate * $update_rate if ( $timestamp > time - $spoolfetch_rate * $update_rate) { print $client ".\n"; return; } # Sends spools strictly > LastSpooled for (my $epoch = ($timestamp - $timestamp % $update_rate + $update_rate); $epoch < time; $epoch += $update_rate) { for (my $plugin_number = 0; $plugin_number < $nb_plugins; $plugin_number ++) { my $plugin_name = "debug_plugin_$plugin_number"; print $client "multigraph $plugin_name\n"; print $client "graph_title Debug plugin $plugin_number\n"; print $client "update_rate $update_rate\n"; for (my $i = 0; $i < $fields_per_plugin; $i ++) { print $client "field_". $plugin_number . "_$i.label field $i of plugin $plugin_name on $hostname\n"; print $client "field_".$plugin_number."_$i.type GAUGE\n"; my $value = sin( ($epoch / 3600) * $plugin_number + $i); print $client "field_".$plugin_number."_$i.value $epoch:$value\n"; } } } print $client ".\n"; } elsif ($line =~ m/^quit/) { return; } else { print $client "# Command not found\n"; } } } sub get_plugin_number { my $plugin_name = shift; my $plugin_number = $1 if ($plugin_name =~ m/(\d+)/); return $plugin_number; } sub debug { print join(" ", @_) . "\n" if $is_debug; } __END__ munin-2.0.75/contrib/perf/000077500000000000000000000000001451614574100153375ustar00rootroot00000000000000munin-2.0.75/contrib/perf/db_files.pl000066400000000000000000000054341451614574100174510ustar00rootroot00000000000000#! /usr/bin/perl # Compare timings with BDB vs files use strict; use warnings; use DB_File; use Munin::Common::DictFile; my $nb_files = (shift || 1000); my $nb_cmp = (shift || -10); print "using db_$$\n"; mkdir "db_$$"; chdir "db_$$"; sub db_update { my ($filename, $epoch, $value, $db_type) = @_; my %hash; tie %hash, 'DB_File', "update.db", O_CREAT|O_RDWR, 0666, $db_type; db_update_conn($filename, $epoch, $value, \%hash); untie %hash; } sub db_update_conn { my ($filename, $epoch, $value, $h) = @_; $h->{$filename} = "$epoch:$value"; } sub fl_update { my ($filename, $epoch, $value) = @_; my $last_filename = "$filename.last"; my $tmp_filename = "$last_filename.tmp"; open (FILE, "> $tmp_filename"); print FILE "$epoch:$value\n"; close (FILE); rename ($tmp_filename, $last_filename); } sub fh_update { my ($filename, $hash) = @_; my $last_filename = "$filename.last"; my $tmp_filename = "$last_filename.tmp"; open (FILE, "> $tmp_filename"); foreach my $key (keys %$hash) { print FILE $key . "\t" . $hash->{$key} . "\n"; } close (FILE); rename ($tmp_filename, $last_filename); } use Benchmark qw(:all); use Storable; use SDBM_File; STDOUT->autoflush(1); cmpthese($nb_cmp, { 'fl_update' => sub { print "Starting fl_update: "; for (my $i = 0; $i < $nb_files; $i ++) { fl_update("test$i.rrd", $i, rand($i)); } print "done.\n"; }, 'fh_update' => sub { print "Starting fh_update: "; my %hash; for (my $i = 0; $i < $nb_files; $i ++) { db_update_conn("test$i.rrd", $i, rand(), \%hash); } fh_update("update.txt", \%hash); print "done.\n"; }, 'db_upda_c' => sub { print "Starting db_upda_c: "; my %hash; tie %hash, 'DB_File', "update_c.db", O_CREAT|O_RDWR, 0666, $DB_HASH; for (my $i = 0; $i < $nb_files; $i ++) { db_update_conn("test$i.rrd", $i, rand(), \%hash); } untie %hash; print "done.\n"; }, 'sdbm' => sub { print "Starting sdbm: "; my %hash; tie %hash, 'SDBM_File', "update_sdbm.db", O_CREAT|O_RDWR, 0666; for (my $i = 0; $i < $nb_files; $i ++) { db_update_conn("test$i.rrd", $i, rand(), \%hash); } untie %hash; print "done.\n"; }, 'dictfile' => sub { print "Starting dictfile: "; my %hash; tie %hash, 'Munin::Common::DictFile', "update_munin_dict.txt", O_CREAT|O_RDWR, 0666; for (my $i = 0; $i < $nb_files; $i ++) { db_update_conn("test$i.rrd", $i, rand(), \%hash); } untie %hash; %hash = (); print "done.\n"; }, 'storable' => sub { print "Starting storable: "; my $hash = ( -e "update.storable" ) ? Storable::retrieve("update.storable") : {}; for (my $i = 0; $i < $nb_files; $i ++) { db_update_conn("test$i.rrd", $i, rand(), $hash); } Storable::nstore($hash, "update.storable.$$"); rename("update.storable.$$", "update.storable"); print "done.\n"; }, }); munin-2.0.75/contrib/perf/rrd_perf.pl000077500000000000000000000043671451614574100175140ustar00rootroot00000000000000#! /usr/bin/perl # RRD update perf testing # (c) GPL - Steve Schnepp use strict; use warnings; use RRDs; use Time::HiRes; use Getopt::Long; my $graph_interval; my $graph_ratio = 100; my $verbose; GetOptions( "g=i" => \$graph_interval, "r=i" => \$graph_ratio, "verbose" => \$verbose, ) or die "invalid options"; my $nb_rrd = (shift || 100); my $nb_rrd_per_dir = (shift || 100); my $step = (shift || 300); my $heartbeat = $step * 2; my $rrd_dir = "rrds-$$"; mkdir $rrd_dir; my @rrds; # create all the rrds print "creating rrds in $rrd_dir\n"; for (my $rrd_number=0; $rrd_number < $nb_rrd; $rrd_number ++) { my $rrd_dir_num = int ($rrd_number / $nb_rrd_per_dir); my $rrdfilename = "$rrd_dir/$rrd_dir_num/$rrd_number.rrd"; mkdir "$rrd_dir/$rrd_dir_num"; print STDERR "creating RRD $rrdfilename\n"; RRDs::create( $rrdfilename, #"--start", "-10y", "-s", "$step", "DS:42:GAUGE:$heartbeat:U:U", "RRA:AVERAGE:0.5:1:576", # resolution 5 minutes "RRA:MIN:0.5:1:576", "RRA:MAX:0.5:1:576", "RRA:AVERAGE:0.5:6:432", # 9 days, resolution 30 minutes "RRA:MIN:0.5:6:432", "RRA:MAX:0.5:6:432", "RRA:AVERAGE:0.5:24:540", # 45 days, resolution 2 hours "RRA:MIN:0.5:24:540", "RRA:MAX:0.5:24:540", "RRA:AVERAGE:0.5:288:450", # 450 days, resolution 1 day "RRA:MIN:0.5:288:450", "RRA:MAX:0.5:288:450", ); push @rrds, $rrdfilename; } # Simulating munin's graphing in a sub process if ($graph_interval && ! fork()) { while(1) { my $offset = int rand($nb_rrd); for (my $i = 0; $i < $nb_rrd * $graph_ratio / 100; $i++) { my $rrdfilename = $rrds[($offset + $i) % $nb_rrd]; print STDERR "graphing RRD $rrdfilename\n"; RRDs::graph( '/dev/null', "DEF:a=$rrdfilename:42:AVERAGE", "LINE1:a", ); } } continue { sleep 1 / $graph_interval; } exit; } # Simulate a munin's update workload while (1) { my $epoch = time; for my $rrdfilename (@rrds) { my $value = 100 * sin($epoch) + rand(); print STDERR "updating RRD $rrdfilename\n"; RRDs::update( $rrdfilename, "$epoch:$value", ); } sleep($step); } sub get_rrd_filename { my $rrd_number = shift; } munin-2.0.75/contrib/perf/test_weak.pl000066400000000000000000000011351451614574100176620ustar00rootroot00000000000000#! /usr/bin/perl use warnings; use strict; use File::Temp; use Data::Dumper; use Storable qw(nstore_fd fd_retrieve); use Scalar::Util qw(weaken isweak); my $number = shift || 10; my $a = {}; for my $i (0 .. $number) { $a->{$i} = { "$i$i" => "$i$i", "$i$i" => "$i$i", }; } for my $i (0 .. $number) { $a->{$i}{next} = $a->{ ($i + 1) % $number }; weaken($a->{$i}{next}) if $ENV{WEAKEN}; } my $temp_storable = File::Temp->new(); nstore_fd($a, $temp_storable); for my $j (0 .. ($number * $number)) { $temp_storable->seek( 0, SEEK_SET ); $a = fd_retrieve($temp_storable); } print Dumper($a); munin-2.0.75/contrib/python-plugin/000077500000000000000000000000001451614574100172205ustar00rootroot00000000000000munin-2.0.75/contrib/python-plugin/README000066400000000000000000000010341451614574100200760ustar00rootroot00000000000000This is a nice OO plugin framework for python plugins. There is an example plugin for watching the number of arp entries on a host. If the project is provided with install Makefile targets, and a patches to give the python module a less generic name than "munin" (in Perl I would have called it Munin::Plugin::OO) then we can make it an official part of the project. Needed is also a python version of Munin::Plugin. Please see https://code.launchpad.net/~msiebuhr/+junk/python-munin for the latest and greatest. Nicolai 26-march-2009 munin-2.0.75/contrib/python-plugin/arp000077500000000000000000000012721451614574100177320ustar00rootroot00000000000000#!/usr/bin/env python """ Linux ARP plugin test Morten Siebuhr sbhr@sbhr.dk 12/12 2008 """ from munin import Plugin from os.path import exists p = Plugin("ARP Cache", "MAC addresses", "Network") # Information p.info = "Shows how many interfaces are listed in the system's ARP cache" p.args = "--base 1000 -l 0" # Autoconfigure p.autoconf = lambda: exists("/proc/net/arp") # Populate with data for line in open("/proc/net/arp").readlines(): if "IP address" in line: continue (ip, hw, flags, address, mogl, dev) = line.split() if not dev in p: p[dev].value = 0 p[dev].label = dev p[dev].draw = "AREA" p[dev].value += 1 # Run it! p.run() munin-2.0.75/contrib/python-plugin/munin.py000077500000000000000000000112741451614574100207300ustar00rootroot00000000000000#!/usr/bin/env python """ Makes it easy to create munin plugins... http://munin-monitoring.org/wiki/protocol-config Morten Siebuhr sbhr@sbhr.dk 12/12 2008 """ def getOptionOrDefault(option, default=None): # noqa: N802 from os import environ return environ.setdefault(option, default) class DataSource(object): """Represents a single data source. This class should not be directly created by the user - this is done transparently by the Plugin-class.""" __slots__ = ['label', 'cdef', 'draw', 'graph', 'info', 'extinfo', 'max', 'min', 'negative', 'type', 'warning', 'critical', 'colour', 'skipdraw', 'sum', 'stack', 'line', 'value'] # Allowed draw modes _draw_modes = ['AREA', 'STACK', 'LINE1', 'LINE2', 'LINE3', 'LINESTACK1', 'LINESTACK2', 'LINESTACK3', 'AREASTACK'] def get_config(self): if hasattr(self, "draw"): assert self.draw in self._draw_modes data = dict() for attr in self.__slots__: if attr == 'value': continue if hasattr(self, attr): data[attr] = self.__getattribute__(attr) return data def get_value(self, data_source_name): assert hasattr(self, "value") if callable(self.value): return self.value(data_source_name) return self.value class Plugin(object): """Facilitates OO creation of Munin plugins. #!/usr/bin/env python from munin import Plugin p = Plugin("Test measurement", "test/second", category="junk") p.autoconf = False for name, value in {'a': 1, 'b': 2}: p[name].label = name p[name].value = value p.run() (It will itself detect how the script is called and create the proper output.) * If 'autoconf' is a callable, it will be called when running autoconf. """ def __init__(self, title, vlabel, category="misc", info="", args="", scale=True): """Sets up the plugin; title, vertical label, category -- all things that are global for the plugin. """ self.title = title self.vlabel = vlabel self.category = category self.info = info self.args = args self.scale = scale self._values = dict() assert type(title) is str assert type(vlabel) is str assert type(category) is str def __getitem__(self, key): if key not in self._values: self._values[key] = DataSource() return self._values[key] def __setitem__(self, key, value): self._values[key] = value def __delitem__(self, key): if key in self._values: del self._values[key] def __contains__(self, key): return key in self._values def _print_values(self): """Print the values for all registered data sources. Similar to running with "values"-argument.""" for prefix, line in self._values.items(): value = line.get_value(prefix) assert isinstance(value, int) print("%s.value %s" % (prefix, value)) def _print_config(self): """Print the output needed for setting up the graph - i.e. when the plugin is run with "config"-argument.""" # Print graph_-variables for prop in ['title', 'category', 'vlabel', 'info', 'args', 'draw']: if prop not in self.__dict__: continue if not self.__dict__[prop]: continue print("graph_%s %s" % (prop, self.__dict__[prop])) # Print setup for individual lines for prefix, line in self._values.items(): # The "label" attribute MUST be defined assert "label" in line.get_config().keys(), "No 'label' defined." for attr, value in line.get_config().items(): print("%s.%s %s" % (prefix, attr, value)) def _print_autoconf(self): """Running autoconf-mode.""" aconf = False if hasattr(self, "autoconf"): if callable(self.autoconf): aconf = self.autoconf() else: aconf = self.autoconf if aconf: print("yes") else: print("no") def run(self, force_mode=None): """Run the plugin and "do the right thing"^(TM).""" mode = force_mode if mode is None: import sys if len(sys.argv) == 2: mode = sys.argv[1] if mode == "autoconf": self._print_autoconf() return if mode == "config": self._print_config() return self._print_values() munin-2.0.75/contrib/python-plugin/setup.py000077500000000000000000000007301451614574100207350ustar00rootroot00000000000000#!/usr/bin/env python from distutils.core import setup from subprocess import Popen, PIPE # Get the current revision from SVN try: p = Popen(['svnversion'], stdout=PIPE) version = p.stdout.read().strip() except OSError: version = "unknown" setup(name="python-munin", version=version, url="http://dev.sbhr.dk/svn/python-munin", author="Morten Siebuhr", author_email="sbhr@sbhr.dk", description="", py_modules=['munin']) munin-2.0.75/contrib/python-plugin/test.py000077500000000000000000000012701451614574100205540ustar00rootroot00000000000000#!/usr/bin/env python """ Makes it easy to create munin plugins... http://munin-monitoring.org/wiki/protocol-config Morten Siebuhr sbhr@sbhr.dk 12/12 2008 """ from munin import Plugin p = Plugin("Approx cache", "files", "Approx") p.info = "Shows how many files the Approx cache stores, split by file-type." def test(data_source): if data_source == 'deb': return 42 return 24 # Set up graph p['deb'].label = "deb files" p['deb'].value = test p['deb'].info = "Number of .deb-files." p['tar.gz'].label = "tar.gz files" p['tar.gz'].value = test # p['gzip'].value = 123 # Run print("AUTOCONFIG") p.run("autoconf") print("CONFIG") p.run("config") print("PLAIN") p.run() munin-2.0.75/contrib/rrdcopy000077500000000000000000000062251451614574100160200ustar00rootroot00000000000000#! /usr/bin/perl # Copy data from one RRD to another # (c) LGPL - 2009-2011 - Steve Schnepp # # Usage : rrdcopy # # Both RRD have to exist and have the same DS (DataSource). # The tool copies all the datasources from the sources into the destination # like you mean it: # - start is the oldest value from src # (but cannot be younger as the youngest from dst) # - stop is the youngest value from src # - asks src for the AVERAGED value on each step time. # # It doesn't do any MIN/MAX replication since this information # is usually not accurate at all when resampling. # # The environmment variable DEBUG is used like : # 0/absent - no debug # 1 - emit to stdout all the updates to dst # 2 - emit also the data that try # # XXX - It depends on RRDs.pm ... # ... as I'm waiting for a patch to use the plain rrdtool cli :-) # # It was known as the rrdmove tool from the pmptools project. # - I changed its name (as data is copied, not moved) # - I changed its hosting (way more useful for munin than for pmptools) use strict; use warnings; use Carp; use Data::Dumper; use RRDs; my ($src_rrd, $dst_rrd) = @ARGV; my $infos_src = RRDs::info($src_rrd); my $infos_dst = RRDs::info($dst_rrd); #get ds_names my @ds_names; foreach my $key (keys %$infos_src) { if ($key =~ /^ds\[(\w+)\]\.type$/) { push @ds_names, $1; } } my ($start, $stop, $step); $step = $infos_dst->{'step'}; $stop = $infos_src->{'last_update'}; my @rra_names; foreach my $key (keys %$infos_dst) { if ($key =~ /^rra\[(\w+)\]\.cf$/) { push @rra_names, $1; } } foreach my $rra_name (sort {$a <=> $b} @rra_names) { my $rows = $infos_dst->{"rra[$rra_name].rows"}; my $pdp_per_row = $infos_dst->{"rra[$rra_name].pdp_per_row"}; my $rra_start = $stop - $rows * $pdp_per_row * $step; if ( (! defined $start) || $start > $rra_start) { $start = $rra_start; } print "rra[$rra_name]:$rra_start(" . (scalar localtime($rra_start)) . "):$start(" . (scalar localtime($start)) .")\n" if $ENV{DEBUG} && $ENV{DEBUG} >= 2; } #$start = 1240020352; # Converting my ($step_sample) = $step * 128; for (my $epoch = $start; $epoch <= $stop; $epoch += $step_sample) { my @rrd_updates; # Resampling by taking the average value for the period my ($fetched_epoch, $fetched_step, $names, $data) = RRDs::fetch( $src_rrd, "AVERAGE", "--start", $epoch, "--end", $epoch + $step_sample, ); for my $line ( @$data ) { my %current_data; for ( my $ds_idx = 0; $ds_idx < scalar @$line; $ds_idx ++) { my $name = $names->[$ds_idx]; my $value = $line->[$ds_idx]; $current_data{$name} = $value; } # Create the template my @values = map { defined $current_data{$_} ? $current_data{$_} : "U" } @ds_names; # Ignore lines with all values set to "U" next unless grep { $_ ne "U" } @values; push @rrd_updates, "$fetched_epoch:" . join(':', @values); $fetched_epoch += $step; } next unless @rrd_updates; print "RRDs::update($dst_rrd, --template, " . join(':', @ds_names) . ", " . join(", ", @rrd_updates) . "\n" if $ENV{DEBUG}; RRDs::update( $dst_rrd, "--template", join(':', @ds_names), @rrd_updates, ) or die "RRDs::update: $!"; } munin-2.0.75/contrib/rtbtrace.pl000077500000000000000000000046541451614574100165620ustar00rootroot00000000000000#! /usr/bin/perl # Real-time visualization of block accesses # with a layout as old DOS defraggers # # Idea borrowed from seekwatcher [http://oss.oracle.com/~mason/seekwatcher/] # Copyright (C) 2010 - Steve Schnepp - GPL use strict; use warnings; use Curses; use Time::HiRes qw(sleep); # Should not buffer anything since we are "real-time" $| = 1; # Give the blocksize as first arg my $nb_blocks_1k = shift; if (! $nb_blocks_1k) { die "Should give the number of 1k blocks as first arg"; } my $win = Curses::new(); # Hide cursor Curses::curs_set(0); my $last_tstp = 0; my $tstp_step = 1 / 25; # 25 Hz # use non-blocking IO on stdin. # Otherwise when there is no activity, the drawing is stalled use IO::Handle; STDIN->blocking(0); my $io_ops = {}; while(1) { my $line = <>; if (! $line) { sleep $tstp_step; $io_ops = {}; draw($io_ops); $last_tstp += $tstp_step; next; } chomp($line); # 8,0 3 1 0.000000000 697 G W 223490 + 8 [kjournald] my ($device, $cpu_id, $seqno, $tstp, $pid, $action, $mode, $offset, $dummy, $length, $detail) = split(/ +/, trim($line), 11); # Only take complete lines next unless $detail; # Only take the C (completed) requests to take care of an eventual buffering/queuing next unless $action eq 'C'; # Flush if needed. Assumes the data is timestamp ordered if ($tstp > $last_tstp + $tstp_step) { $last_tstp += $tstp_step; # flush to img draw($io_ops); # flush the in-flight IO ops $io_ops = {}; } # Fill the in-flight IO ops $io_ops->{$offset} = [ $mode, $length ]; } continue { } sub draw { my $io_ops = shift; use Term::Size; my ($columns, $rows) = Term::Size::chars *STDOUT{IO}; $rows --; # last row is status row my $nb_chars = $columns * $rows; my $blocks_per_char = int ($nb_blocks_1k * 2 / $nb_chars) + 1; # Each frame we redraw everything $win->clear(); # Update the status line $win->addstr($rows, 0, sprintf("%.2f", $last_tstp)); # Iterate & fill the window while (my ($offset, $value) = each %$io_ops) { my $offset_in_chars = $offset / $blocks_per_char; my $x = $offset_in_chars / $columns; my $y = $offset_in_chars % $columns; my $op = ($value->[0] =~ m/R/ ? "R" : "W"); my $len = int($value->[1] / $blocks_per_char) + 1; $win->addstr($x, $y, $op x $len); } sleep($tstp_step); $win->refresh(); } # haaa.. this should really be part of Perl :-) sub trim { my $string = shift; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } munin-2.0.75/cpanfile000066400000000000000000000034351451614574100144540ustar00rootroot00000000000000# BEWARE: this cpanfile is backported from master - it is only used for travis tests # In all other regards (besides being used for testing) it is probably not # accurate (too many dependencies, ...). requires 'Alien::RRDtool'; requires 'Digest::MD5'; requires 'File::Path'; requires 'File::ReadBackwards'; requires 'File::Temp'; requires 'Getopt::Long'; requires 'HTML::Template::Pro'; requires 'HTTP::Server::Simple::CGI'; requires 'IO::Scalar'; requires 'IO::Socket::INET6'; requires 'JSON'; requires 'LWP::Simple'; requires 'LWP::UserAgent'; requires 'List::MoreUtils'; requires 'List::Util'; requires 'Log::Dispatch'; requires 'Log::Dispatch::Screen'; requires 'Log::Dispatch::Syslog'; requires 'Log::Log4perl'; requires 'MIME::Base64'; requires 'Module::Build'; requires 'Net::DNS'; requires 'Net::Domain'; requires 'Net::IP'; requires 'Net::SNMP'; requires 'Net::SSLeay'; requires 'Net::Server::Fork'; requires 'Params::Validate'; requires 'Parallel::ForkManager'; requires 'Pod::Perldoc'; requires 'Pod::Usage'; requires 'Scalar::Util'; requires 'Socket'; requires 'Test::Perl::Critic'; requires 'Text::Balanced'; requires 'Time::HiRes'; requires 'URI'; requires 'URI::_server'; requires 'XML::Dumper'; requires 'XML::LibXML'; requires 'XML::Parser'; on test => sub { requires 'Directory::Scratch'; requires 'File::Slurp'; requires 'Test::Class'; requires 'Test::Deep'; requires 'Test::Differences'; requires 'Test::Exception'; requires 'Test::LongString'; requires 'Test::MockModule'; requires 'Test::MockObject'; requires 'Test::MockObject::Extends'; requires 'Test::More'; }; on develop => sub { requires 'Capture::Tiny'; requires 'IO::Scalar'; requires 'Pod::Simple::SimpleTree'; requires 'Test::Perl::Critic'; requires 'perl', 'v5.10.1'; }; munin-2.0.75/dev_scripts/000077500000000000000000000000001451614574100152705ustar00rootroot00000000000000munin-2.0.75/dev_scripts/common.sh000066400000000000000000000002671451614574100171210ustar00rootroot00000000000000#!/bin/sh BASEDIR=$(readlink -f -- "$FINDBIN/..") DESTDIR=${DESTDIR:-$BASEDIR/sandbox} # shellcheck disable=SC2034 PERLLIB=$DESTDIR$(perl -V:sitelib | cut -d"'" -f2) cd "$BASEDIR" munin-2.0.75/dev_scripts/disable_tls000077500000000000000000000003461451614574100175060ustar00rootroot00000000000000#!/bin/sh FINDBIN=$(cd -- "$(dirname "$0")" && pwd) . "$FINDBIN/common.sh" perl -pi -e "s/^tls .*/tls disabled/" "$DESTDIR/etc/opt/munin/munin-node.conf" perl -pi -e "s/^tls .*/tls disabled/" "$DESTDIR/etc/opt/munin/munin.conf" munin-2.0.75/dev_scripts/enable_tls000077500000000000000000000003461451614574100173310ustar00rootroot00000000000000#!/bin/sh FINDBIN=$(cd -- "$(dirname "$0")" && pwd) . "$FINDBIN/common.sh" perl -pi -e "s/^tls .*/tls paranoid/" "$DESTDIR/etc/opt/munin/munin-node.conf" perl -pi -e "s/^tls .*/tls paranoid/" "$DESTDIR/etc/opt/munin/munin.conf" munin-2.0.75/dev_scripts/install000077500000000000000000000041331451614574100166650ustar00rootroot00000000000000#!/bin/sh FINDBIN=$(cd -- "$(dirname "$0")" && pwd) . "$FINDBIN/common.sh" if [ $# -ne 0 ]; then ./dev_scripts/stop_munin-node rm -rf sandbox mkdir sandbox make clean || exit echo "**********************************************************************" fi make "DESTDIR=$DESTDIR" "HTMLDIR=$DESTDIR/www/munin" || exit # Override users and groups with the current user - otherwise "install" fails, if these users # and groups are missing in the local system. fakeroot make \ install-common-prime \ install-master-prime \ install-node-prime \ install-plugins-prime \ install-man install-async \ "DESTDIR=$DESTDIR" \ "HTMLDIR=$DESTDIR/www/munin" \ "USER=$(id -u)" \ "CGIUSER=$(id -u)" \ "PLUGINUSER=$(id -u)" \ "GROUP=$(id -g)" \ || exit if [ $# -ne 0 ]; then echo "**********************************************************************" fakeroot make install-plugins-prime install-plugins-java "DESTDIR=$DESTDIR" "HTMLDIR=$DESTDIR/www/munin" || exit echo "**********************************************************************" perl -pi -e "s/port 4949/port 4948/; s/user root/user $(id -n -u)/; s/group root/group $(id -n -g)/; s|^(port .*)|\$1 tls disabled tls_private_key $BASEDIR/common/t/tls/node_key.pem tls_certificate $BASEDIR/common/t/tls/node_cert.pem tls_ca_certificate $BASEDIR/common/t/tls/CA/ca_cert.pem tls_verify_certificate yes tls_verify_depth 5 |; " "$DESTDIR/etc/opt/munin/munin-node.conf" perl -pi -e "s/(address 127\\.0\\.0\\.1)/\$1\\n port 4948/; s|(# a simple host tree)|tls disabled tls_private_key $BASEDIR/common/t/tls/master_key.pem tls_certificate $BASEDIR/common/t/tls/master_cert.pem tls_ca_certificate $BASEDIR/common/t/tls/CA/ca_cert.pem tls_verify_certificate yes tls_verify_depth 5 \$1|" "$DESTDIR/etc/opt/munin/munin.conf" echo "**********************************************************************" ./dev_scripts/run munin-node-configure --shell --families=contrib,auto | sh -x fi munin-2.0.75/dev_scripts/query_munin_node000077500000000000000000000000661451614574100206000ustar00rootroot00000000000000#!/bin/sh printf '%s\nquit' "$*" | nc localhost 4948 munin-2.0.75/dev_scripts/restart_munin-node000077500000000000000000000001151451614574100210300ustar00rootroot00000000000000#!/bin/sh ./dev_scripts/stop_munin-node ./dev_scripts/start_munin-node "$@" munin-2.0.75/dev_scripts/run000077500000000000000000000004561451614574100160270ustar00rootroot00000000000000#!/bin/sh FINDBIN=$(cd -- "$(dirname "$0")" && pwd) . "$FINDBIN/common.sh" usage() { echo "Usage: $0 " exit 1 } if [ $# -eq 0 ]; then usage fi RUN=$(find "$DESTDIR" -name "$1" -type f -executable) shift if [ -z "$RUN" ]; then usage fi env "PERL5LIB=$PERLLIB" "$RUN" "$@" munin-2.0.75/dev_scripts/start_munin-node000077500000000000000000000004651451614574100205110ustar00rootroot00000000000000#!/bin/bash FINDBIN=$(cd -- "$(dirname "$0")" && pwd) . "$FINDBIN/common.sh" if [ -f "$DESTDIR/var/run/munin/munin-node.pid" ]; then echo "Pid file found. Not starting" else env "PERL5LIB=$PERLLIB" "$DESTDIR/opt/munin/sbin/munin-node" "$@" fi tail -f "$DESTDIR/opt/munin/log/munin/munin-node.log" munin-2.0.75/dev_scripts/stop_munin-node000077500000000000000000000003631451614574100203360ustar00rootroot00000000000000#!/bin/sh FINDBIN=$(cd -- "$(dirname "$0")" && pwd) . "$FINDBIN/common.sh" if [ -f "$DESTDIR/var/run/munin/munin-node.pid" ]; then xargs kill <"$DESTDIR/var/run/munin/munin-node.pid" else echo "Pid file not found. Not stopping" fi munin-2.0.75/dists/000077500000000000000000000000001451614574100140715ustar00rootroot00000000000000munin-2.0.75/dists/README000066400000000000000000000026741451614574100147620ustar00rootroot00000000000000Thanks to our move to git, dists/ is now obsolete. Each distributions has now its own git repository. There inner structure is up to them, but we can see 2 pattern emerging : 1. Fork of official repository This is the simplest thing to do. Just fork the official munin repo, and add your distribution specific things. It enables a very effective way of sending distro-originated patches upstream. It used to be awkward to use when we where using svn, but the move to git made it a breeze. 2. Standalone repository Some distributions just create a new repo, containing only what's specific to them. It usually takes the form of some build instructions and several patch files. It enables a very simple separation of what's coming from upstream and what's ditro originated. It also was the only practical way prior to the git move. Some of these specifics were usually integrated here, in dists/*. 3. Urls reference. Please note that we don't know about every distrib. If yours favorite one is missing, just send a patch to add it here. Order here is alphabetic, to remain neutral :-) * Debian Different releases are tracked via different branches. type: fork www: http:// git: * FreeBSD type: www: git: * Gentoo type: www: git: * Redhat and Fedora RHEL and Fedora are hosted directly on pkgs.fedoraproject.org. type: specific www: http://pkgs.fedoraproject.org/cgit/munin.git git: git://pkgs.fedoraproject.org/munin.git * Ubuntu type: www: git: munin-2.0.75/doc/000077500000000000000000000000001451614574100135105ustar00rootroot00000000000000munin-2.0.75/doc/.gitignore000066400000000000000000000000131451614574100154720ustar00rootroot00000000000000_build/ *~ munin-2.0.75/doc/Makefile000066400000000000000000000126671451614574100151640ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/ html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Munin.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Munin.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Munin" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Munin" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." munin-2.0.75/doc/_static/000077500000000000000000000000001451614574100151365ustar00rootroot00000000000000munin-2.0.75/doc/_static/.gitignore000066400000000000000000000000001451614574100171140ustar00rootroot00000000000000munin-2.0.75/doc/_templates/000077500000000000000000000000001451614574100156455ustar00rootroot00000000000000munin-2.0.75/doc/_templates/.gitignore000066400000000000000000000000001451614574100176230ustar00rootroot00000000000000munin-2.0.75/doc/conf.py000066400000000000000000000262251451614574100150160ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Munin documentation build configuration file, created by # sphinx-quickstart on Sat Jun 9 11:12:29 2012. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.doctest'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Munin' copyright = '2012, Stig Sandbeck Mathisen ' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '2.0' # The full version, including alpha/beta/rc tags. release = os.popen('../getversion').read() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. html_short_title = "Munin" # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = '../master/static/logo-h.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Munindoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). 'papersize': 'a4paper', # The font size ('10pt', '11pt' or '12pt'). 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'Munin.tex', 'Munin Documentation', 'Stig Sandbeck Mathisen \\textless{}ssm@fnord.no\\textgreater{}', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. latex_logo = '../master/static/logo-h.png' # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. latex_show_pagerefs = False # If true, show URL addresses after external links. latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('reference/munin-async', 'munin-async', 'Munin async client', ['Steve Schnepp'], 1), ('reference/munin-asyncd', 'munin-asyncd', 'Munin async daemon', ['Steve Schnepp'], 1), ('reference/munin-cgi-graph', 'munin-cgi-graph', 'Munin CGI grapher', ['Steve Schnepp'], 1), ('reference/munin-cgi-html', 'munin-cgi-html', 'Munin CGI HTML generator', ['Steve Schnepp'], 1), ('reference/munin-check', 'munin-check', 'A program to fix permissions of munin directories and files', ['Matthias Schmitz'], 1), ('reference/munin-cron', 'munin-cron', 'Munin cron script', ['Audun Ytterdal', 'Jimmy Olsen'], 1), ('reference/munin-graph', 'munin-graph', 'Create graphs from RRD files', ['Audun Ytterdal', 'Jimmy Olsen', 'Nicolai Langfeldt', 'Steve Schnepp'], 1), ('reference/munin-html', 'munin-html', 'Create HTML pages', ['Knut Haugen', 'Steve Schnepp', 'Audun Ytterdal', 'Jimmy Olsen'], 1), ('reference/munin-limits', 'munin-limits', 'A program to check for any off-limit values', ['Audun Ytterdal', 'Jimmy Olsen', 'Knut Haugen', 'Nikolai Langfeldt'], 1), ('reference/munin-node', 'munin-node', 'A daemon which gathers information from the local node', ['Audun Ytterdal', 'Jimmy Olsen', 'Matthew Boyle', 'Tore Anderson'], 8), ('reference/munin-run', 'munin-run', 'A program to run munin plugins from the command line', ['Audun Ytterdal', 'Jimmy Olsen', 'Tore Anderson', 'Nikolai Langfeldt'], 8), ('reference/munin-update', 'munin-update', 'A program to gather data from machines running munin-node or munin-async', ['Audun Ytterdal', 'Jimmy Olsen', u'Kjell Magne Øierud', 'Knut Haugen', 'Nikolai Langfeldt', 'Tore Anderson'], 1), ('reference/munin.conf', 'munin.conf', 'Configuration file for the munin master', [], 5), ('reference/munin-node.conf', 'munin-node.conf', 'Configuration file for the munin node', [], 5), ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Munin', 'Munin Documentation', 'Stig Sandbeck Mathisen ', 'Munin', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # -- Options for Epub output --------------------------------------------------- # Bibliographic Dublin Core info. epub_title = 'Munin' epub_author = 'Stig Sandbeck Mathisen ' epub_publisher = 'Stig Sandbeck Mathisen ' epub_copyright = '2012, Stig Sandbeck Mathisen ' # The language of the text. It defaults to the language option # or en if the language is not set. #epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. #epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. #epub_identifier = '' # A unique identification for the text. #epub_uid = '' # A tuple containing the cover image and cover page html template filenames. #epub_cover = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_post_files = [] # A list of files that should not be packed into the epub file. #epub_exclude_files = [] # The depth of the table of contents in toc.ncx. #epub_tocdepth = 3 # Allow duplicate toc entries. #epub_tocdup = True munin-2.0.75/doc/documentation/000077500000000000000000000000001451614574100163615ustar00rootroot00000000000000munin-2.0.75/doc/documentation/index.rst000066400000000000000000000003121451614574100202160ustar00rootroot00000000000000.. _documentation-index: =================== Documenting Munin =================== This document is rather meta, it explains how to document Munin. .. toctree:: :maxdepth: 2 nomenclature.rst munin-2.0.75/doc/documentation/nomenclature.rst000066400000000000000000000155351451614574100216200ustar00rootroot00000000000000============== Nomenclature ============== To be able to use Munin, to understand the documentation, and - not to be neglected - to be able to write documentation that is consistent with Munin behaviour, we need a common nomenclature. Common terms ============ +--------------+--------------------------------------------+------------------------------+ | Term | Explanation | Also referred to as as | +==============+============================================+==============================+ | Munin Master | The central host / server where Munin | master, server, munin server | | | gathers all data. | | | | The machine runs munin-cron | | +--------------+--------------------------------------------+------------------------------+ | Munin Node | The daemon / network service running | In SNMP terms | | | on each host to be contacted by the | it may be called an | | | | agent. | +--------------+--------------------------------------------+------------------------------+ | Plugin | Each munin node handles one or more | service | | | plugins to monitor stuff on hosts | | +--------------+--------------------------------------------+------------------------------+ | Host | A machine monitored by Munin, | | | | maybe by proxy on a munin node, | | | | or via a SNMP plugin | | +--------------+--------------------------------------------+------------------------------+ | Field | Each plugin presents data from one | Data source | | | or more data sources. Each found, | | | | read or calculated value corresponds | | | | to a field.attribute tuple. | | +--------------+--------------------------------------------+------------------------------+ | Attribute | Description found in output from plugins, | | | | both general (global) to the plugin, and | | | | also specific for each Field. | | +--------------+--------------------------------------------+------------------------------+ | Environment | Set up by munin node, used to control | | | variable | plugin behaviour. Found in the plugin | | | | configuration directory. | | | | (/etc/munin/plugin-conf.d/) | | +--------------+--------------------------------------------+------------------------------+ | Global | Used in the global context in the | | | (plugin) | configuration output from a plugin. | | | attribute | (Note: The attribute is considered | | | | "global" only to the plugin (and the | | | | node), and only when executed. | | +--------------+--------------------------------------------+------------------------------+ | Datasource | Used in the datasource-specific context in | | | specific | the output of a plugin | | | plugin | | | | attribute | | | +--------------+--------------------------------------------+------------------------------+ | Global | Used in munin.conf | | | directive | | | +--------------+--------------------------------------------+------------------------------+ | Node level | Used in munin.conf | | | directive | | | +--------------+--------------------------------------------+------------------------------+ | Group level | Used in munin.conf | | | directive | | | +--------------+--------------------------------------------+------------------------------+ | Field level | Used in munin.conf | | | directive | | | +--------------+--------------------------------------------+------------------------------+ Examples ======== To shed some light on the nomenclature, consider the examples below: Global plugin attribute ----------------------- Global plugin attributes are in the plugins output when run with the config argument. The full list of these attributes is found on the protocol config page. This output does not configure the plugin, it configures the plugins graph. :: graph_title Load average ----------- ------------ | `------ value `------------------ attribute Datasource specific plugin attribute ------------------------------------ These are found both in the config output of a plugin and in the normal readings of a plugin. A plugin may provide data from one or more data sources. Each data source needs its own set of field.attribute tuples to define how the data source should be presented. :: load.warning 100 ---- ------- --- | | `- value | `------- one of several attributes used in config output `------------- field load.value 54 ---- ----- -- | | `- value | `------ only attribute when getting values from a plugin `----------- field Configuration files ------------------- This one is from the global section of munin.conf: :: dbdir /var/lib/munin/ ----- --------------- | `--------- value `-------------------------- global directive And then one from the node level section: :: [foo.example.org] address localhost ------- --------- | `----- value `-------------- node level directive munin-2.0.75/doc/example/000077500000000000000000000000001451614574100151435ustar00rootroot00000000000000munin-2.0.75/doc/example/graph/000077500000000000000000000000001451614574100162445ustar00rootroot00000000000000munin-2.0.75/doc/example/graph/aggregate.rst000066400000000000000000000144231451614574100207300ustar00rootroot00000000000000.. _example-plugin-aggregate: .. index:: single: Aggregating munin plugins pair: plugin; aggregate ============================== Graph aggregation by example ============================== This example covers creating aggregate graphs. The configuration reads the current and power from two UPSes (i.e. two hosts with two plugins each) and then creates one virtual host with two virtual plugins; one for current and one for power. Plugins involved ================ The example uses a plugin for monitoring UPSes through SNMP, where the UPS address and the different aspects are defined through symlinks. The two UPSes, called "ups-5a" and "ups-5b", are monitored with respect to "current" and "power". Thus, the affected plugins are called as: :: snmp_ups_ups-5a_current snmp_ups_ups-5b_current snmp_ups_ups-5a_power snmp_ups_ups-5b_power The original plugin name is actually "snmp_ups\_\_" - note the "two" underscores at the end. The plugin is then symlinked to the given host name(s) (e.g. ups-5a) and what we want to monitor (e.g. power). Let's just take one closer look at one of them: :: snmp_ups_ups-5a_power -------- ------ ----- | | | | | `--- The function we want to monitor | `--------- The node name of the UPS `----------------- The plugin Extract from munin.conf ======================= The following extract from /etc/munin/munin.conf is explained in detail, step by step, below the configuration. :: 1 [UPS;ups-5a] 2 address 127.0.0.1 # localhost fetches data 3 4 [UPS;ups-5b] 5 address 127.0.0.1 # localhost fetches data 6 7 [UPS;Aggregated] 8 update no 9 contacts no 10 11 snmp_ups_current.update no 12 snmp_ups_current.graph_args --base 1000 -l 0 13 snmp_ups_current.graph_category UPS 14 snmp_ups_current.graph_title Aggregated input/output current 15 snmp_ups_current.graph_vlabel Ampere 16 snmp_ups_current.inputtotal.label Input current 17 snmp_ups_current.outputtotal.label Output current 18 snmp_ups_current.graph_order inputtotal outputtotal 19 snmp_ups_current.inputtotal.sum \ 20 ups-5a:snmp_ups_ups-5a_current.inputcurrent \ 21 ups-5b:snmp_ups_ups-5b_current.inputcurrent 22 snmp_ups_current.outputtotal.sum \ 23 ups-5a:snmp_ups_ups-5a_current.outputcurrent \ 24 ups-5b:snmp_ups_ups-5b_current.outputcurrent 25 26 snmp_ups_power.update no 27 snmp_ups_power.graph_args --base 1000 -l 0 28 snmp_ups_power.graph_category UPS 29 snmp_ups_power.graph_title Aggregated output power 30 snmp_ups_power.graph_vlabel Watts 31 snmp_ups_power.output.label Output power 32 snmp_ups_power.graph_order output 33 snmp_ups_power.output.sum \ 34 ups-5a:snmp_ups_ups-5a_power.outputpower \ 35 ups-5b:snmp_ups_ups-5b_power.outputpower Explanations, per line ====================== * 1 - 2: The SNMP-based plugin for the UPS known as "ups-5a" is defined. The group name is "UPS" and the node name is "ups-5a". The plugin is run from localhost. * 4 - 5: The SNMP-based plugin for the UPS known as "ups-5b" is defined. The group name is "UPS" and the node name is "ups-5b". The plugin is run from localhost. * 7: The group and "virtual node name" for the aggregated graphs are defined. The group name is "UPS" and the virtual node name is "Aggregated". * 8: Make sure that Munin (specifically, "munin-update") does not try to actively gather information for this node. * 9: Tell "munin-limits" not to send alerts if any limit is breached. The above lines (1 - 9) have now established the fundament for three different graph pages; one for each of the two UPSes and one for the aggregate graphs. * 11 - 15: Define the basic information for the virtual plugin for aggregated current. Note that "snmp_ups_current" is the virtual plugin's name. * 16 - 17: Simultaneously define and label "two" values to be graphed in the virtual plugin: "inputtotal" and "outputtotal". * 18: Order the values. * 19 - 21: Calculate the value for "inputtotal" by reading the "inputcurrent" values from each of the two UPSes. Let's take a closer look at the components :: snmp_ups_current.inputtotal.sum \ ---------------- ---------- --- | | | | | `-- The sum mechanism | `--------- One of this virtual plugin's values `----------------------- The name of the virtual plugin :: ups-5a:snmp_ups_ups-5a_current.inputcurrent \ ups-5b:snmp_ups_ups-5b_current.inputcurrent ------ ----------------------- ------------ | | | | | `------ The "inputcurrent" value from the real plugin | `------------------------ The real plugin's name (symlink) `---------------------------------------- The host name from which to seek information * 22 - 24: Similarly for "outputtotal". * 26 - 35: Like the above, but for power instead. Note that this virtual plugin graphs only "one" value, and as such, only "one" "sum" mechanism is used. Result graphs ============= The graphs below show one of the UPSes, and the aggregated values. The graphs used are by week, because they had a nice dip in the beginning of the graphing period :-) Source graphs for one of the UPSes: .. image:: aggregate/ups-5a_current.png .. image:: aggregate/ups-5a_power.png Aggregate graphs: .. image:: aggregate/aggregate_current.png .. image:: aggregate/aggregate_power.png Summary ======= We have now, in addition to the two real UPS nodes "ups-5a" and "ups-5b" (lines 1 - 5), created one virtual host named "Aggregated" (line 7) with two virtual plugins: "snmp_ups_current" (lines 11 - 24) and "snmp_ups_power" (lines 26 - 35). The "snmp_ups_current" virtual plugin outputs two field names: "inputtotal" (lines 16 and 19 - 21) and "outputtotal" (lines 17 and 22 - 24), while the "snmp_ups_power" virtual plugin outputs only one field name, namely "output" (lines 31 - 35). Further reading =============== * [wiki:Using_SNMP_plugins Using SNMP plugins] * [wiki:munin.conf munin.conf] directives explained munin-2.0.75/doc/example/graph/aggregate/000077500000000000000000000000001451614574100201725ustar00rootroot00000000000000munin-2.0.75/doc/example/graph/aggregate/aggregate_current.png000066400000000000000000000122351451614574100243730ustar00rootroot00000000000000PNG  IHDRUP!PLTEȖ"""Aٳ7IDATx]r:U੒Cz?pUr\nѦ *lOsN @!cR$f @hy,Y'pofgiq7Ľ7Ľw5᥻C`Y:o b~bDȆ]P'J5~_21{}?_wNT~}RRM=-\ ?IF} 2a_I)tHgT.ZDC=(QWh>7Ett&\./SqL%S(`!u|<=J9,Ś:_eᴞu8!5'nE}L4Z|O,4 mu]?`Ln寈k8 QYN?=Ao9Mkf2S{{jyTaT4&4 i>2jQ+s3Qo( o 4SUxx#eKP5E{A3+k n՜ \y\{3^ϗV'm9!# a+։ܱdS&c+uՉGۍʵ9V~ܘ pLc#"eKbg;+gR Ķs9QjoXj]OހqֆS﫡7R9ê 0{k8O7SAD7_W /;R9êDȰ;3ID { 7|;m&rL{oOc0k75Ok0w|ʖ6_߯ݔOs¼v#.] 1o&ֶ'o{i9;8߯F{.I3uTZ:xÑiԳs`[$>1~9\^ ޮi@w},x|mߒټ)x۾)Ea{WPaJ-o(߭~! ._*?_?j~̻RXyO+43M[/пotv|Tm۫:A{#ǿ9cӳw}K|14[z7fo)ho/%߯c?rCk:<}-ZO^$9S;ԗՁP*,;~&*f(Hjqݘ@#5pCܓYTqhؖ\]UGB_3]z{jM*0;>7TSpV aB>OL^9|C݁e} N\S^{~B~hqZ>DtLf<}_4fzYVq~d{)XpO2Gۋ}z6\ߤCka{[+ js.VV[ qZe>?{Dѩ1{&Jc#y_؜Ƒ>yz;~/W|Q<rvD>{^>/t~Rue>kf{z`؁p8pWe.ol%|l9-dX2/N</-;K{o /d`񋽗;rup`d`^{` ,~~cag:Y/jڼfgdq?Jq)~ fd 7_{?IY FIr5f߾<*w_hrU7Q˵  )s}|jܓHm^JD>'Pɋpw~LDEF& KWkDp;t8MaxNNd*KKqv?`.5c[xɨ `lC^_2m.5a}}9l㦔&(vWF'6fmF\_̦-*;4q%YY5pa!.GmUv6 #3N̩:9@s ػ##׷͹=l :5Gerӗ5͙PbI2{'&YpO\+.ޓ2. '%`u7>QwqώS@qۗ9ZEg6=7RpE bKF3VQhetFK$5Ze#E϶VѸw"BwՐbDkdPSUJpI a@0ͽxEQ[]x$%ZTRJ$FVs.Szaɋ*p!́H ŕN4scQwR{Bv-Q=ɦLťM6cWQ+ɣ9kM̅j*5&)>aWٻ-_ (̭㓋uj!'KB/M&$ԉe~fH&OWÞ}5iQ,ۯc2>BG7_MܶE#$4nʢ;VDWZxj4%^$;]\rm{ DmdSϞ]9Ov"{1xӶa; 18JTTͷN=(w0V WaD;0ڛ"ƮB4Qo`kI\5Lwÿ/Nܗsu{_KG-p d=9z@㳭 ۙ( Z>|uzNx0|9 FXSb {~)#-F-'QJjjtb EP@WO|fKS{i$Xœ7Յę9뤰 v} M7p@# "Qv%F[9 H{|$p U7yJCnjiWb<,'s#hF#A!5h튱y6م vx&P1{S4MRus إڂ{>S du90&:IENDB`munin-2.0.75/doc/example/graph/aggregate/aggregate_power.png000066400000000000000000000072721451614574100240520ustar00rootroot00000000000000PNG  IHDRAPLTEȖ""6oWIDATx]n:" dkLڽpj^|sXNAv"b-Jw[%쿁{B{UZ_U<qN}o։[=߶cʼnzfyFKH(ۉ{poTQG& {~7a̭Tؕ-܇ [$VqP>ΪVzxLdh0 \4c%pOoʾ{c!".n]Z&0 čc3֥`~~8@ Én`m)vA1%w}{r298|my",]Hn_lH~N:Z5U{}K0.E0 "H5 8ƸƪL^af$o;”!ټuH!a-hu}8Nymvc::1w}A q?b 3,k2*kP[o>/½ yun7N7i8ò[QQ!e vuVmXPߦ9>AT?}Tc9kd}?^v~|+1rq{8]P#qF?I>zBd:v]->{b;RHQX_=` /HKW2¨|O=iVDuJ*L=Ît *WwýSwcE#7} mH;S,cyx?n ,ۂ'(S^Z]L vW__t5ܗ=<zGdow:c Y28R?"b'[0+ש籒}7;wK9H$NN]42=ľ>ǵe߸|r?Vn7f33X?QOG{2k$vDZۧ:| [ _Pekv^^.s(FB-vv*8O7bJ FM"7,NU6};/8CK7_Ay% ꠠ;-Y?_-jwteIjnj4j5#GnJƓ@];s˞p.vhi6N96|?y=GjjiN]^\˙ۥo{5q0q%[dNѥ{\ݏE$.־U×=>OHz3b}wgց/վWs(z8Kw˚_x6}zRk_T>#,|\T-R[j_wʠAmy{Lv{Ʌs9ZG_}7jl߫Y}FwPb}Vt}~ZkZm}ߐeNKB<;ۙ>%;=d&c]uwxBYסowbξ?sTdtq$Pޜ&}Ux}Np$w*ΘzZ黊Zsa8\wY]]pFW3)|w{-K}9pLiyoqxxWlȕXfcWweHϿǯk}:WjgX}m> ޲f;{kC~w}7έ4 ޲fߗw;/oi*eך}oYnfj<-g}HA#s9*<;WS07?w4IOmy}^5?a I;jo6'43,rә[fcs >_g8o}??t~續z_X[{g~H&FqڮKalޥaM>y۲zn*/֪5߹:jk`=K;݉YNNny+Fk%m atVNPX#+W}rv)JS,ZKG_Kl.y7*G~V-lB,k2K_wnqcmp[Rdm0I%WIV4- 1G "UF;srQy rHh|֧rɻ\ mMh^p#SR(UK42 7@ָ+kva"tm-+v7]\?'B-=pɵ:֡9l>^]R^±- Ch5aTw-'v` nKII W к;E:rr؅<"JGx!<[YwWm/ w .@ c9াvUcW4)4}߸'V|%r@ݻ ȁ9%܎f;^ĸ-(ZӨA^M?6. nwz ȵ Ld)u="I=p612B)ų@2ܭ;IKr{YD:>suUb9%e/ ;2FI Ehtvx(biFO(q BGK2*bE.}%K9 lI{1iyi2M}2/dɔ%k7}kQJN#IENDB`munin-2.0.75/doc/example/graph/aggregate/ups-5a_current.png000066400000000000000000000064441451614574100235640ustar00rootroot00000000000000PNG  IHDRUP!PLTEȖ"""AٳbKGDH pHYs  tIME =S=K IDATx]=:vXOmxh=@ nR6v+"iܶL2KcY%Jk*a!p nĽvX'߇u~ۣ~~~&`v?BPWfWs/k'L'o=p;]vJ8dKמ2'yC7~2l9[~tTSD~ qSgXw> u*cq{2r;SlжFWƭMv;>w'mK[ȍl{=~bL[\[IpwvzMpok: qSם_ۑ#i_Ui[n_*{\U8-y#s˃366e=M[y.o!2*ћK=tO73Jk{FQ1MwA1A!S*s:|Rҡ ~#ӫ|QۍU.:o qa|{M|=GHOoTrfiK7]I [eZ=O*@wI;W;M})wj#{O q-yuZF^ow~3Ϯ֚OHaq':31oˑ <=ŷ&s =WGl5h>4}7״ٮ$Ы V4VnU]g[$rImi*/1*!w仪O5,!|I^~O[+}߼GLw.vXgM"Vׄ|+7I$]MNwoݠ}G-UG2]>O N|ߝXF`}T߹| 8<"2p1:CWq2ts%& C=qX:B;A|v¸׎&_G'cX"CSU|옠uOUvJԛwS}_Le>EDZ>1YzW5!ԫ=;ϐOrGF^>8~~\Z=p_^Bz~ ~Rk}'u\C͂u\`ުyI5ͦif'%"_~ r7\Z\_ũ6W)߻/hd/}N%;#jםh)hLUCk|="u\XebURUYgu4IULK?Κ8ߒVYUL{Jsv#НU7ko.׸CiםL刼q5r5Tr'Ż/pmvu|wu4ďw:˲g5;nӢWe=}㼽 .?v[+ٕ؁iS3Nj#Hr o6_+5:֏L'Hu{|~wVM;nqo8n>3M@fտ~;2η5W4YNC(C.qEq|NIWPHz%he H?g9FZgZ&Pmh$CEƢy~hl{nsEE܈q# A?k~&h/w2ide΅j=vKς[MZIj&ywPvNLp\n=(zgn[㍃P-^M!f흉ǭ+ڻIBn8EH㆞7͒ xFNom[< + qAFA۵-;Bc]kZ,x7 jPVd{0LZqq#nčo_kLnZ'X|"'=IENDB`munin-2.0.75/doc/example/graph/aggregate/ups-5a_power.png000066400000000000000000000053361451614574100232350ustar00rootroot00000000000000PNG  IHDRA!PLTEȖ""Y$bKGDH pHYs  tIME %= CIDATx]MȻۿ\ @.`CL32lp΋n(R,#y,8c?*e=Ko7KJ xHC;oot9;m ,ʷ|X,\6E:̓*L>\z\Sޝlz(?ϫ*Xu[^*#(+ӥ1twd6Jof ܬ]&nxvW˭q%V*­,kmfmz B֏ *`|#q+yGSӎ[8]o\0%x[j}~/pGd9>7L߳=4~b*37MMoFGˏGg:<,b,Za]^_&Gl F^S}Ho;|7s^1i1vq g%$ck0>{BoBO!&Tɡ6#.۱9foQB&sl[!rwɻI,Ը7R|諌(Dc[̳E>?TDofadߗJ#ڳ lquɷ|K%""Ӏ~L%efY? RM*^m*7-;~p55jο~r׻8fu.oFw"[wh{qTo*;?\럪Krͷ ;.E+WT?$pvՑksS?݅o=|*J%nYq*Sk w|{U,׌ڻWl{P',Ʃj]h؏Nt--wR]̿ddY=!Iͭ1i8L&ߋ{K'dS7MZSO>u|h|NMo꿩&^r|O{9&7X~&oNo> o-G^ |~{|1rj;v4qw 7zj`7X6}MV{g^>{u|s1QY1+>NK^D9sp>Srdвc=/q.߷[/[{>۾',9Jo~~͒}nMoo#o&o;57 &&o&g?:%sӒo~/Koб٭?OolNC7zmIok4^#o&Z3$7M|7M|4>977M|7M|7M oo&o~~3wk\@0f].߽ %­tܟoELR׿g(ʙAWsҤ4>bEi`׮*{P}c fwmDjCR+**iGî7; p8<@>?n]:GqFT&Y7N;̾ h7}Rn$הa݉A<* ͢U&­cGSRh 7&8``). To make it easier to see the configuration, you can also update the configuration with an ``includedir`` on nfs, and declare all your nodes there (ie: ``/nfs/munin/etc/.d/``). If you configured at least one node, you should have ``/nfs/munin/db/`` that starts getting populated with subdirectories (groups), and a few files, including ``datafile``, and ``datafile.storable`` (and ``limits`` if you also have munin-limits running here). Merging data ============ All our update-masters generate update their dbdir including: - ``datafile`` and ``datafile.storable`` which contain information about the collected plugins, and graphs to generate. - directory tree with the rrd files In order to have munin-html to run correctly, we need to merge those dbdir into one. Merging files ------------- ``datafile`` is just plain text with lines of ``key value``, so concatenating all the files is enough. ``datafile.storable`` is a binary representation of the data as loaded by munin. It requires some munin internal structures knowledge to merge them. If you have ``munin-limits`` also running on update-masters, it generate a ``limits`` files, those are also plain text. In order to make that part easier, a ``munin-mergedb.pl`` is provided in contrib. Merging rrd tree ---------------- The main trick is about rrd. As we are using a shared nfs, we can use symlinks to get them to point to one an other, and not have to duplicate them. (Would be hell to keep in sync, that's why we really need shared nfs storage.) As we deal with groups, we could just link top level groups to a common rrd tree. Example, if you have two updaters (update1 and update2), and 4 groups (customer1, customer2, customer3, customer4), you could make something like that:: /nfs/munin/db/shared-rrd/customer1/ /nfs/munin/db/shared-rrd/customer2/ /nfs/munin/db/shared-rrd/customer3/ /nfs/munin/db/shared-rrd/customer4/ /nfs/munin/db/update1/customer1 -> ../shared-rrd/customer1 /nfs/munin/db/update1/customer2 -> ../shared-rrd/customer2 /nfs/munin/db/update1/customer3 -> ../shared-rrd/customer3 /nfs/munin/db/update1/customer4 -> ../shared-rrd/customer4 /nfs/munin/db/update2/customer1 -> ../shared-rrd/customer1 /nfs/munin/db/update2/customer2 -> ../shared-rrd/customer2 /nfs/munin/db/update2/customer3 -> ../shared-rrd/customer3 /nfs/munin/db/update2/customer4 -> ../shared-rrd/customer4 /nfs/munin/db/html/customer1 -> ../shared-rrd/customer1 /nfs/munin/db/html/customer2 -> ../shared-rrd/customer2 /nfs/munin/db/html/customer3 -> ../shared-rrd/customer3 /nfs/munin/db/html/customer4 -> ../shared-rrd/customer4 At some point, an option to get the rrd tree separated from the dbdir, and should avoid the need of such links. Running munin-html ================== Once you have your update-masters running, and a merge ready to go, you should place a cron on a html-master to : - merge data as requested - launch ``munin-limits``, if not launched on update-masters and merged - launch ``munin-html`` (required, even if you use cgi) - launch ``munin-graph`` unless you use cgi-graph munin-2.0.75/doc/example/webserver/000077500000000000000000000000001451614574100171475ustar00rootroot00000000000000munin-2.0.75/doc/example/webserver/apache-virtualhost.rst000066400000000000000000000033271451614574100235110ustar00rootroot00000000000000.. _example-webserver-apache: ================================== Apache virtualhost configuration ================================== This example describes how to set up munin on a separate apache httpd virtual host. It uses FastCGI if this is available, and falls back to CGI if it is not. Munin configuration =================== This example assumes the following configuration in /etc/munin/munin.conf .. index:: pair: example; munin.conf :: graph_strategy cgi html_strategy cgi Virtualhost configuration ========================= Add a new virtualhost, using the following example: .. index:: pair: example; apache httpd configuration :: ServerName munin.example.org ServerAlias munin ServerAdmin info@example.org DocumentRoot /srv/www/munin.example.org ErrorLog /var/log/apache2/munin.example.org-error.log CustomLog /var/log/apache2/munin.example.org-access.log combined # Rewrites RewriteEngine On # Static content in /static RewriteRule ^/favicon.ico /etc/munin/static/favicon.ico [L] RewriteRule ^/static/(.*) /etc/munin/static/$1 [L] # HTML RewriteCond %{REQUEST_URI} .html$ [or] RewriteCond %{REQUEST_URI} =/ RewriteRule ^/(.*) /usr/lib/munin/cgi/munin-cgi-html/$1 [L] # Images RewriteRule ^/munin-cgi/munin-cgi-graph/(.*) /usr/lib/munin/cgi/munin-cgi-graph/$1 [L] # Ensure we can run (fast)cgi scripts Options +ExecCGI SetHandler fcgid-script SetHandler cgi-script munin-2.0.75/doc/example/webserver/lighttpd.rst000066400000000000000000000031311451614574100215160ustar00rootroot00000000000000.. _example-webserver-lighttpd: ======================== lighttpd configuration ======================== This example describes how to set up munin on lighttpd. It spawns two lighttpd processes, one for the graph rendering, and one for the html generation. You need to enable the "mod_rewrite" module in the main lighttpd configuration. Munin configuration =================== This example assumes the following configuration in /etc/munin/munin.conf .. index:: pair: example; munin.conf :: # Use cgi rendering for graph and html graph_strategy cgi html_strategy cgi Webserver configuration ======================= .. index:: pair: example; lighttpd configuration :: alias.url += ( "/munin-static" => "/etc/munin/static" ) alias.url += ( "/munin" => "/var/cache/munin/www/" ) fastcgi.server += ("/munin-cgi/munin-cgi-graph" => (( "socket" => "/var/run/lighttpd/munin-cgi-graph.sock", "bin-path" => "/usr/lib/munin/cgi/munin-cgi-graph", "check-local" => "disable", )), "/munin-cgi/munin-cgi-html" => (( "socket" => "/var/run/lighttpd/munin-cgi-html.sock", "bin-path" => "/usr/lib/munin/cgi/munin-cgi-html", "check-local" => "disable", )) ) url.rewrite-repeat-if-not-file += ( "/munin/(.*)" => "/munin-cgi/munin-cgi-html/$1", "/munin-cgi/munin-cgi-html$" => "/munin-cgi/munin-cgi-html/", ) munin-2.0.75/doc/example/webserver/nginx.rst000066400000000000000000000116211451614574100210250ustar00rootroot00000000000000.. _example-webserver-nginx: ===================== nginx configuration ===================== This example describes how to set up munin on nginx. nginx does not spawn FastCGI processes by itself, but comes with an external "spawn-fcgi" program. We need one process for the graph rendering, and one for the html generation. Munin configuration =================== This example assumes the following configuration in /etc/munin/munin.conf .. index:: pair: example; munin.conf :: # graph_strategy should be commented out, if present html_strategy cgi FastCGI configuration ===================== This will spawn two FastCGI processes trees. One for munin cgi graphing and one for HTML generation. It will create a socket owned by www-data, and run the processes as the "munin" user. .. index:: pair: example; munin-cgi-graph invocation .. code-block:: bash spawn-fcgi -s /var/run/munin/fastcgi-graph.sock -U www-data \ -u munin -g munin /usr/lib/munin/cgi/munin-cgi-graph spawn-fcgi -s /var/run/munin/fastcgi-html.sock -U www-data \ -u munin -g munin /usr/lib/munin/cgi/munin-cgi-html Note: Depending on your installation method, the "munin-\*-graph" programs may be in another directory. Check Makefile.config if you installed from source, or your package manager if you used that to install. Note: If you installed using the package manager on Debian or Ubuntu, the /var/log/munin/munin-cgi-\*.log files may be owned by the "www-data" user. This example runs the processes as the "munin" user, so you need to chown the log files, and edit /etc/logrotate.d/munin. Webserver configuration ======================= .. index:: pair: example; nginx configuration :: location ^~ /munin-cgi/munin-cgi-graph/ { fastcgi_split_path_info ^(/munin-cgi/munin-cgi-graph)(.*); fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_pass unix:/var/run/munin/fastcgi-graph.sock; include fastcgi_params; } location /munin/static/ { alias /etc/munin/static/; } location /munin/ { fastcgi_split_path_info ^(/munin)(.*); fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_pass unix:/var/run/munin/fastcgi-html.sock; include fastcgi_params; } Authentication and group access =============================== .. index:: pair: example; nginx authentication group configuration If you have munin statistics, and need to allow some user (ie: customers) to access only graphs for a subset of nodes, the easiest way might be to use groups, and authentication with the exact same name as the node-group name. Here is an example of how to redirect the users to the group that matches their name, and prevent any access to other groups. It also has allow an admin user to see it all. Warning: If you don't want users to get any information about the other group names, you should also change the templates accordingly, and remove any navigation part that might. :: # Here, the whole vhost has auth requirements. # You can duplicate it to the graph and html locations if you have # something else that doesn't need auth. auth_basic "Restricted stats"; auth_basic_user_file /some/path/to/.htpasswd; location ^~ /cgi-bin/munin-cgi-graph/ { # not authenticated => no rewrite (back to auth) if ($remote_user ~ ^$) { break; } # is on the right subtree ? set $ok "no"; # admin can see it all if ($remote_user = 'admin') { set $ok "yes"; } # only allow given path if ($uri ~ /cgi-bin/munin-cgi-graph/([^/]*)) { set $path $1; } if ($path = $remote_user) { set $ok "yes"; } # not allowed here ? redirect them where they should land if ($ok != "yes") { # redirect to where they should be rewrite / /cgi-bin/munin-cgi-graph/$remote_user/ redirect; } fastcgi_split_path_info ^(/cgi-bin/munin-cgi-graph)(.*); fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_pass unix:/var/run/munin/fastcgi-graph.sock; include fastcgi_params; } location /munin/static/ { alias /etc/munin/static/; } location /munin/ { # not authenticated => no rewrite (back to auth) if ($remote_user ~ ^$) { break; } # is on the right subtree ? set $ok "no"; # admin can see it all if ($remote_user = 'admin') { set $ok "yes"; } # only allow given path if ($uri ~ /munin/([^/]*)) { set $path $1; } if ($path = $remote_user) { set $ok "yes"; } # not allowed here ? redirect them where they should land if ($ok != "yes") { # redirect to where they should be rewrite / /munin/$remote_user/ redirect; } fastcgi_split_path_info ^(/munin)(.*); fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_pass unix:/var/run/munin/fastcgi-html.sock; include fastcgi_params; } munin-2.0.75/doc/index.rst000066400000000000000000000005241451614574100153520ustar00rootroot00000000000000 Welcome to Munin's documentation! ================================= Contents: .. toctree:: :maxdepth: 2 installation/index.rst master/index.rst node/index.rst plugin/index.rst documentation/index.rst reference/index.rst example/index.rst Indices and tables ================== * :ref:`genindex` * :ref:`search` munin-2.0.75/doc/installation/000077500000000000000000000000001451614574100162115ustar00rootroot00000000000000munin-2.0.75/doc/installation/configuration.rst000066400000000000000000000040141451614574100216110ustar00rootroot00000000000000======================= Initial configuration ======================= Node ==== Plugins ------- Decide which plugins to use. The munin node runs all plugins present in CONFDIR/plugins/ The quick auto-plug-and-play solution: .. code-block:: bash munin-node-configure --shell --families=contrib,auto | sh -x Access ------ The munin node listens on all interfaces by default, but has a restrictive access list. You need to add your master's IP address. The "cidr_allow", "cidr_deny", "allow" and "deny" statements are used. cidr_allow uses the following syntax (the /32 is not implicit, so for a single host, you need to add it): | cidr_allow 127.0.0.0/8 | cidr_allow 192.0.2.1/32 allow uses regular expression matching against the client IP address. | allow '^127\.' | allow '^192\.0\.2\.1$' For specific information about the syntax, see `Net::Server `_. Please keep in mind that cidr_allow is a recent addition, and may not be available on all systems. Startup ------- Start the node agent (as root) SBINDIR/munin-node. Restart it it it was already started. The node only discovers new plugins when it is restarted. You probably want to use an init-script instead and you might find a good one under build/dists or in the build/resources directory (maybe you need to edit the init script, check the given paths in the script you might use). Master ====== Add some nodes -------------- Add some nodes to CONFDIR/munin.conf [node.example.com] address 192.0.2.4 [node2.example.com] address node2.example.com [node3.example.com] address 2001:db8::de:caf:bad Configure web server ==================== On the master, you need to configure a web server. If you have installed "munin" through distribution packages, a webserver may have been configured for you already. If you installed from source, there is a minimal configuration example in the "resources" directory in the source tarball. For a more complex example, see :ref:`example-webserver-apache` munin-2.0.75/doc/installation/help.rst000066400000000000000000000015231451614574100176740ustar00rootroot00000000000000============== Getting help ============== IRC Channel =========== The most immediate way to get hold of us is to join our IRC channel: ``#munin on server irc.oftc.net`` The main timezone of the channel is Europe+America. If you can explain your problem in a few clear sentences, without too much copy&paste, IRC is a good way to try to get help. If you do need to paste log files, configuration snippets, scripts and so on, please use a pastebin_. If the channel is all quiet, try again some time later, we do have lives, families and jobs to deal with also. You are more than welcome to just hang out, and while we don't mind the occasional intrusion of the real world into the flow, keep it mostly on topic, and do not paste random links unless they are *really* spectacular and intelligent. .. _pastebin: https://gist.github.com/ munin-2.0.75/doc/installation/index.rst000066400000000000000000000004531451614574100200540ustar00rootroot00000000000000.. _install-index: ==================== Munin installation ==================== This document explains how to get Munin onto your system, where to get help, and how to report bugs. .. toctree:: :maxdepth: 2 prerequisites.rst install.rst configuration.rst help.rst upgrade.rst munin-2.0.75/doc/installation/install.rst000066400000000000000000000105241451614574100204130ustar00rootroot00000000000000================== Installing Munin ================== With open source software, you can choose to install binary packages or install from source-code. To install a package or install from source is a matter of personal taste. If you don't know which method too choose read the whole document and choose the method you are most comfortable with. Master and node =============== Munin is split into two distinct roles. Node ---- The "munin node" is a daemon which runs on all servers being monitored. Master ------ The "munin master" connects to all munin nodes, collects data, and stores it in `RRD `_ You will need to install "munin-master" on the server which will collect data from all nodes, and graph the results. When starting with munin, it should be enough to install the munin master on one server. On the munin master, you will need a web server capable of running CGI or FastCGI. Apache HTTD should be suitable. Also reported to be working is nginx and lighttpd. Source or packages? =================== Installing Munin on most relevant operating systems can usually be done with with the systems package manager, typical examples being: FreeBSD ------- From source: .. code-block:: bash cd /usr/ports/sysutils/munin-master && make install clean cd /usr/ports/sysutils/munin-node && make install clean Binary packages: .. code-block:: bash pkg_add -r munin-master pkg_add -r munin-node Debian/Ubuntu ------------- Munin is distributed with both Debian and Ubuntu. In order to get Munin up and running type .. code-block:: bash sudo apt-get install munin-node on all nodes, and .. code-block:: bash sudo apt-get install munin on the master. Please note that this might not be the latest version of Munin. On Debian you have the option of enabling "backports", which may give access to later versions of Munin. RedHat / CentOS / Fedora ------------------------ At time of writing, only the 1.x version of munin is available in `EPEL `_. If you want 2.x, your best option is probably to install from source. Other systems ------------- On other systems, you are probably best off compiling your own code. See `Installing Munin from source`_. Installing Munin from source ============================ If there are no binary packages available for your system, or if you want to install Munin from source for other reasons, follow these steps: We recommend downloading a release tarball, which you can find on `sourceforge.net `_. Alternatively, if you want to hack on Munin, you should clone our git repository by doing. .. code-block:: bash git clone git://github.com/munin-monitoring/munin Please note that a git checkout will need some more build-dependencies than listed below, in particular the Python Docutils and Sphinx. Build dependencies on Debian / Ubuntu ------------------------------------- In order to build Munin from source you need a number of packages installed. On a Debian or Ubuntu system these are: * perl * htmldoc * html2text * default-jdk Configuring and installing -------------------------- Warning for NFS users ~~~~~~~~~~~~~~~~~~~~~ If you're using NFS please note that the "make install" process is slightly problematic in that it (Module::Build actually) writes files under $CWD. Since "make install" is usually run by root and root usually cannot write files on a NFS volume, this will fail. If you use NFS please install munin from /var/tmp, /tmp or some such to work around this. Running make ~~~~~~~~~~~~ There are make targets for node, master, documentation and man files. Generally you want to install everything on the master, and just the node and plugiuns on the nodes. - Edit Makefile.config to suit your needs. - Create the user "munin" with the primary group "munin". The user needs no shell and no privileges. On most Linux systems the munin user's shell is the nologin shell (it has different paths on different systems - but the user still needs to be able to run cron jobs. Node ~~~~ For the node, you need only the common parts, the node and the plugins. .. code-block:: bash make make install-common-prime install-node-prime install-plugins-prime Master ~~~~~~ For the master, this will install everything. .. code-block:: bash make make install munin-2.0.75/doc/installation/prerequisites.rst000066400000000000000000000025261451614574100216540ustar00rootroot00000000000000=============== Prerequisites =============== In order for you to install Munin you must have the following: Building munin ============== In order to build munin, you need: * GNU Make — Please do not attempt to use any other make. * A reasonable Perl 5 (Version 5.8 or newer) * Perl modules: Module::Build Developers / packagers need * Test::MockModule * Test::MockObject * Test::Pod::Coverage * Test::Perl::Critic 1.096 or later * Test::Exception * Directory::Scratch (err, wherefrom?) In order to build the documentation, you need: * sphinx Running munin ============= In order to run munin, you need: * A reasonable perl 5 (Version 5.8 or newer) The munin node needs: * Perl modules * Net::Server * Net::Server::Fork * Time::HiRes * Net::SNMP (Optional, if you want to use SNMP plugins) * Java JRE (Optional, if you want to use java plugins) * Anything the separate plugins may need. These have diverse requirements, not documented here. The munin master needs * Perl modules: * CGI::Fast * Digest::MD5, * File\::Copy::Recursive * Getopt::Long * HTML::Template * IO::Socket::INET6 * Log::Log4perl 1.18 or later * Net::SSLeay (Optional, if you want to use SSL/TLS) * Params::Validate * Storable * Text::Balanced * Time::HiRes * TimeDate * A web server capable of CGI or FastCGI munin-2.0.75/doc/installation/upgrade.rst000066400000000000000000000006441451614574100203760ustar00rootroot00000000000000================================= Upgrading Munin from 1.x to 2.x ================================= This is a compilation of items you need to pay attention to when upgrading from Munin 1.x to munin 2.x FastCGI ======= Munin graphing is now done with FastCGI. Munin HTML generation is optionally done with FastCGI. Logging ======= The web server needs write access to the munin-cgi-html and munin-cgi-graph logs. munin-2.0.75/doc/master/000077500000000000000000000000001451614574100150035ustar00rootroot00000000000000munin-2.0.75/doc/master/index.rst000066400000000000000000000033131451614574100166440ustar00rootroot00000000000000.. _master-index: ================== The Munin master ================== Role ==== The munin master is responsible for gathering data from munin nodes. It stores this data in RRD, and graphs them on request. Components ========== The following components are part of munin-master: .. hlist:: * :ref:`munin-cron` runs :ref:`munin-graph`, :ref:`munin-html`, :ref:`munin-limits` and :ref:`munin-update`. * :ref:`munin-update` is run by :ref:`munin-cron`. It is the munin data collector, and it fetches data from :ref:`munin nodes `, which is then stored in RRD files. * :ref:`munin-graph` is run by :ref:`munin-cron`. It generates graphs in PNG format from the RRD files. See also :ref:`munin-cgi-graph`. * :ref:`munin-limits` is run by :ref:`munin-cron`. It notifies any configured contacts if a value moves between "ok", "warn" or "crit". Munin is commonly used in combination with Nagios, which is then configured as a contact. * :ref:`munin-html` is run by :ref:`munin-cron`. It generates HTML pages. See also :ref:`munin-cgi-html`. * :ref:`munin-cgi-graph` is run by a web server. If graph_strategy is set to "cgi", munin-cron will not run munin-graph, and assumes that the web server runs :ref:`munin-cgi-graph` instead. * :ref:`munin-cgi-html` is run by a web server. If html_strategy is set to "cgi", munin-cron will not run munin-html, and assumes that the web server runs :ref:`munin-cgi-html` instead. Configuration ============= The munin master has its primary configuration file at :ref:`/etc/munin/munin.conf `. Other documentation =================== .. toctree:: :maxdepth: 2 rrdcached.rst munin-2.0.75/doc/master/rrdcached.rst000066400000000000000000000070101451614574100174520ustar00rootroot00000000000000.. _munin-master-rrdcached: ========================================= Scaling the munin master with rrdcached ========================================= When the master grows big, and has a lot of nodes, there is a risk of disk IO becoming a bottleneck. To reduce this disk IO, you can use the RRD Cache Daemon. This will spool RRD changes in a queue, and flush changes on demand, and periodically. This will replace lots of random writes with a much smaller amount of sequential writes. Configuring rrdcached ===================== Parameters ---------- RRDCached writes the spool data every 5 mintes by default. This is the same as the munin master. To have an effect, change the flushing intervals to allow more data to be spooled. Use the following parameters, and tune to your liking: +---------+-----------------------------------------------------+ | -w 1800 | Wait 30 minutes before writing data | +---------+-----------------------------------------------------+ | -z 1800 | Delay writes by a random factor of up to 30 minutes | | | (this should be equal to, or lower than, "-w") | +---------+-----------------------------------------------------+ | -f 3600 | Flush all data every hour | +---------+-----------------------------------------------------+ Example ------- Create a directory for the rrdcached journal, and have the "munin" user own it. (in this example: /var/lib/munin/rrdcached-journal). Set up a separate RRDCached instance, run by the munin user. The following command starts an RRDCached instance, and can be added to /etc/rc.local. .. code-block:: bash sudo -u munin /usr/bin/rrdcached \ -p /run/munin/rrdcached.pid \ -B -b /var/lib/munin/ \ -F -j /var/lib/munin/rrdcached-journal/ \ -m 0660 -l unix:/run/munin/rrdcached.sock \ -w 1800 -z 1800 -f 3600 Note: While testing, add "-g" to the command line to prevent rrdcached from forking into the background. The munin grapher also needs write access to this socket, in order for it to tell the RRDCached to flush data needed for graphing. If you run munin with CGI graphing, you will need to give the web server access. For a common setup, run the following command, as root, after starting rrdcached: .. code-block:: bash chgrp www-data /run/munin/rrdcached.sock Recommended: If you have systemd installed, use a systemd service. If you have upstart installed, write a daemon job configuration file. If you use systemd, you can add "-g" to the rrdcached command line. Configuring munin to use rrdcached =================================== To enable rrdcached on the munin master, you will need to set the "rrdcached_socket" line in /etc/munin/munin.conf :: rrdcached_socket /run/munin/rrdcached.sock Is it working? ============== If all goes well, you should see the following: Munin logging ------------- There should be no messages regarding rrdcached in /var/log/munin/munin-update.log. On failure to connect, there will be log lines like: :: 2012/06/26 18:56:12 [WARN] RRDCached feature ignored: rrdcached socket not writable …and you should then check for permissions problems. RRDCached spool --------------- The rrdcached spool file should be in /var/lib/munin/rrdcached-journal/, and it should grow for each run of munin-update until it hits the flush time. The file looks like: :: /var/lib/munin/rrdcached-journal/rrd.journal.1340869388.141124 For a munin master with 200 nodes, this could well grow to 100MiB, depending on the number of plugins, and the spool file time parameters. munin-2.0.75/doc/node/000077500000000000000000000000001451614574100144355ustar00rootroot00000000000000munin-2.0.75/doc/node/async.rst000066400000000000000000000051551451614574100163120ustar00rootroot00000000000000.. _node-async: ========================= Asynchronous proxy node ========================= The munin asynchronous proxy node (or "munin-async") connects to the local node periodically, and spools the results. When the munin master connects, all the data is available instantly. munin-asyncd ============ The Munin async daemon starts at boot, and connects to the local munin-node periodically, like a :ref:`munin master ` would. The results are stored the results in a spool, tagged with timestamp. You can also use munin-asyncd to connect to several munin nodes. You will need to use one spooldir for each node you connect to. This enables you to set up a "fanout" setup, with one privileged node per site, and site-to-site communication being protected by ssh. munin-async =========== The Munin async client is invoked by the connecting master, and reads from the munin-async spool using the "spoolfetch" command. Example configuration ===================== On the munin master ------------------- We use ssh encapsulated connections with munin async. In the :ref:`the munin master ` configuration you need to configure a host with a "ssh\://" address. :: [random.example.org] address ssh://munin-async@random.example.org You will need to create an SSH key for the "munin" user, and distribute this to all nodes running munin-asyncd. The ssh command and options can be customized in :ref:`munin.conf` with the ssh_command and ssh_options configuration options. On the munin node ----------------- Configure your munin node to only listen on "127.0.0.1". You will also need to add the public key of the munin user to the authorized_keys file for this user. * You must add a "command=" parameter to the key to run the command specified instead of whatever command the connecting user tries to use. :: command="/usr/share/munin/munin-async --spoolfetch" ssh-rsa AAAA[...] munin@master The following options are recommended for security, but are strictly not necessary for the munin-async connection to work * You should add a "from=" parameter to the key to restrict where it can be used from. * You should add hardening options. At the time of writing, these are "no-X11-forwarding", "no-agent-forwarding", "no-port-forwarding", "no-pty" and "no-user-rc". Some of these may also be set globally in /etc/ssh/sshd_config. :: no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,from="192.0.2.0/24",command="/usr/share/munin/munin-async --spoolfetch" ssh-rsa AAAA[...] munin@master See the sshd_config (5) and authorized_keys(5) man pages for more information. munin-2.0.75/doc/node/index.rst000066400000000000000000000010601451614574100162730ustar00rootroot00000000000000.. _node-index: ================ The Munin node ================ Role ==== The munin node is installed on all monitored servers. It accepts connections from the munin master, and runs plugins on demand. By default, it is started at boot time, listens on port 4949/TCP, accepts connections from the :ref:`munin master `, and runs :ref:`munin plugins ` on demand. Configuration ============= The configuration file is :ref:`munin-node.conf`. Other documentation =================== .. toctree:: :maxdepth: 2 async.rst munin-2.0.75/doc/plugin/000077500000000000000000000000001451614574100150065ustar00rootroot00000000000000munin-2.0.75/doc/plugin/index.rst000066400000000000000000000006431451614574100166520ustar00rootroot00000000000000.. _plugin-index: ================== The Munin plugin ================== Role ==== The munin plugin is a simple executable, which role is to gather one set of facts about the local server. The plugin is called with the argument "config" to get metadata, and with no arguments to get the values. Other documentation =================== .. toctree:: :maxdepth: 2 use.rst writing.rst supersampling.rst munin-2.0.75/doc/plugin/supersampling.rst000066400000000000000000000072731451614574100204420ustar00rootroot00000000000000.. _plugin-supersampling: =============== Supersampling =============== Every monitoring software has a polling rate. It is usually 5 min, because it's the sweet spot that enables frequent updates yet still having a low overhead. Munin is not different in that respect: it's data fetching routines have to be launched every 5 min, otherwise you'll face data loss. And this 5 min period is deeply grained in the code. So changing it is possible, but very tedious and error prone. But sometimes we need a very fine sampling rate. Every 10 seconds enables us to track fast changing metrics that would be averaged out otherwise. Changing the whole polling process to cope with a 10s period is very hard on hardware, since now every update has to finish in these 10 seconds. This triggered an extension in the plugin protocol, commonly known as "supersampling". Overview ======== The basic idea is that fine precision should only be for selected plugins only. It also cannot be triggered from the master, since the overhead would be way too big. So, we just let the plugin sample itself the values at a rate it feels adequate. Then each polling round, the master fetches all the samples since last poll. This enables various constructions, mostly around "streaming" plugins to achieve highly detailed sampling with a very small overhead. Notes ----- This protocol is currently completely transparent to :ref:`munin-node `, and therefore it means that it can be used even on older (1.x) nodes. Only a 2.0 :ref:`master ` is required. Protocol details ================ The protocol itself is derived from the :ref:`spoolfetch` extension. Config ------ A new plugin directive is used, :ref:`update_rate`. It enables the master to create the rrd with an adequate step. Omitting it would lead to rrd averaging the supersampled values onto the default 5 min rate. This means **data loss**. .. note:: Heartbeat The heartbeat has always a 2 step size, so failure to send all the samples will result with unknown values, as expected. .. note:: Data size The RRD file size is always the same in the default config, as all the RRA are configured proportionally to the :ref:`update_rate`. This means that, since you'll keep as much data as with the default, you keep it for a shorter time. Fetch ----- When spoolfetching, the epoch is also sent in front of the value. Supersampling is then just a matter of sending multiple epoch/value lines, with monotonically increasing epoch. .. note:: Note that since the epoch is an integer value for rrdtool_, the smallest granularity is 1 second. For the time being, the protocol itself does also mandates integers. We can easily imagine that with another database as backend, an extension could be hacked together. .. _rrdtool: http://oss.oetiker.ch/rrdtool/doc/rrdtool.en.html Compatibility with 1.4 ====================== On older 1.4 masters, only the last sampled value gets into the RRD. Sample implementation ===================== The canonical sample implementation is multicpu1sec_, a contrib plugin on github. It is also a so-called streaming plugin. .. _multicpu1sec: https://github.com/munin-monitoring/contrib/blob/master/plugins/cpu/multicpu1sec Streaming plugins ================= These plugins fork a background process when called that streams a system tool into a spool file. In multicpu1sec_, it is the mpstat_ tool with a period of 1 second. .. _mpstat: https://en.wikipedia.org/wiki/Mpstat Undersampling ============= Some plugins are on the opposite side of the spectrum, as they only need a lower precision. It makes sense when : * data should be kept for a *very* long time * data is *very* expensive to generate and it varies only slowly. munin-2.0.75/doc/plugin/use.rst000066400000000000000000000044251451614574100163410ustar00rootroot00000000000000.. _plugin-use: ===================== Using munin plugins ===================== .. index:: pair: plugin; installing Installing ========== The default plugin directory is /etc/munin/plugins/. To install a plugin, place it in the plugin directory, and make it executable. You can also place the plugin elsewhere, and install a symbolic link in the plugin directory. All the plugins provided with munin are installed in this way. .. index:: pair: plugin; configuration Configuring =========== The plugin configuration directory is /etc/munin/plugin-conf.d/. The syntax is: user The user the plugin will run as. Default: munin group The group the plugin will run as Default: munin env.variablename Defines and exports an environment variable called "variablename" with the content set to . There is no need to quote the variable content. .. note:: When configuring a munin plugin, add the least amount of extra privileges needed to run the plugin. For instance, do not run a plugin with "user root" to read syslogs, when it may be sufficient to set "group adm" instead. Example: .. index:: triple: example; plugin; configuration :: [pluginname] user username group groupname env.variablename some content for the variable env.critical 92 env.warning 95 Plugin configuration is optional. .. index:: pair: plugin; testing Testing ======= To test if the plugin works when executed by munin, you can use the :ref:`munin-run` command. .. code-block:: bash # munin-run myplugin config # munin-run myplugin Download munin plugins ====================== The munin project maintains a set of core plugins that are distributed in munin's releases. Additionally the munin project maintains the `contrib `_ repository. It contains more than a thousand plugins contributed by a wide range of people. In order to use these plugins they can either be downloaded manually or managed via the :ref:`munin-get` plugin tool. Additionally the munin plugins in the `contrib `_ repository can be browsed via the `Munin Plugin Gallery `_. munin-2.0.75/doc/plugin/writing.rst000066400000000000000000000056461451614574100172360ustar00rootroot00000000000000.. _plugin-writing: ======================== Writing a munin plugin ======================== A munin plugin is a small executable. Usually, it is written in some interpreted language. In its simplest form, when the plugin is executed with the argument "config", it outputs metadata needed for generating the graph. If it is called with no arguments, it outputs the data which is to be collected, and graphed later. Plugin output ============= The minimum plugin output when called with "config" it must output the graph title. It should also output a label for at least one datasource. :: graph_title Some title for our plugin something.label Foobar per second When the plugin is executed with no arguments, it should output a value for the datasource labelled in "config". It must not output values for which there are no matching labels in the configuration output. :: something.value 42 For a complete description of the available fields, see the :ref:`plugin-reference`. Example shell plugin ==================== The base of a plugin is a small option parser, ensuring the plugin is called with the correct argument, if any. Two main functions are defined: One for printing the configuration to the standard output, and one for printing the data. In addition, we have defined a function to generate the data itself, just to keep the plugin readable. The "output_usage" function is there just to be polite, it serves no other function. :) .. code-block:: bash #!/bin/sh output_config() { echo "graph_title Example graph" echo "plugins.label Number of plugins" } output_values() { printf "plugins.value %d\n" $(number_of_plugins) } number_of_plugins() { find /etc/munin/plugins -type l | wc -l } output_usage() { printf >&2 "%s - munin plugin to graph an example value\n" ${0##*/} printf >&2 "Usage: %s [config]\n" ${0##*/} } case $# in 0) output_values ;; 1) case $1 in config) output_config ;; *) output_usage exit 1 ;; esac ;; *) output_usage exit 1 ;; esac Activating the plugin ===================== Place the plugin in the /etc/munin/plugins/ directory, and make it executable. Then, restart the munin-node. Debugging the plugin ==================== To see how the plugin works, as the munin node would run it, you can use the command "munin-run". If the plugin is called "example", you can run "munin-run example config" to see the plugin configuration, and "munin-run example" to see the data. If you do not get the output you expect, check if your munin plugin needs more privileges. Normally, it is run as the "munin" user, but gathering some data may need more access. If the munin plugin emits errors, they will be visible in /var/log/munin/munin-node.log munin-2.0.75/doc/reference/000077500000000000000000000000001451614574100154465ustar00rootroot00000000000000munin-2.0.75/doc/reference/directories.rst000066400000000000000000000013161451614574100205150ustar00rootroot00000000000000.. _reference-directories: ============= Directories ============= .. _dbdir: dbdir ===== This directory is used to store the munin master database. It contains one subdirectory with RRD files per group of hosts, as well as other variable state the munin master would need. .. _plugindir: plugindir ========= This directory contains all the plugins the :ref:`munin node ` should run. .. _pluginconfdir: pluginconfdir ============= This directory contains plugin configuration. .. _rundir: rundir ====== This directory contains files needed to track the munin run state. PID files, lock files, and possibly sockets. .. _logdir: logdir ====== Contains the log files for each munin program. munin-2.0.75/doc/reference/index.rst000066400000000000000000000011011451614574100173000ustar00rootroot00000000000000.. _reference-index: =========== Reference =========== This section contains man pages and other reference material Man pages ========= .. toctree:: :maxdepth: 1 munin-async.rst munin-asyncd.rst munin-cgi-graph.rst munin-cgi-html.rst munin-check.rst munin-cron.rst munin-get.rst munin-graph.rst munin-html.rst munin-limits.rst munin-node.rst munin-run.rst munin-update.rst munin.conf.rst munin-node.conf.rst Other reference material ======================== .. toctree:: :maxdepth: 1 directories.rst plugin.rst munin-2.0.75/doc/reference/munin-async.rst000066400000000000000000000027611451614574100204470ustar00rootroot00000000000000.. _munin-async: .. program:: munin-async ============= munin-async ============= DESCRIPTION =========== The munin async clients reads from a spool directory written by :ref:`munin-asyncd`. It can optionally request a cleanup of this directory. OPTIONS ======= .. option:: --spooldir | -s Directory for spooled data [/var/lib/munin/spool] .. option:: --hostname Overrides the hostname [The local hostname] This is used to override the hostname used in the greeting banner. This is used when using munin-async from the munin master, and the data fetched is from another node. .. option:: --cleanup Clean up the spooldir after interactive session completes .. option:: --cleanupandexit Clean up the spooldir and exit (non-interactive) .. option:: --spoolfetch Enables the "spool" capability [no] .. option:: --vectorfetch Enables the "vectorized" fetching capability [no] Note that without this flag, the "fetch" command is disabled. .. option:: --verbose | -v Be verbose .. option:: --help | -h View this message EXAMPLES ======== .. code-block:: bash munin-async --spoolfetch This starts an interactive munin node session, enabling the "spoolfetch" command. This does not connect to the local munin node. Everything happens within munin-async, which reads from the spool directory instead of connecting to the node. SEE ALSO ======== See also :ref:`node-async` for more information and examples of how to configure munin-async. munin-2.0.75/doc/reference/munin-asyncd.rst000066400000000000000000000020341451614574100206040ustar00rootroot00000000000000.. _munin-asyncd: .. program:: munin-asyncd ============== munin-asyncd ============== DESCRIPTION =========== The munin async daemon connects to a :ref:`munin node ` periodically, and requests plugin configuration and data. This is stored in a spool directory, which is read by :ref:`munin-async`. OPTIONS ======= .. option:: --spool | -s Directory for spooled data [/var/lib/munin/spool] .. option:: --host Connect a munin node running on this host name and port [localhost:4949] .. option:: --interval Set default interval size [86400 (one day)] .. option:: --retain Number of interval files to retai [7] .. option:: --nocleanup Disable automated spool dir cleanup .. option:: --fork Fork one thread per plugin available on the node. [no forking] .. option:: --verbose | -v Be verbose .. option:: --help | -h View this message SEE ALSO ======== See also :ref:`node-async` for more information and examples of how to configure munin-asyncd. munin-2.0.75/doc/reference/munin-cgi-graph.rst000066400000000000000000000050631451614574100211710ustar00rootroot00000000000000.. _munin-cgi-graph: .. program:: munin-cgi-graph ================= munin-cgi-graph ================= DESCRIPTION =========== The munin-cgi-graph program is intended to be run from a web server. It can either run as CGI, or as FastCGI. OPTIONS ======= munin-cgi-graph is controlled using environment variables. See environment variables :envvar:`PATH_INFO` and :envvar:`QUERY_STRING`. Note: The munin-cgi-graph script may be called with the command line options of :ref:`munin-graph`. However, the existence of this should not be relied upon. ENVIRONMENT VARIABLES ===================== The following environment variables are used to control the output of munin-cgi-graph: .. envvar:: PATH_INFO This is the remaining part of the URI, after the path to the munin-cgi-graph script has been removed. The group, host, service and timeperiod values are extracted from this variable. The group may be nested. .. envvar:: CGI_DEBUG If this variable is set, debug information is logged to STDERR, and to /var/log/munin/munin-cgi-graph.log .. envvar:: QUERY_STRING A list of key=value parameters to control munin-cgi-graph. If QUERY_STRING is set, even to an empty value, a no_cache header is returned. .. envvar:: HTTP_CACHE_CONTROL If this variable is set, and includes the string "no_cache", a no_cache header is returned. .. envvar:: HTTP_IF_MODIFIED_SINCE Returns 304 if the graph is not changed since the timestamp in the HTTP_IF_MODIFIED_SINCE variable. EXAMPLES ======== When given an URI like the following: http://munin/munin-cgi/munin-cgi-graph/example.org/client.example.org/cpu-week.png munin-cgi-graph will be called with the following environment: PATH_INFO=/example.org/client.example.org/cpu-week.png To verify that munin is indeed graphing as it should, you can use the following command line: .. code-block:: bash sudo -u www-data \ PATH_INFO=/example.org/client.example.org/irqstats-day.png \ /usr/lib/munin/cgi/munin-cgi-graph | less The "less" is strictly not needed, but is recommended since munin-cgi-graph will output binary data to your terminal. You can add the :envvar:`CGI_DEBUG` variable, to get more log information. Content and debug information is logged to STDOUT and STDERR, respectively. If you only want to see the debug information, and not the HTTP headers or the content, you can redirect the file descriptors: .. code-block:: bash sudo -u www-data \ CGI_DEBUG=yes \ PATH_INFO=/example.org/client.example.org/irqstats-day.png \ /usr/lib/munin/cgi/munin-cgi-graph 2>&1 >/dev/null | less munin-2.0.75/doc/reference/munin-cgi-html.rst000066400000000000000000000026501451614574100210330ustar00rootroot00000000000000.. _munin-cgi-html: .. program:: munin-cgi-html ================ munin-cgi-html ================ DESCRIPTION =========== The :program:`munin-cgi-html` program is intended to be run from a web server. It can either run as CGI, or as FastCGI. OPTIONS ======= munin-cgi-html takes no options. It is controlled using environment variables. ENVIRONMENT VARIABLES ===================== The following environment variables are used to control the output of munin-cgi-html: .. envvar:: PATH_INFO This is the remaining part of the URI, after the path to the munin-cgi-html script has been removed. The group, host, service and timeperiod values are extracted from this variable. The group may be nested. EXAMPLES ======== PATH_INFO --------- "/" refers to the top page. "/example.com/" refers to the group page for "example.com" hosts. "/example.com/client.example.com/" refers to the host page for "client.example.com" in the "example.com" group COMMAND-LINE ------------ When given an URI like the following: http://munin.example.org/munin-cgi/munin-cgi-html/example.org munin-cgi-html will be called with the following environment: PATH_INFO=/example.org To verify that munin is able to create HTML pages, you can use the following command line: .. code-block:: bash sudo -u www-data \ PATH_INFO=/example.org \ /usr/lib/munin/cgi/munin-cgi-html SEE ALSO ======== :ref:`munin-cgi-graph`. munin-2.0.75/doc/reference/munin-check.rst000066400000000000000000000010631451614574100204010ustar00rootroot00000000000000.. _munin-check: .. program:: munin-check ============= munin-check ============= DESCRIPTION =========== munin-check is a utility that fixes the permissions of the munin directories and files. .. note:: munin-check needs superuser rights. .. note:: Please don't use this script if you are using 'graph_strategy cgi'. It doesn't care about the right permissions for www-data yet. OPTIONS ======= .. option:: --fix-permissions | -f Fix the permissions of the munin files and directories. .. option:: --help | -h Display usage information munin-2.0.75/doc/reference/munin-cron.rst000066400000000000000000000021521451614574100202650ustar00rootroot00000000000000.. _munin-cron: .. program:: munin-cron ============ munin-cron ============ DESCRIPTION =========== Munin-cron is a part of the package Munin, which is used in combination with :ref:`munin-node`. Munin is a group of programs to gather data from Munin's nodes, graph them, create html-pages, and optionally warn Nagios about any off-limit values. "munin-cron" runs the following programs, in the given order: #. :ref:`munin-update` #. :ref:`munin-limits` #. :ref:`munin-graph` (unless configured to run from CGI) #. :ref:`munin-html` (unless configured to run from CGI) Unless the munin master is configured otherwise, "munin-cron" should run every 5 minutes. OPTIONS ======= .. option:: --service Limit services to . Multiple --service options may be supplied. [unset] .. option:: --host Limit hosts to . Multiple --host options may be supplied. [unset] .. option:: --config Use as configuration file. [/etc/munin/munin.conf] SEE ALSO ======== :ref:`munin-update`, :ref:`munin-graph`, :ref:`munin-limits`, :ref:`munin-html`, :ref:`munin.conf`, munin-2.0.75/doc/reference/munin-get.rst000066400000000000000000000015171451614574100201070ustar00rootroot00000000000000.. _munin-get: .. program:: munin-get ========= munin-get ========= .. note:: The tool "munin-get" is available since Munin v2.0.52. Description =========== The munin plugin helper allows to search, download and use munin plugins from external repositories easily. A common source of munin plugins is the `contrib `_ repository (maintained by the munin project). Example ======= Download and enable a plugin (by default: from the `contrib `_ repository):: munin-get update munin-get install traffic munin-get enable traffic service munin-node restart # for systemd: systemctl restart munin-node Add a n external repository:: munin-get add-repository foo http://example.org/foo.git munin-get update munin-get list munin-2.0.75/doc/reference/munin-graph.rst000066400000000000000000000060471451614574100204340ustar00rootroot00000000000000.. _munin-graph: .. program:: munin-graph ============= munin-graph ============= DESCRIPTION =========== The munin-graph script is run by munin-cron, and creates graphs from all RRD files in the munin database directory. OPTIONS ======= Some options can be negated by prefixing them with "no". Example: --fork and --nofork .. option:: --fork By default munin-graph forks subprocesses for drawing graphs to utilize available cores and I/O bandwidth. Can be negated with --nofork [--fork] .. option:: --n Max number of concurrent processes [6] .. option:: --force Force drawing of graphs that are not usually drawn due to options in the config file. Can be negated with --noforce [--noforce] .. option:: --lazy Only redraw graphs when needed. Can be negated with --nolazy [--lazy] .. option:: --help View this message. .. option:: --version View version information. .. option:: --debug View debug messages. .. option:: --cron Behave as expected when run from cron. (Used internally in Munin.) Can be negated with --nocron .. option:: --host Limit graphed hosts to . Multiple --host options may be supplied. .. option:: --only-fqn For internal use with CGI graphing. Graph only a single fully qualified named graph, For instance: --only-fqn root/Backend/dafnes.example.com/diskstats_iops Always use with the correct --host option. .. option:: --config Use as configuration file. [/etc/munin/munin.conf] .. option:: --list-images List the filenames of the images created. Can be negated with --nolist-images. [--nolist-images] .. option:: --output-file | -o Output graph file. (used for CGI graphing) .. option:: --log-file | -l Output log file. (used for CGI graphing) .. option:: --day Create day-graphs. Can be negated with --noday. [--day] .. option:: --week Create week-graphs. Can be negated with --noweek. [--week] .. option:: --month Create month-graphs. Can be negated with --nomonth. [--month] .. option:: --year Create year-graphs. Can be negated with --noyear. [--year] .. option:: --sumweek Create summarised week-graphs. Can be negated with --nosumweek. [--summweek] .. option:: --sumyear Create summarised year-graphs. Can be negated with --nosumyear. [--sumyear] .. option:: --pinpoint Create custom-graphs. is the time in the standard unix Epoch format. [not active] .. option:: --size_x Sets the X size of the graph in pixels [175] .. option:: --size_y Sets the Y size of the graph in pixels [400] .. option:: --lower_limit Sets the lower limit of the graph .. option:: --upper_limit Sets the upper limit of the graph .. note:: :option:`--pinpoint` and :option:`--only-fqn` must not be combined with any of :option:`--day`, :option:`--week`, :option:`--month` or :option:`--year` (or their negating forms). The result of doing that is undefined. SEE ALSO ======== :ref:`munin-cron`, :ref:`munin-cgi-graph` munin-2.0.75/doc/reference/munin-html.rst000066400000000000000000000021531451614574100202710ustar00rootroot00000000000000.. _munin-html: .. program:: munin-html ============ munin-html ============ DESCRIPTION =========== munin-html is one of the munin master components run from the :ref:`munin-cron` script. This script is responsible for generating static HTML pages. If "html_strategy cgi" is set in munin.conf, munin-html will assume HTML pages are generated by munin-cgi-html, and exit silently. OPTIONS ======= munin-html has one significant option, which configuration file to use. Several other options are recognized and ignored as "compatibility options", since :ref:`munin-cron` passes all options through to the underlying components, of which munin-html is one. .. option:: --config Use as configuration file. [/etc/munin/munin.conf] .. option:: --help View this message. .. option:: --debug View debug messages. .. option:: --version View version information. .. option:: --nofork Compatibility. No effect. .. option:: --service Compatibility. No effect. .. option:: --host Compatibility. No effect. SEE ALSO ======== :ref:`munin-cron`, :ref:`munin-cgi-html` munin-2.0.75/doc/reference/munin-limits.rst000066400000000000000000000042441451614574100206310ustar00rootroot00000000000000.. _munin-limits: .. program:: munin-limits ============== munin-limits ============== DESCRIPTION =========== :ref:`munin-limits` is one of the processes regularly run from the :ref:`munin-cron` script. It reads the current and the previous collected values for each plugin, and compares them to the plugin's warning and critical values, if it has any. If the limits are breached, for instance, if a value moves from "ok" to "warning", or from "critical" to "ok", it sends an event to any configured contacts. A common configured contact is "nagios", which can use events from munin-limits as a source of passive service check results. OPTIONS ======= .. option:: --config Use as configuration file. [/etc/munin/munin.conf] .. option:: --contact Limit contacts to those of . Multiple --contact options may be supplied. [unset] .. option:: --host Limit hosts to those of . Multiple --host options may be supplied. [unset] .. option:: --service Limit services to those of . Multiple --service options may be supplied. [unset] .. option:: --always-send Force sending of messages even if you normally wouldn't. The can be a whitespace or comma separated list of the values "ok", "warning", "critical" or "unknown". This option may be specified several times, to add more values. Use of "--always-send" overrides the "always_send" value in munin.conf for configured contacts. See also --force. .. option:: --force Alias for "--always-send ok,warning,critical,unknown" .. option:: --force-run-as-root munin-limits will normally prevent you from running as root. Use this option to override this. The use of this option is not recommended. You may have to clean up file permissions in order for munin to run normally afterwards. .. option:: --help View help message. .. option:: --debug If set, view debug messages. Can be negated with --nodebug. [--nodebug] FILES ===== :ref:`/etc/munin/munin.conf ` :ref:`/var/lib/munin/* ` :ref:`/var/run/munin/* ` SEE ALSO ======== :ref:`munin.conf` munin-2.0.75/doc/reference/munin-node.conf.rst000066400000000000000000000070661451614574100212060ustar00rootroot00000000000000.. _munin-node.conf: =============== munin-node.conf =============== DESCRIPTION =========== This is the configuration file for :ref:`munin-node` and :ref:`munin-run`. The directives "host_name", "paranoia" and "ignore_file" are munin node specific. All other directives in munin-node.conf are passed through to the Perl module Net::Server. Depending on the version installed, you may have different settings available. DIRECTIVES ========== Native ------ .. option:: host_name The hostname used by munin-node to present itself to the munin master. Use this if the local node name differs from the name configured in the munin master. .. option:: ignore_file Files to ignore when locating installed plugins. May be repeated. .. option:: paranoia If set to a true value, :ref:`munin-node` will only run plugins owned by root. Inherited --------- These are the most common Net::Server options used in :ref:`munin-node`. .. option:: log_level Ranges from 0-4. Specifies what level of error will be logged. "0" means no logigng, while "4" means very verbose. These levels correlate to syslog levels as defined by the following key/value pairs. 0=err, 1=warning, 2=notice, 3=info, 4=debug. Default: 2 .. option:: log_file Where the munin node logs its activity. If the value is Sys::Syslog, logging is sent to syslog Default: undef (STDERR) .. option:: port The TCP port the munin node listens on Default: 4949 .. option:: pid_file The pid file of the process Default: undef (none) .. option:: background To run munin node in background set this to "1". If you want munin-node to run as a foreground process, comment this line out and set "setsid" to "0". .. option:: host The IP address the munin node process listens on Default: * (All interfaces) .. option:: user The user munin-node runs as Default: root .. option:: group The group munin-node runs as Default: root .. option:: setsid If set to "1", the server forks after binding to release itself from the command line, and runs the POSIX::setsid() command to daemonize. Default: undef .. option:: ignore_file Files to ignore when locating installed plugins. May be repeated. .. option:: host_name The hostname used by munin-node to present itself to the munin master. Use this if the local node name differs from the name configured in the munin master. .. option:: allow A regular expression defining which hosts may connect to the munin node. .. note:: Use cidr_allow if available. .. option:: cidr_allow Allowed hosts given in CIDR notation (192.0.2.1/32). Replaces or complements “allow”. Requires the presence of Net::Server, but is not supported by old versions of this module. .. option:: cidr_deny Like cidr_allow, but used for denying host access .. option:: timeout Number of seconds after the last activity by the master until the node will close the connection. If plugins take longer to run, this may disconnect the master. Default: 20 seconds EXAMPLE ======= .. index:: tuple: munin-node.conf; example A pretty normal configuration file: :: host * port 4949 cidr_allow 127.0.0.0/8 cidr_allow 192.0.2.0/24 user root group root background 1 setsid 1 log_level 4 log_file /var/log/munin/munin-node.log pid_file /var/run/munin-node.pid ignore_file \.bak$ ignore_file ^README$ ignore_file \.dpkg-(old|new)$ ignore_file \.rpm(save|new)$ ignore_file \.puppet-new$ SEE ALSO ======== :ref:`munin-node`, :ref:`munin-run` munin-2.0.75/doc/reference/munin-node.rst000066400000000000000000000036141451614574100202550ustar00rootroot00000000000000.. _munin-node: .. program:: munin-node ============ munin-node ============ DESCRIPTION =========== munin-node is a daemon for reporting statistics on system performance. By default, it is started at boot time, listens on port 4949/TCP, accepts connections from the :ref:`munin master `, and runs :ref:`munin plugins ` on demand. OPTIONS ======= .. option:: --config Use as configuration file. [/etc/munin/munin-node.conf] .. option:: --paranoia Only run plugins owned by root. Check permissions as well. Can be negated with --noparanoia [--noparanoia] .. option:: --help View this help message. .. option:: --debug View debug messages. .. note:: This can be very verbose. .. option:: --pidebug Plugin debug. Sets the environment variable :envvar:`MUNIN_DEBUG` to 1 so that plugins may enable debugging. CONFIGURATION ============= The configuration file is :ref:`munin-node.conf`. .. index:: pair: example; munin-node.conf FILES ===== :ref:`/etc/munin/munin-node.conf ` :ref:`/etc/munin/plugins/* ` :ref:`/etc/munin/plugin-conf.d/* ` :ref:`/var/run/munin/munin-node.pid ` :ref:`/var/log/munin/munin-node.log ` SEE ALSO ======== :ref:`munin-node.conf` Example configuration ===================== :: # /etc/munin/munin-node.conf - config-file for munin-node # host_name random.example.org log_level 4 log_file /var/log/munin/munin-node.log pid_file /var/run/munin/munin-node.pid background 1 setsid 1 # Which port to bind to; host [::] port 4949 user root group root # Regexps for files to ignore ignore_file ~$ ignore_file \.bak$ ignore_file %$ ignore_file \.dpkg-(tmp|new|old|dist)$ ignore_file \.rpm(save|new)$ ignore_file \.puppet-bak$ # Hosts to allow cidr_allow 127.0.0.0/8 cidr_allow 192.0.2.129/32 munin-2.0.75/doc/reference/munin-run.rst000066400000000000000000000030261451614574100201310ustar00rootroot00000000000000.. _munin-run: .. program:: munin-run =========== munin-run =========== DESCRIPTION =========== munin-run is a script to run Munin plugins from the command-line. It is primarily used to debug plugins; munin-run runs these plugins in the same conditions as they are under :ref:`munin-node`. OPTIONS ======= .. option:: --config Use as configuration file. [/etc/munin/munin-node.conf] .. option:: --servicedir Use as plugin dir. [/etc/munin/plugins/] .. option:: --sconfdir Use as plugin configuration dir. [/etc/munin/plugin-conf.d/] .. option:: --sconffile Use as plugin configuration. Overrides sconfdir. [undefined] .. option:: --paranoia Only run plugins owned by root and check permissions. [disabled] .. option:: --help View this help message. .. option:: --debug Print debug messages. Debug messages are sent to STDOUT and are prefixed with "#" (this makes it easier for other parts of munin to use munin-run and still have --debug on). Only errors go to STDERR. .. option:: --pidebug Enable debug output from plugins. Sets the environment variable :envvar:`MUNIN_DEBUG` to 1 so that plugins may enable debugging. [disabled] .. option:: --version Show version information. FILES ===== :ref:`/etc/munin/munin-node.conf ` :ref:`/etc/munin/plugins/* ` :ref:`/etc/munin/plugin-conf.d/* ` :ref:`/var/run/munin/munin-node.pid ` :ref:`/var/log/munin/munin-node.log ` munin-2.0.75/doc/reference/munin-update.rst000066400000000000000000000025721451614574100206140ustar00rootroot00000000000000.. _munin-update: .. program:: munin-update ============== munin-update ============== DESCRIPTION =========== munin-update is the primary Munin component. It is run from the :ref:`munin-cron` script. This script is responsible for contacting all the agents (munin-nodes) and collecting their data. Upon fetching the data, munin-update stores everything in RRD files - one RRD files for each field in each plugin. Running munin-update with the --debug flag will often give plenty of hints on what might be wrong. munin-update is a component in the Munin server. OPTIONS ======= .. option:: --config_file Use as the configuration file. [/etc/munin/munin.conf] .. option:: --debug If set, log debug messages. Can be negated with --nodebug [--nodebug] .. option:: --fork If set, will fork off one process for each host. Can be negated with --nofork [--fork] .. option:: --host Limit fetched data to those from . Multiple --host options may be supplied. [unset] .. option:: --service Limit fetched data to those of . Multiple --service options may be supplied. [unset] .. option:: --timeout Set the network timeout to . [180] .. option:: --help Print the help message then exit. .. option:: --version Print version information then exit. SEE ALSO ======== :ref:`munin-cron` munin-2.0.75/doc/reference/munin.conf.rst000066400000000000000000000100001451614574100202410ustar00rootroot00000000000000.. _munin.conf: .. program:: munin.conf ============ munin.conf ============ DESCRIPTION =========== This is the configuration file for the munin master. It is used by :ref:`munin-update`, :ref:`munin-graph`, :ref:`munin-limits`. :ref:`munin-html`, :ref:`munin-cgi-graph` and :ref:`munin-cgi-html`. GLOBAL DIRECTIVES ================= Global directives affect all munin master components unless specified otherwise. .. option:: dbdir The directory where munin stores its database files. Default: /var/lib/munin .. option:: logdir The directory where munin stores its logfiles. Default: /var/log/munin .. option:: htmldir The directory where :ref:`munin-html` stores generated HTML pages, and where :ref:`munin-graph` stores graphs. Default: /var/cache/munin/www .. option:: rundir Directory for files tracking munin's current running state. Default: /var/run/munin .. option:: tmpldir Directories for templates used by :ref:`munin-html` and :ref:`munin-cgi-html` to generate HTML pages. Default /etc/munin/templates .. option:: fork This directive determines whether :ref:`munin-update` fork when gathering information from nodes. Default is "yes". If you set it to "no" munin-update will collect data from the nodes in sequence. This will take more time, but use less resources. Not recommended unless you have only a handful of nodes. Affects: :ref:`munin-update` .. option:: palette The palette used by :ref:`munin-graph` and :ref:`munin-cgi-graph` to colour the graphs. The "default" palette has more colours and better contrast than the "old" palette. Affects: :ref:`munin-graph` .. option:: graph_data_size This directive sets the resolution of the RRD files that are created by :ref:`munin-graph` and :ref:`munin-cgi-graph`. Default is "normal". "huge" saves the complete data with 5 minute resolution for 400 days. Changing this directive has no effect on existing graphs Affects: :ref:`munin-graph` .. option:: graph_strategy If set to "cron", :ref:`munin-graph` will graph all services on all nodes every run interval. If set to "cgi", :ref:`munin-graph` will do nothing. To generate graphs you must then configure a web server to run :ref:`munin-cgi-graph` instead. Affects: :ref:`munin-graph` .. option:: html_strategy Valid strategies are "cgi" and "cron". Default is "cgi". If set to "cron", :ref:`munin-html` will recreate all html pages every run interval. If set to "cgi", :ref:`munin-html` will do nothing. To generate html pages you must configure a web server to run :ref:`munin-cgi-html` instead. .. option:: ssh_command The name of the secure shell command to use. Can be fully qualified or looked up in $PATH. Defaults to "ssh". .. option:: ssh_options The options for the secure shell command. Defaults are "-o ChallengeResponseAuthentication=no -o StrictHostKeyChecking=no". Please adjust this according to your desired security level. With the defaults, the master will accept and store the node ssh host keys with the first connection. If a host ever changes its ssh host keys, you will need to manually remove the old host key from the ssh known hosts file. (with: ssh-keygen -R , as well as ssh-keygen -R ) You can remove "StrictHostKeyChecking=no" to increase security, but you will have to manually manage the known hosts file. Do so by running "ssh " manually as the munin user, for each node, and accept the ssh host keys. If you would like the master to accept all node host keys, even when they change, use the options "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o PreferredAuthentications=publickey". .. index:: pair: example; munin.conf EXAMPLE ======= A minimal configuration file :: [client.example.com] address client.example.com munin-2.0.75/doc/reference/plugin.rst000066400000000000000000000304071451614574100175020ustar00rootroot00000000000000.. _plugin-reference: ================== Plugin reference ================== .. index:: pair: plugin; fields Fields ====== On a configuration run, the plugin is called with the argument "config". The following fields are used. +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | Field | Value | type | Description | See also | Default | +====================+==================+==========+==========================================+==================+=========+ | graph_title | string | required | Sets the title of the graph | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | graph_args | string | optional | Arguments for the rrd grapher. This is | rrdgraph_ | | | | | | used to control how the generated graph | | | | | | | looks, and how values are interpreted or | | | | | | | presented. | | | | | | | | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | graph_vlabel | string | optional | Label for the vertical axis of the graph | | | | | | | | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | graph_category | lower case | optional | Category used to sort the graph on the | | misc | | | string, no | | generated index web page. | | | | | whitespace | | | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | graph_info | html text | optional | Additional text for the generated graph | | | | | | | web page | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | graph_scale | yes|no | optional | If "yes", the generated graph will be | | no | | | | | scaled to the upper and lower values of | | | | | | | the datapoints within the graph. | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | graph_order | space separated | optional | Ensures that the listed datapoints are | | | | | list of | | displayed in order. Any additional | | | | | graph.datapoints | | datapoints are added in the order of | | | | | | | appearance after datapoitns appearing on | | | | | | | this list. | | | | | | | | | | | | | | This field is also used for "borrowing", | | | | | | | which is the practice of taking | | | | | | | datapoints from other graphs. | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | update_rate | integer | optional | Sets the update_rate used by the munin | | | | | (seconds) | | master when it creates the RRD file. | | | | | | | | | | | | | | The update rate is the interval at which | | | | | | | the RRD file expects to have data. | | | | | | | | | | | | | | This field requires a munin master | | | | | | | version of at least 2.0.0 | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | datapoint.label | lower case | required | The label used in the graph for this | | | | | string, no | | field | | | | | whitespace | | | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | datapoint.info | html text | optional | Additional html text for the generated | | | | | | | graph web page, used in the field | | | | | | | description table | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | datapoint.warning | integer, or | optional | This field defines a threshold value or | | | | | integer:integer | | range. If the field value above the | | | | | (signed) | | defined warning value, or outside the | | | | | | | range, the service is considered to be in| | | | | | | a "warning" state. | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | datapoint.critical | integer, or | optional | This field defines a threshold value or | | | | | integer:integer | | range. If the field value is above the | | | | | (signed) | | defined critical value, or outside the | | | | | | | range, the service is considered to be in| | | | | | | a "critical" state. | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | datapoint.graph | yes|no | optional | Determines if this datapoint should be | | yes | | | | | visible in the generated graph. | | | | | | | | | | | | | | | | | | | | | | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | datapoint.cdef | CDEF statement | optional | A CDEF statement is a Reverse Polish | cdeftutorial_ | | | | | | Notation statement used to construct a | | | | | | | datapoint from other datapoints. | | | | | | | | | | | | | | This is commonly used to calculate | | | | | | | percentages. | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ | datapoint.draw | AREA, LINE, | | Determines how the graph datapoints are | rrdgraph_ | LINE | | | LINE[n], STACK, | | displayed in the graph. The "LINE" takes | | | | | AREASTACK, | | an optional width suffix, commonly | | | | | LINESTACK, | | "LINE1", "LINE2", etc… | | | | | LINE[n]STACK | | The \*STACK values are specific to munin | | | | | | | and makes the first a LINE, LINE[n] or | | | | | | | AREA datasource, and the rest as STACK. | | | +--------------------+------------------+----------+------------------------------------------+------------------+---------+ On a data fetch run, the plugin is called with no arguments. the following fields are used. +-----------------+-----------------------+----------+------------------+------+------------+ | Field | Value | type | Description | See | Default | | | | | | also | | +=================+=======================+==========+==================+======+============+ | datapoint.value | integer, scientific | required | The value to be | | No default | | | notation, or "U" (may | | graphed. | | | | | be signed) | | | | | | | | | | | | +-----------------+-----------------------+----------+------------------+------+------------+ .. index:: pair: plugin; executing Example ======= This is an example of the plugin fields used with the "df" plugin. The "munin-run" command is used to run the plugin from the command line. Configuration run ----------------- :: # munin-run df config graph_title Filesystem usage (in %) graph_args --upper-limit 100 -l 0 graph_vlabel % graph_category disk graph_info This graph shows disk usage on the machine. _dev_hda1.label / _dev_hda1.info / (ext3) -> /dev/hda1 _dev_hda1.warning 92 _dev_hda1.critical 98 Data fetch run -------------- :: # munin-run df _dev_hda1.value 83 .. _cdeftutorial: http://oss.oetiker.ch/rrdtool/tut/cdeftutorial.en.html .. _rrdgraph: http://oss.oetiker.ch/rrdtool/doc/rrdgraph_graph.en.html munin-2.0.75/getversion000077500000000000000000000053631451614574100150650ustar00rootroot00000000000000#!/bin/sh # Generate a version string for use when building. # # * If RELEASE exists, and is non-empty, use the contents of that file. # This is in case we're building from a tarball. # # * If we're inside a git tree: # - For a current tagged commit, return this tag. # - For the "master/devel/stable-*" branches, use "git describe". # - For other branches or detached HEADs, return a # "$VERSION-$NBCOMMITS-$BRANCH-$DATE-g$COMMIT" version string # (with "BRANCH=detached" for a detached HEAD) # # * Try to make it up from the directory name (munin-2.0.1 -> 2.0.1) # # * If we're still looking for a version, just fallback to "unknown". # # NOTE: please keep this file as portable as possible (more strict than for plugins). # Probably unexpected portability issues include: # * '$(...)' is not supported by /bin/sh on Solaris # # Override shellcheck warnings due to portability constraints # shellcheck disable=SC2006 get_current_git_branch_name() { # hide stderr (for detached HEADs) GB=`LANG='' git branch --points-at HEAD 2>/dev/null | awk '$1 == "*" {print $2}'` if echo "$GB" | grep -q "^("; then # we are not part of a specific branch (the reason is given in braces) echo else echo "$GB" fi } generate_version_string() { # try the obvious: is our current commit tagged? head_tag=`git tag --points-at HEAD` if [ -n "$head_tag" ]; then echo "$head_tag" else branch_name=`get_current_git_branch_name` case "$branch_name" in master|devel|stable-*) # public branches or detached HEADs git describe ;; *) if [ -n "$branch_name" ]; then clean_branch_name=`echo "$branch_name" | sed -e 's/[^0-9A-Za-z\.\-\_]/_/g'` else clean_branch_name="detached" fi # "foo | read VAR" does *not* work (variable is changed only in subshell) # workaround stolen from http://www.etalabs.net/sh_tricks.html read -r VERSION COMMITS </dev/null`" = "true" ]; then generate_version_string elif [ -n "`generate_version_string_from_dir`" ]; then generate_version_string_from_dir else echo "unknown" fi munin-2.0.75/install-sh000077500000000000000000000127361451614574100147600ustar00rootroot00000000000000#!/bin/sh # # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. It can only install one file at a time, a restriction # shared with many OS's install programs. # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$cpprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: chmodcmd="" else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 munin-2.0.75/logo-horizontal.svg000066400000000000000000000273161451614574100166240ustar00rootroot00000000000000 image/svg+xml munin-2.0.75/logo.eps000066400000000000000000003746051451614574100144330ustar00rootroot00000000000000%!PS-Adobe-3.1 EPSF-3.0 %%Title: Logo_Munin.eps %%Creator: Adobe Illustrator(R) 9.0 %%AI8_CreatorVersion: 9.0 %AI9_PrintingDataBegin %%For: Walter Biering %%CreationDate: 04.05.2004 %%CropBox: 0.000000 0.000000 158.792969 233.192383 %%BoundingBox: 0 0 159 234 %%HiResBoundingBox: 0.000000 0.000000 158.792969 233.192383 %%LanguageLevel: 2 %%DocumentData: Clean7Bit %%Pages: 1 %%DocumentNeededResources: %%DocumentSuppliedResources: procset Adobe_AGM_Core 2.0 0 %%DocumentFonts: %%DocumentSuppliedFonts: %%PageOrder: Ascend %%DocumentProcessColors: Cyan Yellow Black %%DocumentCustomColors: %%CMYKCustomColor: %%RGBCustomColor: %%EndComments %%BeginDefaults %%EndDefaults %%BeginProlog %%BeginResource: procset Adobe_AGM_Core 2.0 0 %%Version: 2.0 0 %%Copyright: Copyright (C) 1997-1999 Adobe Systems, Inc. All Rights Reserved. systemdict /setpacking known { currentpacking true setpacking } if userdict /Adobe_AGM_Core 233 dict dup begin put /nd{ null def }bind def /Adobe_AGM_Core_Id /Adobe_AGM_Core_2.0_0 def /AGMCORE_str256 256 string def /AGMCORE_src256 256 string def /AGMCORE_dst64 64 string def /AGMCORE_srcLen nd /AGMCORE_save nd /AGMCORE_graphicsave nd /AGMCORE_imagestring0 nd /AGMCORE_imagestring1 nd /AGMCORE_imagestring2 nd /AGMCORE_imagestring3 nd /AGMCORE_imagestring4 nd /AGMCORE_imagestring5 nd /AGMCORE_c 0 def /AGMCORE_m 0 def /AGMCORE_y 0 def /AGMCORE_k 0 def /AGMCORE_mbuf () def /AGMCORE_ybuf () def /AGMCORE_kbuf () def /AGMCORE_gbuf () def /AGMCORE_bbuf () def /AGMCORE_cmykbuf 4 array def /AGMCORE_screen [currentscreen] cvx def /AGMCORE_tmp 0 def /AGMCORE_arg1 nd /AGMCORE_arg2 nd /AGMCORE_&setgray nd /AGMCORE_&image nd /AGMCORE_&colorimage nd /AGMCORE_&imagemask nd /AGMCORE_&setcolor nd /AGMCORE_&setcolorspace nd /AGMCORE_&&setcolorspace nd /AGMCORE_cyan_plate nd /AGMCORE_magenta_plate nd /AGMCORE_yellow_plate nd /AGMCORE_black_plate nd /AGMCORE_plate_ndx nd /AGMCORE_get_ink_data nd /AGMCORE_is_cmyk_sep nd /AGMCORE_in_rip_sep nd /AGMCORE_host_sep nd /AGMCORE_will_host_sep nd /AGMCORE_avoid_L2_sep_space nd /AGMCORE_composite_job nd /AGMCORE_producing_seps nd /AGMCORE_ccimage_exists nd /AGMCORE_ps_level -1 def /AGMCORE_ps_version -1 def /AGMCORE_environ_ok nd /AGMCORE_CSA_cache 0 dict def /AGMCORE_CSD_cache 0 dict def /AGMCORE_pattern_cache 0 dict def /AGMCORE_currentoverprint false def /AGMCORE_deltaX nd /AGMCORE_deltaY nd /AGMCORE_name nd /AGMCORE_sep_special nd /AGMCORE_ndx nd /AGMCORE_err_strings nd /AGMCORE_cur_err nd /AGMCORE_ovp nd /AGMCORE_CRD_cache where{ pop }{ /AGMCORE_CRD_cache 0 dict def }ifelse /bdf { bind def } bind def /xdf { exch def } def /ldf { load def } def /ddf { put } def /xddf { 3 -1 roll put } def /xpt { exch put } def /bdict { mark } def /edict { counttomark 2 idiv dup dict begin {def} repeat pop currentdict end }def /ps_level /languagelevel where{ pop languagelevel }{ 1 }ifelse def /level2 ps_level 2 ge def /level3 ps_level 3 ge def /ps_version {version cvr} stopped { -1 }if def /ndf { 1 index where{ pop pop pop }{ dup xcheck {bind}if def }ifelse } def /skip_image { has_color ne{ dup 256 idiv {currentfile AGMCORE_str256 readstring pop pop}repeat currentfile AGMCORE_str256 0 4 -1 roll 256 mod getinterval readstring pop pop }{ pop }ifelse } def /addprocs { 2{/exec load}repeat 3 1 roll [ 5 1 roll ] bind cvx } def /colorbuf { 0 1 2 index length 1 sub { dup 2 index exch get 255 exch sub 2 index 3 1 roll put } for } def /makereadonlyarray { /packedarray where {pop packedarray} {array astore readonly} ifelse } def /getspotfunction { AGMCORE_screen exch pop exch pop dup type /dicttype eq { dup /HalftoneType get 1 eq { /SpotFunction get } { dup /HalftoneType get 2 eq { /GraySpotFunction get } { pop {abs exch abs 2 copy add 1 gt {1 sub dup mul exch 1 sub dup mul add 1 sub} {dup mul exch dup mul add 1 exch sub}ifelse}bind } ifelse } ifelse } if } def /clp_npth { clip newpath } def /eoclp_npth { eoclip newpath } def /stkpath_clp_npth { strokepath clip newpath } def /stk_n_clp_npth { gsave stroke grestore clip newpath } def /npth_clp { newpath clip } def /graphic_setup { /AGMCORE_graphicsave save def concat 0 setgray 0 setlinecap 0 setlinejoin 1 setlinewidth [] 0 setdash 10 setmiterlimit newpath false setoverprint false setstrokeadjust userdict begin /showpage {} def mark } def /graphic_cleanup { cleartomark end AGMCORE_graphicsave restore } def /compose_error_msg { grestoreall initgraphics /Helvetica findfont 10 scalefont setfont /AGMCORE_deltaY 100 def /AGMCORE_deltaX 310 def /AGMCORE_arg2 xdf /AGMCORE_arg1 xdf clippath pathbbox newpath pop pop 36 add exch 36 add exch moveto 0 AGMCORE_deltaY rlineto AGMCORE_deltaX 0 rlineto 0 AGMCORE_deltaY neg rlineto AGMCORE_deltaX neg 0 rlineto closepath 0 AGMCORE_&setgray gsave 1 AGMCORE_&setgray fill grestore 1 setlinewidth gsave stroke grestore currentpoint AGMCORE_deltaY 15 sub add exch 8 add exch moveto /AGMCORE_deltaY 12 def /AGMCORE_tmp 0 def AGMCORE_err_strings exch get { dup 32 eq { pop AGMCORE_str256 0 AGMCORE_tmp getinterval dup (.) ne AGMCORE_arg1 0 lt and { pop } { stringwidth pop currentpoint pop add AGMCORE_deltaX 28 add gt { currentpoint AGMCORE_deltaY sub exch pop clippath pathbbox pop pop pop 44 add exch moveto } if AGMCORE_str256 0 AGMCORE_tmp getinterval show ( ) show } ifelse 0 1 AGMCORE_str256 length 1 sub { AGMCORE_str256 exch 0 put }for /AGMCORE_tmp 0 def } { dup 94 eq { pop AGMCORE_arg1 0 ge { AGMCORE_arg1 AGMCORE_str256 cvs dup /AGMCORE_tmp exch length def AGMCORE_str256 exch 0 exch putinterval AGMCORE_str256 0 AGMCORE_tmp getinterval stringwidth pop currentpoint pop add AGMCORE_deltaX 28 add gt { currentpoint AGMCORE_deltaY sub exch pop clippath pathbbox pop pop pop 44 add exch moveto } if AGMCORE_str256 0 AGMCORE_tmp getinterval show } { /AGMCORE_arg1 0 def } ifelse 0 1 AGMCORE_str256 length 1 sub { AGMCORE_str256 exch 0 put }for /AGMCORE_tmp 0 def AGMCORE_arg1 0 ne { /AGMCORE_arg1 AGMCORE_arg2 def } if } { AGMCORE_str256 exch AGMCORE_tmp exch put /AGMCORE_tmp AGMCORE_tmp 1 add def }ifelse } ifelse } forall } bdf level2{ /AGMCORE_map_reserved_ink_name { dup type /stringtype eq{ dup /Red eq{ pop (_Red_) }{ dup /Green eq{ pop (_Green_) }{ dup /Blue eq{ pop (_Blue_) }{ dup /Cyan eq{ pop (_Cyan_) }{ dup /Magenta eq{ pop (_Magenta_) }{ dup /Yellow eq{ pop (_Yellow_) }{ dup /Black eq{ pop (_Black_) }{ dup / eq{ pop (Process) }if }ifelse }ifelse }ifelse }ifelse }ifelse }ifelse }ifelse }if }def }if /doc_setup{ Adobe_AGM_Core begin /AGMCORE_will_host_separate xdf /AGMCORE_ps_version xdf /AGMCORE_ps_level xdf errordict /AGM_handleerror known not { errordict /AGM_handleerror errordict /handleerror get put errordict /handleerror { Adobe_AGM_Core begin $error /newerror get AGMCORE_cur_err null ne and { $error /newerror false put AGMCORE_cur_err /AGMCORE_bad_environ eq { /AGMCORE_bad_environ AGMCORE_ps_level AGMCORE_ps_version } { AGMCORE_cur_err 0 0 } ifelse compose_error_msg } if $error /newerror true put end errordict /AGM_handleerror get exec } bind put }if /AGMCORE_environ_ok ps_level AGMCORE_ps_level ge ps_version AGMCORE_ps_version ge and AGMCORE_ps_level -1 eq or def AGMCORE_environ_ok not {/AGMCORE_cur_err /AGMCORE_bad_environ def} if /AGMCORE_&setgray systemdict/setgray get def level2{ /AGMCORE_&setcolor systemdict/setcolor get def /AGMCORE_&setcolorspace systemdict/setcolorspace get def /AGMCORE_&&setcolorspace /setcolorspace ldf }if /AGMCORE_&image systemdict/image get def /AGMCORE_&imagemask systemdict/imagemask get def /colorimage where{ pop /AGMCORE_&colorimage /colorimage ldf }if /AGMCORE_in_rip_sep level2{ currentpagedevice/Separations 2 copy known{ get }{ pop pop false }ifelse }{ false }ifelse def level2 not{ /xput{ dup load dup length exch maxlength eq{ dup dup load dup length dup 0 eq {pop 1} if 2 mul dict copy def }if load begin def end }def }{ /xput{ load 3 1 roll put }def }ifelse /AGMCORE_gstate_known{ where{ /Adobe_AGM_Core_Id known }{ false }ifelse }ndf /AGMCORE_GSTATE AGMCORE_gstate_known not{ /AGMCORE_GSTATE 21 dict def /AGMCORE_tmpmatrix matrix def /AGMCORE_gstack 32 array def /AGMCORE_gstackptr 0 def /AGMCORE_gstacksaveptr 0 def /AGMCORE_gstackframekeys 7 def /AGMCORE_&gsave /gsave ldf /AGMCORE_&grestore /grestore ldf /AGMCORE_&grestoreall /grestoreall ldf /AGMCORE_&save /save ldf /AGMCORE_gdictcopy { begin { def } forall end }def /AGMCORE_gput { AGMCORE_gstack AGMCORE_gstackptr get 3 1 roll put }def /AGMCORE_gget { AGMCORE_gstack AGMCORE_gstackptr get exch get }def /gsave { AGMCORE_&gsave AGMCORE_gstack AGMCORE_gstackptr get AGMCORE_gstackptr 1 add dup 32 ge {limitcheck} if Adobe_AGM_Core exch /AGMCORE_gstackptr exch put AGMCORE_gstack AGMCORE_gstackptr get AGMCORE_gdictcopy }def /grestore { AGMCORE_&grestore AGMCORE_gstackptr 1 sub dup AGMCORE_gstacksaveptr lt {1 add} if Adobe_AGM_Core exch /AGMCORE_gstackptr exch put }def /grestoreall { AGMCORE_&grestoreall Adobe_AGM_Core /AGMCORE_gstackptr AGMCORE_gstacksaveptr put }def /save { AGMCORE_&save AGMCORE_gstack AGMCORE_gstackptr get AGMCORE_gstackptr 1 add dup 32 ge {limitcheck} if Adobe_AGM_Core begin /AGMCORE_gstackptr exch def /AGMCORE_gstacksaveptr AGMCORE_gstackptr def end AGMCORE_gstack AGMCORE_gstackptr get AGMCORE_gdictcopy }def 0 1 AGMCORE_gstack length 1 sub { AGMCORE_gstack exch AGMCORE_gstackframekeys dict put } for }if /currentcmykcolor [0 0 0 0] AGMCORE_gput /currentstrokeadjust false AGMCORE_gput /currentcolorspace [/DeviceGray] AGMCORE_gput /sep_tint 0 AGMCORE_gput /sep_colorspace_dict null AGMCORE_gput /indexed_colorspace_dict null AGMCORE_gput /currentcolor_intent () AGMCORE_gput end }def /page_setup { Adobe_AGM_Core begin /setcmykcolor { 4 copy AGMCORE_cmykbuf astore /currentcmykcolor exch AGMCORE_gput 1 sub 4 1 roll 3 { 3 index add neg dup 0 lt { pop 0 } if 3 1 roll } repeat setrgbcolor pop }ndf /AGMCORE_ccimage_exists /customcolorimage where {pop true}{false} ifelse def /currentcmykcolor { /currentcmykcolor AGMCORE_gget aload pop }ndf /setoverprint { pop }ndf /currentoverprint { false }ndf /AGMCORE_deviceDPI 72 0 matrix defaultmatrix dtransform dup mul exch dup mul add sqrt def /AGMCORE_cyan_plate 1 0 0 0 test_cmyk_color_plate def /AGMCORE_magenta_plate 0 1 0 0 test_cmyk_color_plate def /AGMCORE_yellow_plate 0 0 1 0 test_cmyk_color_plate def /AGMCORE_black_plate 0 0 0 1 test_cmyk_color_plate def /AGMCORE_plate_ndx AGMCORE_cyan_plate{ 0 }{ AGMCORE_magenta_plate{ 1 }{ AGMCORE_yellow_plate{ 2 }{ AGMCORE_black_plate{ 3 }{ 4 }ifelse }ifelse }ifelse }ifelse def /AGMCORE_composite_job AGMCORE_cyan_plate AGMCORE_magenta_plate and AGMCORE_yellow_plate and AGMCORE_black_plate and def /AGMCORE_producing_seps AGMCORE_composite_job not AGMCORE_in_rip_sep or def /AGMCORE_host_sep AGMCORE_producing_seps AGMCORE_in_rip_sep not and def /AGM_preserve_spots /AGM_preserve_spots where{ pop AGM_preserve_spots }{ systemdict/setdistillerparams known product (Adobe PostScript Parser) ne and AGMCORE_producing_seps or }ifelse def AGMCORE_host_sep AGMCORE_will_host_separate not and { /AGMCORE_cur_err /AGMCORE_color_space_onhost_seps def AGMCORE_color_space_onhost_seps }if /AGMCORE_avoid_L2_sep_space version cvr 2012 lt level2 and AGMCORE_producing_seps not and def /AGMCORE_is_cmyk_sep AGMCORE_cyan_plate AGMCORE_magenta_plate or AGMCORE_yellow_plate or AGMCORE_black_plate or def /AGM_avoid_0_cmyk where{ pop AGM_avoid_0_cmyk }{ AGM_preserve_spots }ifelse { /setcmykcolor[ {4 copy add add add 0 eq currentoverprint and{pop 0.0005}if}/exec cvx /setcmykcolor load dup type/operatortype ne{/exec cvx}if ]cvx def }if AGMCORE_host_sep{ /AGMCORE_get_ink_data AGMCORE_cyan_plate{ {pop pop pop} }{ AGMCORE_magenta_plate{ {4 3 roll pop pop pop} }{ AGMCORE_yellow_plate{ {4 2 roll pop pop pop} }{ {4 1 roll pop pop pop} }ifelse }ifelse }ifelse def }if AGMCORE_in_rip_sep{ /setcustomcolor { exch aload pop dup 7 1 roll inRip_spot_has_ink not { 4 {4 index mul 4 1 roll} repeat /DeviceCMYK setcolorspace 6 -2 roll pop pop }{ Adobe_AGM_Core begin /AGMCORE_k xdf /AGMCORE_y xdf /AGMCORE_m xdf /AGMCORE_c xdf end [/Separation 4 -1 roll /DeviceCMYK {dup AGMCORE_c mul exch dup AGMCORE_m mul exch dup AGMCORE_y mul exch AGMCORE_k mul} ] setcolorspace }ifelse setcolor }ndf /setseparationgray { [/Separation (All) /DeviceGray {}] setcolorspace_opt 1 exch sub setcolor }ndf }{ /setseparationgray { AGMCORE_&setgray }ndf }ifelse /findcmykcustomcolor { 5 makereadonlyarray }ndf /setcustomcolor { exch aload pop pop 4 {4 index mul 4 1 roll} repeat setcmykcolor pop }ndf /has_color /colorimage where{ AGMCORE_producing_seps{ pop true }{ systemdict eq }ifelse }{ false }ifelse def /map_index { 1 index mul exch getinterval {255 div} forall }def level2{ /mo /moveto ldf /ln /lineto ldf /cv /curveto ldf /knockout_unitsq { 1 setgray 0 0 1 1 rectfill }def /level2ScreenFreq{ begin 60 HalftoneType 1 eq{ pop Frequency }if HalftoneType 2 eq{ pop GrayFrequency }if HalftoneType 5 eq{ pop Default level2ScreenFreq }if end }def /currentScreenFreq{ currenthalftone level2ScreenFreq }def /invert_image_samples { Adobe_AGM_Core/AGMCORE_tmp Decode length ddf /Decode [ Decode 1 get Decode 0 get] def }def /knockout_image_samples { Operator/imagemask ne{ /Decode [1 1] def }if }def /get_gstate { AGMCORE_GSTATE begin /AGMCORE_GSTATE_ctm AGMCORE_tmpmatrix currentmatrix def /AGMCORE_GSTATE_clr_spc currentcolorspace def /AGMCORE_GSTATE_clr_indx 0 def /AGMCORE_GSTATE_clr_comps 12 array def mark currentcolor counttomark {AGMCORE_GSTATE_clr_comps AGMCORE_GSTATE_clr_indx 3 -1 roll put /AGMCORE_GSTATE_clr_indx AGMCORE_GSTATE_clr_indx 1 add def} repeat pop /AGMCORE_GSTATE_fnt rootfont def /AGMCORE_GSTATE_lw currentlinewidth def /AGMCORE_GSTATE_lc currentlinecap def /AGMCORE_GSTATE_lj currentlinejoin def /AGMCORE_GSTATE_ml currentmiterlimit def currentdash /AGMCORE_GSTATE_do xdf /AGMCORE_GSTATE_da xdf /AGMCORE_GSTATE_sa currentstrokeadjust def /AGMCORE_GSTATE_clr_rnd currentcolorrendering def /AGMCORE_GSTATE_op currentoverprint def /AGMCORE_GSTATE_bg currentblackgeneration cvlit def /AGMCORE_GSTATE_ucr currentundercolorremoval cvlit def currentcolortransfer cvlit /AGMCORE_GSTATE_gy_xfer xdf cvlit /AGMCORE_GSTATE_b_xfer xdf cvlit /AGMCORE_GSTATE_g_xfer xdf cvlit /AGMCORE_GSTATE_r_xfer xdf /AGMCORE_GSTATE_ht currenthalftone def /AGMCORE_GSTATE_flt currentflat def end }ndf /set_gstate { AGMCORE_GSTATE begin AGMCORE_GSTATE_ctm setmatrix AGMCORE_GSTATE_clr_spc setcolorspace AGMCORE_GSTATE_clr_indx {AGMCORE_GSTATE_clr_comps AGMCORE_GSTATE_clr_indx 1 sub get /AGMCORE_GSTATE_clr_indx AGMCORE_GSTATE_clr_indx 1 sub def} repeat setcolor AGMCORE_GSTATE_fnt setfont AGMCORE_GSTATE_lw setlinewidth AGMCORE_GSTATE_lc setlinecap AGMCORE_GSTATE_lj setlinejoin AGMCORE_GSTATE_ml setmiterlimit AGMCORE_GSTATE_da AGMCORE_GSTATE_do setdash AGMCORE_GSTATE_sa setstrokeadjust AGMCORE_GSTATE_clr_rnd setcolorrendering AGMCORE_GSTATE_op setoverprint AGMCORE_GSTATE_bg cvx setblackgeneration AGMCORE_GSTATE_ucr cvx setundercolorremoval AGMCORE_GSTATE_r_xfer cvx AGMCORE_GSTATE_g_xfer cvx AGMCORE_GSTATE_b_xfer cvx AGMCORE_GSTATE_gy_xfer cvx setcolortransfer AGMCORE_GSTATE_ht /HalftoneType get dup 9 eq exch 100 eq or { currenthalftone /HalftoneType get AGMCORE_GSTATE_ht /HalftoneType get ne { mark AGMCORE_GSTATE_ht {sethalftone} stopped cleartomark } if }{ AGMCORE_GSTATE_ht sethalftone } ifelse AGMCORE_GSTATE_flt setflat end }ndf AGMCORE_producing_seps not{ /setcolorspace where{ /Adobe_AGM_Core_Id known not }{ true }ifelse { /setcolorspace { dup type dup /arraytype eq exch /packedarraytype eq or{ dup 0 get dup /Separation eq{ pop [ exch {} forall ] dup dup 1 get AGMCORE_map_reserved_ink_name 1 exch put }{ /DeviceN eq { [ exch {} forall ] dup dup 1 get [ exch {AGMCORE_map_reserved_ink_name} forall ] 1 exch put }if }ifelse }if AGMCORE_&&setcolorspace }def }if }if }{ /adj { currentstrokeadjust{ transform 0.25 sub round 0.25 add exch 0.25 sub round 0.25 add exch itransform }if }def /mo{ adj moveto }def /ln{ adj lineto }def /cv{ 6 2 roll adj 6 2 roll adj 6 2 roll adj curveto }def /knockout_unitsq { 1 setgray 8 8 1 [8 0 0 8 0 0] {} image }def /currentstrokeadjust{ /currentstrokeadjust AGMCORE_gget }def /setstrokeadjust{ /currentstrokeadjust exch AGMCORE_gput }def /currentScreenFreq{ currentscreen pop pop }def /invert_image_samples { {1 exch sub} currenttransfer addprocs settransfer }def /knockout_image_samples { { pop 1 } currenttransfer addprocs settransfer }def /setcolorspace { /currentcolorspace exch AGMCORE_gput } def /currentcolorspace { /currentcolorspace AGMCORE_gget } def /n_color_components { dup type /arraytype eq{ 0 get }if dup /DeviceGray eq{ pop 1 }{ /DeviceCMYK eq{ 4 }{ 3 }ifelse }ifelse } def /setcolor_devicecolor { dup type /arraytype eq{ 0 get }if dup /DeviceGray eq{ pop setgray }{ /DeviceCMYK eq{ setcmykcolor }{ setrgbcolor }ifelse }ifelse }def /setcolor { currentcolorspace 0 get dup /DeviceGray ne{ dup /DeviceCMYK ne{ dup /DeviceRGB ne{ dup /Separation eq{ pop currentcolorspace 3 get exec currentcolorspace 2 get }{ dup /Indexed eq{ pop currentcolorspace 3 get dup type /stringtype eq{ currentcolorspace 1 get n_color_components 3 -1 roll map_index }{ exec }ifelse currentcolorspace 1 get }{ /AGMCORE_cur_err /AGMCORE_invalid_color_space def AGMCORE_invalid_color_space }ifelse }ifelse }if }if }if setcolor_devicecolor } def }ifelse /op /setoverprint ldf /lw /setlinewidth ldf /lc /setlinecap ldf /lj /setlinejoin ldf /ml /setmiterlimit ldf /dsh /setdash ldf /sadj /setstrokeadjust ldf /gry /setgray ldf /rgb /setrgbcolor ldf /cmyk /setcmykcolor ldf /sep /setsepcolor ldf /idx /setindexedcolor ldf /colr /setcolor ldf /csacrd /set_csa_crd ldf /sepcs /setsepcolorspace ldf /idxcs /setindexedcolorspace ldf /cp /closepath ldf /clp /clp_npth ldf /eclp /eoclp_npth ldf /spclp /stkpath_clp_npth ldf /f /fill ldf /ef /eofill ldf /s /stroke ldf /sclp /stk_n_clp_npth ldf /nclp /npth_clp ldf /img /imageormask ldf /sepimg /sep_imageormask ldf /idximg /indexed_imageormask ldf /gset /graphic_setup ldf /gcln /graphic_cleanup ldf currentdict{ dup xcheck 1 index type dup /arraytype eq exch /packedarraytype eq or and { bind }if def }forall }def /page_trailer { end }def /unload{ systemdict/languagelevel known{ systemdict/languagelevel get 2 ge{ userdict/Adobe_AGM_Core 2 copy known{ undef }{ pop pop }ifelse }if }if }def /doc_trailer{ }def systemdict /findcolorrendering known{ /findcolorrendering systemdict /findcolorrendering get def }if systemdict /setcolorrendering known{ /setcolorrendering systemdict /setcolorrendering get def }if /test_cmyk_color_plate { gsave setcmykcolor currentgray 1 ne grestore }def /inRip_spot_has_ink { Adobe_AGM_Core/AGMCORE_name xddf false currentpagedevice/SeparationColorNames get{ AGMCORE_name eq or }forall }def /current_ink { dup length 0 eq{ pop true }{ Adobe_AGM_Core/ink_result false put { dup /ProcessCyan eq{ AGMCORE_cyan_plate ink_result or Adobe_AGM_Core/ink_result xddf }{ dup /ProcessMagenta eq{ AGMCORE_magenta_plate ink_result or Adobe_AGM_Core/ink_result xddf }{ dup /ProcessYellow eq{ AGMCORE_yellow_plate ink_result or Adobe_AGM_Core/ink_result xddf }{ dup /ProcessBlack eq{ AGMCORE_black_plate ink_result or Adobe_AGM_Core/ink_result xddf }{ dup /sep_colorspace_dict AGMCORE_gget dup null eq{ pop false ink_result or Adobe_AGM_Core/ink_result xddf }{ /Name get eq{ 1 setsepcolor currentgray 1 ne ink_result or Adobe_AGM_Core/ink_result xddf }{ false ink_result or Adobe_AGM_Core/ink_result xddf }ifelse }ifelse }ifelse }ifelse }ifelse }ifelse pop } forall ink_result }ifelse }def /map255_to_range { 1 index sub 3 -1 roll 255 div mul add }def /set_csa_crd { /sep_colorspace_dict null AGMCORE_gput begin CSA map_csa setcolorspace_opt set_crd end } def /setsepcolor { /sep_colorspace_dict AGMCORE_gget begin dup /sep_tint exch AGMCORE_gput TintProc end } def /sep_colorspace_proc { Adobe_AGM_Core/AGMCORE_tmp xddf /sep_colorspace_dict AGMCORE_gget begin currentdict/Components known{ Components aload pop TintMethod/Lab eq{ 2 {AGMCORE_tmp mul NComponents 1 roll} repeat LMax sub AGMCORE_tmp mul LMax add NComponents 1 roll }{ TintMethod/Subtractive eq{ NComponents{ AGMCORE_tmp mul NComponents 1 roll }repeat }{ NComponents{ 1 sub AGMCORE_tmp mul 1 add NComponents 1 roll } repeat }ifelse }ifelse }{ ColorLookup AGMCORE_tmp ColorLookup length 1 sub mul round cvi get aload pop }ifelse end } def /sep_colorspace_gray_proc { Adobe_AGM_Core/AGMCORE_tmp xddf /sep_colorspace_dict AGMCORE_gget begin GrayLookup AGMCORE_tmp GrayLookup length 1 sub mul round cvi get end } def /sep_proc_name { dup 0 get dup /DeviceRGB eq exch /DeviceCMYK eq or level2 not and has_color not and{ pop [/DeviceGray] /sep_colorspace_gray_proc }{ /sep_colorspace_proc }ifelse } def /setsepcolorspace { dup /sep_colorspace_dict exch AGMCORE_gput begin /MappedCSA CSA map_csa def Adobe_AGM_Core/AGMCORE_sep_special Name dup () eq exch (All) eq or ddf AGMCORE_avoid_L2_sep_space{ [/Indexed MappedCSA sep_proc_name 255 exch { 255 div } /exec cvx 3 -1 roll [ 4 1 roll load /exec cvx ] cvx ] setcolorspace_opt /TintProc { 255 mul setcolor }bdf }{ MappedCSA 0 get /DeviceCMYK eq currentdict/Components known and AGMCORE_sep_special not and{ /TintProc [ Components aload pop Name findcmykcustomcolor /exch cvx /setcustomcolor cvx ] cvx bdf }{ AGMCORE_host_sep Name (All) eq and{ /TintProc { 1 exch sub setseparationgray }bdf }{ AGMCORE_in_rip_sep MappedCSA 0 get /DeviceCMYK eq and AGMCORE_host_sep or Name () eq and{ /TintProc [ MappedCSA sep_proc_name exch 0 get /DeviceCMYK eq{ cvx /setcmykcolor cvx }{ cvx /setgray cvx }ifelse ] cvx bdf }{ AGMCORE_producing_seps MappedCSA 0 get dup /DeviceCMYK eq exch /DeviceGray eq or and AGMCORE_sep_special not and{ /TintProc [ /dup cvx MappedCSA sep_proc_name cvx exch 0 get /DeviceGray eq{ 1 /exch cvx /sub cvx 0 0 0 4 -1 /roll cvx }if /Name cvx /findcmykcustomcolor cvx /exch cvx AGMCORE_host_sep{ AGMCORE_is_cmyk_sep }{ Name inRip_spot_has_ink not }ifelse { /pop cvx 1 }if /setcustomcolor cvx ] cvx bdf }{ /TintProc /setcolor ldf [/Separation Name MappedCSA sep_proc_name load ] setcolorspace_opt }ifelse }ifelse }ifelse }ifelse }ifelse set_crd 1 setsepcolor end } def /setindexedcolorspace { dup /indexed_colorspace_dict exch AGMCORE_gput begin /MappedCSA CSA map_csa def AGMCORE_host_sep level2 not and{ 0 0 0 0 setcmykcolor }{ [/Indexed MappedCSA level2 not has_color not and{ dup 0 get dup /DeviceRGB eq exch /DeviceCMYK eq or{ pop [/DeviceGray] }if HiVal GrayLookup }{ HiVal currentdict/RangeArray known{ { /indexed_colorspace_dict AGMCORE_gget begin Lookup exch dup HiVal gt{ pop HiVal }if NComponents mul NComponents getinterval {} forall NComponents 1 sub -1 0{ RangeArray exch 2 mul 2 getinterval aload pop map255_to_range NComponents 1 roll }for end } bind }{ Lookup }ifelse }ifelse ] setcolorspace_opt set_crd }ifelse end }def /setindexedcolor { AGMCORE_host_sep{ /indexed_colorspace_dict AGMCORE_gget/Lookup get 4 3 -1 roll map_index setcmykcolor }{ setcolor }ifelse } def /imageormask_sys { begin save mark level2{ currentdict Operator /imagemask eq{ AGMCORE_&imagemask }{ AGMCORE_&image }ifelse }{ Width Height Operator /imagemask eq{ Decode 0 get 1 eq Decode 1 get 0 eq and ImageMatrix /DataSource load AGMCORE_&imagemask }{ BitsPerComponent ImageMatrix /DataSource load AGMCORE_&image }ifelse }ifelse cleartomark restore end }def /overprint_plate { currentoverprint{ 0 get dup /DeviceGray eq{ pop AGMCORE_black_plate not }{ /DeviceCMYK eq{ AGMCORE_is_cmyk_sep not }if }ifelse }{ false }ifelse }def /rdline { currentfile AGMCORE_str256 readline pop } def /rdcmntline { currentfile AGMCORE_str256 readline pop (%) anchorsearch {pop} if } def /filter_cmyk { dup type /filetype ne{ 0 () /SubFileDecode filter }if [ exch { AGMCORE_src256 readstring pop dup length /AGMCORE_srcLen exch def /AGMCORE_ndx 0 def AGMCORE_plate_ndx 4 AGMCORE_srcLen 1 sub{ 1 index exch get AGMCORE_dst64 AGMCORE_ndx 3 -1 roll put /AGMCORE_ndx AGMCORE_ndx 1 add def }for pop AGMCORE_dst64 0 AGMCORE_ndx getinterval } bind /exec cvx ] cvx } def /imageormask { begin SkipImageProc not{ save mark level2 AGMCORE_host_sep not and{ currentdict Operator /imagemask eq{ imagemask }{ AGMCORE_in_rip_sep currentoverprint and currentcolorspace 0 get /DeviceGray eq and{ [/Separation /Black /DeviceGray {}] setcolorspace /Decode [ Decode 1 get Decode 0 get ] def }if image }ifelse }{ Width Height Operator /imagemask eq{ Decode 0 get 1 eq Decode 1 get 0 eq and ImageMatrix /DataSource load AGMCORE_host_sep{ currentgray 1 ne{ currentdict imageormask_sys }{ currentoverprint not{ 1 AGMCORE_&setgray knockout_image_samples currentdict imageormask_sys }{ nulldevice currentdict imageormask_sys }ifelse }ifelse }{ imagemask }ifelse }{ BitsPerComponent ImageMatrix MultipleDataSources{ 0 1 NComponents 1 sub{ DataSource exch get }for }{ /DataSource load }ifelse Operator /colorimage eq{ AGMCORE_host_sep{ MultipleDataSources level2 or NComponents 4 eq and{ MultipleDataSources{ 4 {pop} repeat /DataSource [ DataSource 0 get /exec cvx DataSource 1 get /exec cvx DataSource 2 get /exec cvx DataSource 3 get /exec cvx /AGMCORE_get_ink_data cvx ] cvx def }{ /DataSource /DataSource load filter_cmyk 0 () /SubFileDecode filter def }ifelse /Decode [ Decode 0 get Decode 1 get ] def /MultipleDataSources false def /NComponents 1 def /Operator /image def AGMCORE_is_cmyk_sep{ currentoverprint InksUsed current_ink not and{ nulldevice }{ invert_image_samples }ifelse }{ currentoverprint not{ knockout_image_samples }{ nulldevice }ifelse }ifelse 1 AGMCORE_&setgray currentdict imageormask_sys }{ currentcolortransfer {pop 1} exch addprocs 4 1 roll {pop 1} exch addprocs 4 1 roll {pop 1} exch addprocs 4 1 roll {pop 1} exch addprocs 4 1 roll setcolortransfer MultipleDataSources NComponents AGMCORE_&colorimage }ifelse }{ true NComponents colorimage }ifelse }{ Operator /image eq{ AGMCORE_host_sep{ HostSepColorImage{ invert_image_samples }{ AGMCORE_black_plate not{ currentoverprint not{ knockout_image_samples }{ nulldevice }ifelse }if }ifelse 1 AGMCORE_&setgray currentdict imageormask_sys }{ image }ifelse }{ Operator/knockout eq{ pop pop pop pop pop currentoverprint InksUsed current_ink not and{ }{ currentcolorspace overprint_plate not{ knockout_unitsq }if }ifelse }if }ifelse }ifelse }ifelse }ifelse cleartomark restore }if end }def /tint_image_to_color { begin Width Height BitsPerComponent ImageMatrix /DataSource load end Adobe_AGM_Core begin /AGMCORE_mbuf 0 string def /AGMCORE_ybuf 0 string def /AGMCORE_kbuf 0 string def { colorbuf dup length AGMCORE_mbuf length ne { dup length dup dup /AGMCORE_mbuf exch string def /AGMCORE_ybuf exch string def /AGMCORE_kbuf exch string def } if dup AGMCORE_mbuf copy AGMCORE_ybuf copy AGMCORE_kbuf copy pop } addprocs {AGMCORE_mbuf}{AGMCORE_ybuf}{AGMCORE_kbuf} true 4 colorimage end } def /sep_imageormask_lev1 { begin MappedCSA 0 get dup /DeviceRGB eq exch /DeviceCMYK eq or has_color not and{ { 255 mul round cvi GrayLookup exch get } currenttransfer addprocs settransfer currentdict imageormask }{ /sep_colorspace_dict AGMCORE_gget/Components known{ MappedCSA 0 get /DeviceCMYK eq{ Components aload pop }{ 0 0 0 Components aload pop 1 exch sub }ifelse Adobe_AGM_Core/AGMCORE_k xddf Adobe_AGM_Core/AGMCORE_y xddf Adobe_AGM_Core/AGMCORE_m xddf Adobe_AGM_Core/AGMCORE_c xddf AGMCORE_y 0.0 eq AGMCORE_m 0.0 eq and AGMCORE_c 0.0 eq and{ {AGMCORE_k mul 1 exch sub} currenttransfer addprocs settransfer currentdict imageormask }{ currentcolortransfer {AGMCORE_k mul 1 exch sub} exch addprocs 4 1 roll {AGMCORE_y mul 1 exch sub} exch addprocs 4 1 roll {AGMCORE_m mul 1 exch sub} exch addprocs 4 1 roll {AGMCORE_c mul 1 exch sub} exch addprocs 4 1 roll setcolortransfer currentdict tint_image_to_color }ifelse }{ MappedCSA 0 get /DeviceGray eq { {255 mul round cvi ColorLookup exch get 0 get} currenttransfer addprocs settransfer currentdict imageormask }{ MappedCSA 0 get /DeviceCMYK eq { currentcolortransfer {255 mul round cvi ColorLookup exch get 3 get 1 exch sub} exch addprocs 4 1 roll {255 mul round cvi ColorLookup exch get 2 get 1 exch sub} exch addprocs 4 1 roll {255 mul round cvi ColorLookup exch get 1 get 1 exch sub} exch addprocs 4 1 roll {255 mul round cvi ColorLookup exch get 0 get 1 exch sub} exch addprocs 4 1 roll setcolortransfer currentdict tint_image_to_color }{ currentcolortransfer {pop 1} exch addprocs 4 1 roll {255 mul round cvi ColorLookup exch get 2 get} exch addprocs 4 1 roll {255 mul round cvi ColorLookup exch get 1 get} exch addprocs 4 1 roll {255 mul round cvi ColorLookup exch get 0 get} exch addprocs 4 1 roll setcolortransfer currentdict tint_image_to_color }ifelse }ifelse }ifelse }ifelse end }def /sep_image_lev1_sep { begin /sep_colorspace_dict AGMCORE_gget/Components known{ Components aload pop Adobe_AGM_Core/AGMCORE_k xddf Adobe_AGM_Core/AGMCORE_y xddf Adobe_AGM_Core/AGMCORE_m xddf Adobe_AGM_Core/AGMCORE_c xddf {AGMCORE_c mul 1 exch sub} {AGMCORE_m mul 1 exch sub} {AGMCORE_y mul 1 exch sub} {AGMCORE_k mul 1 exch sub} }{ {255 mul round cvi ColorLookup exch get 0 get 1 exch sub} {255 mul round cvi ColorLookup exch get 1 get 1 exch sub} {255 mul round cvi ColorLookup exch get 2 get 1 exch sub} {255 mul round cvi ColorLookup exch get 3 get 1 exch sub} }ifelse AGMCORE_get_ink_data currenttransfer addprocs settransfer currentdict imageormask_sys end }def /sep_imageormask { /sep_colorspace_dict AGMCORE_gget begin /MappedCSA CSA map_csa def begin SkipImageProc not{ save mark AGMCORE_avoid_L2_sep_space{ /Decode [ Decode 0 get 255 mul Decode 1 get 255 mul ] def }if AGMCORE_ccimage_exists MappedCSA 0 get /DeviceCMYK eq and currentdict/Components known and Name () ne and Name (All) ne and Operator /image eq and AGMCORE_producing_seps not and level2 not and { Width Height BitsPerComponent ImageMatrix [ /DataSource load /exec cvx { 0 1 2 index length 1 sub{ 1 index exch 2 copy get 255 xor put }for } /exec cvx ] cvx bind MappedCSA 0 get /DeviceCMYK eq{ Components aload pop }{ 0 0 0 Components aload pop 1 exch sub }ifelse Name findcmykcustomcolor customcolorimage }{ AGMCORE_producing_seps not{ level2{ AGMCORE_avoid_L2_sep_space not currentcolorspace 0 get /Separation ne and{ [/Separation Name MappedCSA sep_proc_name load ] setcolorspace_opt /sep_tint AGMCORE_gget setcolor }if currentdict imageormask }{ currentdict Operator /imagemask eq{ imageormask }{ sep_imageormask_lev1 }ifelse }ifelse }{ AGMCORE_host_sep{ Operator/knockout eq{ currentoverprint InksUsed current_ink not and{ }{ currentdict/ImageMatrix get concat knockout_unitsq }ifelse }{ currentgray 1 ne{ AGMCORE_is_cmyk_sep Name (All) ne and{ level2{ [ /Separation Name [/DeviceGray] { sep_colorspace_proc AGMCORE_get_ink_data 1 exch sub } bind ] AGMCORE_&setcolorspace /sep_tint AGMCORE_gget AGMCORE_&setcolor currentdict imageormask_sys }{ currentdict Operator /imagemask eq{ imageormask_sys }{ sep_image_lev1_sep }ifelse }ifelse }{ Operator/imagemask ne{ invert_image_samples }if currentdict imageormask_sys }ifelse }{ currentoverprint not Name (All) eq or{ knockout_image_samples }{ nulldevice }ifelse currentdict imageormask_sys }ifelse }ifelse }{ currentcolorspace 0 get /Separation ne{ [/Separation Name MappedCSA sep_proc_name load ] setcolorspace_opt /sep_tint AGMCORE_gget setcolor }if currentoverprint MappedCSA 0 get /DeviceCMYK eq and Name inRip_spot_has_ink not and Name (All) ne and { imageormask_l2_overprint }{ currentdict imageormask }ifelse }ifelse }ifelse }ifelse cleartomark restore }if end end }def /modify_halftone_xfer { currenthalftone dup length dict copy begin currentdict 2 index known{ 1 index load dup length dict copy begin currentdict/TransferFunction known{ /TransferFunction load }{ currenttransfer }ifelse addprocs /TransferFunction xdf currentdict end def currentdict end sethalftone }{ currentdict/TransferFunction known{ /TransferFunction load }{ currenttransfer }ifelse addprocs /TransferFunction xdf currentdict end sethalftone pop }ifelse }def /read_image_file { AGMCORE_imagefile 0 setfileposition dup /DataSource {AGMCORE_imagefile AGMCORE_imbuf readstring pop} put exch load exec }def /write_image_file { { (AGMCORE_imagefile) (w+) file } stopped{ false }{ Adobe_AGM_Core/AGMCORE_imagefile xddf Adobe_AGM_Core/AGMCORE_imbuf Width BitsPerComponent mul 7 add 8 idiv string ddf 1 1 Height { pop DataSource dup type /filetype eq{ AGMCORE_imbuf readstring pop }{ exec } ifelse AGMCORE_imagefile exch writestring }for true }ifelse }def /imageormask_l2_overprint { write_image_file{ currentcmykcolor 0 ne{ [/Separation /Black /DeviceGray {}] setcolorspace gsave /Black [{1 exch sub /sep_tint AGMCORE_gget mul} /exec cvx MappedCSA sep_proc_name cvx exch pop {4 1 roll pop pop pop 1 exch sub} /exec cvx] cvx modify_halftone_xfer Operator currentdict read_image_file grestore }if 0 ne{ [/Separation /Yellow /DeviceGray {}] setcolorspace gsave /Yellow [{1 exch sub /sep_tint AGMCORE_gget mul} /exec cvx MappedCSA sep_proc_name cvx exch pop {4 2 roll pop pop pop 1 exch sub} /exec cvx] cvx modify_halftone_xfer Operator currentdict read_image_file grestore }if 0 ne{ [/Separation /Magenta /DeviceGray {}] setcolorspace gsave /Magenta [{1 exch sub /sep_tint AGMCORE_gget mul} /exec cvx MappedCSA sep_proc_name cvx exch pop {4 3 roll pop pop pop 1 exch sub} /exec cvx] cvx modify_halftone_xfer Operator currentdict read_image_file grestore }if 0 ne{ [/Separation /Cyan /DeviceGray {}] setcolorspace gsave /Cyan [{1 exch sub /sep_tint AGMCORE_gget mul} /exec cvx MappedCSA sep_proc_name cvx exch pop {pop pop pop 1 exch sub} /exec cvx] cvx modify_halftone_xfer Operator currentdict read_image_file grestore } if AGMCORE_imagefile closefile (AGMCORE_imagefile) deletefile }{ currentdict imageormask }ifelse } def /indexed_imageormask { begin save mark currentdict AGMCORE_host_sep{ Operator/knockout eq{ /indexed_colorspace_dict AGMCORE_gget /CSA get map_csa overprint_plate not{ knockout_unitsq }if }{ AGMCORE_is_cmyk_sep{ Operator /imagemask eq{ imageormask_sys }{ level2{ indexed_image_lev2_sep }{ indexed_image_lev1_sep }ifelse }ifelse }{ currentoverprint not{ knockout_image_samples imageormask_sys }{ nulldevice currentdict imageormask_sys }ifelse }ifelse }ifelse }{ level2{ imageormask }{ Operator /imagemask eq{ imageormask }{ indexed_imageormask_lev1 }ifelse }ifelse }ifelse cleartomark restore end }def /indexed_imageormask_lev1 { /indexed_colorspace_dict AGMCORE_gget begin begin currentdict MappedCSA 0 get dup /DeviceRGB eq exch /DeviceCMYK eq or has_color not and{ {HiVal mul round cvi GrayLookup exch get HiVal div} currenttransfer addprocs settransfer imageormask }{ MappedCSA 0 get /DeviceGray eq { {HiVal mul round cvi Lookup exch get HiVal div} currenttransfer addprocs settransfer imageormask }{ MappedCSA 0 get /DeviceCMYK eq { currentcolortransfer {4 mul HiVal mul round cvi 3 add Lookup exch get HiVal div 1 exch sub} exch addprocs 4 1 roll {4 mul HiVal mul round cvi 2 add Lookup exch get HiVal div 1 exch sub} exch addprocs 4 1 roll {4 mul HiVal mul round cvi 1 add Lookup exch get HiVal div 1 exch sub} exch addprocs 4 1 roll {4 mul HiVal mul round cvi Lookup exch get HiVal div 1 exch sub} exch addprocs 4 1 roll setcolortransfer tint_image_to_color }{ currentcolortransfer {pop 1} exch addprocs 4 1 roll {3 mul HiVal mul round cvi 2 add Lookup exch get HiVal div} exch addprocs 4 1 roll {3 mul HiVal mul round cvi 1 add Lookup exch get HiVal div} exch addprocs 4 1 roll {3 mul HiVal mul round cvi Lookup exch get HiVal div} exch addprocs 4 1 roll setcolortransfer tint_image_to_color }ifelse }ifelse }ifelse end end }def /indexed_image_lev1_sep { /indexed_colorspace_dict AGMCORE_gget begin begin {4 mul HiVal mul round cvi Lookup exch get HiVal div 1 exch sub} {4 mul HiVal mul round cvi 1 add Lookup exch get HiVal div 1 exch sub} {4 mul HiVal mul round cvi 2 add Lookup exch get HiVal div 1 exch sub} {4 mul HiVal mul round cvi 3 add Lookup exch get HiVal div 1 exch sub} AGMCORE_get_ink_data currenttransfer addprocs settransfer currentdict imageormask_sys end end }def /indexed_image_lev2_sep { /indexed_colorspace_dict AGMCORE_gget begin begin currentcolorspace dup 1 /DeviceGray put dup 3 [ currentcolorspace 3 get { exch 4 mul 4 getinterval {} forall AGMCORE_get_ink_data 255 div 1 exch sub } /exec cvx ] cvx put setcolorspace currentdict Operator /imagemask eq{ AGMCORE_&imagemask }{ AGMCORE_&image }ifelse end end }def /add_csa { Adobe_AGM_Core begin /AGMCORE_CSA_cache xput end }def /map_csa { dup type /nametype eq{ Adobe_AGM_Core/AGMCORE_CSA_cache get exch get }if }def /add_csd { Adobe_AGM_Core begin /AGMCORE_CSD_cache xput end }def /get_csd { dup type /nametype eq{ Adobe_AGM_Core/AGMCORE_CSD_cache get exch get }if }def /add_pattern { Adobe_AGM_Core begin /AGMCORE_pattern_cache xput end }def /get_pattern { dup type /nametype eq{ Adobe_AGM_Core/AGMCORE_pattern_cache get exch get }if }def /set_pattern { dup /PatternType get 1 eq{ dup /PaintType get 1 eq{ false op [/DeviceGray] setcolorspace 0 setgray }if }if setpattern }def /setcolorspace_opt { dup currentcolorspace eq{ pop }{ setcolorspace }ifelse }def /updatecolorrendering { currentcolorrendering/Intent known{ currentcolorrendering/Intent get }{ null }ifelse Intent ne{ false Intent AGMCORE_CRD_cache { exch pop begin dup Intent eq{ currentdict setcolorrendering_opt end exch pop true exch exit }if end } forall pop not{ systemdict /findcolorrendering known{ Intent findcolorrendering pop /ColorRendering findresource dup length dict copy setcolorrendering_opt }if }if }if } def /add_crd { AGMCORE_CRD_cache 3 1 roll put }def /set_crd { AGMCORE_host_sep not level2 and{ currentdict/CRD known{ AGMCORE_CRD_cache CRD get dup null ne{ setcolorrendering_opt }{ pop }ifelse }{ currentdict/Intent known{ updatecolorrendering }if }ifelse }if }def /setcolorrendering_opt { dup currentcolorrendering eq{ pop }{ begin /Intent Intent def currentdict end setcolorrendering }ifelse }def /OPIimage { dup type /dicttype ne{ 10 dict begin /DataSource xdf /ImageMatrix xdf /BitsPerComponent xdf /Height xdf /Width xdf /MultipleDataSources false def /NComponents 1 def /ImageType 1 def /Decode [0 1 def] /SkipImageProc {false} def currentdict end }if dup begin /HostSepColorImage false def currentdict/Decode known not{ /Decode [ 0 currentcolorspace 0 get /Indexed eq{ 2 BitsPerComponent exp 1 sub }{ 1 }ifelse ] def }if currentdict/Operator known not{ /Operator /image def }if end /sep_colorspace_dict AGMCORE_gget null eq{ imageormask }{ gsave dup begin invert_image_samples end sep_imageormask grestore }ifelse }def /cpaint_gcomp { AGM_preserve_spots{ gsave nulldevice }if }def /cpaint_gsep { AGM_preserve_spots{ grestore currentoverprint Adobe_AGM_Core/AGMCORE_ovp xddf }{ gsave nulldevice }ifelse }def /cpaint_gend { AGM_preserve_spots{ Adobe_AGM_Core/AGMCORE_ovp get setoverprint }{ grestore }ifelse newpath }def /AGMCORE_ctm_stack bdict /push_ctm { stack length size le{ stack dup length 2 mul array dup /stack exch def copy pop }if stack size 3 -1 roll put /size size 1 add def } /pop_ctm { /size size 1 sub def size 0 lt{ /size 0 def }if stack size get } /stack 1 array /size 0 edict def /save_ctm { matrix currentmatrix AGMCORE_ctm_stack begin push_ctm end }def /restore_ctm { AGMCORE_ctm_stack begin pop_ctm end setmatrix }def /path_rez { dup 0 ne{ AGMCORE_deviceDPI exch div dup 1 lt{ pop 1 }if setflat }{ pop }ifelse }def end systemdict /setpacking known { setpacking } if %%EndResource %%EndProlog %%BeginSetup Adobe_AGM_Core/AGMCORE_err_strings 3 dict dup begin /AGMCORE_bad_environ (Die Voraussetzungen sind fr den Auftrag nicht erfllt, da mindestens PostScript Level ^\tund PostScript-Version ^ erforderlich sind. Stellen Sie sicher, da die PPD korrekt ist bzw. da das gewnschte PostScript-Level vom Drucker untersttzt wird. ) def /AGMCORE_color_space_onhost_seps (Dieser Auftrag verfgt ber Inhalt, der nicht mit Host-Methoden separiert werden kann. ) def /AGMCORE_invalid_color_space (Dieser Auftrag besitzt einen ungltigen Farbraum. ) def end put 2 2010 true Adobe_AGM_Core/doc_setup get exec %%EndSetup %%Page: name:1 1 %%EndPageComments %%BeginPageSetup Adobe_AGM_Core/page_setup get exec %%EndPageSetup Adobe_AGM_Core/AGMCORE_save save ddf mark /0 [/DeviceGray] add_csa /CSA /0 /1 [/DeviceCMYK] add_csa /CSA /1 /2 [/DeviceRGB] add_csa /CSA /2 cleartomark 800 path_rez 1 -1 scale 0 -233.192 translate gsave [1 0 0 1 0 0 ] concat gsave 0 0 mo 0 233.192 ln 158.793 233.192 ln 158.793 0 ln clp 1.50117 lw 1 lc 1 lj 4 ml [] 0 dsh true sadj 157.163 167.28 mo 157.163 166.569 ln 155.768 166.569 ln 154.605 167.483 ln 154.605 166.976 ln 153.211 166.976 ln 153.211 163.114 ln 152.049 163.114 ln 152.049 159.556 ln 151.351 159.556 ln 151.351 158.845 ln 150.188 158.845 ln 150.188 163.317 ln 148.563 163.317 ln 148.563 166.772 ln 146.819 166.772 ln 146.819 165.704 ln 145.891 166.112 ln 144.613 166.671 ln 143.683 166.671 ln 143.683 168.296 ln 141.824 168.296 ln 141.824 166.976 ln 141.824 164.942 ln 141.824 162.503 ln 140.43 162.503 ln 140.43 161.487 ln 139.034 161.487 ln 139.034 156.913 ln 137.64 156.913 ln 137.64 161.487 ln 136.943 161.792 ln 136.943 167.128 ln 135.666 167.128 ln 134.388 167.688 ln 132.992 167.688 ln 131.83 167.179 ln 129.739 167.179 ln 129.739 165.96 ln 128.343 165.96 ln 128.343 167.89 ln 127.182 167.89 ln 127.182 169.212 ln 126.249 169.212 ln 126.249 170.075 ln 124.162 170.075 ln 124.166 170.077 ln 124.166 169.517 ln 123.232 169.517 ln 123.232 162.2 ln 122.304 162.2 ln 122.304 157.22 ln 121.722 156.966 ln 121.722 153.663 ln 120.676 153.663 ln 120.676 161.489 ln 119.745 161.489 ln 119.745 169.417 ln 118.818 169.417 ln 118.818 170.077 ln 117.659 170.077 ln 117.659 169.417 ln 116.725 169.417 ln 116.725 165.249 ln 116.028 165.249 ln 116.028 160.37 ln 115.331 160.37 ln 115.331 144.925 ln 114.866 144.925 ln 114.866 137.712 ln 113.704 137.712 ln 113.704 147.565 ln 112.774 147.565 ln 112.774 167.587 ln 112.076 167.892 ln 112.076 169.417 ln 111.149 169.417 ln 111.149 167.993 ln 110.683 167.993 ln 110.683 165.351 ln 109.521 165.351 ln 109.521 163.724 ln 108.59 163.724 ln 108.59 166.774 ln 107.895 166.774 ln 107.895 167.587 ln 106.965 167.587 ln 106.965 166.062 ln 105.803 166.062 ln 105.803 167.79 ln 104.176 167.79 ln 104.176 169.517 ln 102.782 169.517 ln 102.782 164.843 ln 102.199 164.589 ln 102.199 159.558 ln 100.459 159.558 ln 100.459 163.724 ln 99.2949 164.233 ln 99.2949 170.077 ln 98.6006 170.077 ln 98.6006 152.546 ln 98.6006 144.313 ln 97.9014 144.313 ln 97.9014 134.257 ln 97.2041 134.257 ln 97.2041 142.79 ln 96.2734 142.79 ln 96.2734 160.573 ln 95.5771 160.573 ln 95.5771 161.489 ln 94.8799 161.489 ln 94.8799 166.163 ln 94.8799 168.806 ln 93.9492 168.806 ln 93.9492 166.265 ln 92.7881 166.265 ln 92.7881 162.403 ln 92.7881 154.476 ln 91.3936 154.476 ln 91.3936 144.009 ln 90.8125 143.755 ln 90.8125 150.411 ln 89.0693 150.411 ln 89.0693 154.577 ln 89.0693 162.302 ln 88.3711 162.302 ln 88.3711 164.538 ln 87.4424 164.944 ln 87.4424 168.907 ln 87.4424 170.077 ln 86.2842 170.077 ln 86.2842 164.437 ln 85.584 164.437 ln 85.584 154.374 ln 85.584 149.192 ln 84.8857 149.192 ln 84.8857 161.388 ln 83.7256 161.388 ln 83.7256 167.892 ln 83.0293 168.198 ln 83.0293 166.876 ln 81.4014 166.876 ln 81.4014 167.688 ln 80.4717 167.688 ln 80.4717 169.517 ln 79.7749 169.517 ln 79.7749 167.587 ln 78.8438 167.587 ln 78.8438 165.858 ln 77.6831 165.858 ln 77.6831 166.774 ln 77.332 166.62 ln 77.332 168.095 ln 76.3452 168.526 ln 76.3452 169.62 ln 75.2139 170.114 ln 75.2139 168.399 ln 73.731 168.399 ln 73.731 167.181 ln 73.0322 167.181 ln 73.0322 165.554 ln 71.6396 165.554 ln 71.6396 163.927 ln 70.9414 163.927 ln 70.9414 161.286 ln 69.7803 161.286 ln 69.7803 155.392 ln 68.6187 155.392 ln 68.6187 163.114 ln 67.4565 163.114 ln 67.4565 165.554 ln 66.5278 165.96 ln 66.5278 169.009 ln 65.8306 169.009 ln 65.8306 170.077 ln 64.9038 170.077 ln 64.9038 164.03 ln 64.2036 163.724 ln 64.2036 157.526 ln 63.042 157.526 ln 63.042 150.919 ln 62.3428 150.919 ln 62.3428 133.878 ln 61.1821 133.878 ln 61.1821 148.073 ln 60.4834 148.073 ln 60.4834 158.847 ln 59.5542 158.847 ln 59.5542 159.659 ln 58.625 159.659 ln 58.625 164.132 ln 57.231 164.132 ln 57.231 169.517 ln 57.231 170.077 ln 56.0708 170.077 ln 56.0708 169.112 ln 54.9062 169.112 ln 54.9062 168.298 ln 53.7451 168.298 ln 53.7451 166.265 ln 52.1187 166.265 ln 52.1187 156.204 ln 50.9556 156.204 ln 50.9556 143.603 ln 49.5615 143.603 ln 49.5615 154.272 ln 48.6318 154.272 ln 48.6318 152.444 ln 47.4707 152.444 ln 47.4707 143.196 ln 46.8877 142.942 ln 46.8877 137.028 ln 45.8438 137.028 ln 45.8438 145.229 ln 44.6816 145.229 ln 44.6816 154.577 ln 43.752 154.577 ln 43.752 161.185 ln 42.8228 161.185 ln 42.8228 167.079 ln 42.3564 167.079 ln 42.3564 164.741 ln 40.9619 164.741 ln 40.9619 167.79 ln 39.335 167.79 ln 39.335 165.96 ln 38.1738 165.96 ln 38.1738 169.417 ln 36.7793 169.417 ln 36.7793 167.079 ln 35.3853 167.079 ln 35.3853 167.993 ln 34.2231 167.993 ln 34.2231 169.417 ln 33.0615 169.417 ln 33.0615 170.077 ln 31.6699 170.077 ln 31.6699 168.298 ln 30.04 168.298 ln 30.04 170.077 ln 27.9517 170.077 ln 27.9517 169.313 ln 26.7866 169.313 ln 26.7866 170.077 ln 24.6982 170.077 ln 24.6982 169.212 ln 23.7646 169.212 ln 23.7646 167.892 ln 22.604 167.892 ln 22.604 165.96 ln 21.209 165.96 ln 21.209 167.181 ln false op 0.8 0 1 0 cmyk s 20.0093 184.339 mo 26.7466 184.339 ln 35.752 209.991 ln 35.8896 209.991 ln 44.8267 184.339 ln 51.6328 184.339 ln 51.6328 232.784 ln 44.6206 232.784 ln 44.6206 203.323 ln 44.4829 203.323 ln 37.5396 224.144 ln 34.0337 224.144 ln 27.1587 203.323 ln 27.021 203.323 ln 27.021 232.784 ln 20.0093 232.784 ln 20.0093 184.339 ln 0 0 0 1 cmyk f 81.5381 221.897 mo 81.5381 228.226 76.4512 233.192 70.1953 233.192 cv 63.939 233.192 58.8516 228.226 58.8516 221.897 cv 58.8516 184.339 ln 65.8643 184.339 ln 65.8643 221.218 ln 65.8643 224.62 67.7202 226.253 70.1953 226.253 cv 72.6694 226.253 74.5259 224.62 74.5259 221.218 cv 74.5259 184.339 ln 81.5381 184.339 ln 81.5381 221.897 ln f 88.4824 184.339 mo 95.2188 184.339 ln 105.806 213.528 ln 105.943 213.528 ln 105.943 184.339 ln 112.956 184.339 ln 112.956 232.784 ln 106.356 232.784 ln 95.6309 203.663 ln 95.4941 203.663 ln 95.4941 232.784 ln 88.4824 232.784 ln 88.4824 184.339 ln f 120.104 184.339 mo 127.117 184.339 ln 127.117 232.784 ln 120.104 232.784 ln 120.104 184.339 ln f 134.267 184.339 mo 141.005 184.339 ln 151.591 213.528 ln 151.729 213.528 ln 151.729 184.339 ln 158.741 184.339 ln 158.741 232.784 ln 152.141 232.784 ln 141.416 203.663 ln 141.278 203.663 ln 141.278 232.784 ln 134.267 232.784 ln 134.267 184.339 ln f 20.665 186.084 mo 20.665 137.013 ln s 20.665 85.73 mo 20.665 0.750488 ln 158.042 0.750488 ln 158.042 186.419 ln s 158.439 6.28955 mo 156.007 9.93408 149.9 9.95361 149.9 9.95361 cv 151.478 5.99219 ln 143.451 7.99805 135.269 16.4839 135.269 16.4839 cv 135.253 11.5103 ln 124.443 19.7241 ln 126.627 13.624 ln 122.312 16.5127 ln 117.066 19.623 111.115 20.8755 111.115 20.8755 cv 102.108 21.4355 89.2393 32.2954 89.2393 32.2954 cv 90.8037 28.2671 89.6787 19.9644 89.6787 19.9644 cv 81.7539 35.4751 66.6562 42.4546 64.4922 43.3345 cv 62.3242 44.2144 50.9448 49.4219 40.6069 58.8701 cv 30.2759 68.311 26.9932 84.0996 26.9932 84.0996 cv 26.166 87.0615 18.4712 92.0356 14.9258 96.6807 cv 13.3037 98.8003 4.20166 110.166 1.02344 124.069 cv -1.66748 135.872 1.74268 147.259 1.74268 147.259 cv 1.74268 147.259 2.34082 151.571 5.6123 143.87 cv 6.97461 143.597 ln 6.97461 143.597 5.31104 147.194 6.33398 146.843 cv 7.35547 146.495 13.23 140.161 18.041 134.808 cv 22.8545 129.448 39.2515 116.277 54.019 115.323 cv 54.019 115.323 64.042 111.539 71.4673 117.349 cv 71.4673 117.349 76.3628 123.407 83.9307 122.653 cv 83.9307 122.653 84.8184 122.405 87.2373 124.046 cv 87.0957 120.886 ln 87.0957 120.886 95.7861 125.985 104.906 122.38 cv 103.306 120.114 ln 103.306 120.114 104.945 117.7 109.778 120.974 cv 110.065 118.819 ln 110.065 118.819 118.653 123.396 123.064 123.247 cv 121.854 119.364 ln 121.854 119.364 130.949 118.089 139.959 117.532 cv 139.959 117.532 151.181 115.111 158.587 123.294 cv 158.439 6.28955 ln f grestore grestore Adobe_AGM_Core/AGMCORE_save get restore %%PageTrailer Adobe_AGM_Core/page_trailer get exec %%Trailer Adobe_AGM_Core/doc_trailer get exec %%EOF gsave userdict /annotatepage 2 copy known {get exec}{pop pop} ifelse grestore showpage %AI9_PrintingDataEnd userdict /AI9_read_buffer 256 string put userdict begin /ai9_skip_data { mark { currentfile AI9_read_buffer { readline } stopped { } { not { exit } if (%AI9_PrivateDataEnd) eq { exit } if } ifelse } loop cleartomark } def end userdict /ai9_skip_data get exec %AI9_PrivateDataBegin %!PS-Adobe-3.0 EPSF-3.0 %%Creator: Adobe Illustrator(R) 9.0 %%AI8_CreatorVersion: 9.0 %%For: (Walter Biering) (Walter Biering GmbH) %%Title: (Logo_Munin.eps) %%CreationDate: 04.05.2004 15:01 Uhr %AI9_DataStream %Gb"-6CNCf4E@CSil9CdlMNGJghSL5Y3ZLF,P$Y8i7qeu>47!oDFq6Iq/$aMrVgGfAY@$?(5ZOAm_oKj(/m6UZ.u?TdJ4.+-nY713 %I/NQolh;L8\bPt"rH(!ehH8rb-Bt'hIe9E8kP_T&^KWX9BChX7D3`FFEP)%jA/B%QbE),+gu!SUr2WU8hq`bUh>ZHja%tUkFLV;! %gCLoG]tM&TprD40h:m.'kaHtnjkF_BhVWcfht-B]Vu"n=GMguL^UsHW49,0;J)(%sme6P448[e(T7rgrZb*b3qgZ"#s7#X;rkkYC %?b`d-eN_C'5@Y3`N1\q@rT:Q^&o0;4g$E?qG@/)]r9Q:-NrqbI>hnjcl*jYE748\F< %qn(VKO.YjtIboc7KK0tJDn`8^YLrIRus?U+P!?bQF+]A*2% %:OfnQn@sEM)qPCjGCF]3mFsZU,[s('m=mb>ImObMX5>Co[C@G#; %5CIVM))g"#(KcNdmdeSOqSefuS%!mYrR>0CbAV;`r6r^dM;s%pCcZX_@po2IV,+/8pYMO_Tqru_AmlpdDNNJ`jZO=#rE/+DloD/L\WVX(_%d6X?3CE$/&"h^.h68uo?)IY[f:V0ms$co_T35g.mb/2Y %,Ha4X^&N9bN];BqtLX!9n13ueCT-EQ51D-J,TB4gJ?;10:tGYkJ+.;XH$ldq9t\b?/=Fd5BXa#PI4J<%VH1Hq<]h! %f!e[BJO[ELFu!rZnY,ilOL:_KIf/NdDf.f;a2e%KT1o1mi;E$&lfm<(^RMCLFhUW&nN'qPCNjX!c1QI,IYM>DV10=)/p#X`cjlQL4iq!#Q>is4O))&n(^N>fc\hocFrd'(%^YJ;P(]3fPg#me;a*lV;^"_;Tg!:Jd(KVE>(]0,KMt;jI %OY>-O]Ef>Q_Hke%F"s:oT3n-rT'suYcHiE[Eh:V+GruW#rnO(!F)sO3lZ,uXbJ%5Go2815WPfF,0`:S03/?g='e[Bf>_M\tP`S6>0neJ;$T=AEUosZ] %^[ul&iQlrBfd@kinD66F^%&G@G$"+[mG3IXGK"-[>]dgX\p`r+N194B]sHZahtqk0iV@KfM*2AI8$361VY2NIq]j_SOc+^dX7ag3iTkeRGT"(>q.7)&OincX4BrQuZb"#9.iu>QI@B(YRo_doQ2[eAr-)'c$)*N:<(TKWUuJ,3 %N6=gSY3fK9LooaV=0`GhH-)>qS6'a;Y@QgrId^O`:mK]a9a[.DV+RLTV,\=1-!HY#G[:3W&i*YtR]hArTffLmF'gBZp[[fP35fdFK+?tU6=oe9"qJZ)OkOn:%sp] %a%GB5V=j$I@]G+]ZJtCsR4C(tPpuLN*+EIl\MB*7au&gWH!HX#o=6Ui>FCAPbU$::P:Nt"Y?Y^I\^/h%ou11HOhG^4APJl(W)Qo" %W@[A0ZEDB.e^s]/Z85_&]TYN__bO1hL0r7$W1Z[:Bk %0=jL]f0oYOh"@#b+g1-Pe>FncBmPg9[&#o[cV^=C[&<.KlI2'm1d+?WgSFRLY0X:R/;a6_`.1-*?EnW:dBj7?Z/h%6qeq+-]\FQB*("dE^K(of%2&4E0Zu?PEH[q7q.G^nmLMFAV(f+1MgY9_t6, %i**H>,fHUdo+r-7Sfp'V7VU_lNld1![-WFsFBgDIr6HT&q>OP/@J@jd+2=\.)E`7]g;.+FYkE_&_-Y2(p98T2GkDYmnk3,`7D,'q %auV-6eaubjBb@PGQa0AEGW!1!F]u_al(tK1@[s#\nNADBV7+kQX[;/1Tl28j>Q:\>P"/#?W;i8uB_L!KUYe8bm,C+j42oQ(0dTGU %gi[_G(%j@!GZB0ca&`El/RmV`Yu]A8%09[XHsrRYC#SrEa>5FE#\"hhG*\[nq+iUV;EI;s]7&mh[(+[,1sFCKPe%sojuQla\n*_RlXL\.gkdY[334m]Fq %@bN"?il.plisZVh$&*]6#CIPcJEH-Q?;Q_0r)(O_s7XprDn^\tjdTI>i9mcAk6,KLqU$#n='T$Fj'!Cb%XMo^.%m/@D*2*Q4Bk3oV`;de/)lLFAi>i=WBELe^`d:9aUPH8B[HV^FJfd1NZ/!X_dGTfkelJGpYpR[=?])$3,mU^2bgO4BXo/m>s6O'WWXY=X6"5O,gBFA7Pje6S-.ZIq66-N-#j9n"4D3=>j<;\!K5-9Y9 %1TC+U4N1>'@1uHAC*GkdU5lDId@X*!G!aG.:8g>\<$=PaFJq`^.LF^c$KX,pQ(V00@L9'r"fdo^@"Kg7N)?6ZJt;F!MO"XOl4:f9\I'a8._e^4[\;US-&.j_kC1fMgs5s$CkPVc?lpj%j+$u.7@WhcZ#c#Z %<-u(hhd[uge:"5lq'A[ir%pf[S6?an#mhkQON^!W$&L2Yc@D;"TkkFtB$luT\r@u)VemC+0\p=Yp5,>bS'5\k))oVsgM-Q0d<8"6 %(N!5Y9PfoT9bH&6j)m`5CnGFRiLI#,D(g.Be^V8diC4r:c/j6re6NX^?%CDUmt%5,)n+ZuSInB936gC99YPt0rIFb]CYYNnomoZeYNM`89DbX\.k,r1^@f`goiRV6n'T>b29ZWfWU+gTC>@l4iER^:pmfL:9j %-b/L9BIbr!YlQ=+#.=-58"#o3Ie[mZiQI#&\"CB[qr&JJ4Y7eDHm.XM[WO7U/h5V!gpFrNjonBsfiab%2Jh3T-ss]a)%c:Ig,9$7 %bHGi2^+o8UT`/95GRXcNWsSq0F(HF#ZM'/n`GKFF@dM.VMJ3?j:pG*era@=MTgb:O\L,m),28_&igO+@YB.B0ptG%VWE5XOM.G6k %%URY=2Pg',o^[A5]cp_qijW/qYJSo%?5IqTot&Z=MQc>;U[XbUdjD%'8Uef,8cEi2aeA"PpPm>L.G)8kd^ADEZZl[)M%C)bW*<\S`GE0"aDZ=#_PkK`-Ml7F<&K %c\0Pp^k/pg;)BK5-^#!.+3=EbC)WEeJR'MrT90c=#uTG%*scO&:FNjjj9NI4J0tn0\ABMU)0l=(#N--.+b3'Z>"5)[6p*tG6/:H4 %B!)k&L!fR:b$SWB+0>LuhW;(rC:slFYZacW=J'3igCWEiOpoiG?QjJYou]p"7.5!e>RJgjRo8=(PmpI.#C]ij'_+6n1F(^*q:BnnffX0:<97[s,TK\?8@cYoTn_f %;^k#rRU.b7&l<.ca](m"abeO]F:TqEn?EZ_X6T>nCD'qMnF`Hjr5!oqLE3i7`Y^lRg\?SX%;*hK'@D9;XH4BbZ#Op6X4+p8gL'8[ %frYP777cig+EctV]VE`M^=,cfCNnP7470C`$DkNT%?9V(@p4+N$]pJ^R2.'s_IQd9Md<9?"O"/kR>]?sfc+O^L<[*@))TK0^]Oe1 %N6t0X$)+nSN'V&Z==$*DZu)"c&2k_L6JQmnN5jliOWuV/KT8T/Djfr$1qdn71J^buf%Je+a\(7QPg**_jKPsk&lH;,SmNhQh_^D) %=>#_.,+TE`Nn?&)Ih#FVS1P?7Oo"!h,a:Zqq`UE1_g'USQ-`[S`BN^C1H-gE)$-Y\=AB,K(;XT@-VFObSX@aB8goul2PlkC$O;ALR6E<0c)9'rN(3m+$M^:P@NdJcfVj;V5cIZi0\g)g2&52t.H230lSEj2Mb'[2WUeO4r9JPr?2Xu.N %890P8Uk20.oMf"d3Wa`/f3YX=SGoF.=733ol9/>@S!7ef4qB'25LXp6SY!+!ZCbr::o4[8b)rc:Q@u`L-tl,=%Y*C>1hC]Z+3:3/nf9&UC3Jq4@d"0/i@Unk*J63d%VSVp,eDB.i:,B %*-54'O(:PBH74L*;,R>9Gai?A%'@G-QeBiK8/L"*(7I.7Ss'hB[do-Lnb0ncTtq3s?E5?G7sM"o,nW"S#i,_"!BjpAU,07j$@8KS %\HXOOWGi4gkKk?uha9H*.-*JQ_!uRY"E0'7(/6t=J=BbPIh8t.K?#`:_88EX,uYd;?I!p5DH`C)1o;q/oFbN",.fCRfK":Z`?j,a %n$,oSEbp,Pqtn!$++O<)pTpKC=/3O4rOUT7nA!C*n.sDWe\N'#=420ikp>V[oR).7M0^I2^Rni,?:jTKR9lUo8aeg1SlW#tNOdY. %\Gd?]L0N$\%T^[Thmmc.os6'K(/Q8P$sWJu\1mp>I/*4)rq>[62[l^^B*EU`TCuGQmBqDs$fm5;Si?MI%Y'9q7\l//4WJqBqX;nl %b=-6]>ZZEX%XZCaCZ2qX_Ki>=o3Mgf]b\n?3O.R(ZI@]@[/a97X62Y.3iUcf$XU$eo,`2U72EL^nIAp,kP`q3c[/!lV?a8&4+&0I %q,jZP1Ts#YL]8+SiG1.F?YqLpCJDb!RYWUFCd(3+IZ-GN8A*r'/(sj^ejS:!N-NXo%r!nKhY!5YX.LQ,Cd%XhY>m3)&9#MqGqE@' %>eX7Lr9m_K'$Xm`Q+@s)*isS=21):cjk035j\)U)GuFI]Z0?bq)OS_+Jt6m#+U8#7CL=e(a.Ji^lS"4W5HD[DPiMcYG5)0n?sYqm %ZZPu,3;\J)1L]"CcE*P+9*)@QD6]@CcdkYJ^`g=X_icUE-=Qed"YBn'S"Saa3P93bW._0,`%;Yafms.3DqaMc3$E;aSZ+7dF"3quFkO\3O9JSoY&` %?f6f^"upO/7N7VDYX>2-7$:f^/>g$3S\#BV(b?KXAq`TErUEF8NQ?pr6phqbhg\=!`0'?sBfJd+[d:F1nh@2)[s\ZQ:EXrkDJ-jm %P))\Nfd2\bF4^^A+`S),))73Z[Hqpe6q8[c=8h+aW>JR`qZM%8:S0>"A3hk&B/!fo?r&Jt&A0>-68Sc*oE`tY\oH-]#%Hpi34;Au %5Wko$otX0.$OVJdj[MQT&LdeoWFTOK3jM1G%&^TA/b,>>(R:N_"GKFi`*$u;WcW>o79`7Hi],%5n:do&.@!Jn,Zs[WWD0-I4k7+0 %[f!OF-Or%?dp7.,gi3).Kp6:tT[rj\[I42T^SUAmnp7;lIOqVON_@]&2:N67J=8&!qUGWFl@=>+5ef@QIkYs9?`p[.AaP@BK$.s)oRkaedEXCi]nTCS@*H68("ZWB%qjq(,=dMfnegU@B5_nV@[)#^Z;MkiHB)r`^1bq6q.I$\#W/jd-LMrW,?u?T\bqV*a78 %&TckhK+@fUP>Y3=ktgASkOA*kn8np(YV+WO\&68Q9\h-ZEB%MT%gVCl#O1jck_s0;<5pjW1E;e2*L28nS9,N<_Q)O^.tjPdFeL1H %lo?DjME;K*F7CD"E_qVOZS^_s*j94Bd&A<*m?RJ=&j!d\cS1d^(_\pPA2!&X26u@J%lTinXCid"0cp6I_94]E-$9QJA*)rSp#uS_ %lBi7ZO:nt'7\M"`/R5i%#]EY\C"fA>Ju8fG:7Jh`e@/+6b\R'^K,.gp9;Pg?ECST7@Ql$5Qu6",&1A+FH8eVBInR+*&D3.&H9h(Oh3^o1+< %3mfEREJpRj?8Ue_IlY2Ybhm]5?8gq!>u!$lo2gX?e'0VcT66Ld5/5YSk^K`(o`Pni*_X!(A^dQ6p\hVnAa^^#rCn:7)lLGFX2Eie %pp\MShj11TNG$"+l7d&dLhdG*^YGIpbHp(qC6>C4iG[QBDg6D;^Q!!,UB:[u_#@IC&PDc;]V,KlfTK46MF2>J6"T3J:I+l$NX61X %n?K!&p!57AXqs/'lOaZd>;jN2iRi^X#5qrQB;of%D5hFgg1T%.)*WFSeU`@kC&98AGK9"u4,2)2\N8?K`$_DemIVkPbO,5-G(N_D %W-CC)SB-@)@l5F;kYJK&ca%;`=nT_f+p-'R_Ra2#c:;&4I&9n*EL^^bGck$km!Hqo3QHN9rbSXSkuL2kjErI%4*&2c$DTc'#mdd$`T8u+fF.OeLn2/@1CdZAD3eX;f9S>lIo:kMu]%SmTTEj\l'Q4Do>Mg2e(LCUi*U^X_4W;=qBHo2AiiY0jUeY;Y=Mk2uen[9?a\Q5I9SRk5S %O#,3K.TkhQnNi4.FF&VLla.f6nTqPql1&Y`mV'qqYD5m-3fkBO@",m>n%m;%gcIE/PCOAuF+D0(L;T]+CeTg!Oi=KeZs&;65/1ifPpSY;dd'boEbMF#gm6k*u"cH54-=osu"rj/SJU32L'\P#D, %5AEm=KAP-k,fY@k*Gh]rZSbfg;2`<%g).cmef8VQq$)1UiIPJl*+*PRntQ[NZA5OQVG9p,3Z.:q:*SEg3Baln1SF@35gBJn02<&3 %4<[I@g'Z6gl[7a(fY$qFRe54Tc"hCkg/$0DNJ_D@eV:#L[P48dcs:qIBpV:J*:'!OI*S:_H(g^^'i*U)Z6-_gJ*J"2>gQtiSA%l( %2P'j+eRtC5kKUR6LtRKWrmo3n-M7Ot:-$SYq,NdMV7:g$_/%St)`e4"SlfJc8+V4MmjD"b+3EoFtSm$F9fiF4WN:.gMoL0#TJ;&_*SVDhkB#0a'Q@.'(Z0_8to>So0FHo\;\QlllY30DPq4UPGSjeefXj\0F' %<%aM&XI"la"Cp*VP4=MSG"jV-h[$/9]juL8biD04./?W(AGB:)4^D8hqg&8R3#q`i$ia$ql%>D'%qisIR#Lo=Ec=3D4IcAPq'LK*4JnlL!D_"Lc[gQBR<[Y?^h-m9-`/\+UumGk*iQIc2A;)]A",$'.s*:9_jGVGb8fPT,oi55Eq/t %5Us`omHGG9F"6Kl("SemgCnAg%!JlF>#j=&KKJ@![@P\kO>/kHB6Nn"VV1sah^KnE_CI1I#bXJV^P?o3OiMYHq8_5QONS3Pg#/oP8`<^X2O&O0(BgP4@WgoeW1FD!jlg\W<]iGZ*2^%hmnpDWUSu %G4;ZPAG3'>&T]be5O/8`R[uOWi\+k'`5CSItI%qtYBl<<:I+QV=:"amFSI;Srq->?IME8IB(i\.QW7)T1aM`HF7i^q98d_&OY&.6Y7#!23e)VMff-BfF1jO$HLmgh2n?N: %Vkqjh1[_fTigpsKc^V2cB_[uVRuc3K_@Qh*)0m8D$%7noGD(A'fb+cG4BT+tkP6ee'o-:of.:fMEng-3Q.8U"UUi!SO+!XL#('l; %\f\7;&(^K1)BLS)D]7Sq[JE0_n/JN_Q0%s8RtE>J9p>JEG)nAhs7>XTJ%%W?mI%$!^]3?K:]L6SBaq(0edP>b9UEh6cWFGn,P2gYQd!rXf31V8a5=T[`:(d9AF0@L7BhLFY\=SF4Ub7_TA*@+t^\u-l&&7u5rkmpn+23$lVSh]u&(<2G %KtfVDbHKcOrd=V-6Ugb@s8;HP5ORBW\_[4q[^rTn^4bihh*pt.OSM+`N*gOo#.MI^qj0P+mJgC?rSJ@ba.;,dI.qRZCYjK!n:17V %rMT=006c(-9tf#IgDr2^)4lJm&(ojEC"lQYG=>GkoC\/*VHLoLc;NNq0Eit">\s7>]pCk(ZB<$i:2+0MBRN=T(15J %["=d7CF>B"aqH3rBDi1SnM1-hB]]Yg?Y70^Dg[FGlTJ=#;]8*,SI %(%<#Bg>O!kcQ?a'7j&>M_%":^I%\LN38B7)qP:d:NDCT60pAJ]*_5"fHX;Z@9rC,')U!rk,2CA4X=gZunDOg8o4kji3Xd5N[sII= %!h1T1aK#l&s,i(?j'fl`&pPc3Xq^\IFH^+#W.bO8Go#>Q\I`apTUlEO?&dN5@PXW7_^'Ei:l`5;YO!H4D=Q&Tfa];8,QL)>A>,?c\tk$ %!s#a$!`_KYD'%sm$?QSkl>QH8?Q46NJ$?&cQ*,pWm9Be)UQ1,!]7R@`?-s+ijOXrrLVSr\,O@U+X5`FeO>Ogn,(EA$ZW0T>TanJ* %8!Tin2SjLg8=u)V2X;o[>h)@D1VQj2Pt?g5kpc\u`++l+W!d7l1&d2-q6^KY_=9m0SD?\P.>r\K_81(QdfdCe'ZB,:M]S%$4$+Z. %'H"ci8=tNt;NW@#So3gTk2\,CT[rkm[NT)6=C02eSdj)VR)f%GT\.VCBM-DfP2r'ss%Ib?QBtEi.J8,=582n1>pbIHj %aS=.kS1=YBGE:./bOf).0jDa$!@krjkWIIZ!*8$)%/kojM"Q1QrXdAd#C*#em\TeQF"ItVH3dSH3n1:,0;Cb\5ig=AS35iZ"a'?o$*Ddp)KZ^9uHkm`ohr?:0c7"EW0iLTRcB^WS>,BcZjQjIB=`SLe2G%#AQL3+1NS %0h5E8H:OSo2pciH%FGJ)lVgqI7%T?JhUPB2[6nqp\oOh+nr"L2JqUH)fJ(7(jGa4oGJl7-n"ZB+W+;:dBTm>5JoeqUmq?b+i*SLf-c"]hH"q+?1Ck-p#reRGo]2p %n=3X'9&J`f"`dps0M;5:ZB'@CX"4o2TSdDZiWX%r;\k;`m*%cu]JnR$FR6oK_FsR18N;Ou+S(j1rf^/Z]=Ah'c,KS*0%C %Z4mX#'a(-N@&9%;ik:>U..((o#+,P]o@-'em$UYj\;g+&'pZKcV::*3hDd$Q5Tsf\n%b:kGPXK1CNZ@Kd3%;QS)ETkW*AB %(o[pY7Ct\t#\/Of]K(KZ+6t+^Z.^R(SX1U=M)OC_u&`9p:0K[X;UeMRg;ssLDH[_$s[1%\t^n(0rBA81/4abRD7V"_u %e&G13/A=Wgr`%Y!5CEgR(8jc %b>$l&CqoJ3IYV*s496cPJqH4ART;]NXa:Dn:ce9G'Q?Z3FGPgod\4BFhqW?jqsM8p`.QFgZr[bod1;rrKP8\p\(^@.LC`E0b$2!^ %eLj%l]o1U8nEEgHS)o439gSZq%[1gRo'LJ)rEKaU==nj[f1(%d*FBTX0Ds6Hmu30=)($Q!\D3504c\tN4t633*bEd[ih^LhZ.4DZ %T)!g+n7hjQbl-:.H.tH1R(bo]f7V6s>?&%@*(CLODX_fCrFjp[]'jMIS4\lloqtT(n)&VCqk91Hp,i,H8`26to^+sUp[g-2K*.>J %]0#k3\$eu20a_`2TWO,UY[s`0?L`.oF4^9G]A]<)Xl5g':1Tr]U[.o1USn+X\lYr*o7u;j%Y+Aq1ctt.If6PNZMWqYVq^s\6S)@?5lNZQQeX2U%8l>7Ou@(tL^;Y&r)WiDU[8g,s-5HQbMf)@h=!T1 %+;r>uEaWm[6/4p;,Y?IQP1ufKs2_+l*L#^q^_p\MKD4D)h7E?Dd0-N6@#kBZ[DD&E"KN>tT.KLoB:LX52^DN]ho(&r4E=?1-"/r7!DGI'$mD7(VF^##A*t-9I"KGYk$uFVHn#Tm)]W]JdA$K$"$VFWtdb3Qe0[^/rMqDQsgfn.>=BNSJ&Mp[sC/Z:tAbU\7eR>IT]nIjE"B.A=pqhc'a$!eUKWo\gVE\"8m?B/&ZktUWY(FkBa %<7bLpUYPTNV(hoJ(W]ua8cW+&+)]c!Jc7D*a\Tj8H>^o`Y.34N<$E)WT)^hL %a8)XZQg`g[IfJrUgJ<=QrNq0*lp/.b*9&?Xs%sN%])K;`?X0,9HhZX9C-QX1ktcJP>9!S!p&B!t5sS-Z[JYIl/UkSkO73ll?pnrh %1pT0QW8Q2taCjA*h7l]XBJIr`eoB(g>*u)0[FlWi'ndK1*!$GOngsp3;KeO:nF!,"$jH[PPK*4%>$d`&9iTb5RR7>rHOPUh,Tqsh %#Rn7Y+>i,Do7ULrG&oEtN"qgj*[-W"T&7E((R^`L9P*tYTBuR=T-=.;m.P)iJ".c?f/`=<:a2\k@.X*uKn()[p&M8q#[>*gob0\b %":QVF"BK^4Ak0X/Z[ksb393?52gf1d:tr@598VG^hH4iZ"N&K",s]7D#'H>N_0%fd"O)'SXf7=L/Q %phUZK@PRF:4S6eBn[E^),I,Is]W&34R,I(+UTAn&5SK<5''n:G:C#k3\^]L!faudQIQ#P3@#$_D!;,os#kTO:&[Vn$JY7(a6R#o, %%ig[^=[h*s%V@d7&=#cNTH]Vu%qQW4gK'GaLKJ.+@1,J"(2W5$r6>'A-n8;;PnatAX\7u!3NLZhOtf'FFi#q/ZjC&.;!La-81ParJO93d];26MD6,QS_EZLdU4PN-W3DnIHII/(g`YcXq,lci&!Rb)39$am,pC-c&D]lVA %jg726s#M0&T?WYV)At5d.X'CV$p4Q8ND1r5qrt9Q,i\lQ)cq[aroOJ'TsC>=%s2i)mLiE_2k$fo(`q1h6mSWG)TqR(%j_aUBc4"O==7o6A-DE %b?RU9>YPrA;l0%+NVmHCVLN9"P6XeuVkFU0D?Z:gArEB]Qt8:YnsGRdahrF.c,r1a$V-s?60L`=mEJfQ0tJ-<7&"\N5mgBJ?!k&V %]dO#ER("-d&Xj(`&HcKoX075r^M!)j6[Um$0/4A7O=KH:,mB^Z"e%,$LMX;7#s9=%'u91XCmKFA5h@R"\pD^QXun"3) %nMaf%Zb%It66*4"&/%eD[g$fuqDZ?V\uuJKXVL&I,310eIubTq0OsK],-&jZU-Sglh[P4=X %fPUbsO-L\1_g8a2P]LFs.-4j)i2%,MN>9]d7i@\<6JK/92UI,EZrK-Tq[Hlp_Zh"6"?o3m!EkKh"19PR6!p.:r69/W)3M'gaTTEZ %iB=]F)>AmtR-:i^[#MjR?AKe7(_+8??%VRQ1>Y9C#ePN*3*f4N9b[X`h$Ps0<4ViW<>jP.:'FATMYQ$^36e6X(6[LaMs,F*f-r3I %ZKP,;E+$0ET#Xh[Kqltr31XjuL?47_H>m8Gh<')>PljAlAs9WPFF,Z)7CMs1B5c&@JZ7Flp!)UOUKhRG+fmWGrc+FhG?(J/_qijuY6VDi8_1O%RIa7KA>.Kg&bM6a5mekP?pH0I#]I-or03Z:\-)fjcqHc.HV1AohB3:o %i4d=n9+aV_Z(&!Q"Sfr=8.g(%#+Zb4H#-#=*-uE1qq&$l8dclPNb9o*9oiU)*//GDpCCn`U3;6BYX!(?P#._6T3=V9;]CXK%G=8Y %^7-*=HEZ6fR>(@._:bi021n/IX9`:X8Y_j>5bX*(G:XVq_(Pu0UJ;Jd3"#pSb>K'jeF78DaL*-M %hE-*L,dmE_j0)?^TFQ@gT:*9m>M'[G7NN,j&HNQO;P%!qp/k-o!Y*NQfs8cF2P^[RZN?\U/E5bh]F#T9L.KfI,p$KG5`eCKe/'ekO\1KMb.-8` %@!)WbF<<6)Uf=F.';R(%jCeN*7%c-EHaeupGBH+[IfO'f(q%<2U?Zbd<%fok&[_\f+LT#7$8.b%":\;'XX+:f@4Er=1-,rElE(Cq %%9Z&.\sM('j,DiI'l0$-,K^Mj^L]4"ifWHLp*BRg',4eF9]_%7XToCF#Lp[#[8Z-\?JBRg4;1k`)e$8 %]OX9/8.A+2=J@<*P`mO@^;A_,'H$>iK@t[]97-ms)Ff%#7E7=18rp.j=;B#``)dKXSBE.o!j7rqgVgjNgM".;=%Hf/IYk7iqTjhs %iG7BGp#m;&3'1/o[=YG!j@46n5SM70da0jZZrC5'CV[Ej"hL^sd:"OVE\T(\/S>s#8VaEjFmFf.`RY#-F\5;C%=jIRd1m=C?,;]q %Lopd.[qM)k$BFLEG+K1LF8\iHef),O7Y`)m5I0G_lO5irk"M_5j@D]Qj?$O;nJ`;LH"$eF1!blVImpQb=OAP:(e4-Tp'-)"s=ffC[] %O<)Vu=Z>Ihb#Tq]PTFDPhbRM1Lq++79M:VPfFG/G"3SVdbShME69#+$'J<3?69#YS!uoBP:-9Ro(1c;VgEk!3:[(aZb!pU;B)JA5 %A^Mt[1qoZ"*E\1FN$o)2"-?nYfd_Ecr`l0K#UPK/1Wn;EK(Qq]A\eT_S6;S&JAaBp)5A`j)rqrTU>WY#bZpiXS[4F"(X]!/Gj1U8 %23a*k_B*8=crjf!*Qi!<1)_i)6:H_F7uW-sQ)-EuXUCX-=cF?>f"j,,lnC?'dsZeQ&R8r/HrQ=6XKo0U6rrf%_p2L0CBT3![Ni)0^!=VW,&7A- %SNt$$$LUPJ9."COeC#6V/6Hn9Rib9][Y3`q %,kWk'@Wcut09k_;TLQ;!A0U=&=pBP.(WG#9i%/d08o%[>[t2fDEEc4f1s_#0gbOs-Y#7.I*H$CGGWFI1'Z&I5A;uZ`'8830@9!H= %KJ,*i%3h@ZM$Z6RJA;^*6Snr70b=Fo-@H:6ksKbSb^??6Dj[1?s(!7`PCQ&i+;;/99!;nV5l/XG3rnT*YSTM-;T-#0HkS&eKN]'u %NO(]40#Pcl6TBajJ4RcRFe;[@p4>iQbhP"-UIeuO;ih4>?J^_^S%Bl2$5i8FRF)XZnU1OUShQqqP.2@)='O>)&gNlS``7f?Y$;q/AR"&uCG:k5b(AH>>[If;#p<*O@$Rm%q`Xi;0VfeG %2L>c99V;TPX`bP"VDk9`];cD$!K49d`_inZ[b49o(0JrnkmnZ=CbYrX$)m=G;&A$Z9S)O"2oHZ`q_L/6:KU\LsgOR%aW7)=1"VJXB>(D'PJ.:oD.pHj"NqeL2ihjU*X[KUN_L]c&SdCQLB.r!MG3cRN!= %-WgP!8']je;0U$V0TeK;e??kf87f=n#$Xug9G'2e!'(g>VHbV8JYA+HKFt9mLQR*(bY1_sj@5QeJq9:0[h=l4a\a3q9+hL,Q#*!X %Idd&PfU7JG?o*K#L2LR:;J(rEYiHX1e^S!F)O/7`QEdbu#sP=eP)@2&\/BEQJ-"9!VNCZ[YC_uL,8R?"=qB7>!@OU4TO#B?j/Lh1aYtLacJb*>>*kOO0?1t!^'MP.X`DGhl+@q]Ug;M1fB %Vn8)AU86&e)p55I\=elOpS2\Yc7=e-?V;BrK)l\RS&QX8"5,8A.Sj%R.,b;&/S:?3o+8U>-g"Ga'a*VPr@\"\@LRoH2&mBJ8WeJ>q$#hn"93W,#ma'WGNec;2@H.D/-cjHR&MOU9_\Xkq.q37 %<5W8uUd%a]9Xt(:&'e-b"!IeHcN4!2hKn`*$VrAT"dX$'5[O*OqNh2aUJ.[>cBL"'q7#[k+$u<\`+9W1BYI%2[&`ilMJ-]2H5B-lO[m'hJ$0:^9Kfmf&._ %-@D)'oa=Ar"KOV;rLTrM9%-bXdEA?!RfH!1"06t]2NVb[VV$686(ho2k05=Q6@_Mci4PJ$.7W#^!-LZ42I/Re1\"n7SkH%WId0=ZSTtL.^JB7=lN5BHF'`7,-3>nj8M^sF %2`0enG"bOE/YQNR=@FtO+a>/mq/^3R_8;!H&;CXmFZ4?r#XQPmibU.br+&A'hh6sb9qJ?6>4VIf):kJV8IhmZN-M.mr,5D!M!SdH %B:S29?s"[!(NF3KZWf,4(4mhY*F29W,tprb<&rN^,l`W+W]c2]kT;0N7r3AKH_LsH4D$'/i'0Wni?0;jMPO*?$ZtSS*5J.5&+@M5 %JDidTn_JBGGLM$]8Fh02W0O[RnS8]_SfiCA8!0W8u/trr@``M%I?,,/bH#b5k3X$[( %jYf5PDt-iC18Aj4YHj:$OLl_Z]/BT4^0g<7UnSfoaotOt*g8EN:0_G:$VX3(U5UQ?5V;ctQQJ&3kLR]j]tW:"*8=ttYKsmTRo6R+ %-19Wp)(5`IR.B;[^11ZG2dR]P+N=1pe`39;q`sENRCXBi`KDK%K-;0LW\&Rp#mXLHSH$i/?g_tFe1][Kf)'Gji;a`lF`+Y.GdI=6 %Rr+\-g"oplRtP>"=K(6]/@;bn!CPTe3Ok&+B!@dZIi&aZApXl&r_-OCA")/LE&31lh&Se`%@#Q4oqa@A%tJq3.*Ts>&JcE8/o\Zq %bGP>C=g0#qR^5@Xb,aaT0%_m8eAX_\6Ng(XCXa2]Xc-rl_?dJcPY7+=qAArrq0<`Zu7'RcXUDSSS6>usD<8nFJ:o`RcQ\]T#E7'C= %HJIC7/e6_gO.hn,@nWbS&k:Po%r&1p3&I6hp'[ %p$mo+IjHKg.t+a`!7+8%;\Bt?9d5^>'V&@(I3!tQramFPBh]edX=:?Ho-F+!giZm\T]CM^6@H>O($(-s.]ia>^/D<^7:ocBgp1Lk %*h//O+)neb&pjs4XpcK"%J!_)^@3.Vh@/91A;>dULb9TS$l\E"mH4GslhZ'KCTW!]s#1L"od_^?`pK9k5JaI;tGH %:YlE0`PMkBZWAh-l4&)q*G=IJc7X8OXsa@+,j8)6;U0:0et)V)Of^F9muBX8!>7/>#&XaA@$^LBa#M?jLoi_Y^p/.#9)_`oFsOro %BOg%."8`[9PZhr+PW@[cTY>9?ZsE$`bCbd>>Qj9k67k5P!F;u$$'qk,#9_TCO:KX/NaU/f"(3hrk=Jek_[%+SDH_W@d?ECT9VAZW %/\d."k#Gf>Cbm)^M0@G#;d=a;8#u!>U[V[Pak;"_i %H*gui$WY;^i0tIK9+iGqo$g8M%QKJ\.>?e)c;g1-3:p:h&A4@+O_kKFafJhM9D?(a,A@PX/k^2NBVA'SW5'haM9I::?.igG[`-*( %P4Kp3G/.tq"'!8I7COZOWdPR@osg=C0EHmFTuOX[XO6.*2IK`*GX'.t%dO2:H;_SWDJ:99f]Fq5!Lp8h7#u]1J(f!>B$P'1m3FkfM/K1\=&Aq^rn\VWt?R9=#3I7Usc@d.O5K7 %+"odP)W0Ecc2fIa(+Zms.Gi#((fnhV*_7;:Ua[%$lIpQLPoE*SJ3EM&@#j(.(ld^I!!>mZJ3Eep^l?lqLRfaJ`Y@Bb6ta]+JHEj8 %C!kF$*)O\@0jC7sX68Es5%r`cgTr@F%'Yb;[9^i2RfsXDoZTWEd*I\=LpB1t,4__gN1*1uP-sk3U!a/?X]Uiu$$;[P4+MB>,OH6; %O!DD'nHqC:>#\*\s)&_)eSg3danZp#"XoAZApph5#4q^J-qO1PIqD %<6*,7P'0)srr':8Gt6u6roT-m_.C2G5`\f[P@k7R!E5deRk76'S&8^D0K6e4I^1ZbMdd@RD"YS]\jeC"=6m1e(Vh5KfsL"\JU6lX %k]Gc"0e^r=l@Ic5!>oj&qUo%;BbTKq8/H-T(!#C3hQ$![9V8%%g-u6"lcMg3!2^R*2(,E@f96us:aK>GCC;;gJhKg%H=07uHE\#K %b_qq582=TVSF#!-f++g6(pda4.p>M&Hkl4UOh@GMmW2^AD(FgRiSGWS`!pj:P4W"sTIdVYNGFM=AT3H+hN#71;Ci&5AK3tU@7N"m %@n_2*PjtVu)da=KbYfLC;ilW=[*94XhBW=_F82#bJZ4_\m:tBD+j%Fa1'1J$l+nh_@BV4aX7V&iTFgd&d\=)j4%`]T7SrW7Z6Y,q %[n5pE>pfZ5?muj$)4F!IBGNN-8248\@#E;k\j;/>p8eV5"0?T?1Je6gbQjuOUF'`V,@)VdOa;1mQ-9>(b`b)9&a[WgOc3Lt?ZUJQ`=3=m@t[>=/ud*Pr<%L>3.j %D$9-h&OWu8N?Hk1DD)-6*oYb;1ECSd,#E#_Zji%&b;G3i[]ufP?4/\$5hR.`c>sli"S=H!EN=a#JUDW,8)tFn,)Fl%QS;Ff3K0V. %f`d`k1>^Z*%)Bkm.N!arKpigXAsL:LV/32f2%J5*Kc+?\*-A5tPK$b\_Yf*pRH_M=^+IrDkSQ6Q %'(`EKjG+`u!l;*J=`RW[]")bVXblKK=G.K:QVXFoF,%XIJV^aBY_2.-3(7XVAZQ'^Bi1?0S9sne?:Dq&Qk\=jiZJ\1:BDZdOZnY- %%Ou#_,dS0A[j^KHX+1!N4bFAQROKeU]Q.EGVnol*9/U2)E@h_fbDoH-Rta=i9UqIbM<$o1WI#-Z=@>&9EIp;!lrSpV6:"&K=`atC %^Z*7m8sJXPfI3XtBS7ni9#V#e8n+M'^,M;@W$Wc=Jt9H<(3,5QB-lJm12>]6g.FV*]o6d: %,c77q9:B?gd[R!GgkN1(9#-@(fXhk/$5L16Gec@gI[LkU`#,9O)'m^Ca5E%=lX+H,a%d.".Tl3Q*ddFPPKj50J4-n'&p4W+C,Prd %Mkq.=oaW,uOt=6WVp0&ZX(EuB->&8H#mZSI>RKh**F1,'Q?9!s/4EqO>,&TrO<)QURXnH?Hp/9R4TOU_DC@@F(0$RjpMH.=*fO&sT#q![&J\''8`pM1^Ubc4#<9#*;+u*<]AR*M7ld*JD:&LoJ70L^K>#*ht5:T5+VnZ*6Ng %&KLG*(A8oo5okLo+o[e3-hR2R0_GMOg\ZSGZ(HL*H]=+]hZ";X"Baa0H!6(m.Qq5\`Ma@qnZP=D216/;pk#6=SELiM'X=bC81<)R1_$QG+%7=dd[c:NWbJdoBQdUun-:raY-\='1H6_okgXWZAI_(&8u %GTDRfRruCm%n[M*^Y&eohH'@46F1$j+Jl9;^2EQTu5ciXkn`S8d)"#b3E07lh-rZT>"U6W@HVS>qT:+;f`b3t?g,cBAkNJJ]LA %iL^d(CC%P4?+'4nm7('2Ds34iG4e3)>o.saT]@hAu!9"0IBi6eTNj:G-iU%kj&&LMZV$F:!tJX;Y1`Zf5p %F)2XD_IDNT^`hY&;_OFlAe8oHDik,@)MAsi]WSYS/+\XEHff0*7 %Po2Lj@A79jFrURA:cL7O#cN!1glar?(#:nT@1AcI2_^%K"bA)l^`Xb#fu0";i4OpN=82l.jI;C#quOTdfc>RXY\4XHKKnD"`*!dr %>lq&AcPhF$ZkC2N(^-(X0Odpp$j*On/rpDBN."/4PV/folSj?a-WLc?4DG-4VXScLb%Id4WLB9u'eF!dS2!^-O>EI!YOHopk %S_S&UID]\!D+BNnMNOU6q8I%:<*uth@bHKV(H:)3hjcGN:k&]YM2Pd^#gjM5r92CKO/GbW#R6r>0#q?J[WhQ$K4+%V3>r'2m/Ws= %ZdcU.P02=+Xe+R0AQ&pd,EW-FW1^p@;&Lc#7eC-:e>_35la-\trPTmJ4>gM$usd %B;6\2^%O]or%7Eo!\ARZ]M*La0X.ugL6(QP[a4>e>trSUm29E8Zpm3S4]W&#=XMmFb-mto %G!Q1HnY+eVRH==S:/e%$?@-'ar&+7e!H*_L?IitE2R$W%[#aG*[a4>e>trSUm29,omX;@m;`d3,p(Ssg,?iuf^,ueUOt"6k.L3c[ %r6;7^UTp_F(Dolh=j:''O@1#ghi-LmKikC78sgV#cZK0KEn&k%p<&RC.#7pNg)UBgKX0fu]*_0+<7j.T2o^N-m %<+&l)5-,`G4?6_(;'FK`WNn2jQI<1#;?-c1QHO1U5b(.Y_-D"FZhuR[e:Zj#(0R@'B3N0%J7:b` %!NeE^WT[7SFp?+Y)]1*>dGn[V\)\XiQ-.d%^j!Gp@d!"8[#\@+E9P0u_epF?G6]gP73u+4!'.1n%\hp+Q'B/l1ShM-isGt59T\7g %/S>.N!V8Q:@tB2Gndk$'P.$5oi$@/V2^jZlR3FRg%GBnUBa)cQpOcaDjr1)V,Gn&qU:C,tBb! %YUAJ]//]d^+Nat3&ffOiF2K3d'7Et`:I&KT:81nt1,$pDqHikr94+.'ri/YC[uFo&VG6Nf.HqCs"Z>.q&q;>3n"aVF*m!K?THBML %J`G$%lH%>o'W=K45%0>s&<5_Fc1QI:R;-&+[F>m9fnY2i"A%`YW+L;3(4`JCZsGK!ck_'FqBMtl,`+Z<>43a$OCO^:]8/kj.INGN %L9RU(H8__.P/RqZ_:("O0YPV+H$jk%*"SObW3R_.Wd31$']hqp.W]XYFs)i\10Re]!@->jA6oBI#=EPbU__C>M.1dE<,ArST_Prt %PjOoorZmI`MMm_BUk9a'oJnl&r`UB+QC=T@9*"O$;g\Y>fXTp %CSbi$oZCOu_dWo2(i<[8*6\2P*VQtqF&[!DVS@]Tf^lsKZ%lTkA18*]l@$L8]#6iJR,t)&P;mRP8tH$T(]2]J3RNKG23s:gQKhAoTA@4!_6j&cp %(u1DRGFW!LJcnG2QRtD]&6MEp]9+fLfr\umWu,"#8-3i[REPE*6(9+-jg&-VhIgUlbeRUcW0R2YNA&@a\OE5^MGFrCTopNZg&-er9^L:(nP2W4KV6# %a`[`tD'uRBG(R)<-eJFFF@bY^SR;nEYPd3LI!l`M+#GcUNdX/J^G%I:e#tU9_lt8$7HX&i!KLr/Ad/8`+/FPOM(NV2_Xq*'dM2i$j4HL6_T$ %3X.5'$tEMM1K:$g76-4b?TtpO)+n)>ND_&a,cX'i!bi,\Ad4"\X1\6%o[JNUb_H`0e'&NNa6uB[%P6.d"i"tIDY8G]Whk9_7E$"K %1ha7q[kb.'Bb94>da5C=7Kj&=&Ljj`',;F(Q3JBHo&2ND5%*d8(Rc+n!G.$RXdYr7>pFn2RRBMqPsfE6GZCR!JcM:6Te+rMTcm%n %8^tGAS_0AcQ=suGQq9pk2f)?6YlbIdcu6JIb%NVNd3D92cla]2Aj1.//R`0]TJJDqb.ZFN_0eN2_=2_M,L@%>2,f-X'ocD#*_pj> %c]*%j7c^leR]W9fD([lt"jkh^`; %MR&q=UlIJ!VEV&pmmOX.I)LC7U:m:h$NunQ,(TM@ZP]M'$:]J/P+i-pF%=>iCl:TgD!C&?&d!ri&]eFEN5/RT4+g\GFCNtFU@BZD %//DCUU((qL82])%MXM=#>_-d(7kdP'qumeGJqn4.nXgZ\hgfH6CDc6IPl@pgbRgnR=+bVt0TsE.&%6XM.B$XZR"Nlp$P]Rn-4;1A %oPO8/1rr-VQ4&L7k2$[eSS;*C)V@X`SJC:?V?eh?%R1Ta4'!ahX0$=+,l\=u(>c-C=Ar<_&2;`g3.7oWY3ppk'#AO\+4,PJ&T;2l %g%*SY:EWJbJV!D"/X7Yl_hF"0Z"!HK']?o.i1s<.YlM_`\H^Q/a`CXmob>bBUFJR1+/Q%GU2.a1(=`!PYKnfJ$SGd %DVI?5_#!a@R=[qr:WfR1Uc4)do823[T<,jVRnn([1nST7AQ1<<5AS_<5%&pR?h.l^8Xh&o2:R_WGKU,q$$K9`r2"Aj@4P*?\#hW( %gPW</'OWlGr7`%hR[k]0cn8U84&K=R4ifQ-lhL*e3@iO3-FWN6Y %i'YbK=X$N6C%QY-j/_p63Y>qM5H6."V=ZBAot9t3g#NpaJs?T%n_W;1`$*f=_j6=6.XCM-98!HW[IZAaL6=r97,Kgk2E%5'61)2b %<^m7?[fU%D/cg<+\cR5nHS=<#5'dtJkMD]sAeFK8#t+p1>A\"il^I76$QgkbRc[u8TOF,1]VO6/WI3'(+3WU8jL5p!@->nTdk4,, %9?GdLR_js7hFCmSWtW:WPLd0L7FrWGf>i>r4TLArRLVl]5+7/aKEbh\bg!!=a/B,,X=fmZ`Pq-\.%&NiaVnZ3ObP9o+J\5^4M98V>EDeW:QD/X-\!/"&##m1q60rSUt-?HAUA-nIF2K-dfX+2 %hg!)Q[cXL^$ZIJ.or?%jGYg6N4<4J6g^AV;fF6=;6k+BDV&L*C>X_M0@WEYrI4e^>%!7)X+r+^Npqdp+V[Ti105:Le/C)[drHG>cja %7=n^b()9:/R3l4Rs`*;E>@bXqU\=B0W$d+2DB6(&/e%`X2L.3j):/lg<$S5J`f#8A+4)'X'HeCj:W3p1t&MNj#BUUmt1h!p.*PIo84!E'sR6-s%g;'9C %)RS^!-f[\@oc)uNVNc1>Ta[F<@Z3U>O;Fs]aSThIdX/`;kCBt'IL/(FL'lm"Sf$%%&@Enu]W"=E %:c,*lRX6TK/6#WmYdtp%;gJl_#Ko_rC3ro8E+Vm'l\.KuAa8o*S!J6'd&bfp5o*@;0D'5RXYlor3p=9IBa_KdF;@S0^qHDIa=Q*N %GOj)F]+3"K)dN#a!U`ZV&9^[7irO*PkcdI]L+M^0CS">[X9I=k?\4&KShB3^g,_6&*jYD7F`'Nmg>en"R %8$LS1+"%G/k!I.]g\l-heXbkihte)[7Am^8-p^1JnQ>!;"0BYA!T\L`F7-h&$S\J4h2&(mdQqQcCco:1KTa:HI^[)k$ZR,+i7e9O %pIkDZ$1EN1/aChIl4h*1\VT<]!&?<&i<'gd5a)g,M7=nn?E`h!H!t-Ths;oZo!nn+lFWuDg*`#_qF*& %O63Eg#"rt.679ne:eK5)2+X/`/;Rr,3ahI=Si[KilnqC>4tpS3!hSTi"Zbk8la"]56qSUhYd(jsi/$eFnZ$_0&Y/r9*Cj=3 %o\`3Br&Uc](WZedl5L-^WG#VES>2"9joc)4]eiYn.g#PqJW5UC"O=Z%1C":3/F]\c3d1IN*Z1,Se<;/^4Q3u(PEob`5O],7H+6U" %!!=cCl";SSZ0HM9.$9,VWXX;F#V+riSKYrQJ5Ifn@dRY0Ac\WU=YPF:ITY$KguSOCH;^4teG\_Wb(%/d.)B7=nRHgl4WG&_0An6R %)a@7p1BK"5%"RC@7j,E`Y]A`pTBGfbERB@XSfX\pF.+QQJ:$c486`rmoseNi6\"6[8B%Ml7QGuc%cGUjbfO!(RTC'm?oUeLBW?MT %ag@-r5hZ)1Y,%t9,V)ajMZNJ94pTc_EaCYUFam>3q:Lqt,n(6B%rL:<`/HeW#QQolmW4kZn6njY1R&LYmji^uc$K;7V5Zr^qkuXc %8[XRa$f4]J"VOUc8Wn=P9RG*D#!gG^k6&M-,8_T/%LW+%j`q/pkI>qb;irs, %L61F<]a:gu9ZH=D#$[BOcqQhL2^`5&B_,f.',eZ=mpn)4oBK;,59"<@,mD=0:bp8koe\;n7aeOIG[c2[.[=GZ$A"mqKh,F!:Q2Jb %'BGs\&/;'UFtC5dpW]9R'_)]&A0(kX8XGN[6NG1I8]nJg2-e&@&^dOgUHQj=6>r#p=HiFQS]0phj@OefTeJbB?*`HD`[E30Y^dt: %Z@jYu2.WcM_jReP#j0\(!i>%U3%oRk`P$e2(t#$[:0!^MZ=7TCU_b^dAUmPNBG4'98=HsI"IRk-"X@f*e`F/E8,dF(:$O>G>*e?K %Z-5ma*6CGaIO^Y\bRR*MjMD&_Pf4bF\6"H//K06,:6kaSVH+qlbBDoEQK0$Um%\`SZ?rI3b!B:9m>OK8Lql9Xm#C52,N.:XaN!;>.8TaJcmo\5FJi`-:.(#1 %3_3nb*!A6U8\UK`idt-9B'M2Z*K3C.j`0T3V%(U78Kb(c;ULq[!Hael@"e]DK%B5;7_U;ZeIE945n/fS$c`Dr?or0?A,kV83I'"O %4Zrj90-P.3'%U!U!c':BaI1RIE=L'i"EC6_6tR%J$O)(Z3!NR9?`Po9!W[E5\&E>>b7Y1GU`j %LTo*jchtm!"i5(l5+M^R8`TI58q:'*9750SOUCU28W3tJmK=dQ&lg6NA3m2[dPEH?fkYDI(J1-dbp!e3&;#'t-4c?i\hK^HjenQ? %LYleSF-#C>FN[0dJ7b*YqTUQ+You[F6C"BSbm^ssQ>S]*"]>W$(RK?.)E,RA\`_E?>onsV;"3,.&kAWWMmP(i-tU8`_OTc0b<6I`*bPa%t`@91bB+`p7lJ(t4')EXd[Q;Po] %UrVg:/M7/1D5#IR^_hq9&JcW?#j)./)&,W6R4<0`3WO106BPXKg]7YZTWhn,p.Hbm_J#J[+IJmnCX0K\S$J')/T4BIPn15l$+pXd %!b>k4s_h*i"4NAI8dN#+FjbVq``tZVTd>Dd71EsBZ)H!Atd)`d;cnHTWH^8)n %fbOq%V7)uk!i4*X%D'$;!#p#uGLkOX_>qDc$\@+?"@nWb.c0^UBO6qDB3LP@eH@-=&roYYdEZd^,"*1Q)+DFd"1)*)U1s1(1T2"U %6DJ2h'[@jk(nDl^pm9aqZPVq:$spflUYo:;l;2GIW#)k3m9iAm(J_^lL_NkSkL,B"@a?WuRO6?N[8h3T[RP*CTdpN7FG:T>^+^MK %b*M/`;1jCL=DGiMM\XPPa:H@!2"-)8^eCXE8_Q_$,5I60TIm.4&jDimc@b$lr_0R@&dBL,,S`JjTL>)G@AMRKO`"J;Qmlh3X$VT* %)QMX$3#4C=6Dl1(Q>#MJFF:;]PC:%GY(MI=N>fhLAPZb%!I7u]nDM`?";Fr$9P-NkZ@\)MQs0f:&^KfsC!db;c%`%6B\>0sDZT[T %?HUH:b'/W,3UO,FUf!R"=?P_2:Y9b.>/ %$\1Z\D%`P4PIOAn+_`O\E&L1S);]*i-`S$%cBI"#6HjNn,`]k98OPZ<0(oIp&q0"#NA@`fOB8 %Yl?K)%'#K=JKrA=OrJ$_VdcrVA2)$r%Jo>-JcLD6=C8mmVfW=4o+A3DR][]i,hs@/dr5Opbc*3c6(UDTKu?K\nYjC9O5*5HBB(c` %qOFB-1Y9Q8eC7m,-BD^tl:B0pF>+cK$\Wb]g0rMNj;pr54F9)4)T4J0][1[7M/Y@B8j3(>9F(L")[;5jB;VGT\;SR0Me@\6\[#@>=T4='D*],RPEf(%.m^r6q["GW#Om)UrQeTstUSQ/]' %/,ba.i;d'^n`Y6Xb=1DnV+-IW2&jXi*ScjZlH<4)SrrB]GFt>0gu#]%[+?:$?JEkV((T;=FkWHC`uM?^-I^m %80[?JY_!5(KkJ1inI?^fOltDI&PLjt:4UU'%3mQ9NgBi.1=d?%Ubmr-b-Qst %\NBX8[j-p>(b;hm&\j3$X]^#nJ?L9-%^OXM+g#qeXNE!`C')EAN9nG+04X<'Cuj0JP<'uo]Y;fCT3DVX`10"Wkq":E.s*]b&XJV( %$aXq:WtQm"+Tr,5:":ehF:hKU+PHgg:ukj>d+LQHhK0d8!s"fZrLlU##8kSko!FHE+9Rrl:MT43JJK,#rCu*RWFuNQha`-e6gI#8 %R8,h_`JRt86M("A0D**D=ZVYnQ\lPu5Nk3BpL)BpkKmoT'2msrF9t`j'sE0:@0fn+@-8XB)'LZ>\g#]Ho&_o-;NB@"\7^]YCB?D( %@e2p!D9Z[1CeN'RY+kC-MnS<:lg/u7:6W*?[V!'\l'D:j+Ki!L$;(@H(*Lu//DeU^6).gm<+ff?oY>MGdSp?:;2-Ps@Nc*G7-Sl: %SP$BTJ#@_@Znm!SS)hKN&9Iot50-LEi\6.OQAlRsLi\^h=N:EC,OU],](/u(d-&)54.1,s'C,LqT/+hU;6UXH9@i9=PB@4S&M,== %8P"Nq/HE9dX62;)d26MRDB.(:^l[Y01Ng(,s8VH!l]N:/EAdRqG_c5f-cctS&[nb2OXn %06)I;2,ECHAQ#9ZJeWh_LrlX!Y$5O=AZf8eY9RMNC5Q%M.o'O/YhL[XT@Za4?E-n=Pk\ms:T?aOKE2Ao9\nA32*Di$oR`\7l %)nQ*&b%P^G9#EB<`"?sR\c.C;ADDb3^@>=:R-fE@Vd=[Ne)Qjc9M#e&>?'r"^:S3Y;0nRL^/Kd)UpYM.aCIZi>1Kmn40Q]t&\OrN %pocY4Z0P\e0=Yk_2GkYlHBkai-U"%ufVonG98-$19Lg/5!@G,3IM&s4'oM#&,l@uI%XqFLnlm'W@IeoFbp=!Nf41rWe(q]+&sJ6bu])S(2(r(q^S_VfV?pF5or6dU/qJ.h3: %L1u:d_GYqf>W>$L=RuelC\rYlXKt`m2C/6sFO_EOb#F&!L.Fk14qHBTKnC;lJj'&&]^%fn9d*8/ZC3nm*02();K8IB0DboZO($9G %MCfto2;rXhQ"IWQR>Z[=AWa>9q2>rs;d:/olEO/SM'8=b3Zq=Vi^OAUb#dl9:j2u(kl_bVOim9M$Rud&.'dU?:=]0i-;@Z;M@U)V %ME:G"Jpf3@=/0pJu92Yi'gW\9U2Va/k-+@%7lj$#+M:f79S#Ybn2Um9.p<1dY,a1+DhW+&l2TGb2t5BR>Q>)r.J(Ok!$Y'P_aJ8^C3kf%E4D6nf?fA %ba,/#%94[S5bg`Z6mZ#6"gpfS5c&oG9pSd,EAh[a!K?,n??Vbt3seUI9Z=LO-h6ss>]e5&S5Uj`?VTeTUuhY#OS*--FF=s%YU]qm %b)M=4%Fe(8WH#=+PDs[!I1rh!a`%IRc@SWh0k(q"&2F\QB1g,pQY\k;M/pZ>Th*_78tN7Cjh^(RA-o\$'U(ph,;N0IgP!2@uZ!!oq%1[rQQ\7Wu+09'l-KMmjF9nYPaUB,f-H&_`\@ZA1pfr%i_/*E&@WgrGr;=#fnP'kGJklh_sZN.O=I/m1\ %gB=5,m[<76TRpnqZt+](3PXbj<&Mm#'Wdp]>9P"d15N?N0eh]!iD1'g!3p+gAnR`-"b0^;lO.us\)n3'kop;%5!FD4NRkM-5g*\>kH!)QTV?,^[?,)X %:dP+RfpO2(4tL<&/?gtYaHhk;m^f#B,A/KD2?o'&,+E"R8E.XFn46B.Xa=u`d,KW;=5ND^'MUBF@JCsT[-k`W:*=('ShO8;i'g[b %h%&H&\>BW*&/jh@5XtGRP?9\eLP.A^P%o2dnt>%TTVp"bj_SU_TOf7K^cs/V7TCWS6u#^rD95,Y24-U:_YY4`/Ae>gjU5@Dt0%L8>3T$2Md[NSp5W8n@FbC-FYff:htl+@>#[pDT&5JBK0G!-ag64TfLV %elTIje-7QU]hRmI/.;O,l[b6UmWaH\7fj':/!uD"`p:[=Ye\9\S-VZL*]"9;KRC#jl*)j=?5PsNAV4qoa[t[-n$:3++l:t.3"+7= %aP3#g!8:HE]`;Y"l)-5E5[dsTI$fYS-Xkm?$a?T5BMhNS?:ICj'Po)mJYIg,q4$b+;)B!M_ONHS3@W%"\2fPaX9X_fg(/(\Z<&t^@aVV594]eKC>oH$@QjBVllG,WrcN_,b'8]HigCc0*XWZKW+#Y>P'6^jPg!':Qbdln".i*a2F\tFYNRo*1Z %C=,l"/@9Z>^9\ob[g/6"P=A9!;G+`(X;XaFTZ#lO742_Qc;I,j4fu>FLs@,li*jDa/5D@"-MR#cA>fUeWt6u#Crr\\ %Lm]!I$'u*r<_q.YdW-C`=Z4f=r,^1!l__rhe(sZPos(V%#ncJ'.V2g6kkKMl)*q4Q;i?,_-%t8)hA]1k]!Sc&HnUeSl\uQ?Z^p#.R5;=+eDIf12mQJ]8YG^TVQCZXZ5o<>J %K32+*?mqlX<"sF`=7^MHgu6rF8pZR^2EZ#)jCXP9oB*7="g9K7Kipl2/K,RUn'5`86VQ\pG1j6V\pa0hLB[I5SG/5RNgM&4N(GVp %Hrh),/`$%a#HgdEc:l+DFKJ&aWX-BcaBoNo0E@:J":1o5%Zjlk@3cPS?9H1efGR0qG*Yo0@XKk$o$^]Tob\BoCMD7*5,rqOg%GL5 %+q4=X:"Z!"hQ/n_ML-jm_J>;Sk'@RpUnfjQ\HKJ?bR(K0[#C(a:@;W++M40J\L=K)imWN/gt_]1K"4T,g?_'@L;%ApEL(D)[9iiP %(+pVi&V]#FMWm&c#jV..@<@6Z:cfX%W+a9WPECEPU.*q:na91\5Zl^U.)9d1phoFsfei!dE50]p*_dALXDT('F?3W"SsteJAdD6M %1G=*GO\Y&-"cE=^XV[b(p^e'Ydf;eu6;t1o*uL5pe6#4BcmK2h.BKu2?uV&i0dt,*#2o&X\kP]A4;]Tg3<[gZTa#W+l0LB-`LD+S@X(eNp]XY'kQ/tlCt&\BPa-io\:SiUTI;q;V'"3WD(\7n5Bn6IJ79i= %HC+FO>=YS3B`Nb!X!+GgW`*K%8(n@O<+B!Z/8@(`$2X)-ra]sS0Sn?E3X`U9jgaN$_I6NOI"7gC4>K2WIX[V>8Ca7( %'CEugg6W55k7IkjV9*q%G-DI?![Yh+AF)3<""2Xf_?[+)[q`4UA&:ir+EO)ikr,acO9'g5*Ore]m\Mi")3m-Q`3B@G&LBrE^o7?$ %K(G5Sj?5V>l54;]gH2p'?5A'9b&H!8`),%GH.IS+;kQ\1] %MG[@\PhN7"Ouq5<8f'<#cLP!0\[f-HW<5+aBX^J#aiJ5KT]R.7\4_V.Ns;^41*"ZS6i5V]&h1=pMKnKW(MCUU*d %YSGAD#V'5hUl;cOl5)EQQibD)%)>C3_NK1kWn]=)om%WE %HY#qQY1DUgRC#S?ed7)_+)2RkN>A*L75QM0C`44p`$>if>1rM.a76R_CC#SVMNHcDG/t>[?kl0;OM*l%D?O`i-]KZ+J]LA27>k%i %=plm=g4S<3%3bTWrmMoj&9]aV%)!(L!l;A$$'(IIU9l=8l(Sf\*#fV=1#VT*kS2dhAHM?--]j6@b-=_Lr,%_4Q9%h[iPUPn4>`sA %)&HW0H:fmI!jcN>QiYAS+G!!L`&%q@lu#'%XZVYPsF+4t10"oSLfN4&7&\:>7,3="bO!<+72H+"_tH>&s;.n;,a]DrdqFU$=uT$Jn.%n26N;h`gJ %/EY6>s,&aF*uJtkLf<'@I!O@2,j&c+,dE-0N_Pf`XD#SKZ%41l5X]gFLDk"[S98bgj19#(1P"8:(2$s,;A[Zsg/tSX3(Q%0kDTr\ %WmtO0fT:"\R#EpR`4ThN&SpoPHVhnM0eaC@$r:uR(p!$8%MMWUC8IlQI?.d&2OK(6+:'`UTZdRB,+6^df7Cgjkp2X,bpU=9&sh4q %cE/4$aVLsMVburK(%Y"[ePs)Ol9;A6D:4/i$$?+k5ck&eUXQ!R&%_HsiGQG:m]n:Q:n&eJ9K9p;(N@ %5djLAM%8!7Li%Z7l:)JB(s/)/6"9G+[)rJ?b(/RXVlM`X0kX7DW^t*8AaLZY@_-S@0dXpEj.*&`gZAm?j"2-9`Fnl]-tYj>cTh@6kt8B+Yd&s*gm^*P/p%O',P:`Z%s$*li95ECo7j` %6W03Z\?I\\(=pc!ZPYJ8Ju_>BEaTO*U`?rjZ(gGuO7\_s<):#XOX+sf3[NP^BK>oIT:d`4t9FT,H4c`%,-4uOY$jr/,D;ThX%4_pA %BfnXo(_X#R_ma[SXbOVC6r8*_GEMY)[&dk\2(/M%/;.T2$MhT%VJ4^Y/spcI[ill7*r`T0fEKW_(EC5YCSL+V_?Y#C-`>g2C1RC7 %.H3!`3'k71@@`@m#r29>OX#Y!5oGD#LtRT5>`Zd/2%2'S7[-Rl/F^I_>8c"a"u6kW%@/Btjr95+*[$c%%qh]KR;E%(o&BS6uisZbr'dDD`:^feab48+2LnPLVSF9\#_2>M"4"IQ4YU`ftYa"%U;59jc'5[]TBbbB3RQ %RDe1#=-STF[5QeA=\>1G"O!d5+s00Y:8V4Mm$/j-(_YFb8EkHO,-@8bb)u,ZJ3F<574ZGX[P1)FVoZ"FE4m9kk'8XNu %dE95Wn/V5-r9g2;]lP@Z]R07)Z0M]Q@tPOK9Bu5+,,AB%f;O^\oUc2$`\aPZfD0U,RI/94prrj#$I/RX:C<* %cu5.NX]UD/H %f[2LrXH=ie#pDN=(*X7>TadD>I0$N[P8Xi^aOaGcSYE59?rL%>B:S&Z$Hp"MVkpdH-fCEC9G]Kr5`/FE+&>Vn]^SrsDQO.ad0R3tE1LFBWP %*c;<^k/.UM3,?nIHu%Y9QsrK'IU7H.+fmo](i8m^t\K?gG2`W:m+?^7OuK68>l %?;diqGa^6b:$QTNh4R>6>(S3EFA+I)IF+eC1hl0B[$W0lVFg$L,+:?s;3M45UqUWm@[].=1dRG<%CK>,s"#@NCNpVUNma&t?])LB+>RhlX:"j108/"EbT@md!R3o&2-;C@/XlqIQ?"Ti;)1Mq= %UaDk(.Fk4h25rN/l:mI#e"g^)Ur>0)l'aGs-Cq^p[:N%CTkdO>pBGn]Re2+]R!,=f*088%eZ[Bfpd(U&]4VA`kWtE` %Bi(4'P7HU-25rB+)S7?hVFe\(8HUfKn!Z)$-KWWfYOpQ0q*S'J58_!>)mF_Y7$f7hlT<;^$o*))a?U"YLu2;F+'7"?nlL<^LLu6+$-4G/8+`!3BYP4:CghLgCEX0!aZ"p\M+pHOa8=otq^e,W419sAgC.[*P;[*eC.5^$Z(Ajj!ZKFQ..T&1hVYRg) %M.0&B%s^sO`R8rbhrt_O0PR[/Em"Nu2BfMkS?e%11KlR)oX<'=YDLA!1^\Q+BWBd:JrC#*W3i!="M5>GcDK,+u0onpUPkuHG3MX;U3h6>8)Z+BRu[IQ5(h2C+N3(lCB?/:/;r@&uZAeql"Gk:q));!V^U=qC4MIf6[t3i+/"W %i#Q"g&qQ3N`sETKSqIi#CFST>.lfU%SuHg:Xlg>@6m?h+9F=ne>\$,,V+k5_"LX/Q>Mho^dh(WmdnPEK_[U6IXm;. %8l%/\%B6I](-:W`G\iQW##;4#(o0\2b19El,R`mg,mSit6*q:_chK"m$o%/.>2tPNBo31aVo>C-^gY5-?=,=F1$A5JpI: %,a,B_Un!+k!e$N3Wq!5JPqjOZ/[6?'8ekJt66pp5F:8%!RPPJH1H[Qfe3`.3&Mg(hf[)]0'[4Zc)(icE,d/KE801Y`+e``S"cPm3 %M'<>e2Fdj'NkW8&+MV/q6_tATK%Kt4Y`l=3kP6h5TMlNegUhT6^DUpL"q+QTdjQAl.P$T4HG6D&I4",BJc\BaCqW!E_.fAjiPpGs %&8RXsM$`9^@L9Cb0qaA]RZT=J;f<3%dn%QI"fm!P@_Jc(*b[:NjK-"U\Xj&[;G"hip;.uq0P4naA!m=tQZ$%b$F!kFpL.nu+o3o: %bq\GqNB&,'[`PP#YcSlYL`'H$@X(G3.7J23p?e-L21M8#\@fk+'g4!-UVPQkJ&!k`4qj23r*%&X'G;km[2@7l9M[)6=HUl=MBKsb %"r@GN6b9'l47rpbW,tq,V$\l_!p5ABQWjTb^)/j6#DPlXY9Fo% %*J,hX@P-*I_D!ND;S_7:[Csog2cMc6?tfP>o6j:HLDcMleHX!^d`i!^mYABT-q+bDGWI#3IZtW*h_1Z>,h*6AMt^320HW?KioLLs %a?@.P5.]j,d\]0qN/o)E_ft:&27Ik(r0G4H)SVX]`8p#R[J[g'P&esjkTLnZn59JA1bl20:3tXD1.H3Q%^06I+)6s`Q1W#GP(OAX %]M,,$PJoNdls!_9lJc@iB*O.>UHG*NkR7e-XRPH7ED#=T_@fVZ0$4/7>QaiHHg1e"Q,CQlY0ispqNtj448M8CEBFOW@-WM""sBt5 %:9C3r77WOqU=ub9@o%&\9J0:$ku#Tm[e/J`AX#K&j2da.g8-eD\76-aYnX?q(@0Jd3^IMCfSL0h,fufQK41%,h%UdW'VIen7]Z]/ %#)O:`V*t^diK,u*P8c$&<`9B#lkCopo[(fQijeAAn!^`O;%+53keuX0iZV1#(ub8')U=%$oQnr7Rg+?c#U[!V.s=R4dGW9 %#G&[@Y!".[rTG/`RE5.G8bE5+!4n?E#o'f4!Q(M#U&s+VD1$35BI;6ugW.)=fdM2oPI%=I6HIXk#Or@o-,4U`#eLUEVp);JEC()$P#N.>*_6fp7$:Fs.+^[]u_gqqZA<-TTDbAi-FM %O1sXFHg-?V3SQU!ZjJGZ'[5)7Xs+!E%-f^S*:d5BBd2%p915A(,u?tlcI'GZ'RO5*QKt(IH[n99T8#UJ+p:^E>I&W?6ldBf?DrH, %L@nTk4N>38_('u'eARG!$o75?jpL1Bd@ %;HH;X5R0l^$$'`O6S.S#Zo#EsPC);/Rh;o'Yld?"R1!nfm-9tRMSnRUU[JV+l^'a#ZPCbT"g%4>QcM(LmX,DJ+5 %d9ac9N"]YCjNt_pY]7(qEKhQnfJZBJRkua#)PD:p:-BAsju)uO-*c`fn8!t9b@\XeQC5WmW2Q@82euQp`C5^pGcW`2=':O?G%G3p %@P^/D^ru(L6]>D`cnLP4,;7&9Fo%i"M-/sm;,iGZj7L%VKNhCn2dU%9-U+!K)P<*BTFW3iiooBD^14$gBH7r+G3KA3se"Ep\0\I\kWqL>PNSkuP"(L\\*7BHn,_71I'X6h9-.>qtG%k$2/,h@mlA?<]8.Bb*kT,k(FoD'#=N3Etl %.VCdeLWL/,T!2WY%gp;sO;OuaBQZku1,s.u[=Q[1+`tD!gBfWlohY=o,&:MaI^.khWFq`!pV"hK%,>OUF:sf?)5cM %=ef+h3XO4'W]D8>W*Q-IX79BCLtKeE(`3jbV'B]kANX>=fhCC-HY*K<::7JV'c$ob/'2I)Src,n#H+hVQ(%1qNaraP..^$sqYD3> %+-qKm6@PUZ<\_Ze#Bd'N/I"k?`S]XMQou]F,GEIYO>+Ne8JR').1.elI9[-U$$,)d45>WBCRs$C,ja",34W!VAqIg"mU`jMTf#ql %4K/#([+s_t-EB1*N,l/O&=$45A<7Yc$WDk_BOgB379*UU;HD8i;@1h4ZIrags##JL$bkl)kSQJi5b) %]Y#59+?+?SlD^2)Or4SdJdS7=9JHr\YD]m/dcYg7NECQ34+,O^fep^B+JX\1SfKEm1!GcOcNY^rOI[2[K[ue[@#F4`1u5UoShSb2:r?km]9-7t`[]gRBlm_U8?,>tBG7VrKg)ZOUa#h-L;S2@,`gJ#/Pm?a??=E&"L4igW?1Ig&gC>A`sE0X63Wg"S&DlJ\tc'Xc!H*]6##/: %cXJn('#?tnk\DVa.pNN+((pclAKWDbie[QD4".e:3,!A+_&S[S/&@kk",BQ,-)0N\/R!^_(p^kARod_eNK$p/iBaQ'(9e>s]@=g! %MP6W/Fc`CaC()]E[2nqaFKke9>t=WYU=a.P#,pC1<+iIJC*+6)1>Q$8g$;4!-j_Fb>=_IEJs/keghs&;Ti+`^in9LX*=bUc2E!'8 %%B75]:9f=9=h5:sNN57(Kd>O\UtUB'2NP$GV(X&9kGEg*Ng>6JFs[3'CR"ftNd]FT-8h0Oa2t2&m0YP^8+fGe=h-O_-^g.*<]SKg %E.U0,)B]:Lkff\lMd%%EnZmd1ki>N>#Y/?7<&f %FVdr+"h.ZY7Yk%m#,PiMF"E8aW77DoPacg(=^A;q%Lnl*1j'!=qjb\=i*ZpDcIRj4?o$7e7&;l=9]?Q8^glh=l];do9B4;bZpX)Tq&?Y1%G6rDnJjQ<(W.YD?ZU04 %DM0hk(QUK&A8)#9bH=MR_7DI[^"6FB@0p+D?peI%-..a*9l\&1]'s>0==BO+dkFMUOdC>uJdZ!,EWF#VZI33f\sCXGc)SjbT<#sph$IQ@ZI0M*Zk&f%jS&,6_u!UZ_fN>m'.PX;k'(Mub&r=+YRiC;8,d+!,L&<'.JN)8o@pt+RE[XHSI"8N=)!qkD5>\MZbodZ;%i#pTFWW*9=L"n>IB@.IrS`;G!b!bG#P\2fF:$pR1=&*uHc49UBCd%-]>#Gj\4;a1Z*YtM;j],X#"t@UK %^p^\!qcH@<,EKZAXJOO4Q]/LP"1PF5X@AFj0j^]S0QG2"5sht.:tuhJ'#)Gd#F8!0;9[74u[%>B%i$GkmKm"k7:XAf;r'AKnhgImUp:FA]K6rN\78';%N*QWNt\0c/] %5s0'S5p&cLh_^@Hf3-Sc(!fN!a%UR\K,k&bWO)kb$s@Cn'cN?bE+4,KmLm@Ofe9*GSs6b3\(Ytk8Vd5J'dq(^=>$Bp2G`3a6L1Y0 %ncJpi5dH?C+_6PYWkmmjKYN:EmgTiB&6EKd@C5sBlBr5-N4JQ%Ysi=nUm_ca/Kj%EaIbga!_0RW&08ME)=St`b-SdG!r&NoCo0b( %mp4-%Q`XDf,(!R;9/nZC!nq0AiF!3dG`6eT"_,`EfSfTt_L"(q %I@\4_WlkV4[):4b*pe.GNk1SXY9K\$+!b:5Fe6>6m"X].)I^6a/SQ1L$%6UP(FI'3Tn:)bNO='O<"?`^1f\Vk>IV,8mtFau6qEK(ZI0%c@H'oi@JCRc,"tA'9]D%o1E;dQJhHabX+k>V(mh>4QeQ2$g=\I1(d!p6%<(o[-jdedF#b>uDXk %2+W)DUj?7E#3G7!M@VVS-YZRT`."G@3A-cKEfpj..b%,H'J4I/J69,I22bR2kj8.#rTGdCb9W)V/Lr0jgeGO/D!FC;&IqUaCY'h_`$2Qn@E%[#9]U`eoc-RmMh`u/#_\\$a#1!OHBR47E0A46>;2=%ZsXX^HaBcj %X2Gk(O?*m%W_bs>_jE*1.DWkjj5SGi]/ks.M3-%P%i,n]-MV=s`>_=3o?LTZAnj'V0S$=WmAc=$qip2?b`e"?[>NMD6)L5F]S_pu %b*bOa+fO)b/s8r+%Cs?.E0-2Za(`XDG%i^&WcaiqlJRE"_8rB@WI!$\fWOGmo->(p9&$0+5c1-sP09:[VeGW1b-kk2@X,ZYTs#E^ %bE`R>+t@GE%WDKp;?aCo]ds9a[Vf2#bFO`@1Ou?J)Wku.3iEn\EeMdu9YtN[6(q;s6<#Nn"h?FRb(dj'_1NA8jJ"o2ef`F0GncX0 %&=C@Lh4"TT+L6d/"=![C\Sm9h;aHUEG\0d';Ue![#&uuA&qY0N>C4mmJsf2gq)N&dTsH^SF$V@>h96\3@54GLWNRfj;p9F/2Kgb= %-_OpnL6Wo$TP*[/YWo6AV?mocUA>:?RY`22ZCn3%hb=2:1*7KgInPncbaVpCK82b?DIF7`] %k8N-akb0/+NSi:tZY6(a,rlPF@]_u$+c;23YTBN-(JSY\8(l=V*gMbd:2b_7BqMuUU0DaAWEFObA>L]cL=hH*?D7!:bV4s/WL?X: %2eqY>eI&=Ed+I%u66Le`cS8@[L/DM!+5#T/pgNKVPD"f/i)Gs'B'9[@bPLYk0Y4g2?:R`R-7j'gdl9N@C"s/UL=n?)N"?ViPOlb: %A1qc?&_Sd3bYo1k#(oK81XEqL %K*jFBNLCbRK>rKn3gm*,p#eZ_"HQF]GcbW`6a3;"HFD?;MeQ``/Dt\neE/P'" %[im&sU<3f,Wi:$5\4ogd([#IqJ>@+J?EfL!k-U.XMa7LbB1QKcc&o6gCTG%U/VPl_7q642l#O5-Kf@Co#lkKM.$Lem+U-$90XGSg %75eU7p1X\':Xa:F-/4bS#P7$_l(+QaYb_C/:n^ZP!f/h@m1d,k,&],d`2Ek`hEhtRaX,.IkO:Q=<=gb?O1s:te@ZpQTW^EW=k.$6 %Q=VXOCJc!PL=CY@I;h`nW!.Abh*FPnff%EH1@!4_7N-Q]?=.CJh_E'H,)'eIicQ!@bEa(Pp3hhE7a1jN"\OMm*6u^%&EEse@D;o@ %+oL"gSMXITPR\VQ_Gc.ro3a:A]OZ";k'G?Qgam!h/eO"fEJdOZ%/b((kT$Nu+Aa7]o,c&3&>:&D$G]Ui7pKo4)Vq.XD_@L4ce.9n %7*XVZP6ToB?Q8Fnb2^,U=,!M@#r^50P8isP#:C;O(HpQ7aSuSU(Vl_-1nF_4(3H_Lp>doa#`7t(s.!Z9W-B>U?0[In@I-;iZ60Z` %kU7QrPAlsl/I=2'A7R7pl4J[I,'l#;+&XuGlXniln8C$:)[,TD/Y25kCDd[ %/%CodV:`I^Yp,b^5!s%hD.ZW7O[s7_B&sKHW/HfA6_*H%LmC`s,t';g#ic&]^*n+nkcqDhr2Nc'<0'?O>i*-9R`43qD6qkgKk`Jr %"dRXP7PLG]7F>bq4JIEsK>RN?eikM]`5eS;n_/C%]G*1B-q %HoVZ?M`&dIBG2+\FdKbXt@FTGXXuYtO6+W\R\Q!YPVLe[h-0K5p$$j%1S+XN6C@%&,X5#r0pm %!@3r=#[u6pS:;f"[BPq@6:DTNciTtsohku8Q!qYU*NKYB.YBOBSZK;+a9DdsMo7D$[X&m]Au"#Rf!U\*cZ[62[$9t-gQhomK;T;T %ZfqMrg]@Kd0d@DP-+E>>2^#%V+'i*6ek %3Mk0E&:Tmq0"a_oOW5@2jZ4J2Ao"L9a,(u`'%^?-EWafodjlCS(X"tTWI*r>R`mg^"U9+_'aJlk)9"cGP9T!61FqYIT"V3jZP]nO %#.Yb;;UiR?U`0]7BC+<.*ZoAkfj8FuVLR'KSp"BqbHN0>4Wf&L(=+O'1=%)O_b\[@Oal30f[`8 %b`e%HKJ<:63DOfNWcb<#A3MimfX.X+#-*P;nt?1dOf=s8N80o[,TPQ1Bu!RH%Q*2q,&mU>=jk)T&k[7-Din]L=B!CfG9$@;iU1CO %m:14,&WI7ED$]hfK?](,.]&#[0ob$YXd1Oq3b&NV-l0cWX914b)nUT$`^NR[W)re48:bH9.TPHpW2!3KgL]!=Vr6qe1+h:/P1016 %4t0/Z?K._^#XR"GSiRsp)kC[>jZP&.#@OrYn@9U!Q3R?I_oX,$r\N=("mu6*8AuIM-Ja9R2c;%Zm'f[R`E/[oetJs %18]&s;_XVFeFHt*,)([V.k0Zu6FI`E%P!g7fj`I4?-N5i"r^/q!9ho^>9(:R030*lZ90fn0Wc/%UkfRBHoan/#?McT;.!ZD_JFQ3 %'oD03I\Q0P`3$8$M:eokb!4ST>S]YTfgZhUH4_tb]";2MM2#"e:YAUZO-O'TB58uR/'2l'=%M+!a'mcNacONsZfk)]"6uuZ>C$tX`*!o5aVIH47HR:C4q0J87=fmn0i[!"sE2jWq %;M*msj`\mf>bF0l63uQV_i2ja_&XL5jr&c_.1Z,Hgl:Q(%c2i,1YS\GFY]pl3:Os0(_C6Xcp"C(E)Api(k6-6(`@t#1;Oe3$kZn$ %>SZNi2=uqMj<\>WZ"sLfol.?MNV-d`[C.4$?#;Z_S$g1:D@@qsfB\DU2jdXE583mB7,/UW[dNNLMm19g8]L.$`>U];Ym<4SYrHg: %;364dmA)-:o*$V*'4u7Hfo4k@>KSf$>1(.'MrfO?4\i5ba>2H5B%mFD5,/]\k@q_m8#)0kCA_,+&=MY%/l*aW.lC^rVcpp47dS7\ %aC:4QS<*u/dqQr*$%PV3gB?L>HC;YA(W&uON3oN+g-(g0-CD8))7dna"e^=(`\`[0C-IT?)/+h5TUAVJ>I=L0LYILe#L,-,L?`OhFru`J,Ua\-YgJVMP#,OYL8^1d %E[r8c0j8ILS;\OPJu!rLjV$#Y/\[hs99[966O7t@DCEGF`.0QbTn:^lV)?4XL^#8s/;]VsRtM5W/.c=p0*ZC>=cl0;=>@dt[ %l-WC/TL3AUOQ,iT`^t*7>=n6VV$g]Zs+lunKEa9nq#(hMXL"cf,(?&W-0W3>oE3=RNH@&:2ZY(GgXq %`9Lr?$IrNR[;)dbO`VpNJJ`$TZBR=T@\ac?1]Y7JA3Rj$auu;\3uS;K9rYu@O(MLT,Fb/j:?I7%7\D\6&"US.XUguo<5);`)_VL[&l]`V`ho!?QP`&HhAgkM^D, %NJ.)3\pG"T%'QAH4E=]+<$QoB^*]SM\-p=E(Q2n%!$?Q9Z'_q-r5uhRN"ckt(adLc;#qE1m;uT#j[A9='(TujZ.ZrnD%E*q]*Z`C %eg8Qe8j\%S]\$1\5r)K"Wi*m65dfPO;kh;ApkIPInFg6`DkV]^O+\gH:k)S^sfka/:8=Ob1J%aP< %.'(I_Em=9Z9M,&cZ-h"FSZjeuk6`!C94r%g`i6$,f1)P=&HDp3NcU`A_iD&N"[J0aC48+?iS#!+_sU`RZ[Q78WUPnD]G>FM$^E1[Gnj%@4b`3$8KV %hT2N+@gjTUJlmWdV'BEWj(Ua0.uf&b2=!%q").%#5[lj5RB`-`i/+sgO@mXj5J8HkS;CihfdBaF7\4hnmS`X6#)\';2^f/E&trL&P,-UHo2,9TZLXSQlu8plk^@D8 %\kmL3'!"s=PL2Nq-uO"nE"h,\#FS+JqCk7h]98Kib=i>DY899Fc6FgZ_+d6t%ScX5S0[ikZ?iDh7]Qq`WPR#b0gK&@DN6MSE`(J0 %JgGRb@C($gjX!1F1JFO1iq\IE[g6ofiU/"c.dRR-]M;V:8SD/r?toT_(*T3qV&p$@l71hF@Nec.BCN?,'JlEdUENrf=G(DQmarl` %'Z;B2X;1/B*ekoOAdSOmg,Vcj,6=GmBZ!<6;q9Xs@Qho(2'K4F"1[64q[:#@nsJ?rb391gsN*q]V%T4Xg4]K^5;<2Y&H]s2pG %Y^nJk^hjpIQBkIUMck"%'OPQ.n<+mAV1`5q/oR>o5X#USSJi:=ZMt8g-66dF#X1R\Wu(u/HDXZtR`;)%R?sgFkZ`3";2i@+9K/FP %GthD!"05/2]i[-b`RmUuVBh[MCR]($!-1rQU0U+iPXs-Z$Cgn89&[aZq&p(`]MEKgIUX-sMD]cO_Z*MLc.]Vk)%/hN[c"l;JBd)f^!6 %Y%7^Mrm8b6G7"pSkhl5^PO^B5)o3 %b-52a.><49lV!E,[0,+cb4u'AkWmFZ'Z,7+WIuu3dO7'Uhg?1FiIbqU'VSZl62EE+#6Jja&]+IUh(R>C)hR?aE)m %2/;_@$%o5`3uE,T0:!)'M)L8f$5O#:'O%4t"aQ9ef5?BXN$9Ou(p.2N.O8!59+i]m;cfbpJ>G%97CHQ/Ag.AQE#ZZsJiX]pfs?YT %8\9R6ck[^\DjDYlm$DX2#[&]m&o+"(7D+EfM6/Vb_1bo$NRI`T-M`eTN92%G`/#0b.j2Q)+W6[s/W$/#lE?p9Y]^#W75<$/J^7&JW#6 %9>*)uj.m)c,TPIAJ`NS\W?pGQJl0J+VSZ]HaO##&tp^!bX^UN&!q#:b^oJ%jZ@%>*7Z@Iosl)&\97 %/LAP9&iH-?.VAE$8049`^@W[$d:$8\]]r.M*k*f=;$J1h4N$\D)4u^<,8N_#3dla>)EQTDf*b-q;WBR9+/@i@\:1)O<.J0ZBre$u %4$j*mgcc83b@Wm-53&ZnNs%Wkph&km7[QsNk14)UcT)A]"LkdU13qM5PYP5s8f1@`eBe7c4>0n=a1le5>CfLM2NDt_YDA%P-O[te %#*\!sXr="8/N?[K-Rcj[8/<2HMb&NSUKeaQ`U65DH[+S!)E)0XI3s+[fLG33$.X2n)UkZZ1:cGA %BIk\NP6HG\7\j]$@)42KKJ0t+e7hH>`#np18ut+YLnfZ<%#Q;>XS*QFkp(G)8F:[Ja^%l8e8FP'ep#Wk&Ra%`5"G%JmM0$?b'&D7 %==m)TM4ome!F) %TZN9F=iIDt)MhHTH9q5!!'H[%`:_@TC9p"Q8AdC]0p0tW[Kmm*IFSLE'6>(m&V>o*f.t)k-ejX)B>%sYG-4#0McXVOaQ!$`F/H&Q %"5o0WV=0sKK2U=5XA8,&lgCotg>WQt4"LCo2fR1+e)m/B*gZ/kW3U':?kSmX8]Fs[#utA)WN2mH),E4("0,dAQVK12=YA)8LT7_-F7KA8jFdpePtP%oeT$&u-@q2VNXKka*a\H&]Ikt@e613Am<&T_>LJL;dR$$2]"e;-Aq %nOLNhh^hA&NX41K2[]N)>\Jm6b091f%?a,77mBgO>3pcp=%o%+n[O<$O59AqT7g_^d%eX@i0)pJ(O08=UP=mg5;Om+">k@Bk$`D* %!SXX)OnddLaiTM+@mRPOf]*\S!s.pZs-!W);l@R#6=JGg;l$]6N%*:.`mG;r,VTKnD]F`-ks;dp&gdbL3Wfl2T"?W`kq;8O1HLe% %nt'Oo?kVa-,b!VVKSKN$"_5al#ZNPb4@P_AnCj4Q*k6QOO"T1p/d>DrdFdR?erd3E[,*&^CD@&t37;.c^@.ZYMR%@oBQp6.$:k>m %bV$PQIL,/A9E:H#HqDBmQVMi;oUALJ`=?XRY$I0!9fJI*Ps._@oAR,7[+`#23'GDA`"ia;F+l6bSMt^r$.]#.O4s#S%sS'IYD^0o %m,MI)5Q0m0fdD!ImU(?frp[/,]+O!Lqra;)$h4K%EcZ;N]mmI@+$Q-:+$Y)ELk5dUB].eI1B6i>o3_KD(B&LpGJDu4a+$gqB=%6f %5+N?h;!I"&^6`mT;`ir %[SU*ZRNE$mH?o&'AQS9)NU4VYUV>AMr?[eIYU4'n6t0#U@Sod#*-dm)jHtT(7TuOm-I!ZEK-YW>'$mRlXJ4M&X!AOhZCAj@4aQs\ %T^;9uS*6W)4,V"9o[?21,h]<*E6;0meXf9Tmdhfea;`5g3.L4bSgJNh`aX=h;l41V$8!Is3@6^A7jhjeegi]b/k[f3D^o)8B2m&K %+N8"jA/,'@:dGpNWoRJ^+DPqrk[p1g+2AUFLDE(.H'*UsfJc4T_aTk11@OWpOW@!sdZRT[U<7;S %ZI$:q_5;aYVJQD$Mgag[W=VY(aje2\4RUORnS?O>R%*cVjWTR)dEep,FC&B6N;0:Xgb^2+'%7&ha4X1Ur_QRlJFqVbG@;%Qbme>i %!?!]0n?'L%q8t2aC)sS_\;B3TBWY=OrlF-6>2^,f?9dA>YlPe5+jmJnQN_W4'WW`G1O".VjEMAE5eU/M2O9KA^Uhh9h*V43k=KC&l4RPu,oL+YQqJ %TTs(M9\-b8J_s6>><+Uj,*A;bi?T]+CppYQYpUjoOLpl/DDu85!5D.;pUjkX5kV`K%#;!4pk!':BH)s4D&p1?K*1uq\7]l\H4$4 %,'4l5P/oPVg:%o35C'$Gc7.'ihH&QE$N9)]AJW/Dntdg.4dlQFT;6#jMQk4Y9ZgtX>OXQOb:]^gi6c.;Xa0O!ZA@Q#V4!R7M=AKE %rH.ZDW*8>FU8j\_-Wh%:)#f,>!6bskGCP_![8u!&kB2l1Cj)G#RL^2iDr=6-ibLtS.cLF=n.ZE#G/1BA-PbG*],h20UBNNnUOOGuuK>0(hRmPM,s\6;Df %Bocs%cG>6i\\aD`Y5KmJ)TgiU\K]maT28TZ&0`H7QKs9'ltje7(HPCQpd#/89j %'BJWa;,GXo0HQX.8?=Z5AGK9hE#j7U(][US7U0=<#J,Vd\gaZQ0YTDI"'3ZrMaKje?*"Rs(gr:A=r]Vq:._'ukPY!p[%;@R(k3$S %=DtJMHDOcQ`agOl2@Xo3dlsCeYkX.afMhcD`%jajD^Ss[2dE!!"tBl5AsYhi]?DrN\$AAb?D>@#W">Kpr[-=,8Z"D^A)g`EcEj"Y %]O`.[%`&[`l25`[YXhEL$p[W8F@Wd7^;uk*9Z'/a5d!VFMFcSPr4>LVd+$=@BHolrl@MGm0]s:--!l%VTc^P`9&S*O,/@p+R`GQSORf@8D)Ap'&g9_ %NX_\&Z3H(=Noj:[:c-O)3J(80K1h3+`*(5,%Tg+V`duTG/F,F,``WA=7M]o+.Ss\P[K>K0C%f2"'.4)S(SO^='DY>NjtfklaDsjU %P^sl"Cal)hncKP1!kT'a01?/DS_T(=/-q2f*fGZiai)_cfFid$&Ic^sB%k:X.?(nM#VPe=IQhe$<.sqRhL]3P[e$@/.Pb]AJ:'(7 %*6N#.:`9E)8.jh^YNJJMg\4.3/,@pund %0iWS0#N=Y9-Q=Lq>Z_IWZZ-9m?NGIjcB>XDP7lYc%D#sW>F7E.$]@INBihF68U_n<9B4mUVV%Hjgpt->LbSU':r*:18IXY!FeR-j %$%-0IS1@d]al%?'QMr9AJW0fPa_3r]$=KR_ml7Ts`/8e-)f?:Zl]oBl9F$BJ,[nR+h)IcsAbXW*YZ1\(NeV;eUh97h//jIX$%(?e %",gR=(tYL%Qn]*S&B9Ql6&s0eCBOc.JjL6RXIJJ*h8a]+og8QK<)MOTr4 %K.Q9l-+-c(Mhh^TQE(:[\!'E@pEYlIC%O->JR-!?h;B:joQ1`XUBc\uXRkZQ&%)";YE"VC7Q-m;`LV%0/P:o%+*/8P,*+\9V5b(\ %((BnaC;@e]Z/is_Ipf\pcKf'O]QUuif%(ob*F4F*=6u("9qLfgr$h^l)dU@$3DFo16.usq9gqmG#s$urIfRG;=jAiM_o]o;cRHdV %I+4B.HU=[@d[-50RH"R\UC$6:8-_+%+dcGKZKk'%E]/dqZ_WJ)WG]Y?J>`bFP`_.f?A[c(B+59ka8mj+;b!ZG %3*:=;<`*B;NT/p<&m#i_Y?I:gWJkW[!NN6oiSk4X55eOpU?W\=tKU;1JWka!K'G,dg\i-/j?Eg?KcdbA\)4F`Bn" %:q`IF$cOHgB&5[Fk>T$aBhH9pVR"VjSi,2;MAL%O0>+h99XrEO+#'O3r_L8V&WA#>c4KH-enR$8S?FrR:bTfs5"LQ_!CVmqPNi^c %%3;'d0UC^O;^(\['r=Kn1o"!F/2eY=#Wa^f\BYY$Q6M&g"Wca,k%-U(#ba#m'cYaLd3X$S5f=grXINFk):>h`3QjRL\W[[,N&8(2 %44]og3+jfcA0$=O0eRD%U7+o1EW,C@XEG%X4p_Hu=nS$$pi$MIQF0?YQlDRA9ILes6$Ru`e?B&SG(NLb=JNU-c;%WG%d"Q^*Y,Ro\L*63TA2(#rgl'f_;Gg3#"%),AYlUOq.6JZ<=`O?lQD%I"nSSX-Z++3Q;Y*@lHGas1ghf[>D#3nSrQ %i-9Yf9_CdrP@:\1RWXG>T?N1(Tp!6p+N4t;Z`GQ@&O&i#=q0i^n6"e(B.oL!^9ba2;E&9Vi@1!<%Vj,:.EM@?Y> %,,B=**_1"Odj7i^9JGOqEA`TD:sWkl?l]:3bLq8B5oC2EpX5]ZlDeq%VTH\BaQV?^X'*;(%k7rJ_,3OM5A8`qgggP5B#uX+A6q(E %h(DaPJjqRO2HCsMm&%3H^k_0"4-imCle\((rqP'g0r3_ON*>$h8_P`2*8r9W9ZIq810SWa`GoNIaH2F`0Qu-/c5S!Tmb3nWQG?)2 %LI5=E]C@.uVnFmfA]ZNRYR;7cB6`8W*gicc'n7`Mn884j3/nV-1]mk#Nt<6a^BZ%lG]:9U?Rc&#f(uPuZ!1e(&)-e!]gj](24u=T(I9GS7;G[P4,f/!]'\MI"nTj?;P(N\J%>U?C4GhkT#___rhV02%t8T,aRB0" %E^-Y'M2$CY&%dnk*_hUT,9i"jl1_Fn`gdXZ]Q1[u=%bY7?P>7j8b'mN`1Y@Wr?/ImO;%CcekH7/\^ieK[08<;-K^7hI#o4\V;MtP %I17cY;!,c$gWL**lEqZM"")lkkS69$[pd(n$U94,Q0q8r/+K]qZX!6njWpa6X!HgMph(Kj(bNuP0GYqZFOTSSjXAX1"4*uV:?#;. %>$J/V"B4gn)>[bP#iH9"+mFK,#dgL@TgF#-1?e*uBsYb;;`r"664\aFQh$.g_cAVLj=fKf3$SaU>1d2.l?OdQ5g13t/Ph %M-ER(P(uYnnTYNQJ.OUAbLZqR_;;.tl*gUIJgLu<> %B796fUP5)Ag*dHf.SNk?OGfGm-l#iOa0O9cZ.;XqO)McT^[:EtT6;W=m0ghF!`TL?WKL=Zs,kT.d4*N/mKEJTqMlSkU %>CkJ."JNZ\W!Wj2Kb-TY42Xm<5-dR2^+e.>k+j>+WM0Tce+6$+VhA9?^_RE4E5RfSF(gbI<%]S=puo1M5qSlu$k+>LJ$1U,+c9?? %463:ZIs:6qSOiORJ%iVlFIqZNH)_rN(,6oAa'Ut*kEj(pr6q]Jo:NdEqVV1.LIWG6r9X.A?B=*\4hj",^;&G$mdAd-m_dZVDC<1] %4hnS>I"$;=o#9^EQMg^KmF=_=]_h>0p\EA8`J]d`;tRu`j$t21GrO+Q[pP_6]ro!^odIBjb&UL;+7O2T[nh'BY%i1TlK6dfCEH*r %VP?p>G`oQR=,!sOF_\\go^_/75G(0p?QCVVP5fcNIfIC;4E^O-MQ>GHrI<+2St/)oG5%UmP`%3gYH.?&rR:_ie#W9\+76SHj$c2G %]fNp?Vks*u$D4tWHN%m,&?X@$Nf\83go(q/OBq<4AiKoTirHp5CTG1Hb'`H;DMlZgBC\rnc]HH+)WfQW3QG(#4-Pq!9 %='(HT4C!@70UCe"[`*a2ruWbghrUR*h-KI;X]V466Y6:?J(;/X]ZO#f?eml1@DB@KDXG_%Y:fPkICM?nD6`lYhm0]C/qmFOoHcU! %i-$4,/V!ad3-(s8&+Q1pTTm6;E]L/$_[YY*\%QdAr=@dINk*LlpuoJu(BJIro5CqM5(A37%Ub %5=lsRB']:`-k6(:rL^>EG0]X!nin5h_41+a)d<-+,nSE&aH4.'@I/k_3J'\4Im/&II=hJ@GkrX&%qYT/OEq]UcTaA\`-ulP*oB#l %r=uQdaTogg\knRn6;?1r1jN6Fpre]hY'XM+)O@aD#I>_d6qdp'1"L;QI]F*iCDRof]DDGc*D>N)agkqEP,pmff)P0onFpYlHPlbM %AN3lO?5B/XE(Ug]bauH%A5&l011RNP*?X?C*V+>TfjcCaNlM(MWr&J?R6/>l(o_&W;GR_;aT9*NlT^6XoS!gElcD^cqhIYQgqOBK %Sof*E]G/N?8c2Y/]5Q#Gepcnd[%HVB^gb5H#lM6NFFtqCZ][+?ffD9'6k9&emnta3<=688qr]L%G2q&4=0#o`ur'G]_ %rV%rA4C#;ic1>_W]Q(YN`/)m`>CUQt]lmJVe+:>6s*]OfIcHK^a`$>?j2o2GY4hUN55NOKm@S=lLY^k49n)qd:K+Ki&$N`.^Nl1W %Hh4Lbr`jY3J+V./(Cuj>mSW\Qe(a2T[r^5P\@eP7j8AQ6Mt>,]SJ2Y"LU-McOe3_AkfLa<`>@G(Tf75F-g15j(]=Z.N6eGkhZ%gSOcs6]+EI=0j4WS!J2ekUV=T#,"4@a&HbVOVrgY;hgE[qG:?pRgo'Z$WkT^ac7rb\d2e?IHFrroJV/X"X3+^O,\@8/GX^ %gF!MU[pSK/gL&9V^\ZcWQMnB,OCEVHQutZS5LY!UPcGsea`!II!U?di#O=oSV"4D%:t`g,$u^I0ao-+g]*"PTiVpf?f>#ehRqJoW %+-(I1c'AJo(]K:FJ=l;jnRbG-GIg"fF>Z;ZkZ@ej^N`1Ai*Yu[CSgmLj]CVhG<\,WqOC4[CDH^9X3^7[?bJFn4icH/1W=Whq%_5m %]K<.Sf`TpiB*jgKrUn'dpZge6]Rt%KX3A8@4T@fSJ%n"FGl;eCIZ#k!m.V$?_S_FZ^;F0Imr,7[htbZ0,-u+KTjee]pZqbIA(b\$ %c9eWu*H$OJG^O#g]qn"Mqg:qr_YlEJK_\g#q"Z9.j&+Og< %[=3S %qo\\`YFE&mA"1"&j4Nn!-]@5ePJnK3/:M_es)`b\@fKciAAA!DrjQ--n'H+GhnDsHDmelmeo2j9$ %i?FKm`R(mI1@H8GMJa["pdq9K8U6Ntq8r6=U/*uKlbDJ(qXKkqnaZVGX9Zn;5jNsgfL0.%XG7Mf:[?tTp7^r'C?PDGoCk9$eb1p4 %]1CC3;=_9.*+s^I/C9XZ4.=J;s$"[-SZhaR(GBZ4d?3hnhlP^i?MZka-()FEV\@1jX34ip-I;Ht&jfk4&1[;rioJ8Hiaq(UnU9VD5o76@@/?d')U5qr$_h%c^\HgfDtY/;RI?RjJD;/fPenl`84p%qQ'RpA+0HLR6m71,ham2TZ39qjrp)5CU$;l&5/&LUh&I %UR/`f>Zos49+jAlMU3i"'GTcGNp9(C-DFL[`Nh)&$c<1K`gE(l03:@rbNBX]_8%C4,?V&-fbfehQi>:q7':U>pZ;$NS[VkXegGG=/?[aZ;Z?L]-pm]nZs*R"[34o3+ %8"k&.f"*+nY.r*'ns]iJ)1'U(*tOWg/\gO?m(`Q,IeK)Xa;:1MIJ$o[NOS*"Hua4Nq=NqLd.odk^]3p)r/3-JiX7@/WY[[KM)\K$ %NQrqZn$iL.+rpYWaRVI.\Tk@j>i%#\]RBIWFG8"?/d5VMms[fblTOC#phgMLR(8AG>e]0#]-G=g#I/g'SM!os+HMdVFn"gL-i7Lhj@2K%%4&"VYWZUDXr9])tJ!K %*_J:^WD=qo1\2Wrdb[jf_N03@rKj"crU^!<2/U4Kf_a7e<:DCJc;E %L7.*#0s!6)[RQ)m4U6AWOMpn:cLrJ[Y5_*6B`%[tYb;#,DYgWoEdeS$oC(GMmV=@*/ng?HO3FpjV6YFfl:DCQi`+aGB3P,9OKM[d %OKMD(]+sXh\J1'%Y5I\^#QA46Si?8V4Su\g-db2>-?E"1!K,3pW8jTf&>]moMHo5.9Ns?^kKme+9X=]S5lZGHk#!U*FfEuI`8Y%IpY%a[k-_Y662A>]hYhp_]@4NuMe8N!,5#NJZs]J[Angi/ %Yut6]NpSmt]4p(P/a'%gXq%`:6;k)4^SgVuD6Ja#.hjMQ/^pHchgbOtTq%cm2A3]C=u&f7,%qs/J*eQ\:oC3PSNe!i5mQh%GNUp2 %pR$PP7`0CAXY@/$3NmlpYu[1RCm\G9SQm[ %:AU/_;/4L>^Nt%s<+_dUQK6E:ThM&tjZ#)7-kdD4EG3`r`nOVMf2Z#I43_iZhS)C:Wj(4#%G3k5'AT):2`GNX>$W;TJ?% %U;-X%NlpeR?p_;Rf@G9C&Sp=;YIqBV]7"\.qt7/-Y,4WbX.]cnJ'IRp_1S:_ncI:'"\)Y@CjaY(tQh %IeS33VYt=\n\tK%%/L2MU8kqiK6Ae?%qF/88,.@:IC!O%StV&(m9r3>bdWl9jl^R7Pk?8#br9ccX<+#aIXceoSD.uO[$@1(r:CXa %CZ%bR:"&g.?/@dsN_bs^np&7(8:n50U$F_Wrc_$Y[Uiu/QnGo'2#ko%?9\DYn1B#T;LS`g@\FF8i8cU1ao&4>6iP7DX7$>mgDoU* %^qO(eEOejBj5'+'QbU91r:Xp4'^>&sA1.6J=81kg?X;PQHUYn[QbM[kDa'VL*O#J0X8BHC_3]UkH1gMC]keb*j"D3uqn`"XJ+pt#2g:!o0_b:$O)LUsdF+Odk5JucNlCHNI*Ek`DtKC*(U(")S+SG\+3ku,lq\_^kP6l#57Q5Mq<+8G8:$2a %CY6n`Vl.#q(VRGj]t1nHQ,h^PDjG<s@#Z %dI=J,RGi](^-$m*0;eWilW:E1R"nn'KROl<6GIm?nFG,Im^%!2>%[0DGLb%5%.pl=>Zs8]g\ncI?iS'si.#.bF@1>]rngf?=-jGI %fq%NFgg=BeBulg!W@=1>fS %i>18+?ToH3bP'URfg!,q3NpLC9`$KO0Bop$V=s];/E,cI:$V*C0Ddi;4Q)#]Ci]+pk7\lj'1pNAcCq^m,,h,e:=0jTO'W+E*#ieLJjt?5b %^9tNbk*+4UbJK*[7(@GCk"f0JUde,imWqQo)DPb^(:o]kVL2?F:NCV&S1g&5VIRI_:0\([oN4me%`a;!,h:UNQqs_tiM\H$,X3Rj %VN]`D8[GYsautXR(_LA?+-3f4(5A#XI6K-^-dHuL\s#1UoV4_lj$=IDRqJiRTg&k5?kUaD?f0"'iB4>Ci3nDa1'Mt$O>+p4pPldK %hu0nB%6GsX+3u;dL+'o4!0sCBrYE%9+jErQ=0afhaAQOdVCD(HKR*NXO)HJ;],i;)g8uZ-$GN'qJ@RcbS6(0]g`g*$!puf(K]T"' %=WX3MU$D2GiB1AZEoVR3nZa+$eM]WXZ8d?!h@7=X'kYP(7M^U*2h,S\!HLRdIpVaM$,'$S&`Kf/VP&?Wo8L8TF%cH"949f:74-fr %H)MbtngY2nioVtVaW2,)\]'-?gsu3nX78&6*Xrb-b:PUO)PK;hCGb1K_K7Qp"qqgnYG=Fs#5+= %]5cRj(k)P"_qkN0"*i#,n8t(oD%IgYMl')3X2*B!8H:gYCo?[\p]$!]-.uhU46*XqnM$L/B;jX/,KMXoC`M$DTYuk`,]![c)r3Ol %WQ5@^1DmnsaCtm:G\n_LDWe"2a9:;K@i:g'#?[W:IWhkcB.4\N-;E]p\VJs$0mq:)bYMj7cf[EkQ4!>i10^fA!69A4Odf@#So,qXb^L8"0"`903Y8ujM9!i+Z1>]PR6]b#3R;IP^ %a)GNchieKBEF#+VZi%$87tJCPD#Af,:NAL=F:^@.5$F-hY#C-b`*/54-8+\^kD&*;0/,Y:'1].k0l3UoHlb$;):;:)m:Lb$`/p6L %%aM,7s-]nd431goC.l#ajW=$7FG^EOc>-I[gLJSc\U&JpH-'@s9TqWc\O/UjU%Y>'p`aig>Q2+Cc\EpsbGtld&7%04%U(dRQHZZC %SS[LW"tVFL@GiV'5A$"2q?Yu3EK@N;'@_6(!FlXTVAf4UPT^MRU5W2KF5]SUZsgbs;cD.RjNX(W4CX+t2,[iL,agJsG_SND7#('> %ApM`(F"Y'[,`>enY7$9'lS8G$I(Y1>C:o56kf*b-WDeolW-V^2,7[6lR,9L"NiXBZdPgEiEYZ)'$'Vdq_uF)I&G %)[d3ICloTq;HN^jXeDLd.0J(/iiD=eo_?W%s1V-K6=OhkXuA`]6C0oAlZbk=])O<;#MUVrN))`3"]sMPn(jpI9FI;[& %VA.`B3ejah1T\rgRDB?!*Fiic!?T7(X9;;>Z\m-9b;"QCE75<@0Zs%7g5@j\4k<)F2>oBqIa,EYn-bgChm#L7\T>Gq=X,4,'3mJo %PdXA]$M@h#n/HCW==ob1%3\("@,lR*%@A5\RMspS3E8?Zhe-c+"hbq?WoRiWcEO4N%*]^AShB(\--)Q*gT08U*:qR$V[]&dLNX'[ %oW(\eO0rtF-9Ta&A7rl88i,q\XsW\_"<^#S6kCn!XG+\IgbuX%5FuHlE+9oi.o\1:J`ou'AiH1's8V^!&a4`A15IWq(q`1@(eakH-RITP*ufI5c,TLOk(:&s&GA %M_L$e#>*SB7Xn/ %M@i9>hPe9m9m,`'%lQ/nVE+o5C"aK_&At3Qc>1ge7h]MDZM7e(=J35uad=UFEMj1q8?6m!dr\'IQIKC&oQ]k[=V+jWBRN5sJ_je,CGNh<4N_tTb/:?f %H4#Bm]pUR-,V:20>eY0IX6I-V7FH^/X[us&7F](",c*e&,_:(-/It(u)>'!B.WoWQWgFB''B/t)@)TdWSc0;'K3@rSpR!tcmAO]:&/GkF8qc,+ZQLs3fEpZF8.jDR\pqI[CJKQ %S#)!W=XurPFE(7b=;DY0!T'cmck^7p/X)f`#s8BajmsARRHSd?B12LK5B,fZL-]ZPgn-cYE]SWZrq4ark%,8jGD?EQ\\9Mk-i\SG %kg;%"mk0+%\bL,dGr*.G^%e'=MZ(WYQ_3di^59b"$b/=S^Amg9$(;mpVV3!QT*M<52ld%GF*N/bC;0@ %o9HfR0E+?mEE3"\kkkD]Jb6&o:Ol'?Tsr9^ %AI9_PrivateDataEnd munin-2.0.75/logo.svg000066400000000000000000000232171451614574100144310ustar00rootroot00000000000000 image/svg+xml munin-2.0.75/master/000077500000000000000000000000001451614574100142365ustar00rootroot00000000000000munin-2.0.75/master/Build.PL000066400000000000000000000015331451614574100155340ustar00rootroot00000000000000use MasterBuilder; use warnings; use strict; my $version = `../getversion`; chomp($version); my $build = MasterBuilder->new( dist_name => 'Munin::Master', dist_version => $version, dist_author => 'The Munin Team ', dist_abstract => 'The Munin Master', license => 'gpl', requires => { perl => '5', 'Digest::MD5' => 0, 'HTML::Template' => 0, 'Log::Log4perl' => 0, 'Storable' => 0, 'Text::Balanced' => 0, 'Time::HiRes' => 0, 'Getopt::Long' => 0, #'Munin::Common' => 0, }, build_requires => {}, recommends => { #'Net::SSLeay' => 0, In Munin::Common? }, ); $build->create_build_script; munin-2.0.75/master/DejaVuSans.ttf000066400000000000000000022767041451614574100170020ustar00rootroot000000000000000FFTMOp<GDEFX]XGPOS/GSUBN֘LOS/2" Vcmap .cvt i9<fpgmq4vj<gasp glyf}o2|head?p6hhea $hmtxvNUhkernk/6i4<~locaJTUlmaxp i namepMi@=postL3Hprep; x\h>rmrm "W           "##$HIIJLMQRpq|}     "#     ; < < = I J P Q Q R U V W X o p q r   ~!"qr|}2334Y \DFLTzarabarmnbraicanschercyrlgeorgrekhanihebrkana*lao 6latnFmathnko ogamrunrtfngthaiKUR SND URD MKD SRB 4ISM 4KSM 4LSM 4MOL 4NSM 4ROM 4SKS 4SSM 4 kern8kern>markFmarkTmark\markdmkmkjmkmkrmkmkx    "*2:BLT\dlt|x8  8 >/024<7j7:I`j0&:  sv{sv{ &,28>DJPV\bhntz::::r 4 4 `Iqrtuwxyz|Iqrtuwxyz|JPV\bhntz$ l N>X  &,lwlwlwfn " &,28l`l~l~l`l~l`Z& #HNTZ`flrx~tt;888  !"    ! "(.4:@FB :v| $*06<BHNTZ`flrx~hhh=DhhhDhh=DDnnnnhh   !# J P)rr0tt1v|2339  J P%r|,3378 $*06<BHNTZ`flrx~ &,{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ $6HZl~ cj cj cj cj c c cj cj#sv{>DJPV\bhntz*  &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv|     $ * 0 6 < B H N T Z ` f l r x ~      & , 2 8 > D J P V \ b h n t z     " ( . 4 : @ F L R X ^ d j p v |     $ * 0 6 < B H N T Z ` f l r x ~      & , 2 8 > D J P V \ b h n t z  "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~{U:t!N8'Qn ppjjj,v,,vjj  XXXXD[j[j, 8 8>>j pjjj^jj,,,,,,,     8 8 8 j j>>, ppjI^`k/#eYYYcP`{U:tii!NQnU!Q{++++++jj++jj++jj++ 8 8jj 8 8jj,,X X ,,XX,,X X ,,X X      j j,j,j j j,j,j>  ++pp++,,,, ,,,,,,,,,,2  pp++pp++jjjj++jj++,,XX,,XX,,XXjjjj    XXjjXXjjXX&j&jXX&j&j[j[jSjSj[j[jSjSjXX 8 8jjjj 8 8,j,j>>SS&j&j>++jjj  pp++j++ 8jjjj++^++j++,XX,XX,XX,X X   >SSp++ jIII^^^```kkk///###eeeYYYYYYYYY$AMpBDcs~2#sv{BHNTZ`flrx~F 'PV\bhntz "(.4U0+0008q00800i00E0 0100000P=i0v00v00d000UU8000U $*,022 45 78:> "0 $6HZl~ cr cr cr cr cr cr cr crIqrtuwxyz|RX^djpv|``& b lrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv|     $ * 0 6 < B H N T Z ` f l r x ~      & , 2 8 > D J P V \ b h n t z     " ( . 4 : @ F L R X ^ d j p v |     $ * 0 6 < B H N T Z ` f l r x ~      & , 2 8 > D J P V \ b h n t z R``S`4rrLRLX X X X [r[r~x,LLRLLRLxLLLxx4RI^`n#YYY`R``S`++++++LL++LL++++LL@LL@XXXXXXXXxxxxxx++XV++,,,:,,,,:,:,,,,,:,:LrrX+F+Frr++L&LRR++LL++XXXXX~X~X X X X RRX X & & X X &&[r[rSrSr[r[rSrSr~~x~x~LLFLRFSrSrR&R&R++R&RL XVX++++LLRL++R++++XXXxXxXxXxX~X~4S4S4++&RIII^^^```nnn###YYYYYYYYY%%//88Mp')Hfghij4?QZ2[Iqrtuwxyz|rx~`{{{{{{{{` <BHNTZ`flrx~]xx@[")@>E"~~x2x::-."> @FLRX^djpv|]kxyyyxyz[f"w)h>yEy`P["~[~t`zxy2{`uxJJ::++-.   !" 28>DJPV\bhnttbbbbt`~~`~` Z R   !"# $*06<BHNTZ Y &,28>DJPV\bhntz "(.4:@FLRX^djpv| $=D]468QT9Zd=grHw{T D6L  $Js}- {{8> 7pv| $*06<BHNTZ`flrx~L/'s.}////////s}/////s{y5D;/}R7$&(,268DFHLRVX-* [\]^klz?   $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv|     $ * 0 6 < B H N T Z ` f l r x ~      & , 2 8 > D J P V \ b h n t z     " ( . 4 : @ F L R X ^ d j p v | L\/.Rs''}srJf;RRsRR%}^Gb`R////}}J////Rs}f7R/'z`RR///.RR'}r`RTTRTcRRJ@@RjRjRbRb}RRRRRRRR}R555RRaRt;Q'RRRRRRR}}^G^dRRR::R'aHRR_R:RGR R~RJ}'/'}'}^TTT@X}Tg^GX^//LBRRf,4$R'_zRf4L}`ReT'sR^G^5s/RRwRRJV1vvvR;nR RRL5s/<\R&Rx9\wR}RO$= D]$>BCHIJKRT  UV--WEEXNNYTTZYY[aa\ll]vv^{{_`bf iJqLmFFIILL""3366|}346;>FJNRW S S [ ^ ` b e e h m t t x x | }      ~ ~ "36QT7VW;Yd=grIw{UZ&&m((n]foy{|}??~   !"# $*06<BHNTZn 6ntz "(.4:@FNT\bjpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz &,28>DJPV\bhntz     " ( . 4 : @ F L R X ^ d j p v |      & , 2 8 > D J P V \ b h n t z     " ( . 4 : @ F L R X ^ d j p v |     $ * 0 6 < B H N T Z ` f l r x ~      & , 2 8 > D J P V \ b h n t z  "*06<BHNTZ`fntz "(.4:@FLRX^djpv|  &,28>DJPV\bhntz &,28>DJPV\bhntzL\/.*s''}srJ{#{{;j{//{{s{ {o{{'{}{^{G{b{`{{'{{}}{Q{{{{}{\LX;\//''{ssr`{{{'{{{./'}{r`{{T{{{{c{R{R{J|@{@{{{jj{{b{b{}{/{{{{{{{{}{{{3{33{^{a{p{{;{Q{'{{{}{}{^{G{^d{{{{{::'a{H{{/{{j:{G{ J{~^{}J|E{}{{{{E}{p{{t{}{j{{{b{{^{~~{}{t{^{{{{/'{{{H/rtOs}s'LsoqGGYNsT{a{E{{{{@{{t{{{{}{{{{T{`{kb{{K{{{{{{{{t{{'{///{{4{^{c{s{"{O{,s{O{%'}{{{O{t{t{e{sK{{{{'}{E{b{{{{^{{{T{{TT{{@{{{{{}{{{{{{{{{T{g{b{^{G{{{{^{{LBRf,4${' _zf4DL}1{`{e**}T{{'s^{G{^{{{3s{{/ {0{{{{'l{n{T{T{wJ{{{1{{v{vqv{*\;{n{{{L5s/<\&Rx9{\{w{{{{{}{m$= D]$>?ABCDFG  HI55JBBKEELHIMNNOPPPRVQXYV[]X__[aa\ff]ij^lp`txe{{jkl pJxLmE]deiikkmmooww.DL ["#a*+c.6e@@nMMoXXp^^qbbr|}suvxyz3;{>GJNQW[[ S S [ ^ ` b e e h m t t x x | }      ~ ~$'*02588::QTVWYdgrw{ && ((!]f",/0124??5  "#( J P.r|533@A$*06>FLRX`flrx~ &,28>FNTZ`flrx~{{{{{{{{{{{{{{{{{{orr{r{{{{{{{{{{{{{{A{{{{{{{{{{{{{{ {{{{{{{{{{{{{{&!0#5PKr9KD &&K9a}au9aauaau/&DaDDkkDDDDkDD)ak}/DDa9}D}&&9}k}k}&D aDY}aaauNaaau}}k}ka aakkAk&k}}DHVaD)kkDN9a}au9aau/9a}au9aau/9a}au9aau/&kD&9a}au9aau/9a}a9aa/D?}DVD aDKr9KD &&Kk}k&/<&O$$%%&&''))**++-- .. // 22 33 445566778899::;;<<==HHIINNQQRRUUYYZZ[[ \\!mm"}}#$%&%'( )*+!!,,-((. /  0  ""&&100::?? 2 3 4$$%%&&''))** ++-- ./22 3344 5566 778899::;;<<==DDFFGGHHIIJKLLOOPPQQRRTTUUVV WW!XX"YY#ZZ$[[%\\&mm'}}()* ++,,-../"/&&010101234352678888393:;;  3<3<=<;    !! "" ## $$>%%5&&''!((?++@--@//@0011"33@55@66A77B88C99D::??4EFEF G43H4IJ H HA I IK J JL K KB L LA M MB C D M N O^$%&')*+-./23456789:;<=HINQRUYZ[\m}  "&0:? `$XAYAEGKMQ SW .DFLTzarabarmnbraicanschercyrlgeorgrek"hani2hebr>kanaPlao \latnhmathnko ogamrunrtfng thaiKUR SND (URD (  MKD SRB  4ISM FKSM FLSM FMOL ZNSM FROM ZSKS FSSM F    aaltaaltaaltccmpccmpccmpccmpdligdligdligfinafinahlighliginitinit ligaligalocl locl&locl,medi2medi8rlig>rligHsaltPsaltVsalt\     'PX`h "*2:BJRZbjrzJ\ nvX               p   *  H   R !$% B6##66>9LM *_ J K L M i$=EEGGIIKKLMNOWW      ""$$&&((**,,..0022446688:;==??AAHHRRTTVV  **__  J M &   &$$4F!!$$4F""$$4F##$$4F$$$$4F%%(0AD&.6EEGI&.6JKMN&.6OQSS&(0TW&v6Pblv",6PZd        $%&'()*,-./024578:;<=>A B  !$'*-0D$&(*,0268<@DHLNPRTX\`dhlptx|Megp#%B  "%(+.1l3.4:>BFJVZ^bfjnrvz~ QQSSUY^egmop)1B  #&),/2l3-39=AEIUY]aeimquy} QQSSUY^egmop)12  ww vssvw~&8Jlww zw zw utrq utqrtuwz 00> $*&$*&$J 8 "(IOILOLI OLIRl$*06< xwvutsrqP{NzMy &,!xwvutqOzQzRfnp0$B 8  WVWA(:FPZfr "   " $; <V p0 q(/ QF WX VR")567DF o ogfhdeji#9?FLTZgfhdeji#9?FLTZ,-DO *"&?,-DO\  X6 usvtyzr3{w|x  ! LM *_ YYYYY33f . `)PfEd@ m,$, x~OSXZbw~%V_  :UZot?5JR>PjGv#.[jx{EMWY[]} ' d q ! !I!K!N!###!#(#,#u#z#}######$#$i&&'' '''K'M'R'V'^''''''()) )A))))***/***++$+T,o,w,}-e-o..%..MGMQWn+?KO6<>ADO#t QWZ\pz 1Ya  !@Z`ty? 7LT@RtFn&0]w{ HPY[]_ * j t !! !K!N!S!###$#+#s#z#}######$"$`%&''' ')'M'O'V'X'a'''''')) )@))))** */*}**+++S,`,q,y-0-o.."..MDLPTb&0FN8>@CFR pv^\TO>=<4/,+&" mljiga`_^][ZYWVUSQ~}|zyqpofbY+)320/." zvsoQPOMI>8642\ ?><;:96530/) A,gb^0%$#qhkkkkkkkk5k1k+k'k!kjjj|"|snmlkjig_W ~bOQSWXZZ\bpwz~"#7 %1VY_a "$?D  F  HIJK!:L@UfZZ|`o}tty??#-/U 57JLRT:e>@PRjt  FG nv 3#H&.V0[_]jwx{{ d   E HM PW YY [[ ]] _}  2 g v    ' * d j q t  / 4 J L N P!! Q! !I [!K!K !N!N !S! !# ## P##! R#$#( X#+#, ]#s#u _#z#z b#}#} c## d## e## f## z## |## }$"$# ~$`$i %& &&'''@'' D' ''H')'Kd'M'M'O'R'V'V'X'^'a''''''''''''())) )  )@)A )) ))))))*** **/*/.*}*/**S**`++b++$}+S+T,`,o,q,w,y,}-0-e-o-o...".%....MMDGLM"PQ$TW&bn*7;=L&+Q0?WFKgNOmosw|~68<>>@ACDFOR #ptvV89;>@DFFJPRkՠ)]   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a rdei xpk rvj \s gw @ O MT il|=cn XT Dm} b T: @  y qz5fqu-J3T99NR7s`s3VV9s3D{o{RoHT3fs +b-{T#\q#H99`#fy```{w``b{{Rffw;{J/}oo5jo{-{T7fD)fs, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-,%%I%%I` ch #:e:-ff@ /10!%!!fsr)5 5@ K TX8Y<2991/0 P ]%3#3#5qeB@KTKT[X8Y1<20@0 @ P ` p ]#!#o$++`@1      91/<<<<<<<2220@   ]!! !3!!!!#!#!5!!5!T%Dh$ig8R>hggh`TifaabbNm!(/@U" '&( /)/))/B" ) *!#*- ) " & 0K TX8YK TKT[KT[X@8Y<<<1/299990KSX99Y"#.'5.546753.'>54&dijfod]SS\dtzq{---@A$*.U# jXV`OnZXhq) #'3@6$%&%&'$'B .$ &($4'!%   ! + 1 4K TK T[K T[KT[KT[K T[X18Y9912<0KSXY""32654&'2#"&546"32654&%3#2#"&546WccWUccUVcbWWcd1Zܻۻa ۻۼ 0@      !         B  (('+'$ .  .'.'!!199999991/9990KSX99999999Y"2]@ " ) **&:4D ^YZ UZZY0g{ "-  ' (   2'') #**(/2; 49?2J LKFO2VZ Y UY\_2j i`2uy z 2229]]3267 >73#'#"5467.54632.#"[UԠ_I{;B h]hΆ02޸SUWDi;#QX?@Yr~YW׀c?}<$$/1oX3go7@ KTKT[X8Y10@ @P`p]#o+{ 7@  KTX 8YKTX @8Y29910#&547{>;o @ <99103#654<:=JN@,       <2<2991<22990 %#'-73%g:r:g:PrPbybcy #@   <<1/<<0!!#!5!-Ө-Ӫ--@ 1073#ӤR@d10!!d1/073#B-@B/9910KSXY"3#m #@  10"32'2#"  P3343ssyzZ @@B  KTX@8Y1/20KSXY"]7!5%3!!JeJsHHժJ@'B   KTKT[KT[X8Y91/20KSX9Y"@2UVVzzvtvust]]%!!567>54&#"5>32Ls3aM_xzXE[w:mIwBC12\ps(p@.    #)&  )KTKT[X 8Y99190@ daa d!]!"&'532654&+532654&#"5>32?^jTmǹSrsY %Đ%%12wps{$& Ѳ|d @   B    K TK T[X 8Y<291/<290KSXY"@* *HYiw+&+6NO O Vfuz ]] !33##!55^%3`d^@#    KTKT[X8YKTX@8Y190!!>32!"&'532654&#",X,$^hZkʭQTժ 10$& $X@$  "% " !%190@]]"32654&.#">32# !2 LL;kPL;y$&W]ybhc@B991/0KSXY"KTX@878Y@X9Hg]]!#!3V+ #/C@% '-'0 $*$ !0991990"32654&%.54$32#"$54632654&#"HŚV г "Əُattt$X@# %!"" %190@]]7532#"543 !"&2654&#"LK:lL>$& V\s[#@<21/073#3### %@  <2103#3#ӤR#٬@^M@*B$#29190KSXY" 5Ѧ`@ #<210!!!!^O@+B$#<9190KSXY"55//m$e@+$     &%K TX8Y99991/9990y z z ]%3##546?>54&#"5>32ſ8ZZ93lOa^gHZX/'eVY5^1YnFC98ŸLVV/5<4q L@2  L4307$7CM34( (+(I+*(I,=M<9912990K TK T[KT[KT[KT[XMMM@878Y@ NN/N?N]32654&#"#"&5463253>54&'&$#"3267#"$'&5476$32|{zy!orqp ˘s'6@   0210].# !267# !2'ffjzSb_^^_HHghG.@   2 99991/0`]3 !%! )5BhPa/w.,~ .@   21/0 ]!!!!!!9>ժF# )@ 21/0 ]!!!!#ZpPժH7s9@ 43 1990%!5!# !2.# !26uu^opkSUmnHF_`%; ,@ 8  221/<20P ]3!3#!#"d+9.KTX@8Y1/0@ 0@P`]3#+f B@  9 KTX@8Y991990@ 0 @ P ` ]3+53265M?nj @(B  291/<290KSXY"]@ ((764GFCUgvw    (+*66650 A@E@@@ b`hgwp  ,]q]q3! !#3wH1j%@ :1/0@ 0P]3!!_ժ @4  B    >  91/<290KSXY"p]@V   && & 45 i|{y   #,'( 4<VY ej vy ]]! !###-}-+3 y@B6 991/<2990KSXY" ]@068HGif FIWXeiy ]]!3!#j+s #@  310"32' ! ':xyLHH[[bb:@   ? 291/0@ ?_]32654&#%!2+#8/ϒs R@*  B     39991990KSX9Y""32#'# ! '? !#y;:xLHHab[T@5  B    ?  299991/<9990KSX9Y"@]@Bz%%%&'&&& 66FFhuuw]]#.+#! 32654&#A{>ٿJx~hb؍O'~@<    B %( "-"(9999190KSX99Y")])/)O)].#"!"&'532654&/.54$32Hs_wzj{r{i76vce+ٶ0/EF~n|-&J@@@1/20K TX@878Y@  @ p ]!!#!ժ+)@@   8AKTX8Y1299990]332653! ˮ®u\*$h@'B91/290KSXY"P]@b*GGZ} *&&))% 833<<7HEEIIGYVfiizvvyyu)]]!3 3J+D {@I      B     91/<2290KSXY"]@  ($ >>4 0 LMB @ Yjkg ` {|      !   # $ %  <:5306 9 ? 0FFJ@E@BBB@@ D M @@XVY Pfgab```d d d wv{xwtyywpx   []]3 3 3# #D:9:9+=; f@  1 ]@ /<20KBPX@   @    Y3 3 # #su \Y+3{@(B@@ 91/290KSXY" ]@<5000F@@@QQQe &)78@ ghxp ]]3 3#f9\ @BB K TK T[X8Y991/0KSXY"@@ )&8HGH    / 59? GJO UYfio wx ]]!!!5!sP=g՚oX;@CK TX@8YKTKT[X8Y210!#3!XB-@B/9910KSXY"#mo0@CKTKT[X@8Y<10!53#5oXޏ@ 91290 # #HHu-10!5f1@ D10K TKT[X@878Y #ofv{-{ %@'   #   E&22991/9990@n0000 0!0"?'@@@@ @!@"PPPP P!P"P'p' !"'''000 0!@@@ @!PPP P!``` `!ppp p! !]]"326=7#5#"&5463!54&#"5>32߬o?`TeZ3f{bsٴ)Lfa..'' 8@  G F221/0`]4&#"326>32#"&'#3姒:{{:/Rdaadq{?@  HE210@ ].#"3267#"!2NPƳPNM]-U5++++$$>:#qZ8@G E221/0`]3#5#"3232654&#":||ǧ^daDDaq{p@$   KE9190@)?p?????,// , ooooo ]q]!3267# 32.#" ͷjbck)^Z44*,8 Cė/Y@     LK TX @8YKTX 8Y<<991/22990@P]#"!!##535463cM/ѹPhc/яNqVZ{ (J@#  &#' & G E)221/990`***]4&#"326!"&'5326=#"3253aQQR9||9=,*[cb::bcd4@  N  F21/<90`]#4&#"#3>32d||Bu\edy+@F<21/0@  @ P ` p ]3#3#`Vy D@   O  F<2991990@ @P`p]3+532653#F1iL`a( @)B F 291/<90KSXY" ]@_ ')+Vfgsw    ('(++@ h` ]q]33 ##%kǹi#y"F1/0@ @P`p]3#{"Z@&   PPF#291/<<<290@0$P$p$$$$$$$ ]>32#4&#"#4&#"#3>32)Erurw?yz|v\`gb|d{6@  N  F21/<90`]#4&#"#3>32d||Bu\`edqu{ J@  QE10@#?{{   {  {]"32654&'2#"s98V{>@ GF2210@ `]%#3>32#"&4&#"326s:{{8 daaqVZ{ >@   GE2210@ `]32654&#"#"3253#/s:||:/daDDadJ{0@    F21/90P].#"#3>32JI,:.˾`fco{'@<  S  SB %( R"E(9999190KSX99Y"']@m   . , , , ; ; ; ; $( ( *//*(() )!$'      '/)?)_))))))]]q.#"#"&'532654&/.54632NZb?ĥZlfae@f?((TT@I!*##55YQKP%$78@  F<<2991/<2990]!!;#"&5#53w{KsբN`>X`;@    NF921/290o]332653#5#"&||Cua{fc=`@'BK TX@8YKTKT[X8Y91/290KSXY"@Hj{  &&)) 55::0FFIIFH@VVYYPffiigh`ut{{uz>]]3 3#=^^\`TV5` @IU U U U   B     K TKT[KT[KT[K T[X@8YK TK T[KT[X8Y91/<2290KSXY"@" 5 IIF @ [[U P nnf yy          %%#'!%""%' $ ! # 9669 0FHF@B@@@D D D @@VVVPQRRPS T U cdejejjjn a g ouuy}x}zzxy  { v } @/   y]]333# #V`jjj;y` C@F      B   K TKT[KT[KT[X@8YKTX8Y91/<290KSXY"@   & =1 UWX f vzvt        )&% * :9746 9 0 IFE J @ YVYYWVYVV Y P o x  /]] # # 3 dkr))`HJq=V`@C        B     K TKT[X @8YKTX 8Y9129990KSX2Y"@     # 5 I O N Z Z j        '$$  )( % $ $ ' ** 755008 6 6 8 990A@@@@@@@@B E G II@TQQUPPVUVW W U U YYPffh ii`{xx   e]]+5326?3 3N|lLT3!;^^hzHTNlX` @B K TK T[X8YKTX@8Y2991/0KSXY"@B&GI  + 690 @@E@@CWY_ ``f``b ]]!!!5!qjL}e`ۓ%$w@4 %   !  % $  C %K TX@8Y<<29999999199999990&]#"&=4&+5326=46;#"3>l==k>DV[noZVtsݓXX10#$@6%   #%#C %K TX8YKTX@8Y<2<9999999199999990&]326=467.=4&+532;#"+FUZooZUF?l>>l?VWstݔ1#@  1990#"'&'&'&#"5>32326ian ^Xbian ^V1OD;>MSOE<>L5 b@ <2991/0K TX @ 878YKTKT[KT[X  @878Y P ]#53#3+e#!Q@+     "  "<<<221<9990%.'>7#&73JDFHAMf fIX⸹)**'# 32!b`@!    <<1/2<2990K TX@878Y66].#"!!!!53#535632NL=ty-=))׏/я^R#/@I -'! - -'!0 *$0* $ $(st*(s099999999919999999907'#"&''7.5467'7>324&#"326{r%$&(r;t=:x=q%%&&s7t@?s9q(&%%s>v:@t8s'%$|pprR@F  B     fe f e<2299991/2<2<290KSXY"K TX@878Y@(' ' ')((79  ]]!#!5!5'!5!3 3!!!c`Tþ{yT9{3{JD{3@ <210##  \= >@54&.#"#"&'532654/.5467.54632{?>?>S8alӃ\]>9̭IXW:fqր][;;ȦI.Z.L-[.K''PGZsweZ54m@''TLf{xf[1,pEF)@dd1<20K TK T[X@878YK TK T[KT[KT[X@878YKTKT[X@878Y@````pppp]3#%3#^y/IC@&=>:A$104G$ 7aD=0^* D^ J21/02#"$'&5476$"3267>54&'..#"3267#"&54632mmllmmmmllmm^^``^^⃄^]]^\^BB@zBCFInmmmmnnmmmmng^^^傁^^__^]⃅]^^! "s;)_@3(%%  * "(kl"k *22999199990!!#5#"&546;54&#"5>32"326=P,]uu>DIE~bRhP{@p?Dq[[""CO@Mr%# @I    B   o o n<2991<2990KSXY" 5 5%-+#-+#RR^@ 10!#!^d10!!d/8L`@6EBC?2H09JC 9 $HE301B54&'.'2#"$'&5476$#32654&'2#'.+#^^``^^⃄^]]^\^ㄘmmllmmmmllmm}{{nWXfi`C.;I6Bf^^^傁^^__^]⃅]^^gnmmmmnnmmmmnb>KL?gwyVpMI`3Db+/10K TKT[X@878Y!!Vu=  @  Z[Z10"32654&'2#"&546PnnPPnoO@v+..ooPOmmOOp1.-rB .@     <2<21/<<0!!#!5!!!-Ө-}}^J@$}}B ~9190KSX2Y"!!56754&#"5>32 "?XhU4zHM98rn81^BQ##{l0b(H@'    #)~&~ )999190#"&'532654&+532654&#"5>32 \e9}F4wCmxolV^^ad_(fQI7Z`mR|yOFJLl?<:=svcE`sRf1@ D10K TKT[X@878Y3#fV` M@%  !   NF!2912<990"`""]3326533267#"&'#"&'#% )I#ER2bf*V H<9 NPOONN;9 %@]] 91290!###.54$yfNݸHF103#F#u@  ' 1/90!#"&'532654&'T76xv.W+"J/;<+->i0Y[ 0.W= ,@   |]|| 12035733! c)t'+n`d.@  klk 9910!!2#"&546"32654&PXγгi~hi}|P{ݿܾsH# @I  B   o op<<991<2990KSXY"5 %5 +-+-#^R^  ^R^  &{' d 5?&{'td 5b&u' d 5 $@/  !# #%" " "!& %999919990KTKT[KT[X%%%@878Y@ ttttv]33267#"&546?>7>5#537ZZ:3mN`^gIYX0&DeWX5^1YnFC98ŸLVV/5<6hk&$uuhk&$suhm&$vu  +@ ]1h^&$tu #+@ @O# /#]1hN&$ru  +@ 0?  ]1hm !@T   !!  ! !!!B     !  VV!"2299999991/<9990KSXY" #]@  s P#f iu {yyv v!# ]]4&#"326!.54632#!#TY?@WX??Y!X=>sr?<҈_Z?YWA?XXN)sIsrFv)H@9  B     <291/<0KSXY"]@gww  ]!!!!!!#!59=qժF՞su'&&z-k&(uuk&(sum&(vu@@ ]1N&(ru @@ @]1;k&,u/uk&,s/u`m&,v/u +1XN&,r/u +1  g@    2  y<291/220@(   ]]! )#53!!3 !iP`P5~.,3^&1tu"+@ 0?""]1sk&2u'usk&2s'usm&2v'u+@]1s^&2t'u!0 +@ 0!?0 !/0!0]1sN&2r'u +@ @O]1? @M    B   <291<290KSXY"  ' 7 7w55v8vL57y5yy5f +@< +,  )&  *&& &,+,* # )#3,99999999199999990@*WZWU!je!{vu! FYVjddj(|svz( ]] 324&'.#"&5!27!"&''3>_'y=_''NOy;WfNPƀ[gX@CHp@CpDfbMKYg[KKX)k&8uu)k&8su)m&8vu +@ / ]1)N&8ru +@P_@O /]1k&<ssu =@   ? 2291/0@ ?_]332+#32654&#'ђ/@0-'!  **.  !' $'$-F099991/990@@'(     ! "&  : :!MM I!I"jj  ]]4632#"&'532654&/.5467.#"#:A9`@IPAtx;e\`Wqqs`/Q*%jd_[?T>7;[gp{-f&DCR @?&/&&]1{-f&DvR @?&/&&]1{-f&DR (,+1{-7&DR.< +@ ./<.<]1{-&DjR -( +@(o(P-_(@-O(0-?(-( ]1{-&DR%@&,,& 2882 ++1@ ?5?/5/]0{o{3>@C'-%= 4%:.-*1 %?47&%7& =&-7"E?<9999912<<29990@0+0,0-0.0/00@+@,@-@.@/@0P+P,P-P.P/P0+0@@@@@@@@@??? ??0,0-0.0/@,@-@.@/P,P-P.P/ooo oo`,`-`.`/p,p-p.p/,-./]q].#">32!3267#"&'#"&5463!54&#"5>32"326=DJԄ ̷hddjMI؏`TeZ߬o0Z^Z55*,ywxx..''`f{bsٴ)qu{&Fzqf&HCqf&Hvqf&H"+1q&Hj@@ ]1f'Cof'v\f& +1F&j +1qu('@^%{&%#${##{#({'(#&'('%$%(('"#" ! B('&%"! ## #)&' ! (%#" QE)999999919990KSXY"?*]@v%+("/#/$)%-&-'*(6%F%X X!` `!f"u u!u"%#%$&&&''(6$6%F$E%Z Z!b b!z{     {zzv v!x"**']].#"32654&#"432''%'3%F2X)6 ~r4*!M!ü޼z&77kc\̑oabd7&Qquf&RCsquf&Rvsquf&Rs+1qu7&Rs .+@ /. .]1qu&Rjs +@ @O0?]1o )@ r <<103#3#!!oAH +@<+,&  )&  *&& &,+,* # #Q)E,22999999199999990@p(?-YVUV jf!{    { z{ {!"#$%{&%--&YVUZ(ifej(ztvz($$]] 32654&'.#".5327#"&'')gA\*g>}66]C_56`?`!*(Ou))Hn.Mw834OMx43NXf&XC{Xf&Xv{Xf&X{ +1X&Xj{ +@ @O0?]1=Vf&\v^V>@ GF2210@ `]%#3>32#"&4&#"326s:{{8daa=V&\j^+@ 0? /]1h1'q;$ +@@O]1{-&qJD+@o]1h'J$+1@oo]0{-&OD"+1u&${u{&Ds'k&&s-uqf&Fvs'm'vLu& <=/1qf&Fs'P&&zLuq&Fs'm&&w-u@]1qf&F&'wq&Gq @_?]1 q$J@$ "    GE%<<1/<20`&&&]!5!533##5#"3232654&#"F:||ǧN}}daDDa3&(q=q'qH@p]1m'yu(@@]1qH'H@p]1P&(zuq&Hu&(qu{&Hxg&(wo@@ ]1qa&H!+@!]1sm'v\u* <=/1qVZf&hJ  <=/1sm&*yuqVZH&JsP'z\u*@?]0qVZ&hJs'^*qVZ4' J;m'vu+ +@ / ]1dm'vuK*+1KQX88Y@ @@]:@    8 22221/<2222203!533##!##53!5qʨ"ʨ9Qx>@!   N  2221/<2290#4&#"##5353!!>32||}}`Bu\zzedx^'t.u, +1g7'+1Y1'q.;,+1H'q+1gm'y.u,+1VH'+1u%'d,u 'JLP&,z/u<<1??]0y`,@ F91/0@4D@P`p]3#\`{f'-\,@1V'M8L@F1f_m'v.u-+1V\f'+1j' .' N` @(B F 291/<290KSXY" ]@_ ')+Vfgsw    ('(++@ h` ]q]33 ##%kǹ`!jl'snv/Jl'sZvO<1KQX@8Y@O]0j' /' O@@]1j'q/'q9O @]1j'y1w/'ysOK QKSKQZ[X@8Y1u ?@   : y<<991/900P]3%!!'79Pw^Mo;jnH ^@  z z <<991/90KTX @ 878Y@ @ P ` sz p ]37#'7Ǹ}Lɸ{JZjXj3l'sv1@O]1dm&vBQ @?O]13' 1d{' Q3_&1wg +@ /  ]1df&Q +@]1'QU~V;@  AKTX8Y21@ /0!"#367632+53265PͳNijQRW1fOCCoa`ZVd{;@  NF 21/90`!!]+5327654&#"#367632dRQi&&||BYZuccH``01`e22wxs1'q';2 +@]1qu&qsR+1sm'y'u2+@]1quH&sR#+1sk'{'u2quf'Rs ;@   299991/220!!!!! !# !39OAg@AժF|pm|q{'3@1 . ("%4"1 K1 Q+E499912<2290@%?5_5p55555????? ooooo ]q].#"!3267#"&'#"32>32%"32654& H ̷jbdjQGьBN5Z44*,nmnm98olkp݇Tl'sv5m&vBUT' 5J{' UT_&5w}g@_]0Zf&U +@]1l'sv6om&vBVm'vu6  ))Ic:1of&%V  ))Ic:1u&6zou{&Vzm&6wu + ""Ic:1of&V + ""Ic:1u&zP77u&zW_&7wsg +1@_]07&Wq7p@]1F@   @ @ <<1/2<20@@p ]!!!!#!5!!  ժA@7C@  F<<2<<2991/<<<20]!!3#;#"'&=#535#53w{%&sQQ''PO>)^'tu8 '+@ ]1X7'X&+1)1'q;8 +@ / ]1X'qX+1)m'yu8+@]1XH'X+1)o&8iX&X| @@@!]1)k'{u8^f'Xe)&8u`&X'Dt'v|:+1V5m'EZ+1t'vr|< +1=Vm&^\+1N&<rsu +1\l'sv=Xm&vB]\N'zs=X&]\m&=wuXf&] +@ ]1/#@  L<1/0!##53546;#"c'&яN()g ,D@% ")%,$'".EG* ,(%#'F-<2221/<204'&#"327667632#"'&'##5353!!STTSSTTS:YX{{XY:E/tssttsstRd0110d}}P)C@#   . *29991/90"]!2654&#!2654&#%!2#!"#546D+ |v݇f>orqp ˘0_i1F&8@# (EGF'221/067632#"'&'#!%4'&#"3276s:YX{{XY:NkrSTTSSTTSd0110dtssttsst 3@  . /21@  / 9/04'&#!!276!2#!#ONDNO|N8DCDCD>@  G /221@  /ij9/0>32#"&'##34&#"326s:{{:"QrdaadDs'0@  0 <10>3 !"&'53 !"shSzjffbGGaaHH_^9'(9^_sZd$D@"! %  %  0%210&&].# !267# !2676;#"'ffjzS` SfM?nb_^^_HHgh$bzq"N@$ ## HE#210@ $$$$$].#"3267#"!2546;#"NPƳPNM]-GFE0iL~++++$$>: a .@   2 99991/0`]3 !%! )"#5465BhPav/w.,~0_i1F.@  .21@   /0)!"!!"$54$3!!@DNN|#+qZ?@G E221/0` ]5!#5#"3232654&#" M:||:ndaDDadqVuc'T@ )E Q E(]99@   (99@%S 910%!"'53254%&'&326&#">kGxfu'~@3cnBOFFu\0%p9 *E +@    21@ /0!5!!5!!5E>9+uD@& 39190!!"56$3 ! 7327upo^   2`_FHg[{(@@$ )) #)* &)190.54$32.#";#"3267# $546؃ YsrSǾmTj^У%!| &${spw21%%ݐf#A@  2991990 ]!!!!+53265ZpPM?nժHVe@#   LK TX@8YKTX8Y<<9912299990@P]#"!!+53265#535463cM/ѮcMPhc뻫Ph*Nsd&I@43! F'1@'$$'990%!5!# !246;#".# !26uu^[DM?npkSUmnꪖ_`%Rv%@ 'P $&]ĵ 91@ %$&222990@ #%$$<<$#$%#@$"! #9927654'&'3#"'&5476736,3,,3,6hC.KddK.Ch B9Iy\\yI9B z^ȮwBAWWABw1G*O@, *&NF+291@ '&&  #/<<9990%27654'&'5+"&54&#"#3>323LTWJ>ymoF||BuLibep_!edg .@  KTX@8Y991/9903;#"&n?M-– R E@   >f3@)B 6  999991/299990KSXY" ]@068HGif FIWXeiy]]!3!+53265jG?n+Vd{Ks 1@ 3221@   0! ! "!&32sy:;x Vb[[z=g&24v'X Rs3@ !  <1/0!4&#! !2!2"327&nzy;pa'Xܯ–bb-LgFqVY{!:@ """# E"9104'&##"3232"327&&&idRصRQ@TVt1098``:6:@   ? 291/0@ ?_]32654&#%!2+#"#5468ʄv/ϒ0_i1FV$O@$#% %G  F%22991990@ `&&&&]%#46;#">32#"&4&#"326siL:{{8(adaaTV@  ?  2299991@  /9990@ @u|]#.+#33 326&#A{>ٿJx~hb؍Oђ r!d@ -" "99991@B!  "90KSX@ Y6 327# '&546?6764'& {璑z<;YZL-|숋_ppٶ+23@@md{'@  !! RE(99991@ '$$(90@S !S BKSX99Y"]@/)?)_))))))]@% '$&((*//*( ( ))$]@.,,,;;;; q>323267#"&546?>54&#"Lf@eaflZ?bZN?$%PKQY55##*!I@TT((7V6@   O 221@   <20;#"&5# 54!23%&'&#"3wMc/R5!n|wj=hP`@o,0A37V?@ F<<291@/<2990!!;+53276="&5#53w{KsF0j&&էN01`>X@ @  991/2990K TX@878Y@@p ]!!##"#546;^vժ+Zi1F7I@  F<<2291@  /<299990]!!;#"&5#53546;#"w{KsբcMcN`NQfT@ @@ 120K TX@878Y@@p ]!!;#"&!n?Nժ=–_&84i' XN:@!3   1@   <2220!! 47!5!3254'5!X ƱXw>*a"Lav-@   /<91@ 0%254'&'5!'&'&33cAnMagn"ʦmWDtz–d@  @ @99/1@  /9990@        BKSXY""#3 632#54&9%NZUUIG9\[ny6P=V{j@  K TKT[X @8YKTX 8Y9991@:        B    9990KSX2Y"@      '$$  )( % $ $ ' 755008 6 6 8 A@@@@@@@@B E G TQQUPPVUVW W U U ffh { F]@%     # 5 I O N Z Z j ]+5326?3 67632#54&#"N|lLT3!;^0XQ99) hzHTN43`rr:T*\@5    B  B K TK T[X 8Y9991/<20KSX<<<323#L:s_%'ST_ijxzX"Jh0@umHLIwKK!!C12\RI`1]5@ F1@  0 4&#!!!%$ $5& )sQ;-%,%hV)$yhL?`3@  F1@ 203 4&#!!!32!"'hi;-ԧc%,&cV)$yJX$!"'&'5327674'&+#5333!plnUQQLITNPc9:V>}ws}#(rAbLrV{@@  F221@ B 0KSXY#36763254'&#"s4QҸMNr98xܭz BR1pqWBAV&@ F10@ @P`p]3#V''V:@    <<2<<219/<2<203!!!!#!5!5!5!s____,Ԫ m'?' f'@'qf'@Gf$'-/V'Me/V'MvOf'-_1V'M>1V'MeQhm&$wu<1{-f&DZ +'+1`m&,w/u  Ic:1^f&  Ic:1sm&2w'uquf&Rv <1)m&8wu<1Xf&Xv  Ic:1)3&08X1'q{;)Z&86X"&X)Z&80X"&X)`&80X"&Xq{h3&${-1&qR;h3&${-&DH4'q>{o'qs%T@!$"43 &<1@"#%&99ܰ KTX"@8Y<203## !2.# !2675#535!5yyuu^opkC XSUmnHF_`%'XqV{ 4X@"2% G,E5221@ #% ) 2/3 &)/99<20`666]4&#"3263#!"&'532767!5!6=#"3253:aQQRZ9||9=nXF]@,*_EG^[cb::bcsm&*wJu!<@!T!$!]1qVZc&JJjm'wu.m&Nwu* +1KQX88Y@ @@]se'42qeu{'Rse1'q';qeu&qsm'wuyXL/f&TVdf'%  Ic:1 '=' ']'q']Gsl'sv*qVZc&Jv-5@8221@ /203!327653! '&5!#>=B>d`gd"dPNOKZ߀xxv 9V@@  221@ B 0KSXY%#3676324'&#"8WST=<HW5xz7 GF3k'uu1dd&QChs&s\}{s&s}Hl's\v{oc&vefl'svHc&vhp&$|z{-d'Dh6&$x>{-H'eDp&(|zqc'H6&(x>qH'Hsp&,|Yzc'fw6&,x>>UH'$sp&2|Azqud'Rs6&2x>quH'RTp&5|yzJc'%UT6&5x>^H'-U)p&8|zXd'X)6&8x>XH'X'v6o{',V'S77'WRs. 56$>54&#"57>54.#"5632?4o1\}p_s54&#"57>54.#"5$32Fp>!BlJc(v];?"AW?-1CA#E ptgDZX%KlaF='.`[b[3XpVU 2#PQ̝qpD(4%3254'"632!"'#67&5#"'&76323 76'& %44nI5"C0:XY|ˀ|YX:ST$TTTTT- H:E<$d0110d^jtssttssq% ;W@$3=E (B!8;7B/E<̲ ;]91@$3< ;<,<990" 7654&327654'&'52 '&54767&'&5476!˸jkkjpkk_;̨_`Lm䖋_``aCUtMMMMMN'|OEH-AA+Mdha "ccttttُcc"FYXSJqq 4C@6E B42()+&BE5221@4)".559920" 7654'& '&5467&'&5473327654'qSRRS SSSR:4HRQ;4?+IHIJ,MMMMMNMMJ@b@Y "ccttttُ"#VKYIAAAAAtw>\V@ B  K TK T[X 8Y991@ B  /0KSX@ Y@@ )&8HGH  /59?GJOUYfiowx]]+53276=!5!5!!Hri&&gPP%01oXV`@   K TK T[X 8YKTX @8YĴ@`]99Դ@`]1@ B  /0KSX@ Y@2&GI + 690EIWY_fh]]+53276=!5!5!!۞Hri&&5ejLP%01%hP&$@{-&D_u&(zqu{&Hz{s3&2bqu1&qs;s3&2iqu&RsO'z't2qu&sRs3&2jqu1&qs;1'qr;<=V&q^\p\%3254'"632!"'#67&73%44nI5"C1- H:EVy` 8@   OF 991990@  @ P ` p ]3+53265F1iL`aq #/A@1E%G +G!E0<<<<1@( . /22220 6& 23632#"'#5#"'&76'&  7/ST$Trrrrˀ]STTSST$Tjtss ^ŨŢtsstjtssqV{ %/D@1E$G+G'E0<<<<1@ *.! 02<220'&  7"'##"'&763253632 6& STTSST$TrrˀrrST$TdtsstjtssRŢŪjtss|3 #!#'#7'7 3!Jafp|҈2F;R/o]jY'FF8O ",'&76!27&'!2767# '#&# rfuSv=:efc.1 tsfjwv9tFXh$xYv+!f //_H$$\/ح ]"+'7&576!27&'32767#"'&#"i`UUQ.-Y_vcPNONMRS]7GGcc^N lOU ^q+$Vqrg j ;@   : <<1/<20@ 0P]33#!!#53ʿ_w1##'!5!7 !4" gZ8f,i> XRBY bo{=4'&/&'&54632.#"3#"'&/&'&'&'53276 23@LLfLNZDE11?PS{W*L'TrGY$alfccaFF'K((%$JK((**T@%$!,KL[@~$=&[#5-,X3`!;#"'&/&+=!qjN\1*LlTrGY=Z^e`1~$=&[? %P6@ 9991@  /0##32654&+"56;2'񍚚EOZ*,FP{7@   991@  /032654'&#"5632##/dLUIVVN}AH+Fnt  (\@ #  . &%)<229991@(% #/99/<20*]!!!2654&#!2654&#%!2#!#53[D+ |迿ɐʇf>orqp ˘p _@ 8AKTX8Y<2<21@   29/<<2299990]3!33#! 5#53!3265˥ߦ®j*$}h0B33#!!!!#7#!#!AX .AA<VF㪾FqB&-1&'&'!3267#"'#&'&3273&#"#So+Jajbck{cPm!)81G\9/Zo Z 6Z44*,!  C "2JcfRY@    9 KTX@8Y<2991<2990@ 0@P`]#+53265#5333RM?nʿwHVS@$   OF<<22991<2990@ @P`p]33#+53265#533#F1iL`(aؤsf$C@$  %" %  %2299199053;#"&5# !232#"nEMMT–\\xEEqV@{$H@"%"%G E%229910`&&&]#"&=#"3253;32654&#"@F:||:Li1戮VּdaDDada= T @  ?  !<299991@!  B  /<229990KSX9Y"@"]@Bz%%%&'&&& "66FFhuuw]]#.+##53! 32654&#A{>ٿJxʿ~hbw؍OJ{=@ F<<<1@  /<20P]###533>32.#":.I,h<ĤfcΡ3!733!#!53!ٗ ٗwјv9 V`+5326?!533!33!+N|lLT3!øLùmhzHT33`{ ,@ .% F-22991@-&%"*-%  9990@1?$?%?&?'O$O%O&O'_$_%_&_'o$o%o&o'$%&'$%&']@+?#?$?%?&?'?(?)O#O$O%O&O'O(O)_#_$_%_&_'_(_)]2654'&#"367632#!3267#"&߬A@o\]?^^fe~ST`Te__Z+f{b:9ml)Lf01a```FE..'qZ{8@G E221/0`]53#5#"3232654&#":||ǧdaDDa{ 8@  G F221/0`]4&#"326>32#"&'#3姒:||:/Rdaad` $C@  !G! F%22991/0`&&&]4&#"326>32#"&'#46;#"姒:{{:Z[/Rdaad~Ӝ}}{ 0@ ! !"EH!<106763 #"'&'5327654'&#"LQQU]SRMNONPccccPNON5#$+qrrq+qs{'/O@( ,,H"E02991@.*%00@ 11111].#"67632#"'#47&'&!23254#"NPc'>IjJ?_SPI 9/-U:Me5++rQ,3H=Y}/)9DhQ#3 :#:9KqV@$K@$%"%OG E%221990`]#"&=#"323;32654&#"@F:||:Li1戮VּdaDDad^ؙa=q$=@" %%  GE%2210`]546;#"#5#"3232654&#"iL:||ǧadaDDaq{"r@ KE#91@  #90@)?$p$$$$?????,//,ooooo ]q]47632!"&'532767!7&'&#"qkcbdcjfg ]\RS^,*4cdWWZZq{A@$  KE91905!.#"5>3 #"73267qN ͷjbck 9Z44*,#ė|{ 4w@6.('4 KE5<Ķ&  91@/.'""5 5@  &"90@ 4 &'<<<<<%6'6'32#"'&'&'&5>3 73;#"'&5Nf  R`\Lladbck $˸&&i+@WR֊>8E#Z`vg'#d4*,#)u10`Z|I|*|>i@@603273;#"'&5|PUTZGUU]UTNHtCDFEwGQPabLq_&&i+@WR@\l%88ZX83,-F@.. NBj10`ZȦFq|/;@ 1 &,E01@00)0#90"327654'&+5327654'&'2# 76`cchҗUTNHtCDFEhqr<V`K@   OF<<22991<2990@ @P`p]33#+53265#53F1iL`(aؤqV 0U@)  &#-* *-+& G E122991/990`222]4&#"326!"&'5326=#"32546;#"aQQR9||9iL=,*[cb::bcaqVZ` #C@ # GE$21/990`%%%]!"326!"&'5326=#"43!aQQR9|=ͻ,*[cb:*qO{8@4 E1990%#5!#"!2.#"326Ae{-h]_cƳO|$$>:77>>`Rd`#y@ %  $ĵ 91@  $222  990<<<<< 3#"&54767327654'&'bB_j&;;&j_BC(::(xܱSccS$-EIdccdIE-`d`#y@ %  $ĵ 91@  $222  990<<<<< 3#"&54767327654'&'b)rG,EE,Gr)C'88'bLx>>xLb-!@2FF2@!-VX`9@     NF21290`]332653##"&||Cua{VfcdC@!   N  F2991/<9990`]#4&#"#46;#">32d||iMBu\~aedVd!J@%  " NF"2991/9990`#]+53265#"#46;#"632diLiMHa=~a >@    F<<<2221/<20@ @P`p]33###533#¸`<Ĥn`Mt` '@   221@   /2205!#3!53t褤K#<@ % V V$<<1@#! !//2<903327673#"'#&'&#"#67632= &}33[ &}33[ %$RIJ %$RIJLT5@  <2<1@ /9/<2033##4'# 7632&#"3=5*7M\TK9V_ (@  F 1@   990;#"&5y=x1F|t(L6$@#&#" F%<̲#91@B""  " /9/ 990@$#@  **8;ILT[q ]@$$%$$5$7E$FT$\ ]@    ]2!"'&'5327654'&+5!#3!CicUQ^cdjTmcd\[je8+lh%12KKKJ3Lb&^@PP F'<91@  #''<<<290@0(P(p((((((( ]%#"&5332765332653#5#"'&Cb`ruSSrw=ZXyzVUy=<b`^zbze32>>Vb&a@PP F'<91@  #''<<<290@0(P(p((((((( ]%#"&5332765332653##"'&Cb`ruSSrw=ZXyzVUy=<b`^zbzZe32>>V{0c@PP)%'F1291@ %*!*-(&/<<290@02P2p2222222 ]>32+5327654&#"#4'&#"#3>32)E__RQi&&ru99wSS?yzUV|v{zH``01NM_``gb>>Vk{Q@N O F2991@ /9@   990`]#4&#"+532653>32k||F1iLBu\satedVJ{;@ N  F21@   /  90&54&#"#3>32;#"R||Bu&&i1F``edH10d` y@BNF 991/<2990KSXY" ]@068HGif FIWXeiy ]]!3!##`ylqu{ ,@  Q E2210"!.265!2#"qt蔔98q$`I@  E2ij 991@   /<<@ 9/0!!!!! '&76!#";:E*%xxxx%`ݛlklm>|$2@ &E E%1@ #%<202765 26= "&'"&H`k&InI&k`B"F:.aע ģ0[1[0T\l6puypVi`/@   /2991@  /90%!"/32653#r%832JI,:.˾ fcVJ{:@  F2190P].#";#"&53>32JI,Li:.˾atfc~{%@ 21@  /29903!5346;#"iLAat~{%@ 1@  /29903!534&+532ʴLiAa`@4  B      F299991/<9990KSX9Y"@]@Bz%%%&'&&& 66FFhuuw]]#.+#!232654&#0s2âJ{Qpwu t]:'`iVNM``E@  F299991@  /29990332673#!32654&#Q{Jî2s0jp|Ɓuw`':]t i`MNVoV{0@C  S('  S'('B1 '(!.1' ($R$+E19999190KSX99Y"0].#"#"/;#"&=32654&/.54632NZb?ĥdXLie@f?((TT@I!* ajYQKP%$V4@ O F<22991@  99046;#"+5326cMF1iK»Ph)aV O@ !O F!<<229921@! ! !99<20546;#"3#+53265#53#5cMF1iK`NPh(aؤi7V5e"O 1@ 04&+532;#"&McKi1F(hPaV2@   O 221@  /<20!3## 54!346;#"#"3276w5RcMów|n!o@`Ph3A07^3@   /<<2991@  /<2990]!5!4&+5323#{Ksբ>`N7V=@   F<<2991<2990]!!;#"&5#53w{Liൣa>`C@     NF2221/222220` ]3!33##5#"&=#5!326:CuȮ||h=$#^lfk`8@   91/20@ 3 3#f%.]`8XV`@"B  OK TK T[X8YKTX@8Y2991/0KSXY"@B&GI + 690@@E@@CWY_``f``b]]!!;#"&=!5!qjLLi/F7e`ۧa%X`!@  "KTK T[X8YKTX@8Y299<21@  /<0@ BKSXY"@:&GI #+ #690#@@ECWY_#``fb###]]!367632+#47!5!3254qjL"TA`:&>R~ie8FX`ۢG7W9W`/=3<;4%6]XL/` @ "!̲91@B!  !9/ 990@ @  **8;ILT[q ]@  %$ 5 7E FT \ ]@    ]2!"'&'5327654'&+5!5!`q|/=@1 %,%E01@0 0"0( 90";#"327654'&% !"$5467&'&5476EwEFDCtHNTUhcc`a|p<:!a>>`V.9@ F<<991@   /<203#33## 54!3#"32767Ku_+xG`͋BA0 L` ## 33R9L T#`@ F1/03!!`3qV $C@  #%% "GE%2210@ `&&&&]32654&#"#"32546;#"#/s:||:iM/daDDadaX$L@ & %<<ij#1@  $! /<2KPXY032765&'&#"56763 3###53T?V:9cPONNLQQUmlprLbAr+#}swԤX$M@ &"#E%<<ij "#1@ $!# ##/<2KPXY0535&'&5476!2&'&#";3##plnUQQLNONPc9:V>ws}#+rAbLrq &) 76'& %3!!!+5#"'&7632/ST$TTTTT iL:XY|ˀ|YXjtssttssH^Lۓd0110MqL4@#5#"'&76323!2!"'&'5327654'&+5 76'& Z:XY|ˀ|YX:jejbVQ^cdjTmcd\]:ST$TTTTT3d0110d^L$8*mh%12KKKJjtssttssq 3: 76'& %%!332!##47!#5#"'&763233254#/ST$TTTTTghL<):XY|ˀ|YX:FXjtssttss_ 3<;4d0110d^6[7@F.#"#"'&'#"'&5#533!!;5327654'&/&'&54632NZED11?QR|{Za]gQQ{%&sfccaFF3,@LLf?((**T@%$!,KL[[!&PO`>''M5-,QK($)$JK7V&/!05476;#"+53276=#"'&5#53!3wxWQîc&'QRF1i&&QQ3%&sN[V((h)``01PO`>''7p-9D!6!2&'&#"63 #"'47!"'&5#533276'&#"&57!3w{UQQLNONPcccO+eKTIQQ;BS_r(ր%&sz#+qrfr v)2LOAPO`> 'KV ''/Vo5+5327654&#"#!##535476;#"!;67632oRQi&&||ӹWWc'&-BYZuccH``01/яNUV((hce22wx#5.#"#"'&'#34632327654'&/&'&NZDE11?PS{|Zb]hf8b_caFF2-@LL?((**T@%$!,KL[[!&2-,QK($)$JK @   F<2991@ B /0KSX@  Y@B &GI   + 09 @@@@@C EWY `````b f]]3!!!+iLLۓ6 333# #333# #6ttttU=63@    <2<21@  220!#!#!#!#6kkUXrXJ3@ NF 21@ 0%#"&54&+53232653#׃Li1FęaBþyVv!:@ #NF "21@" ""0%#"&54&+53232653;#"&'׃Li1FPh2FęaBþyfu0@ 32tNN^luu)qJy}wYYk\g88u:KSX@ 32tNN^lugrB0)qJy}wYYk\xkW6Vr88 #@<<1@03+5327653#zt43r,Bttx66XVru@ 1@ /0.#"#3>32.biuu$uT  qksa97H <1 /032653#5#"&'H.bitt$uT  qkJa97Hu' <1@  /<032653;#"&=#"&'H.bit0B,rg$uT  qkJ V6Xlx a97 !+33276?3327654'&+CFCDtk=%%(f{n!!"}K'))'K}N;[--s?5/.6 333# #6tt&+53276?331/.N]D0 {{bp"#WK/itftf&t  @ 10#5Rڬ@u1 ܴ? O ]ܶ ]<1ܲ]90526544u@XX@sPOOP{X@?X{POPPu1 @    ]<1 Բ]90"'&4763"3sPOOPs@XX@PPOP{X?@Xu+@ 91@   032765&'&#"567632#'y7$#?q22110335WDDFk[@*7K$@ ` XFh_@Cu-@ 91@   0#&'&547632&'&#"3kGDEW53301212q>$%6y[AmC@_hFX ` @$K7*@ 2% % g 25-5g'|?f=u912]90K TKT[X@878Y3# #fg|?fLu91<Բ]90K TKT[X@878Y@ 5:5:3]]33|g?f7@ u91290K TKT[X@878Y3#'#f?f7@ u91<90K TKT[X@878Y373x^@1@/0#^+b+qsRf3#ff #ofv^@1@/0%#^++Tq^#onvsR3#lo#E@ j,5!##–,dU 533##5#5Dud&u!5!&>ߖ)9H W@ VV1<0K TX@878YKTKT[KT[X@878Y332673#"&v aWV` v HKKJLDfN@ d10K TK T[X@878Y KTKT[X@878Y3#  @ V xV104&#"3267#"&54632X@AWWA@Xzssss?XW@AWX@sssLu @   '1/90!33267#"&546w-+76 >&Dzs5=X.. W]0iJ7c@$   VwVv99991<<99990K TK T[X@878Y'.#"#>3232673#"&9! &$}f[&@%9! &$}f[&@Z7IR!7IRfB@991<20K TKT[X@878Y3#3#߉fx%3;#"'&5&&i+@WRd10`ZȢf '#7'373\\]]\aa``u # 5473733254/MMz /1/03#zttu/2&'&#"#"'&'532654'&/&'&547632j1549W++](}24NM9>=D@?>=RX o(l00GF@99 a /$*+MW33 k2-*)*IX01 u! #'#37 ͉H+uX@ 1/0!!5!AGЈX'@??//21/]0!!5!3A4X@ 21/0!!5!3AhhX'@pp0021/]0!!5!3A4X@ 1/0%3!5?p+v'qqm 93vJ!_@ Vw V v"99991@   "<<99990K TX@878Y'&'&#"#67632327673#"&9 &}33[&@%9 &}33[&@7 %$RIJ!7 %$RIJf6@ D910K TKT[X@878Y # mXfvqPf6@ D910K TKT[X@878Y3#fs?f<@u991290K TKT[X@878Y3#'#?fsH7b/q|  )1H+d%@ 910@4D]3#hF)I@ dd 91<20@#4D`````````ppppp]3#%3#^y)7{"@ V@ V /1@@ /0632#546?654&#"7pihX,#w3-.>GZdH3UC=A   (6%""($4fCf<@u991<90K TKT[X@878Y373NxsD/1/0#DD'4]fB@991<20K TKT[X@878Y#!#͇fxx)1')1H VV/1 /<0#.#"#> v aWV` v ")KKJLD( @0#3Ӥ?#55#53pp{53#7"op{y3#@uUCqPUv$<#5353#ĠxxxF33##xx2xU?p!5!#Ik{1@V/K TK T[KT[X@8Y21@ /0532654&'3#"&=X.. W]0iw-+76 >&Dzs5V @  V21@ /0"&5463"3VZ||Z(55(}ZY|x5'(5 M3!5353D M#5!##걈ň$ #53533##Ġxxxx 5! zV '+53276=0RQi&&``01wV %3;#"'&5w&&iQR10``fSC'SjC( @V xV1@ /04&#"3267#"&54632[6'(55('6y|ZZ||ZZ|&65'(56&Z}}ZY||jT @03#Ӥ#uzLuD/1/0#D`tP#5!#fJc9X#"4533273273" v aWV` v "6KKJL9HS/TB  #"'&'.#"5>32326SKOZq Mg3OINS5dJ t]F ;73 !;?<6 7=xh!5xhh5!Ĥh'`_^NO'ygfFXY @  V21@ /02#52654&#Z||Z(55(B}ZY|x5'(5[3!53[J!!5#>J*>c9X632#&#"#&'"#72;tv gfv ifvtR+ '7'77}`}}`}}`}}`p}`}}`}}`}}` .54675>54'&'C!RI 7!RI 0PQn +0PQn : '  fCqPfvH7FbV+I#5!#!Ֆ֖V,2!5!5!5!>>2xx3#3#@`tt!#!–*>,Jf'73327673#"'&'#7&'&#"#67632Bmk  &}33[& !Bnk  &}33[& g  $%RJI g $%RJI J!%'.#"#4632326=3#"&3#3#9 $(}gV$=09" (}gT";薖Җh! 2-ev 3)dw.CJ"ttc( 7!#'73!'3p~(͛3#557'2d͛~~x&'&4767@*,,*@rNPPNr*,@A++{OPPN1'+!x050567654'&xrNPPNr@*,,*{NPPO{++A@,*.Do2>&"762"'"&46264&" 5O57O5>||=>||66O5555M75m?|}A@}|6M65O5p pk Ppk!!p kpT!!p ଔ* '#'&'&#"#67632327673#"'&O,$e5Fqp[?9ZO,$a9Gqp[?9J7  $0GJI "7  $,KJI pn w(5!'3#7ws~~d͛q` !#!#!#Sb+e !#####b+tf@103AntVH@10%#AnH3y`V #"'&=3; #V!. {q{'yOF{'y#sRf1@ D10K TKT[X@878Y3#fFR&jl@_]@_q0hf'&HFyuf't*f',}f'z.f'4(f'n9f'h=6'.Mh$%j@ 1/03!!)ժh=@ B1/0KSX@Y !3f5:9+(\=;+s!2@"" "#3"10!!"3276'&' ! '&76>b܁܁:xżp[bb,j.h<@ B1/<0KSX@Y3#3#:9&+031b *@    <<1/0!!!!!!29iggqs2;3 F@B   <<1/220KSX@   Y%!!5 5!!>!8ߪp7<s'<@) !%(<<<<1@' %'/<<<<0367654'&'&'&76753#–bbʖbbWssWWssW=;;s.@ <<1/22<20!6'"'&336763#ּՂnʊnhg椌gHN&3@ &("3'1/<2220%!567654'&#"!5!&'&576! cccd?IH1/GGaʦa>”XN'r/u. +1N'rqu9 +1qf&Enf&PIVdf'Kf&MF*&Yqy *@ ,%E+99@ ?/]q@ ) !/99@<<10@  ]@IIIJN LNIK ]@:9:88? <>]@ + +*))]@  ]@++]'&#"3273;#"'&'#"'&763 N,-=MKLyHc( #) Xn^T).^,ru7 nik%1)0T*XoW)&V!7@E F21@  90%#! !"3 5 4# yYo 0kEdZ&J:@ V`@@ 1@ /<20@ 993#&+532i^;,_1FLdVD~qu-T@(/E( Q!E. ]99@%%.99@S910&#"#"'&4767&5!232654'&'&fu5KxD7VUV[a~@Fu\0%p̥@$OF(Iqrs`g |2=@" 33'(#,34 '0E310&'&547632&'&#";#"32767#"'&546p<@ KQX@8Y1@ 20%#457654'&# !5!ʄOTJPE* :;f,KOxsPWKL,#%5,*3Y'iVd{1@  FN  F21/0@]#4&#"#367632d||BYZuccH`e22wxqu$!O@ """#E QE"2]21@?]0@ w##]!3276'&#"2#"'&76EVSI 6VQ@=񈉉d~uvn` @ F1@ /0;#"'&5c"$lYoRR`+.0`b` I@   F 21@ /<20@    <<33 ##Gb`/ZFB?= F@ 1@ /<0@  # #'&+5z~J/k`ue<2~V`wJ`B@1@ /20@ 99!367676'&'31!xdLjE.*{`T|p5dwY|rNįtkR&@@ (" %'1@ '#"'<90%#457654'&# %$47#5! $ڄOTJPE* :MKOxsPWKL,#%5,*,X$Rݿ qu{RJ`/@  1@ /220!#3267#"&5!##J117,#J%x\c`PH? XV{1@ EQ F]1067632#"&'#44&#"326=;{:+fZ#adqR{$6@ !& HE%1@% %0 !2.#"32#457654'&-ULNPƯPTJPE* >:##++LOxsPWKL,#%5,*q` 1@  QE]1@ 0"32654'&'!##"'&76sRVVOcm񈉉qnsȷzn휝dm`#@  1@ /20%;#"'&5!5!!$lYoRR\ W0`b*`+@ E F@?? ?]1@ /<0327676'&'31'"'&5R27ki;jF-*eb`+@EvfwZ{sxvpVh )=@+E(#E*<<1@ *'*<2<20"27654'&'2##"'&7673=A__UVF6˷džfB:VVMpˑRh]p[nmNssg.;Uda@    <<91@  <<90%KSX@   99  9 9Y#&+53;'$ܕ11FA3N11F~0)~pV`6@   <<1@  <2<<0&'&53367653#EkUJ|CUvܷ%aw~LB,BTxnc#n'`8@E  E1@  /<2<0 433233243! &aƏ˪ޏƛa!)R@O@+}&Mj.*&jYquf&}S*f&"Y'f&]YVj 3! # # wHV1M% 'G@)E& F(2Բ?]1@ ("((Զ?]990267656#" '&76#327>&iPDyz]6;~oxҤ]Y:PWp=l޺lǧ_ը,嶖ꀰ-ўqu$ 7@ !EE <1@  04'&#" '&4632  1BSxyJ̃Я#/p~ZZ7Ai6deBWQ I@ "!9Ĵ?@]1@ /<99@ o]0#4''&"562%62#"FR**RMw(oUCHk&_*SKHv H# 0r{C @[)/Bf'nfPWQN'rufpV'A@)   $E(<<<<1@ (  (<<<<02##"'&76327676'&#"DžǷdžǷqMTVMqqLWULc휙owgsugHgusgAm`E@ EE91@ <22205!#%$! 47)323764A,Ma")aM:GϤ*RѧOp[g9&'&47#"54654'&#"563277632327"'532! `7"7$>9[@[`7"7>9[&F]_I I5l|"O z:6hl0'[Ml |"Oz:6hlf$11sXD@!  ܶ0]9ܶ0]1@   <0#&'&76!   76';:{HpҳI椤qVu{ <@!E E ܲ0]9991@   <0"32654'&#&'&7632sVVUVVV9kjstntstu n}{R$.@ & #%1@ %"%0 32#457654'&# '&76)F`{[mzYTJPE* :xe+wTOxsPWKL,#%5,*eNqRQ` 4@ " E!IJ]1@ ! !0")!"32#457654'&g-[oPTJPE* >LOxsPWKL,#%5,*#)@VF'6  (<1@ ( $(0347632&'&#"!!#"'&'53276`1213$)),x:KAb933.1220W@Rd >Qoɏ?s K_7"'&76'&526n 'BQ_'BQ_[~,`*l#FR`*l#FRB@ 91B/0KSX@Y #!3&pM]rV`!#56! #'#64?!"QhRR_@0:IKiXL}/M4!wx#&'#&' #'nd2Fb.-t`4#M!P^sK=W@< 9:?5 +,">99KSX +9> &1>29<90'6767&'&'#"'&46733276=332764''3=D۴vayͤgDd''dey{d;]TCHI}rHGFFtAGCT_8d榈d*0QA^^^Fkmihhimw'AFU(`%S@!'E  E&99KSX"Pe^Ґ8*7D ! ! 12԰.#AL.#^Yq4+& "H4B;;=/?"+VhPOV !! 7654'&#"#676! 3 7llc^#,V)ۄe]6?fضdVj{ # 7654'&#"#67632327\B\\TP%I/yYk}oSKu,2R¤ຐs5%! &'&#"567632 67632'&#" ;!53276n"?E! rK,/ 4'Kr !D<&tEGGH h=" C(FK#C "&E !!6{5%! &'&#"56763267632'&#";!53276[96:@%((%@:6-:IkI:8=3553gs%+$67632! '&76!2767&#"327*W8QU{2Τ|sK^lȺhiieb-sJV"1Pһ '$Astxssq[/&67632#"'&76!27674'&#"3276I,)e[xtgO_\SG]EZSTVXXTRS7xJF61𢢜Pһ ''rsstxsst,V4@  <<1@   <220#5!#!#!3`d`du7U3@  <<1@   <220#5####!3_pzpppg3#"54654'&#"563277632327#"'$47(`7"7$>9[@[`7"7>9[@[|"O z:6hl0%[Ml |"Oz:6hl0%?[MV{$:@&E QF% ]1@%" %04767632#"'&')! $'&  7Z6;x[Y: +STTSST$T%Уb^#10dX4tsstjtssq{FVyMsaq{!&'&#"!!32?# '&76!2%%cjf_[_fMJOhk en(' c\\c( +{!56763 !"/532767!5!&'&#"'(ne khOJMf_[_fjc% ؜c\\c Vs'& @  >  91@ B  /<290KSX@  Yp]@ 6II YY @  &)5:EJ ]]! !###-}-!+V` O@ F  1@ B   /290KSX@   Y!!###`{`UV{'4767632#"'&'!!#5#5'&  7Z=;{XY:eSTTSST$TfZ#10dȪpptsstjtsss'Hs'&y3s''yk&uuN&ruBBBB|#I#IabhFaF`C`#BC`CUXC`C85YBB#Ih;5#I@PX@855Yf4@  <1@/20%+532654&#!#!5!!!2L>o||Rh"9+Fjk&sus'N@  2<1@  IIPX@8Y0! ! &! !!! 'zOFӐhgս6,XNf-T/3@   <1@  /<20!565!32#!% 4&+pٕxL@+8/Xڦ5@ 2<21@   /<2<20!!#3!332#4&+326 z6࡟9d݇,@   <1@    /<202#4&#!#!5!!||Rqf9+Fk&su3k&uu#m'yru; )@   1  /<20)3!3!#++h$.@  . 21@  /04&#!!26!!2)DlN݇@%j@ 1/03!!)ժe4@ <1@  /2220%!!67!3#!#p&axު D+?x4&A((v@   <2991@B   /<<2290KSX@    <<Y@ I:I:I:I:I:I:@  <<<<33 # # # 3DDxM(?@ * %)21@  %&" )02#"$'532654&+532654&#"5>I8z,|йԳƆ\qѲ|!ĐBY+wps{M("3 y@ B  6 991/<2990KSXY" ]@068HGif  FI WX ei y   ]]#!33j+3m&yu# + KT KT[KT[X@ 88Y1 Y@   2991@ B  /<290KSX@    <<Y3! # #_yT:%@   1@  /<035675!#!T>Wxfb/X++0;+s2;@ 1/<0#!#;"++3s'&7#> 1B /20KSX@   Y%+53276?3 3 OM?w.-!suٵ2&]*jklyj =@!   <<<<1@ /<2<203>54&'$%53# W==U+  -=;; )@  <1@ /2<0)3!33#;ʪ+$@  21 /20!!"&533!3_||xdv+ *@    1@ /2<<0%!3!3!3OOʪ+++o2@  <1@   /22<<0)3!3!33#OOʪ++< *@  21/0!!5!!2#4'&#!!276GN6ONDPO+DCDCF&, $@   21/04'&#!!2763!2#!ONDNONDCDCo#N@ <21@   IIPX@8Y0! 7!5!&! 56! ! 'oOzFՎaa0&8@''!&$#(  !%$'2<1/0"3276'&76! ! '&!#3~܂܀s;:ŴL椤kj@@  21@ B  /<0KSX  Y3!!" &$54$)#!:ƒdv'V+w{-{Dp7):@+E'Q! E*21@*$ *9902#"'&5476$%676"32654&}:[;z631-~LӔ{0w)v ,u8w>` /@ " F!21@  /0!2654&#32654&#%!2#!r~~hhVlj9_ZZ^SJJOgyr`F1/03!!`3k`4@  <1@  /2220%!!6765!3#!#}v[(bt:d6(U3Rq{HF`@   <2991@B   /<<2290KSX@    <<Y@ I:I:I:I:I:I:@  <<<<33 ##'# 3?nn`QO6m|(N@ &* )1@ #)) ) KQXY KQXY0#"&'532654&+532654&#"5>32|PZG]twGabLx\l%%pZXkYF@\]y` ?@B  F F 991/<2990KSX@  Y##3y`}`y&# +KTKT[KT[X@ 88Y1` Y@  F 2991@ B  /<290KSX@    <<Y33 ##Tsŷ`OQ5Ls`$@ F  1  /<0356765!#!L8D{X^~ŷoPO` M@B   F F 1/<290KSX@   Y! !### >? ˸ʹ`'P` '@  F F 221/<203!3#!#U`7qu{R`@ FF1/<0#!#`3`V{Sq{F<m` 1/20!!#!<1BB`3=V`\pVg (3B@5E)! '.E4<<<<1@,41$ 4<2<20327&#"#"323>32#"&'4&#"326/{brrb{9SS99SS9{brrb{/Ǩ<9^N5=L^^LN^Ǩ;y`[` (@ F <1 /2<0)3!33#9U`33R`;@ F21/2#I #IRX 8Y0!!"'&533!3Hf\45h)_Vu;;` )@ F  F 1 /2<<0%!3!3!3ڹ"ٹ`3+`2@  F<1@   /22<<0)3!3!33#"ٹڹ`333R>.` ,@ E  21@   /02#!!5!!!2654&q8$~͓7_ZZ^`'">`%@ E  F21 /04&#!!263!2#!z~~@9LZ^_n7q{M@ H<21@   IIPX@8Y073267!5!.#"563 !"'q2 ǚ-VړiVFHL{ :@ E  F2<1@/0"32654&632#"'##3Jq и¾.`At"`<@  21@ B  /<0KSX  Y;#" .5463!##zwwVtS^a\'qk&CZq&jBBBB|#I##Iabh#FaF`C`#BC`CUXC`C85YBB##Ih;#5##I@PX#@8#55Y/V?@N F <221@ /<20#533!!>325654&#"#߰Bvz||яLmedY).ПĞm&vq{N@ HE221@  I IPX @8Y02&#"!!327# ǟ 2ғ-{FViګVH>=o{VyLFVyML`6@!E  <1@ /<0356765!32#!!%2654&+L8DثX^x~~~ŷ7oPv_ZZ^`8@E   F2<21@    /<2<2032#!!#3!2654&+N޹"\~~`7`73_ZZ^/:@N F<221@ /<<20#533!!>32#4&#"#߰Buʸ||яLmed*m&voyk&C]=V&^` )@ F F 1  /<20)3!3!#TfUf`3s48@$%6 )  51@ $-/<2<0"'&46733276=332764''3#"'&':y{d;]TCHI}rHGFFtAGCT_8d{{ђed''deFkmihhimw'AFf^^^^'`]:@  <<<1@    /<20!2#!!5!53!4'&#!!276XNpqONDNOQQfDCDC:@E  <<<1@    /<20$4&#!!2!5!3!!!2##~~EW^͓Lʣ+#3376!2&'&# !!!2767# '&SvwhfstgFtsfjwvú 9$#G_//wƪ//_H$$O{#2&#"!!327# '&'##33676>\" , Ux{ z{FVAW^3VH`3ʀ !#!#!#3 73` !#####3 Ñkk`_ !#!#!#!#3!3  o_<9d7`!#####!#3!3 kÑkk`_s@   9ܴO]9ܶ@@]9991@B  /<<9<20KSX@  Y@]##767!#'&'!ʓdսxQPtՀ`>YY~b҆12z(k{`~@   9ܲ]9ܲ0]9991@B  /<<9<20KSX@Yp]! #4'&'##767E]kKV:VS8V‰Jl&VtO\KtU'4! !#'&'##767!#3!PtՀ`ʓdսUn>qd2z Y~b_49n(.`! !#4'&'##767!#3!7kKV:VS8V‰]w&VtO\Kt`?sVszS#"&#"3276&#"#"'&54763!27654'4327654!"567376767632'&#"ssD#`At bTDt;<}J5?u_hFAXVRuťޠsj#B#' "2ZbrRUgr %',azQ^XRj7&6J- @' WoWdE\`[tO#"&#"32632&#"#"'&53!2654'&'"#5223 54'&#"5673767632&#"vmDPb!',-cX;b12i?,ZnN .rr. >._- > ^ >‘  tӪ ҫ q{&P%327654'&+"&'&'#";67>2# '&5476!36767623 !#"'&'&r-HVV?- ,4, -GVUH- ,4 .xt. 4 .wt. 4 `ta  _tp_   颈   袉   vt&"'0'&#'s3'<cS'&<sV'9@  0Դ/?]1@   /0]!# '&76!2&'&# 3!#SvwhfstkSh$#G_//ӂqV{9@  HE1@ /0@ ]! '&576!2&'&#";#UQQLNONPccccɖ#+qr͹rq;'''7'77'77did}}didii}}}d}}}}dBz/!"'&'&'&547676763!476767623 8  8 g    ) M #&#"56763 v][Jw}$)/K'*Ca"53#7 a#55#53g M 365%$# ʭf'rQ q\t{F` &3@MZg#.#"#> #.#"#> #.#"#> #.#"#> #.#"#> #.#"#> #.#"#> #.#"#> v aWV` v "8v aWV` v "v aWV` v "fv aWV` v "v aWV` v "v aWV` v " v aWV` v "v aWV` v "AKKJLQKKJLKKJLKKJLKKJL)KKJLKKJLKKJLX- #)/'7'7'7%'%53-#%5#53 3#kyo\wyo\zV\Ly[`@¬@_ӤRӤRZy\yW\zn[wyo\ԤRԤR߬@¬@Vm&=yuV8&>!:@  <<<1@    /<20!2#!#535334'&#!!276N訨ʨONDNOQQfDCDC&E 9@ E <<<1@  /<204'&#!!276!2#!#5333>CB>ytts9L^*..+URRRя>'+#!2'674&+327'7Uj~ rGj#u~{Sqrے-,9/~V{)%'7654'& 32'#"'&'#367632*nOSTTSSTFoWl{XY::YX{ ]ststsjts].01d d01j@ 1/03!3!)2$ F1/03!3!`:33G )@  <<1/<20!!5!!!!!N)#l8U` +@  <<1@  /<20!!5!!!!!?`۪ f3@  <1@/0#!!!2+5327654&#)qmL>87||9ժFwrKK"V `3@  F<1@/0#!!3 +5327654'&#rFRRQn!&&1`GQ``07 )(33 3## # # 3׈)D"AMF`33 3###'# 3?nfz!n`QL6mu&z9u|&z3! 3## #E#A`33 3###Tw8sŷ`OL5373! ###ʭd_dTy%u`37533 ##5#`eBTse``avFOQ5a!33#! # ##53ʨ_ʨye=3!!3 ###53dTsŷ}}z}5OQ5}2 _@   2991@B   /<290KSX@    <<Y!! # #!2_=y+*` _@   2991@B   /<290KSX@    <<Y!3 ##!*8Tsŷ`OQ56@    8 22<1/<20P]3!33##!#"dA9@`1@  F   F2<21/<203!33##!#W`39L -@   8 221/<203!!!#!#)"d9` +@    F221/<203!!!#!#W`3ͪJft8@<1@ /<0#!#!!2+5327654&#;"rqmL>87||9+wrKK"V!`3@!F <1@  /<0#!#!3 +5327654'&FRRQn!&&1:`GQ``07&.sAY%.54>323267#".'#"$&54>73267>54.#"+9lR2*DaSN}aF-?jQ&h;>e3.x=&QUW+Byc[sp8<{R?S0 $0>&1H3!(BT1kBtW22Tp{:SJ#&4t}f|}ާbm:E/fcYC(+G[`_&bnqxz?P4>73267.54>3232>7#"&'#".>54.#"qKц][-2`X'V$?/(PtMBpP-\_#D-)*%-8%7CFIGԑLV"- !(,!(؜XFrXbr> %gx@]sA9hY^    , Tָ&^dc+KiB&HiCsu''z-qu{'z ,@ @ @ <1@  /20%3##!5!!A+<m` (@   <1@ /20%3##!5!!B1BL<=V`o@  K TKT[X @8YKTX 8YI:9120@BKSXY"%#3 3;^^DNl!#!5!53 3!ssf=V` !!#5!5!53 F;^^`XXNl=;%3## # 3 3p\Y/su A{+3;y`%3## # 3 3q!r))kLHJqG5@ @ @ <1@    /2<20%!33#!!5!!+A+B`3@  <1@    /2<20%!33#!!5!!xZ9B1B9L|.@   <221@  /20%3##!"'&533!3_qm||x˪Awr7ٟd`F@ F  <221@  /2#I#IRX8Y0%3##!"'&=33!3f\45h)L _Vu;;#"'&53;333###;qm||֐wr7ٟ9d+`5333###5#"'&=3f\4+ _Vu;0$@  21 /<0!2#4&#!#z||f9dK"*I@#$ $3 +291@ $ (+<2076! !!267# '&'&=3%!&'& ":Cppoż vzKB@bHam`_F$$UgkL>D9||f{%.i@.&&K /2@ p000]91@& &"*"/o]2</]90"'&=33676!2!32767'$'&&'&#"XY`09Jt⃄ fgjdcbchneNRS]\RZF1!&łZdc4*ZZWWu'Puf{'Q,(vm'y[uFH'f532+5327654&#!#3!qmL>87||qwrKK"9wV`3 +5327654'&#!#33^HRRQn!&&,%wGQ``07$)`6V!#!567!3#:bCux+8.%5ժV.V+`%3##!56765!s{{v^̳;bVdžf;1@ 82<1@  /20%!#3!3+53276q"L>87h_9dKKV`/@ F F2<1@  /<0!#3!3+53276WRQn!&`3``07V!#!#3!33#;"9dժVV@`!#!#3!33#W{`39V/@ 221@  /20%!"'&533!3##_qm||xɪwr7ٟd+`G@ F221@  /2#I #IRX 8Y0%!"'&=33!3##Hf\45h)p_Vu;;V%3####! !+-}-VV`%3####! !H{˸ʲ>?V'P`yOh'J+1@oo]0{-&O"+1hN&ru  +@ 0?  ]1{-&jR -( +@(o(P-_(@-O(0-?(-( ]1H{o{m'yu@@]1qH'@p]1uQq{uN'r ulq&jTm(vN'rQuF'jN'ru&j:yXL/`T31'q;y'q3N'ruy'jsN&r'u +@ @O]1qu&js +@ @O0?]1saqu{7sN&|r'uqu&}jso#N'rguq&j#1'qr;=V&q^#N'rru=V&j^#k'{ru=Vf&^N'ru&j^j #@   <1/03!!3#)ժA` #@  F <1/03!!3#`LFN&ru&jGV9@  <<<1@ /<20!!5!!!!!!+53265N)#iGRiL`na8VU`;@  <<1@ /<<0!!5!!!!!!+53265?`nFRjK۪`na=f+%+532767 # 3 3*SfL>7( ^Y/su bzK5sx+3;Vd` +527>5 # 3 dkkCQO5r))`&9as mHJq=;3 3!!# #!5!suNt\Y+wD{;y` 3 3!!# #!5)) ~q4H &@  21@   /03!!"!"$54$3!fONDNONNCD#CD+fq` %@ F E21  /03!!"!"'&763!5>BC>9sttyLZ+.i.*RRPRUC 09@2&)  1291@"-(1220!"32765#"'&54$3!3327653#"'&NOO_KV! 3j^nN?4pi;?nhf1CDP_m}`61f[JJOZxx9qs` 08@2F&) E1291@" 1-(1220!"32765#"'&54763!3327653#"'&=C>A@j\-1C]^fety>dhd.*^\:9m4l01a`RUaPOORAsxx%7@@9., ,#81@'2-28904'&+5327654'&#"567632327653#"'&'&\]OOQRSrsdeY憆GGRQ?4pi;?nhf0!JK;$& hi|UV!bb[JJOZxx8PaF|5G@7., ,#61@66'2,6 KQXY04'&+5327654'&#"5>32327653#"'&NHtCDFEwGQPabLqr<=ih<>dhpb8f83,-F@.. NO]@AHOHXDEORAsxueV<):@  '+%*1@!'(/90!#4'&+5327654'&#"5676323#s\]OOQRSrsdeY憆GGRQJK;$& hi|UV!baV|)?@ !+) *1@ / KQXY0%3##4'&+5327654'&#"5>32ȻNHtCDFEwGQPabLqr<dhpb{v^̳;b`WORAsxue{-`6@F  F221@  /20327653#"'&=!#3!zgh<>dhpbW`WORAsxue{`3s0@  1@ 0# '&76! &! !2653d-|e'%{9!Ҏ׿qF{0@ E E1@ 076!2&#"3253# '&q кĽbZZb/n||r|r|>禞f/@  @@1@  20327653#"'&5!5!?4oi;?nhin+[JJOZxx}q`2@  1@  2 ]0327653#"'&5!5!x>=ih<>dhpbB1VFEORAsxue{~{R|ITf:/@ 1@ 20356765!+532765!T:WxM?77fb0dKLøLVs`/@ F1@ 20356765!+532765!L3DF1a.&{X^}з0)oPT 35675! 3 # # !T>Wysu \Yfb/X+3{L` # # !56765! k0X^̶8D')`HJoP~ŷt32654&#!##!23 #h /ϒ0*3V{ ##"&'#3>32&  k\{::{T%+ܧ$`tad dakj3&$54$)!!!!!!3!!"d;>v78ȒFwtw{&/!3267# '&'##.5463!632.#"%;#"w ͷjbckVteVgKww^Z44*,'ėS^a\s4qVZ{TD:V5`ZTfs%9@' !&<1@!/<035675!!2+5327654&#!#!T>WxqmL>87||fb/XwrKK"9+LV `'9@)"#(<1@#!/<0356765!3 +5327654'&#!#!L8DFRRQn!&&,{X^~ŷGQa`07$)oPft!?@ #8"2<21@ /<2<203!3!2+5327654&#!#!#qmL>87||"dwrKK"99V`#@@ % !F$2<21@!#/<203!33 +5327654'&#!#!#UFRRQn!&&,`7GQa`07$) !!#!3#q"r+A9` 3##!#`9L3` F@   8A!p] 991@  /2  9033265332#54&+! '&ˮ® ,gQ]*-呐u\GCF1l[R.$)K@  8Ap]2<991@  /Ĵ`]0 ]376! #54&#"!2#54&#!$ˮîXgQ$9 𝶫F1l[%D@   8!&p]<2991@    /<<0O']32#54&+#!"'&54! 4&#"3)GgQG*ɟn(!ˮî5ZrF1l[=ó|#ӢI|H@   8Ap] 91@   /90O ]32#54&+#4&#"#576! YgQGˮîːZ`F1l[O 9$\)$30!2#54'&#!3276=3! '&X_`07QWWWWˑ呐1[[F1l*1jiij 9㒕$2%!67#"'&543 2#54'&#!3 7654'& f<0I|q4_`07Q5˧OPPOOPP'.ƪV][[F1l*1LL]]]^^]])D@8 :  2]99991@  /0%!2#54&#!3!2#54&#!}gQXgQF1l[F1l[)@@  8Ap]<991@  /0]376! #54&#"!2#54&#(ˮìXgQ$9 $F1l[-:#'&'&763!&'&#"#76! 32#54'&!#"327654:gimINK(*WWWː\!%_`05л9:E5:. rs TfLQR2jjiu$[[[F1j,1i--Q@+#! '&4763!332#54'&)"32765pG혐nG_`07TZ5WWWWܕ.|n[[F1l*1}Hijji):@ 8Ap]21@ /09]363 #54&#"#ˠ(ˮ;dK2V 3@ : ]991@ /0@0P]!2#54&#!}gQڶF1l[327653#"'&!#3|%3x*%qXdq`>WWK7}bbpiOA$3! '&7#'&=33!2#54'&#%" 76'&ɼżg``07Q_`07Q|y&bc\[F1l*1[[F1l*1 椤)!## '&33276=3)ˠ혐WWYWd+&jiih) !2#54'&#!5 uw _`07Q1k,[[F1l*1f'1?%#"'&543267#"'&543 327%&#"32 7654'& oUIeβr0I|q9I9~dX/? 9.YOPPOOPP@$2iw'.ƪdkWM( ]]]^^]]?@  8Ap]1@    /90O]%32#54&#!4&#"#576! )GgQìː!F1l[ 9$\,3276=4'&#!#5354763!!"!2#5# '&WWYW07Q `_# Q70X_`ˠ璐ijjgl*1[[1*k[[Fd%!! '&332765!2#54'&#)呐WWWW_`07Q& ܕ$ujiij[[F1l*1S" $53 6&#!5!2654& #4$ 5JRS覥A ++.WHNMItYa[J\n@@  81@   9/0326=3! #"&=33®ìGœgQm 9-!F2lZ) 3276=3! '&576%7%5zZ[WWˑz=s9W/hiik 9ψ&dAU)7@  8Ap]1@   /<90]376! #4&#"!ˮî$\uB)4'&#"#576! %5%$76aZ[îː 1y=\gW/ίgj 92dAU##576! #4'&ˈKuˮ9)uBGlP| 9\̍P0%&'&43 2#54'&#!3!767654'&'& Eq4_`07Q5e, 7OOPܪƪV][[F1l*1L,@B@^^]t~H@   8Ap] 91@   /<90O ]32#54&+#4&#"#76! YgQGˮîːZ`F1l[Ou$\)8!!# '&5332765332#54'&#^혐WWYWG_`07Qd)jiih [[F1l*16).@  8Ap]1@ /0]376! #54&#"(ˮî$9 uS0@ '&53 7654'&#""#6767&'&5476! "327654'&RQJRSSSSRefg#RHJIIPacIJIJcaW"ccttstNMMNMMM *c" Y[`XX^[Y01YtAAAAtY10 =@ 8  Ap]21@  /0 9]54&#"#363 3^ˠu2;dss:\,<47632#"'!2#54'&#!##"'&=337654'&В􄑑I_`07Q _`07Q*]WW]_WW_rsppzzpS[[F1l*1=[[F1l*1A>T]=BD=[V>Cs2167654'&4'"!"'&'5&'&547632qG^CC95+<&0kljxw{vEB[eK[ 4D~n>=>@%c3A +mlpp/E# ,,W`aru~^#33vx%"#476327653[RBhj[RBhjTDDjlTDDjl}fC^7#47! !"33254'&'#" q3U7a\ "9S A5z\&NZ%03!Z}4b`&^@PP F'<91@  #/<<<290@0(P(p((((((( ]%#"&5332765332653#5#"'&E``ruSSrw?XXyzVU|:<b`^zbzh02>>Vd{?@    N  F22<1/90`]54&#"!!#3>32||Buܟ6V edqV{ <@" GE!<221@  !032654&#"##"3253!!/+:||:Z/\RdaDDadOV{=@ N  F2<1@   /<  90!#4&#"#3>32!d||BuZVH`ed X?@ NF2<21/90`]3!!3276=3#5#"'&>>|TVCuddZL PO_bvfcxxqV/{<@ G E221@ 03!#"325332654&#"Zs:||:էRdaDDad,@ F<1@  /0)3!!32#54'&S[zM`01LI[F1i&&Vd{>@   N  F2<1/90`]!4&#"!!3>32||VBu ed\V6{ )u@ +G  F*2ij$!!$ISX $<323#'&5476#"3276#§:{5`4xBdBJ4/' daZ+h|{Nvqq<q/ 4@ ! GE <21@  <<0!"32765#"4763!33ƈbOMSK}<zaksC+D߫LVd5@  N  F21/90`]#4&#"#3>32d||Bu\edV` @F1@0!!3y^ VI@  NF221@  /< 90@]32653#5#"&5!#3||Cu`a{fcLq0\@ 2 $G,E1Ĵ,1@ 011(1<<0!""<<!<<#"327676''&5476;#&!!'&'&4763[AS].SD81N/Vɮ!qZsIR\++(VL-%)$?뮘VX:@     NF2190`]332653##"&||CuZ{VfcdKqZ 4e@ GE5<@ (''*%%*39/ 91@. '/ 90@ `6666]32654&#"#5#"325&+"'&5473;2/nD:|WCv>!%7)/kPըdaDE<6pG5P0,!K7V9{;@ N  F21@   /  90!4&#"#3>329s||BuH`edTX-b@ (N  F.<<2 -9   /1@%/<<! (90#5#"'&=47#5367$732%326=4'&#XCubdzzp>BiO>AycW fcx{Iʪ`&$%8vJMO;) +?@-% $NF,21@ &$&)$/90332654'&/&7676;#"#5#"&|| M.=<(`Cua p0.- */(fcVy`2Z#G@%  N!$21@  !   /<90;32653#5#"&5#"'&5476;#"||Cu;^PZl}YYa{fc^PzKWV{!<@ #E F"<2<1@ ""0!  3!!"'&547654'&#"#4632/Q@'$C#@l;qsDE E+G56dZY0Y^cԫeed{QFV;`%X@##'  &9/1@#&&990%   ! 3!!"'&547676/&5476;#&(3W:'$F[L2se`6g+! E/>A/(32||BuƯ`ed X`XV-=@    NF21@   903326533!#"&||sCua/Vfc{%i@  PPF&<<1@ "  /<<9  90@0'P'p''''''' ]3>32#4&#"#5#"&533276BYƸ||zUVCdȸ||XW{ed\_`fca_\Vd{7@  N  F21/90`]#4&#"#3>32d||Bu\ edqVZ{J`@F1@/0%!!3y&"`V%k@  PPF&<<1@ "  /9  90@0'P'p''''''' ]3>32#4&#"##"&533276BYƸ||vYVCdȸ||XW/ed\_\Vfca_\V{$U@&E  G/<2221@"%%<<IPX32#"&'!!#54&#"326չ:{{:+Īdaad)qu{RzV*"-6u@83 .# *E7<<<<<1@&7/  "7<2#99#93.  90#,<<. #"'&'53&'&547632##4#"27654'&,Dd%Kcfep_{5S#al~EU@<%I]7_E8BQ-a`ta2N-bliZn!vFDs:#+IJ>8@    NF21@   /90332653!!5#"&||^CuZ{OfcR@<21@//073#3#R` 27#"'&'3U oo,rrONcAUUWDC <21I:03#3#D-dC'KRX@8<1YC %  <<1@  <5G.i=dB]Gg`":T)yX`!  1  /204&#!5!23!5!&nZͦy–1CZ`G 1B /<0KSX@      Y4&+532##n̒[^ޕ<S"Xh`$1/20@]1#!5!t/яd`4@ FN F<1 /<0@ @P`p]!#3#4&#!5!2snvy–t`FF1/0]!#3t`X` #  1/20@ ]5!"#7XNrXGяy Kd` (@ F N F1 /<0@]!#4&#!#!2dny–/``*@ E F1@  <0332654&+532! w`ҏ/t`FF10]#3tXV` , FN 1 0@  ]#4&#!5!2nV#–X` @ EN <1 /035!26&#!5! #Xt뒦X&@ F N1 /04=!3!#T[CLzld` )@ F N F1 /0@]3!2%!4&#!6n`–X`^@ F E991@  /<990BKSX@     99Y"#673632!5!4&WWHFdaxѧȠ˨Vt`FF10]#3tV X` %F  1 /0@]4&+532!5!ny–X(` *@ E 1@  20#5! !"264&+" я0D_ЍNO`U@ F 991B/2990KSX@  9999Y%67676535673VGu",:pΈLƒ4U}*p>1=!"$Vd`1@ FN F1@ 0@]#4&#!;#"&5!2dn\pTQV#–U;zdd`,@ E N F<1@  /0! )5!2676&+;#"&5*4{\Lwuq`U;zCVp`D@ F  91@ B 290KSX@  Y#3>=3#q_V`}՛C!`J@ F  991B /<0KSX@      <=3!5!CcMgXC"`ԛ:V`,@F F<1 /0!#76654&#!5!2#3l)WzB'*˺u,/HVv.4X` ) FN 1/0@  ]!#4&#!5!2ny–`/@F F21@   /<<033$763 76763) :0nLaT`Sl+7`+@ F 1@  /<20!#4&#!+53265#5!2ndDrL~y–a; `')) `')- `'--`@ D103#`n`@DD1<203#3#`|"%0#4'&'37676537653#"'% '##5 rb{ .q & q-aT !}Bs12j{@E#$]} q!<"ibP-F`)*5"2767#"'&54767&'&5&76 '##5M@V:118UF%/>7P6.N@?^G?D)7-#F}Bs)^ &# \*$@.") n F>]KH*!#TH#bP-F`z %3#%3#3#%3#ƴ>^< %3#%3#%3#3#%3#>>^!#53ӤR@ 327654'&+5336767N5G4pQf$h?FA@6b ! eI(R[2* #53 3#ӤR%@-$%#5754&'./.54632.#"'/XZH߸g^aOl39ZZ8{4<5/VVL89CFnY1^5YVeU"756767&'&54767632&767/SD435gcbnZdF31`9:H:ZU!LOTAKv?=0ps2#nl '{R'z>oy3#&}9&m~ &~& (f&X} (f$3  !27# '&5767"$JKԖ^`e~h'?6`vc–e4- (&X}?}R%67654'&'3#"'532# b&\}q  ?%#&'$473327676'&/3327653323#"'&'TPxmil_Qb_y^@@$;sR,%@n\Kf% I01_2F,k>GHܳ&%0l}=J"5^.327654'&'&#"&#4763&547632#bzL,5;(.;Dn2KxAZM\MObxX'*9:X DD(NOf7*(?$S-8APH&}? "327654'&'2#"'&5476B!799[]KB{ƶ`Q%T*WE{R,,9.UMAx|KU#JN @ &"34'&!5 767"'&'&547632?,3/V%._]g>v-(tYhYH9!$3/,;̠X*VL_ !"bWg3ZfJ6%#"'$47376767654'&'&'&'4762#&'&'&VfxH?Ba=~T;~BrC:@_` B(EN><}9M I&huqc- !P85J.39sJ%*==!'&"7*S@UYD J&o~ $5%5%HHnnnn$&567&'&54763233"/#"'&5332767654&#" %!lE?I(7 /4KU^r8Z #08 " -d$* 9^W4'6O'&n=NV)qaK" %$5%%5%HHnnnnn$5%Hnn$-&'&5476323"'&'#5276767654&#") lE?I(7$# +EȓV " - 8_W4'6O -n=*{nmp" %$5%Hnn8(#"'&54737676533254'3'&!9EO)"a 2=`YG g -SGL(E?4mmb}8T"RY$6îs9It6Y ! 4&#"32>"&462X@AWWA@Xz柟?XW@AWX栠h732767#"'&'gC*6:)kXZZC5"LMD6{S )L}@FOwO  4373ËF3# !#'3%1yI !nR#'337673#" %1BR{6)coajr!nUPymL%#'37676537653#"' %1/(/H/; 'G 44.5WY9!nr|> @2%,*;l>3  *"2767#"'&54767&'&'&76#zf\MOYp0;JcX~VI|eepdkAXH,7p 4C@#90L@rRiUZhsBBsǮuu5aU#'#"'532N%bU`DK*22<!&'3673b~ĚZ00ZĥxU:Ũ ;6I<3#&'#6̴UxĚZ00Z~bI6; :d#"'&'&547632#54'&#"=:i_{\ %Z[,,G\O98<SGU37e{a}UwnWl42@B^!x$%-`+-!d! M fM&I&9 &9 &'~& && & (&Xz8 (&X? (f&X~ (f&X (&X (f&X (f&X /'I>\ r'|>\ &'|\ &\ :654'&32! '$&73! 76767#"'&54767632)B,4((7(*Hnق@AZAd#?zKbNLZB`.+M;3*)3P&ڴF=)d \^tL"9;l&NKCW4,E$2Hf6&x~&xx)-%2767654'&54767#"'$473$62 #dGf>5?AhXPA7.EB|=Q#!w*6(  %{{qeVUI&b \^~B")+&&j|H#"'$47332767654'3HdnaPm/1]]LGL"fh8D%jdQ45b`ޜ ('&X}? @r'|>nJor&o|>m}~RLR%'&547632&767#"'#'3X\lTX\D8/0E= %1Bx:=$!"4'Qjr!n8j$(327654'&#"327#"'&5732#"-2!WZWXZV%2-Z(.5__52ZJkV0B7,g`p5oU%mao3/AbM3))I<<d (@  1@  0"32$  h P3343ssyzZ (@  1@  /20%!5!3%=Je+HH=  21 /203!#3ulh=   221 /0)5!!5!3=lȪ=   21/0%!!!3!l =21 /0!#3!=l*=1/0!#!3!=lcr8A'91/0#3ASuNA (  < /<10%!3!#N{ 2@ EEܲ@]91@   /<02>4."#&'.4>329[ZZ_PGr䆇䄄rEMp`77`p_88 1ŧbbŧ1 y@ 1/03#+q!/@ E  EԶ 0 ]1@  0 6&    z>z='+@  2291@ /2903#36Q*=q33# =qCq @ 1/<0)3!39Uq"q @ <1/0!5!!59qKqO!>@#E E"ܲ@]ܲ@]1@  /2<0%!!5!&'.4> 2>4.":RJr 惃sKRQ[ZZ{ 1ũbbŨ1 p`88`p`88 %@    21 /03"3#!5!p9 fq2@ E<21@  /<20!#!##"&6 54'&"3qvCf^]8mr^:<UfɃ]8ƃD '@   <<1@  /0#!!!y5!Փ/= '@   <<1@  /03!!!}5!Փ/ %@ <1 /0!!27654'&'2#!3,R4,,=iXXXlι]Oz}I__ҭ$;@   ܲ_]9@   /999@ 10#4'&'5!4B 5McAq_9V= 491@ /̲]촍]0 53#T9+!-@ #"1@  !/203432>324&#"!4&#"!}x5%^ZHZlK--Xh&|ŕnc= &@   <<1  /<<0!5!3!!#KK?=9@  <<<<1@    /<<<<<<0!!5!3!3!!#!KøL=??q!@ 1/0!!9UqqK==1B/0KSX@Y! #tFC00B~+n 4@ <<1@    /<20327654'&+!!2/!!m]%i ;@ED\TqQE=4."XErrJSRJrCEoJ[ZZO{ 2Ʀ1 { 1SV/p_88_p`88} @ 1/0#!#}+B} #@   <1/0#!#3}Om +@   <<1@   /0!%!!5!!z;  TKѓ+qO $=@&E "E%ܲ@]<<ܲ@]1@  "#/<<02>4."%#&'.4767673 [ZZTXErrJSRJrCEoJR"p_88_p`88 2Ʀ1 { 1SV/ qO(#&'.4767675!5!!2>4."XErrJSRJrCEoJRNQ[ZZP 2Ʀ1 { 1SV/ p_88_p`88b/1/0!!VBf#"&/#332?E=9Qct2 %xf" %/x $Dp/1/03#=f7u91290K TKT[X@878Y3#'#f[fE9190@ Ueu@ )9IUe]]!5'3{Bf3326?3#'#"&'Bx% 2tcQ9=Ef$ /% "[fC9190@Ueu&6FZj]]5%3%[{fS/1/03#̭F'/1/<<03#%3#\yu  <1/0#527#53gu  <1/03"3#  gd 1/03#!!Mdd '@  <<1@ /03#3#!!Mޒ1/0'!! '(033!!3'#67654'&67654&udruxtNMddx>DD>xIIv! RTxXY`aw,0dc1-!:;z{t{*L@$% E+<<<<@!#91@$+<@ (+0%"3254"3254#"54!#"543263 #4#"h??AA??A'+,LW@@@@@@@@pطQQ9/@@1(. #E0<<1@!0%* 00"3254"54$3  !2632&#"# 54-654!"`@@@CvBըiUv˫:knL?o@@@@N;Ejfae:.88U8327&'"254"%47&5476! #4'&# 63 #"'632# i60IKhh*)7!o^RX;*:9u`/'"6OfqAtqLI $\9.ȶlQ!6@   E"1@"  "0463 #"&'7325#'&&7'6met "xCBCquЍ h! ACBB )2@  #&E*<1@  *%/0"32654& 4''&5432#5476$ % U%|{e6Lj` %"%:yx~)RhKK>  65@$- 3 (E7<<<1@ 5/7&7"32654&4763  !27632! 54-654!"#"`$ % 琺By#xJi:OknLIo %"%0yKpjNdfDQcwiC|85sr *;@&%   E+<1@")+&+02654&'&47&7'73%$$!% l݁6ZA| $! $Vm-G4 p?{1@ F1@ <@0%"32544!  #"54$32@@@)@@@@Pvv .<@- " 'E/<1@ $/-)/<20%"32654&672#4#"#"'&#" #"53232l$ % L 7*>(z*M#6&8"$ %"%3|0ۯqiPWu|+?@-$'+ ,<1@ )!,&,<0%"3254"3254 #"5#&767663 #4!" @@@@@@!Ӣ7y-^@@@@@@@@edm%W ,9@. $  )E-<1@ '-+"<0"32654&4323254#4#"%$7"@$ % 쐋'(uj %"%@կ̰Xsgh\_"9@ $ E#1@# #<0254#"53265$54767653!"'#W@@>z]U]iTrs@@@@pegu/ssHs|2@  E<1@  0"325447&763! 3%$5@@@ԶMg@@@@R&Ѩ'LBHs2@  E<1@  0"325447&76! 3%$5@@@ԶMg@@@@<%Ҩ'hBY E"32654&!"32654&&''"&5623253765$7465&'7$ % $ % Kfg饤IJ %"% %"%IKbv4ˋ42@7-]fn9h%A@'$ F&1@&<<@" &0!"'# 432!32533253"3254hfg襤>@@@ JJ=|\@@@@@h} -?@, (,$ E.<1@"&. .<<0"32654&2533253!"'# 47&5432d$ % AfgB %"%4˩/JJ=%܉Mh -?@, (,$ E.<1@"&. .<<0"32654&2533253!"'# 47&5432d$ % AfgB %"%4˩JJ=%܋L@`$@1@  <<03!23! '#"543225O)3Ɯ)`,88{r *;@&%   E+<1@")+&+02654&'&47&7'73%$$!% l݁6ZA| $! $Vm-G4 p& ,7@  '#E-<1@+.%.0"32654&4! ! &# ! ! '&54323 c$ $ 6buUKX $ $8${nE{N%O 0@@2, %&E1<1@%/1!*<0%"32654&&'&'&5! 765! '676%&4% $  ,D )@ ' 1#-E5<<1@ )6/%!60"32654& 4%$54!232#"'&#"! '&5432h$ % ${ajjh@MqKy)LJm_ %"%1EYl0xP^b8Rsu_|]F'"2''&'$!32'&547"32?6AS2;9’hhNU~ +;9jq!Ban' u _ +@   /991@ /0! &7623$'4'74"Y#!A[VB8?<kP$U.FM?>={{+@  E1@   <0 ##"2#"53254#"n=;C>@{jVR777r&" @ji  /1  /<20! ! !5 74! %&?%~?>~@i$@  /1 /<220! ! 3!5 76! %&>%~?>wJ~~@ji*@   /1@   /<20! ! !5 74! #5%&?%~?>~N@i.@  /1@  /<220! ! 3!5 76! #5%&>%~?>wJ~~T3"36654'#"5432AA\(DeN[̼o[$N[u%@ /1@ /0"3254"547&54323253r>Juum@s> [yu?{EBXF_ '656%"'&76! 4"3YVA!. {x9322674&#"CCjFPH OQ$!%!p'(FnJv-O!3] $ $z{&01, ("32654&&3 #"4/&5432N$ % s $ˌeqɘzm %"%82y,v\#"6@ E#@! 1@ ##04$54%&&5! $#"57"3254ix@@@X4|`Pٳ ?@@@@ ""32654&5&'7!$#"47#$ % dt.; %"%Ȉ_p 8>u%t/;4#"#"'&#"$#&532327632! '&57"32654&"3C2z7J,"/IN\=0BWTO3H$ % Xt\DD\t] 5<\UCfwpv  gH %"%V@/1/03#V~!'@ /1@  /<0'6"%)56574 65+*+UGm++),}݅.p\(>.4"!27676327673!#5654#"'&'&#";&543.%2~*&IHHܝBOg(LBC]i%>e>.`h>3A?~= h\$kb8:;-F_Zkf2)N !@ /<<1@ /<<053533##5N؎؎؎ P>r@ /1@ /0432#"73254#"ЄLTPPHHH` " 7654&' ! '&476^L:NbX1coqoh`WĒcg&24764'&#"676'&'&5476  pHgc/5pIu upHECle\gUܚsuϨcy\$24"27#&5432# '&5?$5+r%3]f́|pHFPfouTapH/%24'$5432327#"'&#"%$'#"54322533]L/|tkZ1AQf(3Ɯ)DjR:jTh8KOpt$68{cW%24"$'&5?$532&'&32!r|T9lc ~x?LvTamY<KcW-224"7&5&326532&'&32$'&324!B}b$|T9lc ~xr=Ch(筭 ?fXmY<KLvttY4@'&''"&54323253765'$543227#"$#""32654&fg饤u ^|uISL\>$ % ,IKbv4ˋjEaTW8ҋ %"%{ &%"324"324#"54!#"543263 #4#"h??AA??A'+,LWpطQQ%Rpt MU"32654&254"#&76767%4#"#"'&#"$#&3232763276'767$ % nnvp+-"2D2z7J,"0IN\=0J%.3?5xv'Q %"%933hk//3wt\DD\t 5<\UCrTF-2bG;"b,i $5354#" #"524"m~ŶejsX\|9~ LX"327$"3273253!"''&76324%$7&76%$5#0#&76262654&'&A?A?fxԅ$8$+Rb,7Hu Ӣ5r$!% @@@@@@@mӔJce$3- /ԋu cd $! $~ I"327$"3273653%"'%5254%$7&76%$5#0#&7626A?A? Tcb*@RX6&$Hu Ӣ5r@@@@@@@mo6J,/7'- /ԋu cdPi.".54>7!5!!"32>54&'7i7eȬd7&KlGqǔVXxyӚYlūc66clJ7^sz֟\[{6yEr2b\TZ@#!#".54>7332>53!w!KNM#hN&?Q*nq-Nj=8kT3$ KfWxc*s@nQ/+Lk?Z 5!4.#".54>2!/A%'B/+(=B=if:y'D33D( R0oCEOc88cO'MP.4.#"32>7#".54>7!5!!"@YmEgLLfjJkoX؁q؝XGxdI(YjiMKkii۫uuZ8!4.#".54>32i+Kg<>lP-7:p0M6NifL>@kK*0Rp?>?1ill3eMF}gZ,#!#4.#".54>32!|/@%&@0&%BE;hRPg;(C03F(#P/MCOe96`PFZ(4.#"32>7".5!5!>32&/Oj=kOOδMEHHjMoP.)NpF@pR00RfLLfan0/IP- %#"3!!"$&546$3!!J׉@@ט`a( ]wxԟ\Fww2P:G!!3!n!.x1Z+(4.#"32>#".53>32`+Li=?jM*,Ni=>hL+iJEMfLK{W06\xCKxT.4XwA_bKr62NpZ+4.#"32>#".5##!>32*+Lk?AlM+/Pj<@jM*KihNIHk?pR0,PpEBnO--OnβKKgcBvj12KZ+"32>5!#".54>3!!5!!Q@lO--Ol@>iL+MghONiL.QoA@nQ//Qn@/eMMehJ{PS$!4.#"#4>3!!"632,Mh<>e-PKhr>iM,fL?nR0*'R} gM1Tr@aKfPc K4.#"32>2>73#".5#".54>2*LjOwϙYVz|՚X0/':Yr?DsU0 E nǬc67dȭe7><qU'!RkL)[z{֝[W.>#K]59_|D 6clkǬd77dk{Z'kE"1%P#".5!5!2>53KeiL*-Nj|fH'eMOfXAqT12Vq>P&!#"&'.5467!5!32>534JEp=AB7D4+! )#$+e;:iP/05IGHeLJ )RpEn),/,Nj=Z""!#".54672>53!`NgiMNL6--Nk|jN,+eMKgV[@n6@nQ//Qn@]S#".5332>54&'7SLfiJ,Mi>=iL+>5{-H3eMKg@nQ//Qn@6|>/dfdS 4.#!!2>7##!!!2/Oj;;jO/JGHܓ.gM@qR0,Nl?dEHCMPbF4.#"32>5>54.#"#".4>32YywКYXxԗR6aO"?/$/ .@KZj>mȬd78ekoɭc5[])D0z֞[[z{֞[WwAoV5'//!6cǫc66clv?GOPb 14.+32>#";+##".4>3!2>mTEETmFUl>>lUFk\܀EFޥ^^ހWܢ\YqA@n@oWVn?~ٟ[][ڟ[]F!#!3!3!3FS!4.#"#4>32/Oj;53`EIgJ,Mg<:iO/02Kf>mQ0,Mi=nB-#".'332>=#".533267653BLi`R0Lc8;jO/FIiJ,Ni=:c'YgKSkFzZ40Sm>1/Jg@mQ.+(P|S!4.#"#3632+Kh<7g4QɑeL|?nR0++N|uaKfPk*!5#".54>32.#"32673YUlǬd77dlps[.\YS$wЛYXy^Pr2@6clkƫc6JH,Z՞Z;;xXZ)"32>5!#".54>3!3!Q@mO--Om@>iL+MgiNNi.QoA@nQ//Qn@/eMMehJ35S !4.#!!2>7#!!#!#!2 )4)2VsA4(AsV2*=&$;,S~U+ 9/XZ.?#".'332>54.'.'.54>32#6.#".KfbT1Ng:jN-VU^]4R8eMRlHzY3/Qn@72*63UeMTkH|Z4/Rn@)D BFRZS#"'!!332>53SLf,Ni=53 KihN/Pj732>54.'.54>32#.#"]~|ۤ_-H3K7>kTSj==jS8mV4/Rn@8gQ6';#w٥aL~מY[{:omn:vQNVl=53 ,T,Pf:l #D;%?.a=4.#"32>7+!!#".4>;5!5!54>2-  -)//?%&@.%?/:fPW2WvCDwX22XwDp=  @xY9lY|PW!%! %674#"&5! % %1,lշ._z+,S.+RLo ۤTn8d`'675$!2363 ! ##&!"#"32CxuM6sc*rE) PlaؕyZdX!&732#"&5 ][*8F e]N/I3^@[7rr2dX'!&732=6+537#&5! nN ggGVzkB3L.ķ@JKW~Xq\,d!$75&7! &324'"6Z^,CH!IJ:QU,X\$d56#"! !2363#"32UTcD>0R^<]td'6#"$! +.!TueudY! 473254+5365!5 Wb 퇇2mNEIJ(bC+d`3675$%2363363 565&#'#"#'#&#4%"fDjPQUOR Tg@! 5y<O-6d! !234#"#!#"2mLC{%  }>e~! )!363#"7Y`PlB   ry_d56#"#'#"$!2363 H LDVza!t#rd!! 4732+53274'$53X`4"gzҶ/c7Qib6ȕ!6G))=HdY2! 3325 '%5%Uc| CGko 4Y_nd9$5$#"#'#"$%7367 > B)oQT7-ngDP5kn1w5! 3324&547cTɜW\wؠ?c-'9dY: %3! ! %#d6*Q&q)QGFޕd$! %35#$ 3#3%#" 5;54 X`dHrrr44OfkQؔcdX2&!"'#!525#"3$%2363 #"321ZG\KVOvBppdY_!! %$54#"'! ! 4'7_GD `U6I@bYsrg8A:ԃM){6\lY(3324'7%#"'#723! ߫fB߻cV̿0?7YpdW $!6=3! 47$$5! eڞòkHuLL8TWJ&)*d54&#"'675&%'%"t_CCt?h]|KytJfqI8=ۣ&*2 5#5#5#d 2"4;%"4#"32;ѹF|pux$LRQ´h=@ B1/0KSX@Y %##.d+hK'Eh)hO'zt@1B/990KSX@Y sNO'z)tN'r)u'ew^?1B/990KSX@Y 5](&xyw^O'z1t'56'&56'O'56O&E'E'EO'EO&O'z0'wE&O'wEO&w^O'z?0 3#!38Ygg`nC^^n7]^7nn7]]0d"&533265453zWA@XzCss!AWX@+!U#454&#"#462zX@AWzB+@XWA!s0U!5!2654&#!5!2@XX@s0{X@?X{0U 4&#"32>"&4623X@AWWA@Xz柟C?XW@AWX栠H> %'111 ]]1<203!3CC~K3#K!5!${1V #5#53533zz{{1##5!z$ %{{:'U'"'=wq'h9hK'Eh0hO'ztw^:<1B/0KSX@Y7 5wM40w^O'z)tw^N'r)uw^'w^:21B/0KSX@Y%5^xyw^O'z1t'56&9'56&O'56O&'wE&O'wEO&O'wE&O'wEO&w^N'r1u<291B0KSX@}}}}Y5`sbbs]103C)8)K'E)*@ 8AKTX8Y1  /<03! #4&#"!!ˮî$*\u)O'ztw^ 2 <1 /07! )5! )w5BhPa.,~w^O'ztw^N'ruw^'y` 2<1 /0%! )! !`aPhB5jiy`O'z"t&''&O'O&'w'(O'wO&('y'(O'yO&(' ~21@  0# $54$!3#"3nn͙ nn{'|'|w}'dy'F> %@ 21@  /90"32654&"$54$32#Bz_̀#R3IK'E %@  21@  /90"32654&#4$32#&f̲_ȭT#R3{O'ztF> (@  21@  90%2654&#"3#"$54$3Bf̲_ȭ벃F>O'ztFN'ru (@  21@  90%2654&#"672#"$53z_̀ʃIO'z5t'F'?'~'|?O&~O&|'F&O'FO&?'~&|?O'~O&|?&~  $~ ]21@ 02654&#"632#"&53XP^J\TaaQ_VFTHUGQK})~J8 2654&#"03#"&54632xOaT\J^P_KQGUHTFV}i~F'x'F'x'F> 1 /0#4$32#4&#"#fK'E 1 /04&#"#4$32f#O'ztF> 1 032653#"$5fF>O'zt FN'ru  1 03#"$53326f餗O'z5t 'F&?'~&|?O'~O&|' F& O' FO& ?' ~& |?O' ~O& |?& ~ ] ]1 03#"&53326yaO\T~JPML 32653#"&5T\OaQLMPJ~w:1/0!#!5!)+jK'E!j@ :1/03!!)ժjO'zt!w:1/0!5!_++wO'zt#wN'ru#j/jO'z5t&5&w'&!'!O'"O&"5'#w&#6O'$wO&$'&&&O''O&'&&]10!!3 nC ~21@  0! $54$)!"3͙ nn{3!5 nw} (@  91@  20"32654&'2#"$547!5__ȘLӦnjFY 'i<FY} )@  91@  20"32654&'!!#"$54$C`^ȋMӑnj 'zi<<w "@  91 /20%2654&#"!5!&54$32__ȋfLnjw'z<>w'r<>FY #@  91 /20%2654&#""$54$32!C^`șMgnjFY'zT<AH}':w}';:3'AFY'yA3'BFY&ByFY'rT<A\ 2654&#""&546 !j>_IEcI_(0MJBSKFXCIn~|Q;n."&5332653ܨabaaJPMMPJ\ 2654&#"0!5!&546 _IcEI_>jm0(MICXFKSBJnn;Q|~w 1 /0%2654&#!5!2#bŘ쥒FY 'OFY 1 /0%"$54$3!!"Cꏙƥ᪑FY'z<Ow  1 /052#!5!2654&᪑w'z<Qw'r<QFY 1 /0"3!!"$54$3CbƙFY'z<TH'Mw&M;3'OF&O13'PF&P1H'Qw&Q;H'Rw&R;3'TF&T13'UF&U1\"3!!"&5463RiPYnvDZHCn~}w^ %5-5 ^j22F  ? 1 /0!3#$53TCc Xon2K' Eh @ ? 1 /053#3  cCT-ncCO'z thF   ? 1 /0%#5%3# c--noXF O'ztjFN'ruj @  ? 1 /0%!#3#c-gCcnO'z3tm'fF'f'h'hO'iO&i'jF&jO'kFO&k'm&mO'nO&n&m  ] ] 1  04&+3#XHǜV+.#"#"&'532654'&/&'&54632Cw7Bh#-8GC>=JGBAm'./G?;=~ÇH)@@V\`RʺªV\`RʺªhZ·%XhZ·Fl632#4&#"#"&3326tҪºR`\VҪºR`\VX%Zh۷ZhFlO'ztF'32654 !"/.#"3"54!2!rz|K٬42 swUҤ'4X˧|`í~pX˧|`J3~F'z<F'763 #52654&#"# '4!"326(24׬'Uvr!24֭٣K|zsp~ȕ`|Xp~8=`|F'z<&F&'F&O'FO&'FU''FU&'FU&'FU&'>72#52654&#"#"&'463"326[*'sobI=J>",BR\*$jt_UV) '2654&"#"'&54632! 33265,B:d:B0<~JIjˮîB,">>",BVU_tjN*$u) '"2654&'632#"&5! #4&#",B:d:B0<~JIj!!ˮîUB,">>",BVU_tj$*\) '"2654&74&#"#! #"&547632(B:d:BB®!!jIJ~<UB,">>",Bu$*Njt_UV)O'zt)O'ztS^$264&"&546; )5! '&Vhf# fw_:@ 91@ B /90KSXY%4$32#4&#"!7g#ʲfhXdfF.=@ 1@ B 90KSXY#"$533265!>ʲf"fw_?@  91@ B 90KSXY '!32653#"$5g"ffd餗 K'  '  O' ;' ;O'  '   O'  ( (2654&""&546323326=3#"&=bFntnPX/Q,CEmaZT:KMMKFHn|ppX;oBGj9$ 3>2654&"!&546323326=3#"&=!"&54632!2654&"bFntnP?+/Q,CEmaʔ/bFntnPZT:KMMKFH;XppX;oBGj9|ppX;T:KMMKFHFY<@   91B /0KSX@ Y!"3"$54$3!7YꏙbXhUFY'z<w8  91B /0KSX@ Y!26544#!wb gXw'z\<FY:@  91B /0KSX@ Y'!"$54$3"3!YhbƙXiU𥒥FY'zi<\'%!"&5463"3!\=.̞RiPYB~}nDZHCw%#535!53!3##q=ԭ-!%#5#53!3!3=~0Ԥ!O'ztw533#!#5!5#5q=-ЭԤwO'zt!3#!#!#5353=ԭ0~!O'zVt 33#!#!5#53m unfy~n ,@  221@  /990%2654&#"672#"'"#3z_̀ٷ{O{ʃIH+'sZ@  21  /0# !3! !5aPh//+jiN !!!5!;VnVN#5!5!5!53!!75$i2$i*mւVxnVnՆu!s #'#37 ͉sH+'Y &s & O& 7&  7O&  &  O& !!!!#!YX  !!###!YX  !!#####!YX    H!!#######! \YX     !!#########! YX     !3!!  !333!!&  !33333!!e    G!3333333!!     !333333333!!      !3!!#!?r !333!!###!?r   !33333!!#####!?r      Y#!3333333!!#######!?r        +!333333333!!#########!?r         SC !3!!#!YX\\SC!333!!###!XX\\\\SC!33333!!#####!\X\\\\\\S FC#!3333333!!#######!ZX\\\\\\\\S C+!333333333!!#########!YX\\\\\\\\\\!33!!# #!՚rՙr %!3!!#!!2^DD^ Wc !!!5!5!!!wsX #5!! !!'!%'! !7%!77'7!  ww u||||||||||||u  G7+/37;?CGKO!5#535#535#53533533533533#3#3#!!#3%#3%#3#3%#3%#3#3%#3%#3??????𨨨!!!!aOq:#[!' 7#}CrarCrrD:[! !rarC}rbar=` !#!#3!ff`G [`3!!!!!!!! j /t`Ӕ&{o{4=J%#"'&=!.#"5>32>32#!3267#"'&32767%2654'&#"JԄ℄N ̷hddddj||MI؏ii~ST`Te__ZjkSR\]i߬A@o\]Z^Z5*,=>` #% 54)3#4+327#!5#53!2x9||ԙf_ڪrĐq{Fg`32654&#%! )s7F0Ǔ$g` ! )#53!#32654&+7F0ɖzٍ`` !!!!!! /`Ӕ|1#"&'5327654'&+5327654'&#"567632p<54& #.54! ì++f++$$>:#tNPƳPNM]*U3MY + 3267>54&#"'>3 '# 5467'7*(Ou))Hn.Mw834OMx43N)gA\*g>}66]C_56`?`q{&/=5!&'&#"5>3267632#"'&'#"'&732767276'&#"qN ffjbdjQGhi񈉉ijBN℄RR\]VVUVVVZdc44*,nmn67윜78lkpĘZYWWsttstuq/u{ 4&#"#32/8qu/ 32653#"4/8`!264&#%!2#!#N[cc[H^^>2`!.54763!##"#676#";jpkla;;?î545w?@@?w iQP%$q2^66**TS++2`!&'&'3;3!"'&546#"37545â?;;a|lkp w?@@?wS66^2q$%PQicQ++ST**<m``$ 653 &53sXٹ};ML+%!5!2654&#!5!#TZ`fcL||BtN5353!5!2654&#!5!#Z`fcxzʤ||Dv/{&#!5!2654&#!5!27654'&#!5!#|vz{\MN`_`gb>> E__ru99wSS?yzVU=`YV5`ZX`]x`73264&+5%5!2 'Ӏ{n Fo}ɽBdd>Jm7{3!!I{/=`N`#!#`I``JZ^`367653#5&'&3U9VˆmmV9S`1Ms,}},uMLs` h !3#'!#ZgVXVq`!!!!!5!#!.AeW"___DXI &327654'&#327654'&#%!2#!g1221g̼^-..-^EOO)(N^h+&&MO%%X@? ]65dL.- rUpz 327654'&#%! )[ZZ[vNONN]eefe !!!!!!R-@___S !5!!5!5!5@-_/__H~$5#5!#"'&547632&'&#"326NJYXe|}}|\SRFFPOWWVVWCj]/rssr'y5UVVUL 3!3#!#΀2Wr3# 3+53265A@1(TFDE`Tli 33 ##-<azBm3!!_ 33###|{9="G 33##|_{EEG ##3G|_{EDEH"327654'&$  '&RQQRQQQQwvvwtww[\\[[\\[\vvvvuvG>@"327654'&327654'&'52#"&54767&'&54763sCDDCstDCCBR65<%j<=0ER^X65`l<=ca==ll*6RI)++LK,++,KL++5##,&)$%LY+8:6iG2278PyAAyP87'21I.* 32764'&#%!2+#Y0110YQQQQ))))]?@@?[ #'&'&+#!232654&#=)&''y.,,LPO)*s\^^\$ )(GTD<32#"&'#3t4554455$pMPPPPMp$uuc@AA@@AA86Z[[Z68^gG3#5#"'&76322764'&"Jtt%78NPQQPN874555555S^8Z[([Z@AA@@AAG#!32767#"'&547632&'&#"@AsC?>>>BADbc^]SSt44Va:: 2j88a WW[ZQRmT3210YGMK SX@ 2KSKQZKT[X888Y1@   /0Y5!.#"5>32#"&73267GsC}>?CŻthVau2koamTebXTb2&'&547632.#";#"32767#"&5476G&%HG{065>=f,K,,+*Ib]W-155_;65-9553+,$$4O,, ^$'U13 `fa<))R`1#"'&'532654'&+5327654'&#"5>32FLHG{065>=23-KX+*Ib]V.156_:65-9j2RQ,+ H4O-+]4$'U 12  `33a<))G 14'&#"327#"'&'53276=#"'&763253J44^]4444]^4PP=7633223r99$88NOPPON88$tm=>>==>>FNO e 45k37XX"XX7_z3#53ztttu 33 ##uuZu2u{"4@ $ #32>32#4&#"tHKYhuu'oMLl+yRowtHJZiw[Wk\sa97EBEB~wZXku4@ zx66X6VYYk\sa8BDG 6@ KSKQZKT[X 88Y1@ /0"32654&'2#"&546]ml^]ll]ǁqqpoWGu 67632#"'&'532764'&#"G0336^_]^:5311213p?>>?p3121 XXYY _ ?@@? G4'&"#46320T6667zWVoBAA@qWWG27653#"'&506667zVWoBAA@qWWu#3>32#"&$4'&"27uu$pMPPPPMpf4554455b_86Z[[Z6@AA@@AA#3#;#"'&5#5350Hww33UUPM,V-,vTPn3327653#5#"&nt''N^67tt+78Jy~{Y,-65\c`9nA!5!27654'&#!5!#Ue22<KLg#"FS10gg%dAl88u{(#"&53327653327653#5#"&Q+<=Rnxu$$IZ54t$$KY45tt(78LMlE!"z[+,64\c[+,66Zcb;F&33#&{{y #! !&'3254554#"t nυ9F}攥^ؙ83a _{3#5&+532{t<,||GXG+&#" '&54767&54!232654'&'&yAJZVWVWW!/bL+"766^]l9=P(r(B4?KWXXWr]$,O'(@?Ajp69G  )"27654'&'2##5"'&5476734 )=;67-!XQVVQs~SVV@h)%661FQ:5}t?3XJOZUUXR=\ ,Ajq@:%'#&+53;'&^sa,(^ra,GX]:DFYzg duudnsd&sdyodsdy67632#"&'#44&#"326&_%sNo%ti\[jj[\i92ض78"{qqrG xd%tdV{(!2.#">32#"&'#32654&#"aQQR9||9F,*[cbbc#Lct`5!#3#3!53#53t𰰰त TV/%+53276'7#3/F0j&*06G#367632#"'&$4'&"27tt%87NPQQPN78f5455554_s^8Z[[ZA@@AA@@Gu&'&#"32767#"&54632u1122q>??>q22110h;533` @??@ _ GKv+325&#"47&'&54632&'&#"632#"Z%0\R@5`$^4412/412q>??5{3 * &;/Z ` ?@@biG.&'&#"32654'&7#"&54632''7'37 i:;n\[nO$$ZY drP =67Tb1#"'&'5327654'&+532654'&#"5>32N+,QR2658-56:_651.V]aIV-+K-32==l/|GHL ))unn77wU:8P#P,i/0\+53276=#533343r,Brrtn x66XU P#PG ,5#"3276#"'&'53276=#"'&54763J]4444]^44tPP=7633223r99$88NOPPO>==>>=۠NO e 45k37XXXXn3327653##"&nt''N^67tt+87Jy~{Y,-65\cO9I 5333##53#Irtggttt\\jz~ ;#"&5C,rfpUWlwI 5!#3!53IMjjo\\E\\I5!#3#3!535#535IMjjjjooo\\\\\\V`3#"54;33#'#"3276ztteztry "3rKNB ,|ssW?#5$ z~3;#"&5ztC,rfSVXlx[`+53276'7#3`34r,Bttax66XS gq3!!q_u{467632+53265&7454&#"#4'&#"#367632+=32#4'&#"43r,B0t*pJz>?t'(N^66x66X6V~a88BDwY,-56\uU 4'&#"#367632;#"'&5P''N^66uu)89Jy?>0B,r34Y,-56\sa8BDzV6X66xq 33##q-{{~G 2#"'&5476"!&'!3276WVVWUWWU6//1w &6^]6&WWWXXWWWW@9\[8E-AA.G&.#5!#3!535&'&5476767654'&OpFVVFp^nCWWCnt6%66%4#76$\\FWWG\\FWWE[*,ApoA-9*A@+Fa:.#"#"/;#"'&=32654'&/.547632;1j8W*,]({44MN9> 0Br34@?>=RX l)k`GF@rb/$+*MW33 V6X66x"j2-*TIX00476;#"+5326z73zno>43r,B0]Me30U:Jx66X6#3#;+5326=#"'&5#5350Hw43r,B033UUPM,ax66X6V -,vTP^!533!33##5#"&=)3276^ntgtuut+87Jy~''N^61\\`9Y,-6/G&5!327654'&'5!# '&54767GE()78Z[78*,?G$"ZYYZ!"J\{':?KY7667YR8>#{\8?>LRRQRR<=:u2653#"'&53QHuDEEDuHPZs{>??>{}ZPz3+"&53?27654'&'&gH#"YZ,rftA Z87)2:08?>LRRlwpU67YQ8C&# #3{{ s7n !!!5!G'L\^=R^7!!#;#"&=!5!G'LC,rf>\^=R VXlx ^7^n#47#5!5!3632#'3254#|`\'Ln& m,7!!^R^=jR37!2#"'&'5327654'&+5!5!hCQ>63``;??C5~Ex>?::hn\& =;M|CD m**PJ*)]R^G !32767&'&"2#"&76So/6^]6/ +66,ǗWVVWVV*MWXMmGYXFovw^wwwv[f!5!73[f3!Px[f#'!5f[f!!#PU騋fBf 3#'#35fxBf 73#'#˴fxh'${-{'TDN'zs%N'>E&%&E&%&Esu'l'sLvquf&vCO'zt'qbN'>G''qZ'zG&'qZ&GOw&'z[quZ&Gz'&'qZ'^&GZ&(q^'HZ&(q^&HK&(7qK{&H7v&(qv{&Hum'yu&(zquH&H'zK#O'zvt)/P&I @s&*2"qVZ&JI;N'zs+dN'>K;'+d'K;P&+j@dN'>Kt;&+ztd&Kz9;&+ 9d&Kv&,Jvg'LYZ&,tF&ajl'sv.l'sZvNj&.&Nj&. &Nvj'/''O jk'*u'/S1'q(;j&/J'Oj'&/\'&Ol'ssv0f&PvO'zwt0'FP't0{'P3N'zs1d'Q3'1d{'Q3&1d{&Q3'&1d{'&QsZ&2fqu &RsV&2lqu&R'jotrsZ&2jqu^&RsZ&2hqu^'Rl'sv3Vf&Sv2O'zt3V'STN'zs5J&UT'}5J{' UT1'q}; "J&q #T&5TJ{&UO'zt6o&%V'6o{'Vm'sv'z6of&V&VvW&6o&#"O'zt *o& +*O'zrt77N&W#>'q77'W&7b7&W'r&77''&W)'8X`'{Xv)&8vX`&XK)&87KX`&Xu7)Z&.8X&+v)4&28X'Xh}&9F=7&Ymh&9=`&Y^Dr'u|:V5k'C ZDr's|:V5m'vZDN'j>:V5'jEZDN'zs:V5&ZGD&:V5`&ZJ=;O'zs;;y&[g=;N&;j>;y&[jfO'zps<=V&\f\m'vu=Xf&]\&=X`&]1\&=X`&]d&KfN&Wj->V5&ZB=V&\{a&D/P&A@7&#"#4>32"#"'532654.546m@f_@&9dc07CjjCӴmob)F[dd[F)Z@hoϋ\(Ž}_-C-->T\_EFvX5P3) $2BgCquHh'${-{'!Dh&$u{-{&DTh:&${'Dh:&${-&Dh[&${'Dhu&${-'Dhm&{-f&"hZ&${-'DhZ&${-'Dh&${-5'DhY&${-&Dh&{-&3&(q{&H&(uq{&H^'tu(q7'H:&(q'H:&(q'H[&(q&Hu&(q'Hm&qf'& Z&,#uD|& &,.&Ls&2'qu{&Rss&2'uqu{&R}s:&2lq'Rs:&2jqu'Rs[&2jq'Rsu&2equ'Rsm&'quf's& sgk's'ubvf&vscgk'u'ubvf&Cscg&b'uv{&c}g^'t'ubv7&scg&b'v&cs)&8X`&X{)&8uX{&X}_k'suqif&v{r_k'uuqif&C{r_&qui{&r}_^'tuqi7'r_&qi&r{r&<ur|=Vk&\C!'v<=V`'t\&<r|=V&\`^'tru<=V7&w\qa&E ppqa&E Hqf&E }qf&E qf&E ~qf&E qm&E vqm&E Dha&& p#ha&& f'& }|f'& f'& ~SXf'& om&&1 Qm&&x Na&I pDa&I 9f&I } f&I %f&I ~Of&I R-a'* p-a'* 7f'* }|If'* f'*" ~Sf'*^ oVda&K pVda&K Vdf&K }Vdf&K pVdf&K ~Vdf&K Vdm&K Vdm&K a', pa', f', }|f', nf',3 ~Sf',d om',t Qm', Nna&M pna&M f&M }'f&M <f&M ~Qf&M =nm&M nm&M Aa'. p5a'. Kf'. }|Kf'. f'.4 ~Sf'.p o"m'. Q)m'. Nqua&S pxqua&S nquf&S }equf&S Tquf&S ~quf&S a&4# pVa&4} Of'4v }|Yf'4 f'46 ~SPf'4w o*a&Y p=*a&Y *f&Y }'*f&Y !*f&Y ~`*f&Y W*m&Y 8*m&Y Ia'9b f'9 f'96 o3m'9L N'a&] p^'a&] T'f&] }Y'f&] ^'f&] ~'f&] 'm&] c'm&] ^a&=N pqa'= if'= }|uf'= Cf'=t ~Syf'= om'=B QPm'= Nqf&E tqf@f&I TfAVdf&K VdfBnf&M fCquf&S {quf`*f&Y 0*fa'f&] M'fbqVa& HqVa& HqVf& HqVf& HqVf& HqVf& HqVm& HqVm& HVha&  oVha&  oVf&  oFVf&  oFVf&  ohVXf&  oVm&  oVm&  o2Vda& 8Vda& 8Vdf& 8Vdf& 8Vdf& 8Vdf& 8Vdm& 8Vdm& 8Va&  oVa&  oVf&  oVf&  oVnf&  o#Vf&  oTVm&  odVm&  oV'a& YV'a& YV'f& YV'f& YV'f& YV'f& YV'm& YV'm& YVa&  o\Vqa&  oVif&  oVuf&  oVCf&  oVyf& ! oVm& " oPVPm& # oqH&Ezq&EqyqVf& $HqVy&EHqVf&@Hq7&E qnqV7& gHhm&&yuh1&&q;f&&B RhfVh&& oxa pVxaH <ܲ?]1 Դ?_]KPXY̲?]90IIPX@@88Y#55#53xgJ7FJm'tjVdf& (8Vd{&K8Vdf&B8Vd7&K qVd7& v8f'*b Ruff',n Rf V;&, of' p  f' p. BJm't pnH&M$n&Mqn&M .%x7&M q.zm&M r0gm&.y.uY1&.q.;f'.q R}f!~f'  f'  _Jm't *H&Y'*&Yq$*&Y *DVa&U pVa&U *7&Y q'*m&Y rm&9yvu1&9q;f'9 Rf#5a'6 F)&j lFRfCV'f& 0YV'`&]YV'f&bY'7&] qOV'7& Yf'4; Rf"f'=D Rf$NV&= osRfvxaH ܲ?]<1 Դ?_]KPXY̲?]90IIPX@@88Y53#7"͔gd10!!dd dy/10!!dOydy/10!!d8ydy/10!!d8yy/10!!y&__J&BBB@ 10#53ӤR?@ 103#ӤR՘?@ 10%3#ӤR@#5R՘?m '@   1<20#53#53ӤRӤR??m '@   1<203#%3#ӤRӤRլ@@m '@    1<20%3#%3#ӤRfӤR@@m #5!#5RmRխ??9; '@  YW Y <<1<203!!#!5!oo\]9;>@   Y W Y <<2<<2122220%!#!5!!5!3!!!oooo\\3!   \ 104632#"&3~|}}||}3q31/073#k1/<20%3#%3#V #@   1/<<220%3#%3#%3#ki3#iq L #'3?K@D$%&%&'$'B@ .(F4 :&$L%IC'1+C =  1 =I 7+ ! L9912<<2220KSXY"KTK T[K T[K T[K T[KT[XL@LL878Y"32654&'2#"&5462#"&546!3#"32654&2#"&546"32654&WddWUccUt%ZVcbWWcdWccWUccܻۻۻۼܻۻ q r "-7;EP\"32654&'2#"&546"32654&'2#"&546  &54%3#"26542#"&546"32654& WddWUccUyWddWUccU<¹ߠZucbcNWccWUccۻۻۻۼ5ۻ(`3(`u(`&  ,(`' ,&  X(`#3W`u(`&  ,(`& ' X , #'#Rs#G@%Bon29190KSXY" 5s-+#R#I@&Bop<9190KSXY"5 +-#^R^  &K'N''=NO'^O$#5>323#7>54'&L Za^gHZX/'-93A% #C98ŸLVV/5<4BR-5^1Y7| B_ % ij991@  <202$7#"$'56:<hh~vvuw~ign % ij991@  <202&$#"56$6;>nvv~hhgi~wuI3 # #bbc$$v=' {' { 3_!!V_+@B10KSXY"3#-\X 3!!#3hX^#"#JX 53#5!!53X^JݏޏJ&""gJ&"JJ'^"d] 7 91@ B  <20KSXY327# 'du](; 2###׎辸( 3+"&5463yv}~}|( ';2+v~}O|}=k {B# #5#5R#۬@n&  =o'  BC''Hd1#"'&'&'&#"5>32326撔 錄ܔ撰 錂1OD;>MSOE<>L~ 8| #'7!5!'737!!qaqqaq)`rrbqr2 535353,(`$' ,& '  XfN 53!535353fXp fN 5353535353,p  3#3#'d 3#%3#3#3#dipD %53535353#!5!3!,|f  feP> 3#3#3#>w 3#3#3#3#W "27654/2#"&5462332233VVVVVVVz@ <<1@03#3#zttttg? @   ] <291<290KTKT[KT[KT[K T[K T[X@878YKTKT[X@878Y@T /9IFYi       "5GK S[ e]] !33##5!55bf]myf !!67632#"&'53264&#"y^^a`<~B9>>Eoo4h6_ MLKJq ff\/"327654'&&'&#"67632#"&547632X3333XW33331221DD &9:DTTXWll122m45[Z4554Z[54bg KL1LMONuv l!#!liH30Y *:"32764'%&'&546 #"'&54767327654'&#"55j]\655T./RQ./SZ85UVUV56-/.UQ100/SS0/*,+KLV,++]12Hdt::dJ01:7PyAAAAyN98?&%%$A?&%%$S.532767#"&547632#"'&2654'&#"1220DC #<9EWXWXkl122Xf33XU5443g KK/MNoouv rh\Z4554Z\44k !!#!5!Q_i_k_8_83!!'3_a!!!!''^_o #&'&4767TRRTe^///._~g3#676'&ge_/../_eT)**)~~~u0@ 32tNN^luu)qJy}wYYk\sa88WT dC{d^TtdbTud?C dfC d\T dlC dYT dST d d8 d  doif dgif dMrdGxdGdu!sdGydV##"32.#"3267!!!!!!Oc%eNLbbL:/667756GFDFG ks9'.473&'3267#"'#7&'#7&'&76%73&'hA>/(%:@w]ayA9&AX}R4>C5Ai<)^_HH?WghйKp(`,%6767# !2.#"3>32.#".aXj]aye6{_]w|^0n&<$'/_HGghGG_^ٜu]\Y!!!!3###5qZpP~WHE9Eb#!!53#535#535632.#"!!!5-쿿=OL=tyB_))HB+#&'&#"#3676323632#4&#"#̪m49wSS>YXyzU6%X\xruxGM_a`f21>&>E3\u"&)''#!333#3#!###535#53355KO8~8~OO4&{{&&{{{ P32654&#+#!233!!;532654&/.54632.#"#"&'5#"&5qzzWQeGl`[z_<`HJU];Ufɘ/ϒjqqR>N#55YQKP%$((TT@I!*##`3E326&##.+#! 32654&/.54632.#"#"'&ٿJx}A{>[b`cae@fLNZb?ĥZa,/b؍$~3YQKP%$((TT@I!*;"&)-1'#53'3!73!733#3#####5!73'!!7]:1000019]zu }Luuguuguuuu_ % #4&#!#)"33!3_SV*$oN&1@: "+ /) 2+"!)#&  , & &*!/<29999999999122<20K TK T[K T[KT[KT[KT[X222@878Y@z  1Ti lnooooiko o!o"o#n$l%i'i-  !"#$%&'()*+,-2   USjg ]].#"!!!!3267#"#734&5465#7332[f A78 ʝf[Y`(77(6bbiZȻ{.# .{ZiHH"{/ #/{"G(33!!###5uX_Tws1s!5!!77#'%5'&PPM4Mo؈onوn9 -bw'67>32#"'&'"326767654'&'&67'>7632#"'.'&/#"'&54632326767654'&'&&#"32">1aJ{%A01Q[W7>/W1   >$<  . #dCw-^URB$`>DL_K>.3b @N\uLMiI(S395l9,8G(/&  -9)ЗiRm:3Xwdg7? 2j7#=5(6$ 629T/ (2M !:5S}$@{mbq~Es/4 -& "TAB`]|@8nRkcd]aC".)5'632327&547632#527654'#"'&#"%654'&#"o|@X"07PYtaTk~j[IwmqJ2530D#24!`NkBX``S㫣†qJ323!!!3267# $547#5\J5 ;_srigCS1r{jJ,{ +kv67&&UB{\* {;^~FE/0K?{w!,&'&#2767#&'&576753w[TUeeUT[Y\Y[dsye]Y\[CvlCi----iH$"u9Bt"#BuflC3!~d=!5!'3 G~d=z!#'73!5~~͛=z5!'3#7=~~d͛F 3#%3#%3#yfPF 3#%3#%3#%3#ky)=z #'73!'3#7~~<~~͛͛C $(B"326=7#5#"&54634&#"5>32%3#.#"3267#"&54632pSHfmƩogDc\GD^o8yy8o^IICBRCI M >OW\ 7$44"C +EI.46'&#"#&'53254&'"326=7#5#"&54634&#"5>32%3#VNz$p;i0ʪ%={pSHfmƩogDc\GD}|49d$, !5Lf,1BRCI M >OW\ 7$s'!.#"3267# !2'Y藣yyYjzS #bvAZ4-4ZBuHHghG[!!m&r&F+,/-/ܸܸ,(и(/A&6FVfv ]A] и ии# /!"+!0153&'&'6767!!5&'&76wI3cc3I86QLNN7887NNMR48_ki:rq;zn #++$ * rn<(2.#"3267#"&54632%3#"326&$  &54^o8yy8o^IICDkavva`ww~44"K <M-1332653#5#"&.#"3267#"&54632%3#\QPcu`^o8yy8o^IICDLriuD P44"K{Ro#&&r)Io!6767632#"'&#"32767#"'&'&547!#"'&54632327676"#"'&'&54767632l(9BKc{=&%%03!((!,739%7`lG;7 25]hB4,'5  'B[QF$%]c'G  %! }Kr~,1ьIg)*!&!(D;w},75;!_']7:y}[Ϟ\@4>#,!, 'QFj(JG4$$,*)/9yK#%P73276767654'&'&#"&'&"'632654'&'&54767767#"'&'672#"*i(X%# 1FSE/ O.55FuPU[QF[00rl~"KI}!;IFs;n;_T^͌Q79}w^l.Gyr\[4O9%#i#^MX;yv@c}e.ID\7I;>2V秉uӰ3!3%!!!!!!nnq  dx+%H#>54&#"#3>32u j_ y/wFx \/HT^Ȧ^m$RZ3%632##"#'7-P4-> {|a\=BcL;t9#"'&5476323276765"#"'&54767632thn<7# ;KQ>!|Za,4(XM!},‚<7D9#7.M=.1?@ '(MXI(' jF!2?632327654'&54?#"'&#"632327#"&#"jou9!ydG>PPPP5ʺ68^nm{z}}ȋo֏zZ'PVaK~pmdykb^OP681/::b:DnJ327654'7#"'&'$#5"'47676766767632#"'&'&'&#"32nZS_n0VBRny#HB?X!$9BMw>7l. ;7%,;(ӧuy,D0&3273#"'#67&5477632654#0)W:K32#"&'####53&  O:{{:ܧ$}daad}j %# !3!# dX0dd q+6+/BB/,/<-ݰ.<-ް#? < # 9 FhH)##Ii;BB=#IbiF`FaC`#BC`CUXC`C8Y& <BB00<İ< 6< <9 FhH #Ih; < ְ ݰ,9, FhH &ְ& #Ii;/,#Ih:1#IC`#BC`CPX& ,/C`C8K RX #IC`#BC`C@PXC`C@aC`#B C`C8YYYBB=#IbiF`FaC`#BC`CUXC`C8Y#)<BB1#IRX   <  < Y3525!463"!4632#"&732654&#"5!6jgggg92299229k̀k@4nNggNNggD{{ "-! ! ! ! '32654&#%!2+# JR12)uyӲckkc?L00ey wXQPXdn;C0<67632#"'67327654'&#"#"'&57&547276545[ۄFIyeL )qz]E& JEYq:?.蔁0.A ƂMkeLPק<+(h|H=y|n=B {u.F/4_NT 33!27&#%!2+!67654'&,d.@nX<-]\,q jdZ)VV)s!)%#'# ! % 7& 676'&B 3y;:x+lllli$ #ab[ 2222jT%%5$c$B2 _327654'&'&'#"'&5476323276765""'&5476!6?232767#"'&B=]iS\ZV30Fn7;#FfS9!!< #5,h";<2XngZR{,##9>;K!QIag£S D5@7*'S:y}*7H0 5#!,Il @3Xnh0{(2r:=OSlIX&54'&#"#"'&527654'&#"3"'&547632763227767654'&#"R(O*\xggfg-.@@?@@?\QA@@@S6fggfeӻp/$~AB}:1$ -*MJJ@f[+8vuuv zVWWWXWWVVW\uvuuu# bW1W{|^1$h{vC[SK\GChfy /2 &.2&'&+3!.+!! !27&#676'&%3LDEx-Me5q>HJxnu1EA+ZY*01/O~hbb)j)V>U)-  /!/ и/ ܸи!ܸA]A)9IYiy ] и /9 ///+ +0132654&#+#!273 # #s sNCI/ϒ_6۬kk%T$+.3&##&'&''7#!27%7 67654#?\A>:AٿKE6ToF^~_ ,8~|T3Jۏ/HDh0& ,ok؍]-Dbg('4.#"#"&'532654&/.54632733###UW'AG/E8pi4sG[d/EK7?8pc|3iиY"*/( VAO[`*,2,* M=H\T(l0`!!#!!!!!!!3!!rso+` `ffff'F >@!    b b cbc91<<2<<903#######5Jq7rqr/B^^"h %73# ' 3,o-MoF+,\ %#!!!5!8kO8d qddd XL/ 654&#!5!5!5!!2!"'X $''ߦԧc̆eeaԊfJN=NsDU767654'&#"#"'&5733272632632!"'4'&'&#"'6763232767654'&'&#"_}yj#1Q\$####,TGG\n#?QY>kDM4giMqE#"'&'&5476?&'&547632#"'&547654'&#"3"32767'_ilE_ml=Oc{T3-2") %+fa@aP/Z_|{w:maZu> IhA"%@_l$=PczS2VN-2!$+%$+@e}N069na[u>_T M#"'&'!#!"'&547632327676=!7!&#"#"'&5476!27327#X':'7?<=**M_4. B^l{>!'Ba>nG#&#w4$B00!K=DcK_4B( 03B{>ceDInFT=I,Fw7K. 0# )5!!5!3#Pʪ9Bk32767"'&'&47'&'&'#"'&547632326765&#"6767632377632#"'&'&'&#",5(.'*'E`97y{7a;f7;>F3.^PeMD*#7@,j!HhH<=.%_yipp3 T}B',$ *5܀/,,@!;Da97TVM;nwF^O?/,%!;>jytX<;}f?E'_n H''#  .hJ) 4&#"322#"&54WOmVPm˜ݢt}t{أأg4 4'+5654/&4?'&547 '&5474/c2>Bd=VE/b5c2ltc2c2uc1LS2?Bd,>8?]/c6c1LS2tc1LS2c1LS2903#!".54?>3!4'.#!".54>323!2O,""$%@;5H *Y[#$"x2 1[G(  WA,!2#"&/#!"54?>3!!"&5462TPl 0%= -d,mF"$mG- .7#*(/ $"Sae(!q~B;V&!"&54>323!2#"&'&5 mG * 5G 0%9 . q~( 0 (/ &Js!S'DQIF 4632#"&3!53#5!pQOooOQpoTQooQOonuyy5yZR; ! ! ! ! HH#[[breH !#y;:x L`  !!!!#!3#'!#33 # #DjwZDZ֏R``C5MR.}$z`-1%5"'&'&5#2327#"'&5#!#"#463!#3#, 9Yl(Ht*=Z2dr!Z4@'!8 ֦zEB bLs{dYsZ{3#"#4763 3׮UEEl4FũdGQnCF\xB*WbOZ=0 3%!!,:*nq dd3!3!!!! nn8q  qwS ! ! !!5 5Y*dccS!!6$3 !"$'53 !"kJu^uopkoSUggHF_`2/.2%!#!5!)+!5!_++!# #3bef9WJ " )327&#!3676654'&|tK"P"coAfյ|cv~dAA xPfUmZ #2!7#"547632!3 32767654'&#"* 6B8wx!Nbb|˞"#>|OO'vN 2wx87tKsO=  =d01 PD10d^dTd6Jthi[{ (232767# '&5477632!7!654'&#" N&#G_yZ\klmk}Z5fF 9NJC0<7h:J(u*oDMcFPZd82vRsO 3#3#!!ɸ.Ԇ$N9`V 3##676#732767!ɸ.fʆ#5H2K1i0/N)deеT0Hd01``;&0 #473>32#"&'532654&7>54&#";Ht]h202޸SUWDi;2[UԠ_I@Yr~YW׀c?}<$$/1oX3gQX?@Q` $@   F 21@/0!5!!5!`oX&{' 5ud^X&t' 5ud^&{' 5 d^^&t' 5 db^&u' 5 d?^& ' 5 d~&{' 5 df~& ' 5 dw&{' 5 dbw&u' 5 dfw& ' 5 dlw& ' 5 d&{ 5,'&,,&,',,(Q&,9h9&9,,&9',, &9',',,-&,;=;;=&;,=B&;',,j/s'&'0yL&LLpY&L'LpLA&LY=`Y=&YLD=-&Y'LDL=&Y'LD'LL$J&L[;y`[;&[L[;D&['L[LyOq{FqZG{Py }  ) !3 !## !5hPPh55~ji.,w# + ++ A]A)9IYiy ] A]A)9IYiy ]%"+++ + 013 !#3 #32654&#! )5HHNhPaY.,职~y }(1C3 +3 !32654&+! ) #"35# !35#"&546!`HH5NNPhthNN5H/ó., ji~s'H{d?8   2@ @@ 00 ]1@   990@   <<@ <<KSX << Y5!!dx=xUZxx @   991  2@ OO ?? ]0@   <<@ <<KSX << Y3'#'-Zxxvx<xuP8   2@ OO __ ]1@  990@   <<@ <<KSX << Y'7!5!'7Pwx=xZwxx @  991  2@ @@ PP ]0@   <<@ <<KSX << Y#737Zvxxx76767632&'&'&#"#"'&/#7!#/)85,0F"<;NJX[GR7<"#!2)85,/$#?2WG[XJN;?,!F0O<:" %7xxUZxaxxaxuP8 '7!' 7!'7Pwxx>xaxUwxx>>xxwd?8 !5!3#xwx-xZxY %'3'!!5xZxZxvx檪uP8 22@ O O _ _ ]1@   990@   <<@ <<KSX  <<  Y!#3!'7'8窪xwx-\xwZwx !5!!7#7\xxZxx+xvx7!!5!7'3'xxxxxZxxvxxvxd>%52#!5! 767>54&'&'&>42/+-+-':1 Hxwxܪ-)o=  xwZwx(.46<=69)-d>>3276767654'&'&'&"5476767632+#5!5 6 +/24>A1:'-+/24>xwx  =69)-(.46=<69)-xZxvP>54'&'&'&"3)'7'7!#5#"'&'&'&5476767632# 6 +lxwx>42/+-':1A>42/+ׂ  xwZwx-)96<=64.(-)96=dP8X#532267676767632267676;'7'7#""'&'&'&'&'&""'&'&'& xwx 0$#$   "%'-0$' !  ' '- xwx  ('Z&("  "(&Z'( -xZx$ -#%"&* 'xwZwx ""&*  *&"" dPF%'!5!!'7'7!pxwxpdxwx^:5xZxo:xwZwx* %'7 !^ b9YXxbZ  #!5 xwxoxZx[ !'7'7!#xwxxwZwxZ  !5!3 ixwxDxZx[ 3!'7'7xwxDxwZwx 7#7!5xwZwx=xwxd? !5!3?=xwx-xZx,-eX&7#754767676 #4&'&'&"9xxZvx.-\ZnllnZ\-.BB54'&/#7!!#"'&'&'&54767D !BB54'&x\-..0YXplgtTY0../Z#,@#B"!BB@RNJV]xwx]TQ>]xwx]xLii `iiT4]xZx]4]xwZwx]JiiiiuP8!7'!7!5!7!'7'7!'7!5giiyYuI0]xwx]uIiixK]xwZwx]Kxd?8!!5!!]xwx]7Qix]xZx]xi#'3'#'x\xZx^xhP8^xvx^huP87'!5!'7'7!5$iiQ7]xwx]iix]xwZwx]x737#73jhx^xvZxx\x%hh^xvx^8dP8!7'!!5!'7'iili\]xwx]]xwxiii]xZx]]xwZwx7''3'7#7iii]xZx]]xwZwxliii{]xwx]\]xwx  #7!##PU?,UvU,?UP5#'#5!#5'U,?UvU?ԄU4 753!5373U?ԃUPqPU?U 433!'3ɕPU?UqPU?,Ud?8!!!!5!!c$R&xwxxxxZxxuP8!5!'!5!7'!5!Q$܊xwx&RFxxxwZwxxd?8#''''#53777?(FncxwxFn-FnxZxFnuP8577773'7'7#'''unFxwxcnF-nFxwZwxnF3'!!!!#!5!5!5!'-Zx((ت&&xvxTrx#7!5!5!5!3!!!!7Zxx((&&xxrTxd?8 5!!5!35!dxqxUZxxa 3'#'3#3#-ZxxbvxrxVuP8  '7!5!'7%!#'#5PwxqxUwxxw( 737533-vxxvxrxv4k?9 !#3?xvxתx~\xuI9 !'73#'7!uxvxxvvx7?~ 5!! !!  d }*^V 3! !!d}*p  d HP~ !! !!    ^V #!# !!!d e n ^V !! !3 3!!!E*dr*r$| \d^V )3! !3#!5#3 3 ȃ\Pdx @t %#!5#3'!3!3! !33'ȡdxd:tZdd\nt^V%#!3!3! !3!5#3ĹtIt\Px^V%3 3!!! !!3 37r*kd d| ^V %#!5#3 3!3!! !!33 37ȃ:͊` \h u}~ 7!! !5#35! u\Pdx f:bȃ  zM!#7!!#Mc"?,^xc?x^zM35!3!5!73zpc?Jx^cr+a?^xJ^V 3 3# '! !! !  e   dCuP8)5A '7!"'&'&'&'#5367676762!'7$"!&'&'!27676Pwx 21@=:C.2  21@=:C.2 _x_R#)l$h$#R#$Uwx@21.2@@21.2@xw#w;' , utP'7!5!'7!5!'7!5!'7Pwx===xUZwxתתxwZd?D5!3!!#!dx3xUZxmmxuPD '7!#!5!3!'7Pwxͪ3xUwxmmxwdPD3!'7'7!#!5xwxwwxwxmxwZwxmxZxd?D5!333!!###!dx⪪YxUZxmmmmxuPD '7!###!5!333!'7PwxYxUwxmmmmxwdPD333!'7'7!###!5d xwxdxwxmmxwZwxmmxZx7?@  !JBJAu}@ 7'!5! PJBł}BB7}@7'! ! 6BB A}BBh %!3!3۠ՈR+nm+A&6FVfv ]A]+ +0132#&'&#"327673#" B!OO!BzcI7͙7Ic_L 0"'&547632654'&#"563 3276767&#"\m`cu\6% GGnth r5?,/H@3H5,Y:$UeI+HQ\N,tqzSd69->eSY׮l !5!!5!!5>+5!#7#53!5!!5!733!Kcd04+^^``k](673#"'&'#7&'&$32 '&#" 32$767&'&YjiEd80~i?/c`RQQ$g'-"SRR:;nSz_'BTc_ N@DROg`8@91/90@cmpxyvn]] !3!^DC?`%! !3f<?I!!"$54$3!!!W?JGcGK@ sJxNL``ȟMOx]I&/!!!!3!!"''&'&54$;7#"ؖI$$$GA?d`,,cFU;}YI7ʟ 7c``JxH NGx]g% $54$)!!3!+*(FiNv%FrO:0QI&'&'&'!5!2#!5!676767!5?JGcGK@ 'JxNLȟMOx]I&/'7!5!!5!&#!5!2+4'&'&'3276765 I^Q$$GA?d`,,#FT;}YI7ʟ 7c;JxH HNGx]g )5%2767!5&'&!5(*FiNv%FtFgP:1R, //01!!,wq@gg120!#!# }wq@gg1<03!3wJ}w; ]@    91990@0QVPZ spvupz  Z pp{ t  ]]!! !!5 7AJI3!-10!!ת !#!5!3!!5!--+}ת W+и и и / + +и 01!!#!5!3#-Ө-5B<%?P%73% %#'TUUTUTTUDGrXY %=} *@    91903##'%\sB}}`s-Pb;=v& Xus=e& X s 127#"#"'&'&'#"'&547632676;#"3cd3668+MI6641C;ItY^^SI6?+((C;ItK@tkHMfpEF?$Tx5@ejre!93Ex5@#/;&'#"'&54763267632#"'&%27#""327654'&1C;JsY^^TI6?+((C;JsY^^TI666cd3778s~d3778]$Tx5@ejre!93Ex5@ejreMHMfpEFHMfpEFI%!3!~,I%!3IfIA//+к99к901%&'&'3!!#4'!&'7`'JAW`LqR]+X* Pʋs^(Rs57756u5 +  // 9 9 901 7&'7%%'6 676r{EG%y44RW!L!$Ҿ &!L {JP+3#+fJ+ 7+и//9 90137#'PMVo)gnJ+3#3#@+fJ+{//и/ܸи ܸܸ и и// // 9 9 9 9013737##'[P]ME+qd @oxpAn!3# ih^T3 3##"T^32#4&#"#P(*7332653#"RP7*uM>2&#""&'7327~9GA~9G⧅}}uM& i i%uM& i' i% iJuM-6?67632&#"#"'&'7327&'&5476767654'&'SOJMG79GcBnnVsSOJMG79G]InoSu=,EG%,=,HK%DAF7K|oUDAF71IosV/HgjG$4.JhgH$uMMQZc67632&#"!67632&#"#"'&'7327!#"'&'7327&'&54767!!67654'&SOJMG79G~SOJMG79GcBnnVsSOJMG79GSOJMG79G]InoSu~=,HK% =,EG%DAF77DAF7K|oUDAF7$çDAF70IosV!.JhgH$+/HgjG$uMmqu~67632&#"!67632&#"!67632&#"#"'&'7327!#"'&'7327!#"'&'7327&'&54767!)!67654'&SOJMG79G~SOJMG79G~SOJMG79GcBnnVsSOJMG79GSOJMG79GSOJMG79G]InoSu,~=,HK%2=,EG%DAF77DAF77DAF7K|oUDAF7$çDAF7$çDAF70IosV!.JhgH$+/HgjG$uL.3&#"7#'754'&'#"&'7327#4767>32";EY?w^H6H\O3,,HO;E+@/VfmVmHO?u]HH]sM3 gz.VrmV_zuM<%4'>7'7&#"7"&'7327&'&54767>2=,HK%=Q Hl;EYLmHH7'&#"'"&'7327&'&54767>2=,HK%m#6,=iSH;EcHKs;E]InoSuJ.JghH$6B0+@TH?HK|z1IosV32326ian ^Xbian ^V2NE;=LTNE;=K23276767632.#"#"&'gV^ naibX^ nai2UK=;ENTL=;EN1).#"3".54>323265.#72#"&:QHRdhNi\dnx>@HRdhNi\dnx.ttlH=YOHL\}X[lH=YOHL\}W#"'"#322{dfftX{dfftX#*$0!#.5476767654&'30ND:323267#"''cDXbia]yeEVgia`yS LTNE+~F KUNE,F #"/&'&#"5>32326!!ian^Xbian ^VeoNE;=LTNE;=K`#"/&'&#"5>32326!!ian^Xbian^VeOE;=LSNE; =Kkb%&32767#"'!!'7!5!7&#"5>32%H\ iaBP﹉lZXbian3}o -X"OEd8LSNE;I"#"/&'&#"5>32326!!!!ian^Xbian^VeOE;=LSNE;?Kk˪.#"/&'&#"5>32326#5!7!5!7!!!!'ian^Xbian^VLoKɦoOE;=LSNE;?KL˪s˪sB.32767#"'!!!!'7#5!7!5!7'&#"5>327b K`Jqia'+\+zlh>Tm?u2^Xbianc"%]OE˪Nt˪=LSNE;%N;?@.9*-" *19" <-<<219999990#"'&'&'&#"5>32326#"'&'&'&#"5>32326ian ^Xbian ^Vgian ^Xbian ^VoNE;=LTNE;=KڲOE;=LSNE;=K43267#"'3267#"/'&#"5>327&#"5>29+Vgia@LJZVgia}9+Xbia@MHZXbi a KUOE8KUNE; @^ LTNE8LSNE;f@59#"/&'&#"5>32326#"/&'&#"5>32326!!ian^Xbian^Vgiaq^Xbian3VeLOE;=LSNE;?KҲOE;=LSNE;?Ky5P#"/&'&#"5>32326#"/&'&#"5>32326#"/&'&#"5>32326ian^Xbian^Vgian^Xbian^Vgiaq^Xbian3VײOE;=LSNE;?KҲOE;=LSNE;?KҲOE;=LSNE;?K"32?632.#"#"&'!5!5gV^naibX^naiUK?;ENSL=;EOȪ+  %5 % $%5$[g&Y%ZhӦ69%676767!!"'&'&'!5!!5!676762!!&'&'&[C-87VYYW6 8.CC.8d 6WYYV7 e8-,CE[<0[2332[39\DD+N+DD\93[2332[0<[EC,` !5!676762!!&'&'&!![C.8d 6WYYV7 e8-;++DD\93[2332[0<[EC,`'  ' &  ' &  0' &  .62' '  W63& '  ` 3654'!!5!&547!5!!4434w~0IG00GG2?8>;_8` !!!!"264&'2#"&546HdddeH;k'**z{DbFE``bq+((d:svv`K!!!! &!56뗲`!!!! 3# $c'`!!!!33#$'c`!!!!!!'+]^*^]N䰰` !!!!!3!Np!NNf`07GO!!!!#"3###535463!3267#"&546324&#"'53#5#"&4632264&"?$mmC???DNB&H#$J'`qk[Q_C<17HBB@,I\\I,@p`ctiG6B?9i=$#tu#gSSS`*!!!!>32#4&#"#4&#"#3>32!]?U\Z79EPZ7:DPZZV:;S==:xoHOM]QHPL^P%U20=` ,!!!!3#7#546?>54&#"5>324eeb_--B6'Z0/`4\o$-,N2A+,/-7#!^aO&E++ '>@"     <291<2<<990!!!!!'7!5!7!}/H{};fըfӪL !@  <<<<10!!!!!!ת4!5!7!!!!!!'7!5!7!5!DQ"rn遙RoLT˪˪T˪  )@    <<10!!!!!!!!K T@.B $# <2291/90KSXY" 5 !!@po V@/B$ # <<291/90KSXY"55 !5AǪV 3!! 5 !!@poV !!555 !5BkǪ!5!7!5!7!!!!' 5'`ȉ)P"_=6@ss1stFpo!5!7!5!7!!!!'55'`ȉ)P"_=6ss1stF. 5 5:6:6pr pr . 55556:86:'!67&'&54767&'676'&'{)#Y4JJ4Y#))#Y4JJ4Y#)AAAAGF㞢GGGG➣FG2;;;<<;2;5$?$%5%67$'W eĔd?NĔ])]o& bR)`q% Rd%'%5% >zmzF<˶@6 o@hGp%5'75%7-孈m%˶C@ʴ@hGp/V !5!%5%%%!!'/xvH-rf5LOlUrC@=Vlь=/V%'!5!75%7%5!!' GWb[mmNL>ߪwe=ت=$%#"'&'&'&#"5>32326 5jbn ^Xbh`n ^Vg@ND:3232655jbn ^Xbh`n ^VfNF<>LTNF<>L>)P14%&#"5>32%5%%%3267#"'&'&/' k Xbh`'+kuE%sk ^Vhjbn "Pv1-LTND9ATj͊LTNF<= &TN#wf=J;N} 55 58@'poN} 5 55@'pom`!-%5%%%'5%%5 MM`ZDOA@FZDt@m*_TW&o}䎲w&-r~bUm`!7/%5%%'%5%75%Jvad",,V`bL"_D2,/*/&O{¸[&}P %5$r osaa^~||P 55%$so a||^a)W!%5%5$gV$}]]x|)W3%55%$Vg}$BW|]]RW(%#"'&'&'&#"5>32326%5$ian ^Xbian ^Vg$}NE;=LTNE;=K$]]x|RW(%#"'&'&'&#"5>3232655%$ian ^Xbian ^Ve}$NE;=LTNE;=K$|]]&%5$%67%'Et֋$k}uU)?eKtuu" K 9''567$'567&'%=⃹t֋~}uRU)?Kuu,ަK9'_%!"54763!!"3!슊@^`@ƍ^`_75!27654&#!5!2#@`^@Ȋʣ`^; #";3!!!!#"54763^`0rrndflppꊊ^`&pphƍ3 32654'&+ #!5!!5!32#^`0rrpp9^`phƍ7!!!"'&54763!!"3!Ɋ@_`@,ƍ^`7!!5!27654&#!5!2#@`_@Ȋɖ,`^ȋ '!";!!!!'7!5!7&'&54763!7!!ʉ_`'}E=aLT>scL0R^`5ƍ7 '327654'&/!5!7+!!'7!5!7!5!^`__BV 5cTpX?bLm>U`^`C 7 Xȋ5j )5!7!!'!"'&54763!!"3!.Bqx-qxDɊ@_`@Z54&'&'$  &'&'&547676!!#!5!]\LMLLML\]]\LMLLML\bc1111cbbc1111cbdd''LMmjML''''LMjmML'dbcwvwvcbddbcvwvwcbee$7!!"2767>54&'&'$  &'&'&547676r$]\LMLLML\]]\LMLLML\bc1111cbbc1111cbתa''LMmjML''''LMjmML'dbcwvwvcbddbcvwvwcb$3?"2767>54&'&'$  &'&'&547676''7'77]\LMLLML\]]\LMLLML\bc1111cbbc1111cbxyx''LMmjML''''LMjmML'dbcwvwvcbddbcvwvwcbxyx$7 "2767>54&'&'$  &'&'&547676pxg]\LMLLML\]]\LMLLML\bc1111cbbc1111cbpx''LMmjML''''LMjmML'dbcwvwvcbddbcvwvwcb$73#"2767>54&'&'$  &'&'&547676]\LMLLML\]]\LMLLML\bc1111cbbc1111cb''LMmjML''''LMjmML'dbcwvwvcbddbcvwvwcb$ 2L"264&'2#"&54>"2767>54&'&'$  &'&'&547676ZPnnnoO@v+..]\LMLLML\]]\LMLLML\bc1111cbbc1111cbAoPOmmp1.-rB''LMmjML''''LMjmML'dbcwvwvcbddbcvwvwcb$+E %#'-73%"2767>54&'&'$  &'&'&547676C4f4C4/f/]\LMLLML\]]\LMLLML\bc1111cbbc1111cb1XSXYS''LMmjML''''LMjmML'dbcwvwvcbddbcvwvwcb$!;!!!!"2767>54&'&'$  &'&'&547676]\LMLLML\]]\LMLLML\bc1111cbbc1111cbj''LMmjML''''LMjmML'dbcwvwvcbddbcvwvwcb$37"2767>54&'&'$  &'&'&547676!!]\LMLLML\]]\LMLLML\bc1111cbbc1111cb8''LMmjML''''LMjmML'dbcwvwvcbddbcvwvwcb$!%!!!!#!5!QX>ddYee$ !!!%!!rPX>ת\$   ' 7 %!%!!=kyykyjjX>xjyjjyk$$ 3#!%!!aX>J@ <1<033!!upJ!#!5!3JI!#!5IssI35!3!|33!!Nup| !#3!!!!.NN$J !#3!!!!.$J !3!!!#3GupJ !#33!!!#3.GVfupJ!#3#3!!!!.cGGf$J33!!!'!'Ssj\s=u5Y6pJ!!!!'!#3!7!sjshxj56$$J!!'!#3!#3s6s=5Y6puJ!#3!!!!!'!#37!s:jsjG$-56$]*5$%67654&#"'632#"'732654'&'$@e=M>P7sZw㔰Zs7P>M=e.(Y7O0<0:>~jy[<<[yj~>:0<0O7Y]*327#"&5476%$'&54632&#"ee=M>P7sZw㔰Zs7P>M=e@.(Y7O0<0:>~jy[<<[yj~>:0<0O7Y( 51  ^ bb:d 5! 5bd 5! ^bbb:yg62"'&'!"&462!6"264S몧Q3Q3TW4drOOsOOSQ3CB3RU4CDPrOOqyg"&462!6762"'&'!$264&"aS몧Q33TW4QrOOsOSQ3CB3RU4CDPrOOqbgR 7!6762"'&'$&"26b1[륢S4OsPOtO.D/YR3BPQqOOy;d 3#!!#3%!5!( 󀨨 ds <!##5!#T~N 35!3 3#K#"T^ !!3# K@ih^T !!3 3#K@#"쪠T^~ )3!!&'.'&ZVF%,E=Ώ?~%FVZDA?=~ !53*,Ԫ֪w # #}}wJw 3 3!#wJww@ 1@ 0"# #4$H̭9B( w@ 1@ 02$53 3H4CC1 (B9#uTHF103#F1  !!'+]^*^]䰰3#3#!5!7 !! 'RLxxLux66x<ux6xx6x'B  ' ''ٛ>PNq^D^'B %  !'''tNP^D'B 5  5!''6bNP'B5 5tN>]P'B 5 'Nt>P`32?632.#"#"&'!5gV^naibX^naiUK= ;ENSL=;EOȪcy 33#cu?Ik8ff%q#cy 33#cffI?#q% )!"3!!"'&5463!! '&76)!"3!k:((P:jZYk񼽽jȊ ()9:PZXD  ȋ )5!2#!5!2654'&#5!27654'&#!5! !YZj:P((:kɊj XZP:9)(ƍN$!4&"#47632! #4'& PtPZXD|p:PP::ȀZX8x8Ȋ:1$2653#"&5! '&3 765PtPZX1::PP:8ZX:8Ȋ|84'&'##47673#Z:KK:ZllY:::ZaȌlala4###!5!5!5!333!!!!'5#Y~~~~,,33ͨ^ 3# 57Ѧ^ 3#55=d//m.   5 5 5 :6:6:6pr pr pr .  5555556:86::6:.  5 !5! 5?@Npo. 5 5!55?ްop9 %5 5!@op9 7 5 !5!?)W5$%5$Ti}$_|x]])W5$%$5iT$}B!]]|!&!%'&'57&%5$%67&%7*?;i@]0qw^%KA6#(AF+3273267#"'' 5cCXbh`^xnieEVhjb_zl]@LTND*F JVND+Fpo"%&#"5>3273267#"''55cCXbh`^xnieEVhjb_zl[LTND*F JVND+FͰW&&#"5>3273267#"''%5$cDXbia]ymieEVgia`yl]$}. LTNE+F KUNE,F]]x|W&&#"5>3273267#"''55%$cDXbia]ymieEVgia`yl[}$3 LTNE+F KUNE,F|]] 7%'%5 '瞃۞L О  @Y8@\9@a ' 7%͞G۞О@?Y@<9@}5!%57%!!'71|Iv\' :qߦ[@Z8@_}7!!'7#5!7%%%9Jpv\]FGjq8@ǹ@ 3!!"'&5]9Deo>ܚVf]>#3]J] 4'&#!5!29Deo$VfX,&'&3!3#76l<(enP==Kne(!< _EA_ <]> 3#!5!2765oeD9>יfVu(3(7@% !!!5 5!!37d  hrv! !! $<Ff +   276764'&'&">  &vvrn66\]]\6666\]]\65kk\SS]\6666\]]\6666\!;>32#"&'#'%53%&  s:{{:!8#!rܧ$daad]chaam@j.!3!3:^ &ۺ+#+#+A&6FVfv ]A]A]A)9IYiy ]+ + $%+$01! 4$32! 4$#"35%33!??qqW|A?rpG~+/ 8?+3&+3+A&6FVfv ]A]A]A)9IYiy ]3и/A&&]A&)&9&I&Y&i&y&&&&&&& ],9+ + +0)+001! 4$32! 4$#"!!56$7>54&#"5>32??qqWO\R!>/_N;sa=0>A?rpGM"?U(?N&:$}:iF D+B5+B+A&6FVfv ]A]A]A)9IYiy ]A55]A5)595I5Y5i5y5555555 ]5B9,5B9,/A,,]A,),9,I,Y,i,y,,,,,,, ]ܺ&9;9+ + )"+)?8+?2/+2/2901! 4$32! 4$#"#"&'532654&+532654&#"5>32??qqW v@X[}DuskcZX\[4yk_=hA?rpG]0OLGN<:,+>2+201! 4$32! 4$#""32654&.#"632#"&5432??qqWN\\NN\\Ta/w N 5jA?rpGb[ZbbZ[b#P =  "#/$/ܸ#и/A&6FVfv ]A]A]A)9IYiy ] 9!9+ + !+01! 4$32! 4$#"!#!??qqWkQ1A?rpGK '?K +=+1F+1+A&6FVfv ]A]A]A)9IYiy ]A&6FVfv ]A]AFF]AF)F9FIFYFiFyFFFFFFF ]%F19%/A%%]A%)%9%I%Y%i%y%%%%%%% ]+=9+/4F19%7ܸ+@+ + ":+".I+.C+C4C901! 4$32! 4$#""32654&%.54632#"&546732654&#"??qqWT__TT__jivvWQMKRRKMQA?rpGPIIPQHIPIvSttSv\\=BB=>BB 4@+>)+>+/8+/A&6FVfv ]A]A]A)9IYiy ]A>&>6>F>V>f>v>>>>>>> ]A>>])>9A88]A8)898I8Y8i8y8888888 ]+ +  2+ ,;+,5&+501! 4$32! 4$#"532676#"&54632#"&2654&#"??qqWUa.w O 5kN[[NN\\A?rpG$O <b[[bb[[b &2>+#+#*<+*60+6+A&6FVfv ]A]A]A)9IYiy ]A00]A0)090I0Y0i0y0000000 ]A<<]A<)<9<I<Y<i<y<<<<<<< ]+ + -9+-$%+$3'+3$01! 4$32! 4$#"35733!"32654&'2#"&546??qqW͞u>@EE@?FF?A?rpG>>'*6ޗ{5!!X3 2!@ 2 5!!5!!5!4)4𬬬 !!!!!4)4XXX 333 Nf  !!!@@@ Nf  53353353353𬬬 3333333XXXX 333322s's' !!!!@@@@22s's'!!!!\!!#!!#\!5!Z!!X!5!$Z!!$X3!-Ԭ3!-.*!!@Ԭ!!@.*5!3,,(!3,X5!!@,(!!@X3!!- 2Ԭ3!!- 2* #!!!P@ZԬ 33!!P-#,Ԭ!!!@# 2Ԭ #!!!P@.* 33!!P-#\*!!!@# 2*!5!3,Z,!!3,X !5!!#@PZ,( !5!33$,PZ,!5!!$@Z, !!!#@PX !!33$,PX*!!!$@X!5!!Z !!!!-XV !5!5!!,ZV!!!X!5!!$#Z !!!!$#XV !5!5!!$#ZV!!!$#X5!3!,-,Ԭ !3!!,-XԬV 5!3!!5,-3,*V!3!,-X*5!!!@,Ԭ !!!!@#XԬV 5!!!!5@,*V!!!@X* #!5!3!,-Z,Ԭ !!3!!,-XԬ !5!3!!,-Z,* !!3!!,-X* !5!!!!@Z,Ԭ !5!3!!$,-#Z,Ԭ !5!!!!$@#Z,Ԭ !!!!!#@#PXԬV #5!5!!!!P$@V,* !!33!!$,P#X*V !5!533!!$P-#ZV* !!!!!@X* !!3!!$,-#X* !!!!!$@#XԬ !5!!!!$@#Z,* !!!!!$@#X*5!35!,-𬬬!!!-,XX33*!!@@*DH5!5!xX333x 2 2H !!!!-Rx !!##xmsZxH !!3!!xm3-sZRH !5!5!5!,NX 5!###lZZXH !5!!!5!4l t,ND 3!!!--Dx 333!x,ԬxD 3!3!,(D 5!5!5!3,,D|X 5!333,,(DX 5!35!3̠| 3!!!!-- 2Rx 333!!xs 2 2Ԭx 3!33!!-s, 2ZR !5!5!5!3,,X !5!333xtZ, 2X 5!3!5!33t, 2H !5!!5!4R 5!!###sZZH 5!!5!3!!t,-sZRD 5!5!3!,-DX 5!333!,,ԬD 5!5!333!DX,!5!5!5!3!!!!,,--R5!333!!###s,,ԬZZ !!!!5!5!333!-s t,ZR, 4763!!"Q[yY[`~| 4'&#!5!2.-Yx[Q`~=?x 5!2653#xY[Q[~|2Ψx !"'&533![Q[Yyx2|~>3m 2>#3> 2> # # 3 3>ݲ}#$cc|5!F3F~|5!|iF3P|!XF!@F~|!|iXF!@P5!5!!5iVV333PP~P!!!iXVV#!#P@P~P;(;!O;!O ;!O;!O;!O;!O;#!O#;(!O(q(!((!((!((!'(I(!]((!((3(:(' q( #'+/3!33!33!33!33!33!3mnmnm 4('/7?GOW_gow#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573#'573(;(!%)-13#3#3!3!##!#3#3#3#3#3#3#^^(ll(lm#;( #q:(!&9 '( 9(& &!"9(&!"9(& &"'9(&!&"'9( '9(& '9(& &!'$! $!!!,7r+uv ))xxp) )$7632#"'327$%&#"%632#"'~~~~eMM>yJJJJJ6````qq|qq#u"@91990  9%-p) 327$%&#"%632#"'MM>y````qq|qqr' '/7?G%&'&'6767&'&'7%'676727"'64'7&"'62&47\+;.81F9K58.42d;E9G,:.80G9J6&8.;+d1O9FLL&_`JnLL'`_n<1& j(0=Ju &,A=N:0('<1& j(0=Ju &1<>EB0(n_II'[[JnII'[[p) %/36%632#"'327&#"6767&'&6py AAAA,+-,,-+A@@Rqq|qq%%mܱ[0$ %@%|"p) )73276'&#"7632#"'327$%&#"%632#"'r99:9rr9:99XWXXXXWXMM>yB!!BB!!oe33eje33````qq|qqp @ 104767632#"'&'&pihѵhiihҵhiѶiiiiѶiiiip $32#"$27$%&#pkk<MAk^a``p $32#"$"3pkk<MAk^``p $32#"$327$pkk\MMAk^>``p $32#"$%&#"pkkAk^>``p $  $"327$!pkk]<MMgAk^```p $  $"!pkk]<Ak^`p})6%63"'pRqq)#2y|q*q( 2654&#"!|~}}|v< ( $%632#"'327$%&#"!IMM>y_O````|qqqqH( ( !#%&#")%632OyyMMqq>~``  3327$3!#"'$@1>qq``) %63"æqv`) 2#%&#u)q>` 527$3Muyv`>q "'$33yuMq`p)%632#%&#"puqq>``p03327$3#"'$puMMuyy``>qq!$ !$ !$! !$!$3! 2654&#"4632"&nȊce;~|ddcc||}$!%!!d r<$!%!!We r<$!%!W7 r<$!%!W7 r<$ !%!!!!+c,b r<<!$ 462"! W|VV} ,|VV|V !$! c  !$! b  p(  7& $  %;<*X֖$ !!!!!!,7,rWb<)) Ie$ !!!!%!!,crWbM)MM^??@7`d?\gOOOOy>*<?v^h"3263#!5276;'4?'4?26u'6"gP39.4! '*C0.xV#m14He '1l1 Z+dd?33 #&'&+"'&#"/573;2?"#'57#&'#"#5676!5:+#9,p!j[%+ > 7VCCc":8}V .e3B=Se` e9*=9 3@=}k %C`:d;emu}'S3273&'3327&'67&'67&'67'32654'&'2327654&#"3672 $54767&'&47'&327632#"/#"57#"54?'&5432'&27632#"/#"57#"54?'&5432'&327632#"/#"57#"54?'&5432'&27632#"/#"57#"54?'&5432'&327632#"/#"57#"54?'&5432'&27632#"/"57#"54?'&5432'4327632#"/#"57#"54?'&5432'&27632#"/#"57#"54?'&5432'&27632#"/#"57#"54?'&5432'&27632#"/#"57#"4?'&54327'4327632#"/#"57#"54?'&54327'&27632#"/"57#"54?'&5432&'67&'67&'67'&327632#"/#"57#"54?'&5432'&27632#"/"57#"54?'&5432'&27632#"/"57#"54?'&5432'&27632#"/"57#"54?'&5432'&327632#"/#"57#"54?'&5432B~ %<z*+')+(@&'$||e<-A}]\B-71SLoWj\vLL)(0/ (( .1(%%,* # $ )*f$% +) $ #*+f%%,* $ $ )*  \o  [ %)#&'%&)#`#$ *) $ #+,U  Q  0 E%% +) $ $*+&EC&V*,)-)-*,%&%&fБfU 3HhfeefhH2pu^QFs棥sKQGh!99!  !77!  4 4 22 K44 22 22  11                   7        %&%&%'%&%'%&22  //  g               44 22  ->O`q +&'&54?632332?654/&#"2#"/54762#"/54762#"/54762#"/54762#"/54762#"/54762#"/547672#"/54762#"/54762#"/5476%2#"/5476%2#"/5476%2#"/5476D.2`{4&/<) e>O ,4H3R 07K $   $   #  #  #  $   #  $   $  U $   # " $   #  7Q=KG<s-8PZy9z _e""#/2dt0&2j ,: . 4 . = ,  ,   -  -  -  -   .  .   ,   -   !! WV9`8 !! 7 ! !WVDu9`8N I 7%7&54769 }V&7A 6$ 8'^4? !2 7%7&547!&'6I@Y%14HFS"="l-2DC[9 &! 4$32 4$ #"&54>2JJhhq0^mNMn2Z^Z2K7iwBNmmN1Z00Z} C"32654%"32654&%#"&54767654$ #"&767&54! ggJIhIhhIJgg[ZQoy y}WZ[zADgJIggIJggJIhhIJgU\\Q srW\\^} A4&#"26%4&#"326! 547&'&632 $54'&'&632hIJgggMgJIhhIJg#@@z[ZW}yOOyoQZ[sIhhIJggJJggJIgg ][[Xrq Q\\} "32654&7#"32ɏǾ/`T_ȐɎ;P12Y}1"264&"3264#"54327&5432#"'&'3xyx& کZTdIU  k#5AMYer3#"'%&547654'!#"'4%$53!76=332654&#"#"&54632'#"&54632#"&54632&'&67632#"&'&676'.547>'.76$6&'&54%6&'&6>#"'.54>32#"'.54 [$gi< D""D =if%LW쥨驧r^]]^ !! !! . . *)X,),*))+. } +G  G+vKK9__9KKݧꧦ]]_""""s!!""W&. - . - a)," "  ))    !) /     p%-5AMYdp|5#!4'&'5#2#"&546"264"264"2647>'.7>'.676&'&>&'&7>'.%7>'.676&'&676&'&53!76=3%#"'676%27+%&547654'7327&'$%'#327%654'&54718楣. . . .  - -Y - -))G))))U*)>- - ~- - VK; yA C0B Ax ;K'6FJ> $06# >JF6&@@1AeA1@@H磤椣筁 . . . .E - -- ,1))),(9)())u- , - - G77W6 W77G D&& ee˥ &&D "(=pp=("u !!'!Pn8hv "!!'!##+572367676MoL)>u eI3?ba8hA:F;/Itxv !!'!  ##' Mo_h[ei[i8hi[ef[l[@36273 ##'5) U.WW1@ US Vdv#,5>~3+&=43+&=4%3+&=43+&=43+&=43+&=43+&=4%33 #&'&+"'&#"/573;2?"#'57#&'#"#5676!5\:V\9\:\:]:&]9[\::+#9,p!j[%+ > 7VCCc":8 #8d#7$6$8;$7i$7 #9pPL  )Z. ;6ZV Z3%Y63 .87p  3DMy!674#!!6?676545&#'323276767654#3#&'&'454632767!672!&=75$/563&43!32+'!67#>54&53# ? I :W0 96;E,Q 2:&l6x0 bm! o۸"\>%Ef~e2U6g!6V#p5C+ C ? P9 @7H4XmM7RV /M(=H: ,qLUD)8Wqke-Pex NW =$ U  /0c)H?2@[nDF8T$.J? !' !T4XKGwL5_K !'7W4Z~wDS&5476322632%632#"'&'#64'#"'&'&54654&'&54767632xJX%&XA,B:\8 [EMH95##Fl% !9@!#jL p_Mi#"?8" %lF##58HN4hok@RRr*%te BB9'7*$%) "fXS5EIf" )%#,7'9CB >E3#"'4332327$'#"$4727%672567654&5&oJ7.b9M D ,B3 qY 5**]d=HN9% sW$,J ]T-MMm@ed: ,'Z M'cM&T)$$ < I2%!"&54676737#&'&54>;7!"&546767!7!"&54>3!6763!26P+=6/2D>R+>2,+v*>>+2  ,2 =,2  =,3>,2463!2!2#!!#!32#3#!>*v+,1>+R=D206=+P#,>3,=  2,= 2,  2+>{"D%4&#!"!0#"3!!"3!#";#"3&'6737#&'6737!"'67!7!&'63!67!2I0!6OS SS: SS>SS]]J]]]]h\\, Bv*>K%39LKIOKHLKIhghghghgE?-L!D72654'6#"'4#"'54#"'54#"'675674767#%$4:JILLHOKHLKIhghgighgD>-sJ1 b6'SS cRR SS?SS\\K\\;\\]]!A*>K{!C%254+'3254+'!254#!'!24+!&#!"463!!2!!#!3#3SS?SS *vA!,]]j\\\\K\\IKLHKOIKL93%N-?EghghghgiL!C32=732=7325732'654&#'%2&'&5&'5&'IKLHKOHLLIJ:4$N->DghgighghSS=SS SSb SS'6a!0J)K>*B \\]]:]]J]]}O &*.26:> 3656;2#'7+"/#"'+"5&54775%"'5476;25'7&56%635&56;374765'75'76=4'&+ +"'4!#"'4543$365&5&#%#754'&5&&547'5367&547+&'&'735&2?"5%75537'7'3533553535'32767&5%2?&#%55'5757757751:e,$?F?Y>F_LA3ELH3,8LYLlEF'!0< k#gF  EeY!! Gp&iq.8ZN$%`BCf F4"4._?ee3&{E(1-+$Kt8 -  $Gs sM rEF"2 >_plTErf^5.>=9|5"-l)d ,&>vv]cccWpC-+ d8 Bpp>W]oaxvuPp82,D ^8, ^B$K+ "1R[+e*; 2 W QP I&? gpo% w ^SA$ 2 9i-5n02 Ai&IY^P]D%\??\OWC ,,1 /211/=;7777=321811{908hN%b\Dh,)h?17I21!122223 21&2%2#"'&=477654'#"'5473Bq4|l anN ilm b 9 b؍MOb>YaYƮ58l7P P@ $0<FX + &=6&# 3 6=%&#"';27!5%67%!&'&'2+"'&=476r cR~UY082.ԍ_W_V"+}IR8D).P9H'S]ٱZYHYoX(I_ ;.2lOP%.G6R%&I8d)Nl>54'67&54&#"&'632.547#"'&'#"'3267654'7327323.#'654'567654&&5476;'&'%&+"#"8DH$$yU ?L[>!WtJ([Fho*m.2\=w\`|UP7:/E" @7?EP]Eix pF@T5ym,"&eB@q(A _% #+B7!N &".OS$XE/K(Aa]dLP*'FCaYr=C44mo C (FKWYFvbph'UD'R< $d#+?Vm#327&"#"'7'632&'$54#&73254'&#"'5&567#&''5$'67'654'6'5$'67'654$'67&'654'''5$56732#"'&#"&'$'63&47"7&'7&'7&'7&'54'6546767675477&545?&''5&#" '6%35&'.54>23#67!&#"W  OB7[l#> F_Vh " "@.,=6tJ4Vp1EQJqMi vhpHI!:JJJ =4m\8B*?o v!"t,`s&*_~P1>5='g=>24<+-s[,*&sd1PT>3J@='h<42J-H#*YT_Y)*)X^TY*$D  ?>}>  *0t"J.  &b54CUE ''!`9 !,(MTE *! }q~=/+)f[4f !B" <@0&9c?"V+GoMK~a? }b9e\ P&0@k"?c*GEJX ?e}9 \4 \6 '''' 6\ N(&'65&'67327&+!65+"3yyys{w ccޱqXeXc6 6 c ,35'533#3!'#'5!5!5#53!5!5#!!-ʷ}} ckvG G @<<3ffX苜qXGccGJ 326&#!2+73 ### 3(ttvgnؐB(33#!!#'!'57!5#'5735׫$"q~q+!#!573#'5!3!'573!#'73!#'5;jjŠJss<wѡIjj8/w{,32#' 3%+ &5%6323'#57'53^VQ6>ѨABؒ6ʞG2k >Y3~||~Obs32732753"'#"'4323$4'5;+"'#"'53275'&'&5?5572%#&'&5%634%476=%@.!%,BE,#!-Q2" $nL/PuHED832#"&546324&"26%! !  Őb{=&*<<*(;E;R::R;KJ67Ϛ{ɬ)::)*<<**<<*):<'L67I&' &' &' &' &' &' &' &'  @FLRX^djp3264'&#"&47367'676756273#'#'5&'&'7&'677&'67'%%&'&'%6767%&'0/CB^0/AC/pkTcR|'N(OfUippqUfO''NQaQh!$ b)dLQk KRt!% c'd&//^000'N'|P_PfppoQ`Qy'N'P\ QgppmQ \Py,  M N>&`7" bK*V&"g{ M M %1=! !  54 #&'&#"#46324632#"&%4632#"&67KJ]_EASvwSAF͒D10EE01DD10EE01D7IL6a]U@SS@U1DD10EE01DD10EE %1=! !  54 3327673#"&4632#"&%4632#"&67KJ]_F@SwvT@E͑D10EE01DD10EE01D7IL6a]U@SS@U1DD10EE01DD10EE %1! !  5# '&'32654&#"32654&#"67KJ;lWPihQV<=UU=-1\ H0e%FKSwZGr=;=NN$E| 1 ?'_>?@7`d@\hPPPPy?+<>w_VG{?,rCA+ +"'5$76%&'547327676=&#~jt1/Q}](+VRxbO P >nS]] =fP+! &56;2'5$%75#"3ui1.P~N](7P,VSZycOpO >S\^ f0:1>7#'#53'&'&54767&'&=33676=3#326'&i($lm$(($[Uu&tU[$&uU[[UV$|ddb e|$% ZSSZ %_TYYT* $4&#"326&5432!!##53&w衤礡PP䤣L~||* $32654&#"%#"54767!5!33#b衤礡7䤣L~|| $&$76+"'&5'476!7!ttsstEus pid5s qttrtt<֤ꧦg\ulS5264&#"#43233#!5 z{ym㗗y{(|j#53533#632#4654&#"#*jjoon}mZyH{zF2 1"32654'#"&4767!!53#5!!3!!#3!!pOO87O:=0LmkL/>Λ2  1O79NN970LؙL1KӘJJ-'<%#5#535&'&'5'73'3#'73'676=35'73'33◰zhNgeMjzzTThOʍ7NjYYӖy?! #!!!'!27674'&#.d ;6zFH%QM_\ǃ$P<]$!#"#&5463 67!2#654&#"V⩁"T]ts]U"X"1((1"u." 6&'67>3"#"54767&'&#52&͕LVa{.+ؔ)0zHUM\&ϖ=Bll)'ҕ*l8lB=j&'5 %$ 56?63#'[Wtutu4ZZ//[[5  @Eo&<"3264,'532'&54632264&" &$#"#"&547>B_^^l;͓hI^9l:͓hI (+|TlgMLx)+{TlϔgMM M>54'.#"32463227#"&5454&#"#"&'&54767632254&K2q'$#K1o'#0ߴGdAoc.% 3t88bWDs-Kx68<32>32#&'567'45'#&+"#4'3>$4&+"?w(K>R0D32>32gYYYD,.:?#)v$E?w(K>Ro}vvxJvaAjtAO]ƀwϧ!5!3##'!5!~2k<@i8080k<j)127632#"'#576&#"4'5267>327&'"SkQmyz,~zi2@:$(.-)zW] ݾgvx-aX[&ŝ9{'Q32263227632&#""'&#"#"'&#"#'3232762327632&#"#"'&#"#"'&"#'Es- p86rV+)|m^?_354.#"!&'.54>325467675#53533#63232>54.#"P#3JTRJWVJQSOMJ4"?*&ElnhPL$ llill %LOhnlD')----+)QPQ((QPQ)+/ 6klj$?6FWWF6?$jlk6 }++--JHNRh|&'4>32"'4>32&'4>32&54>32&54>32#!5!'!567>54.#"32767>4.#"327732>4.#"327>54.#"732>54.#"M_ 6694S55.+C55C&.66 V\+55 c$M##$ 6$#$s`%#$d0"%)h #"#_33@]22-"40446/*33UJ"+33^1/K=0T* ####  #&$$&##&$$&#  B #### *"$$" U!'-2!35!#3!53573#'5#5!35!75!!5'57!s\\ss]]s JRRIJ~֛E77__vtt4!v7CQ^&54767&'&'5676767&'&54>32! 535#5##3654."!2>4.#"  <$))+N-N*)N-M,**%:  @ v<-MTM-?K5:66459<5&?HPPIK* ')+K**K+)' *KIPPH>&5<:6uN|l||l|-I+N))N+@6:55:5Q)5>o654&547!&54='&'654'67.5476;+"'5#"=6&'76767%25#654&'Fz-6 Z8. ,N0H!h6%`+EH )#M ;,Jga#iR k' M +1^hgo8:(@s.Pmz nx?.#1p#41`&>%!ac,,LHJ x}647| + OJJ)!0 P[32>4.#"32>54.#"!5&54767&'&546767&'&4>32'&'.#":e79e89f76e`[S &(*UM,N)(N-KV)&& \@ECApd88dpg669:%N&KRS* 'TM**MT' *SRK&N۠:9}qyyq}c $Tdhy67&'&"!3!67>54.#"!&'.54>325467675#53533#63232>54.#"!57!&'.54>3234'67632!P#3JTRJWVJQSOMJ4"?*&ElnhPL$ llill %LOhnlD')----s=BDw@>=))==AwDB=+)QPQ((QPQ)+/ 6klj$?6FWWF6?$jlk6 }++-- !yCB{C!$$!C{BCy! JHLP&'4>32"'4>32&'4>32&54>32&54>32#!5!5!M_ 6694S55.+C55C&.66 V\+55 c$))_33@]22-"40446/*33UJ"+33^1/NNOOU%)5!5!!35!#3!53573#'5#5!35!s\\ss]]s ^^/oo#E77v4@4767&'&'5676767&'&54>32!&535#5##3  <$))+N-N*)N-M,**%:  @%v<5&?HPPIK* ')+K**K+)' *KIPPH>&5<:6n5|l||l|L3?HN654&5473#!&5454'+#"#7&'654'67654&547;2547#";65'"3%:U"-6 Bu Zg0krX0c-h8E+`%s H>4wM-'9.QY / o8:qhPSmh #%Bz1"0@)5"@YR0.&54767&'&546767&'&4>32; &(*UM,N)(N-KV)&& 9:%N&KRS* 'TM**MT' *SRK&N۠:9C##"'##56'##"/547?^'5@_*SU&/UL ;Yԧ9UP(` XI.s222732#&547636=4'&# #4'&#"*t pz&=<xQ>hG:V Hek%PF5NP B|-&pA&NFX &&5 <F:^;" V gdG7236;2"##'65##"'&5476;235&'&=476e x<JT`(GeRUdfB3 VNTMT,P$ 66$0_ u3dUdt_}s*$"Rt0XX__/ik=ZG8*F 1 . ъf)MC =g9EkO 9!(-);&  ]t!y" & 2| ba$ U+  #8M35733!&54?'7'327!!"'&%#'7367654'77'7'&#"'676ի,&T>=c#]K9.U:1ʈ%`T?7>54&#"5>32&54?'7'327!!"'&%#'7367654'77'7'&#"'676]T@1$J=c#]K9.U:1ʈ%`T?32&54?'7'327!!"'&%#'7367654'77'7'&#"'676Z _3lFHe5^\VOosHGJI)`VKm1Sj,&T>=c#]K9.U:1ʈ%`T?=c#]K9.U:1ʈ%`T?=c#]K9.U:1ʈ%`T?=c#]K9.U:1ʈ%`T?=c#]K9.U:1ʈ%`T?=c#]K9.U:1ʈ%`T?32#"&e|e(<X<ħñ"32#"&$2#".46e|e(<X<ħñ"@<#"4.#"e|e:<#"< !<"#;zch =B4.#"$32>4."e|e:<#"< !<"#;"< !<"#<@;zch =B54.#"##"'5##"$'&'0!5!5&'.4>32!!676767'%''H&(G()G'%H(%'V W3WImuw>DE}AB|GE=md^JW4W Vs'H''H'(H''H`XAK|@X1(ԁ3"|}DD}|" 2/ "1X@|AX1# / 673&/'67 &'"&'6?&'3 ' K[]><+Gg['fBBe&\h?(K?]\K !;32T $ #AC,MMMv A5p_9D-M**  B@0"@R//>wA&oc/D&3.YaQ/5"1'"uE62/u= =!m- .... y 7%  %  32+#".=!"&'&'#&=4;7337_% 8)0/_^^M^1/ 9534<&&<&*(D>?GGzB6C{GG?>D9/C}&632#"&'.#"'#!#!#Ҹ62K#+~KF0R!9'/Nx_TV_T 'NQ9;:#8HL"CD|))Z) 532>4.#";267#&=&$32735&'.4>22[02[24Z1/[)'5*+X A323#67#&"#"/&'&547&#""'6%676V n*[n%'ZxL0<{2;&b;0&8a>!U*~EmLK}`? {a7c[ O&0>j!>a)E~CKW ={d{7 [+M57LL75M-Z '*''*' Y (5[ J5( \d (5J [4 ''/7O_2#".54>&'32367&%2327654'&''67&'&'&'676765467654'&#"7>326323#"'##"'&'#"&'&54767&'&54767232&'&#"6&%6767&'&'&#"676&5467&'&6732767&$$$$OG3%V cc V%4GL944m/122102/.303112.OF}6&V e"w?>v"pt #87! vn":;@A<:"nx !66# sp%./13/.UVT\<>"$!! !"#">kc V &6|FO 93399 <>#"#><  "$ZTU./43..V5$##$59gT;&'9Z^^Z9'':Tg9'(''&()I8:9889: Z_59eU;'( :8.>euvc>-7:bccb;7-?cwud?/8KWZZW **D@@D+8(':Te95^&)(&''(DA:AD.*!Y[[Y!& !-x67&'67&'4&6%67.'%4'6&#"&'6767&54?67&'&#"#&'#&'5&'"'67&'&47632>4.#"%2#".4>'7,3 3%/0),7=*#0*+3.22'8  YfT,1'').UfY >98 "2 B2;F_ XB?2C 3" 894ihgikce"S[XVWXZ#ejpMcNTvJKrZ1VlLWMI p jk%nA V{ww[11[ ww{V @#fd-#JM 7B/""0C7 NK",df#νhhοggQUXXUd %3!'#!52#"62#".54>" h9|M463%&$$5 O Dn; $$$$33'554#$/[QwGSGUW GJGX .5CK&5432632!!#!##53&4&#"326!&&#"327&54654'XP}}P~C;7?_Xej;A>7'sssLFF~||ב-  䤣lrrq)-5DL&'&6767&'"'&'&'&5'476!7!! 76'&'&'6'&utss-5 l&kpid=pDi/tEust,2}ts5sqtt-ԛ1 k&iꧦ g\}ul  An?\27/rtts,͓}qt)8GO'"'!!##53&'&54326!7!&'&36'&&5'47&#"327674'U`P}zpidu>7;C˂;C>xtsK) ||LGD g\uls螝՞䤣hkrr .4&#"326&54762!7!!!##53&w衤礡ᩨhn&䤣羚 o[tꝇ|| +D#"'&'&'&47>76327'7'%'27>764'&'."(F3"D"&%#}bV`ZZ^;D"&&$[X]:3G9:]:F=~=HS]^X&% iiD^29i\=<<92-1X?:<91*=X62'%'!!#5!5!5&'&'.546767''7'''7"2767>54&'&'&4p69].(EGGE@Z-<81VDEGFF'19T]9T:G5>+.11./:95>+.11./:9 \2:a(Eb_E@( %CE_bG(Hij:ο\ij+.wBAw./+.wABw./4+F!!#"'&'.546767675!5!' 2767>54&'&'&"<-Z@EGGEDVRbfNZ@EGGEDV18kbbjC9:/.11.+>59:/.11.+>5疑 (@E_bEC%##(@Eb_EC% kajP/.wBAw.+/.wABw.+ +F####"&'&'&54767>32333'7 '%32676764'&'.#"ܖU (@E_bEC%##(@Eb_EC% Uܭkaj/.wBAw.+/.wABw.+<-Z@EGGEDVRbfNZ@EGGEDV18kjC9:/.11.+>59:/.11.+>55 @  10432#"732654&#"陽…5 @  10432#"K +@kk k kKTX8Y104632#"&732654&#"ϑϑϘuSSuuSSu͒ΐSuuSSvvdPK!)7eK RX@ *.,&"($ k3,k($kk8991@&"6k0k 8<2<299990Y4632632#"'#"&7323&547&#"%6547232654&#"dϑRDDRϑRDDRϘuS?>Su^222Z>?SuuS ͒!!ΐSuXqpWv28ML88LM{WpqXuSSvTZ`z8Rm3#"2767>54&'&/2"'&'.5467676"2767>54&'&/2"'&'.5467676R#)$#R#$ $LK:C.25521@=:C.25521@=R#)$#R#$ $LK:C.25521@=:C.25521@=zZF)(JG()K.2IF21.2FI21F)(JG()K.2IF21.2FI21 J7Qk>767632"'&'.'!"'&'.546767632$"2767>54&'&'$"2767>54&'&'#61@=HK:C.25521@=:C.5%'21@=:C.25521@=HK:C.6#R#$$#R#$$R#)$#R#$ $5[51.2IF21.4`]21.2FI21.5[F)(GG()FF)(JG()KR 5%%%xr6׊eMM^xxV)7654'&'575#!&54767'5!s_vR$N::N$Rv_{aT,X@X,Ta{4b\)1%==%1)\b4ߴ:`\KDDK\`* 4&#"326&'&5432#w衤礡$PP䤣L~{Y,326& '6 !!#!5!(+~uP.Gjt ~||, # !!#!5!L>>0oJ;||,'!5!737!!!!##53z{{{z{|zhz|{||WxT% ! !5! #3 35!'T??LLwLLJ|A|JZt|J|,$264&"&7673% %&uuu>hH]%VgVYFhݦuuv#gGέҔEgEY$&'&5%6;2#"'!!##53uN)$#<^tfFp!E&J <ԩ;  ||lPj'#"'&#"'&'&'&47>7632327>76&'&'&/&'&'&47>762!2!%327>764'&'.#"&#"327>764'&'&s* 0$+$$$ 1#*# ZaZ%% NT12 4 #HH  ")mROeb  , 0  +   ) . $J . %'.D"&B 1 $C mR )Ky    !   V!Edz267>54&'."#"'%"'&'.5467676;27>4.'&+"'&'.54676762%632$"267>54&'&.&&.&m,mQjP(!N!"(! aVf&&bZ55!("!N!(PjoQm,.&&.&q    l?W,>&#< A#"< " (( " <"#A <#&>,W?~    lOOj3!#!"'.'&47676?6767>'.'&#"#"'.'&47>763276;%32676764'.'&#"676764'.'&#"32eOuRd2!  HH# 7   ZTN +Za21#+$0 4$$$+$0 's  *   * OK) Rd#!>& 3"9*$"D. ' - D! 2 . , T% #: & (  IZx-4H67&'&'&+"'&'&'&476767632%632 #"'%#"'&'&'&54767676;276276767654'&'&'&"276767654'&'&'&""'&'&'&547676762"'&'&'&547676762'&'&'&547654'&'&'&";276-&#"+"276767654'&5476%327%&"'&'&476762I  Q\C--%("(/*0.,+"( /X]\9<\X/"$)0*3')"* %1*0CR[        22 2 2 2 %'   &J  &%C\d#_*]OhXC%&  J&   O]*       ")&`&"'$"/' <%ZS  % SZ%< /'* "%5"-($# ;8\= !  !  " /VC "  !  !  [uV/+    V^au 767>54&'&'&#"&54767632 '.5467&54732#"#"676767#"'&#"'67654 ozwbda_f_zx|wbdaM,krnulspsnunNJ*D$ lQ$" 6*D?"5'K(2- # >   :72 331cd툍i`4331cd퍇>mwn<;;8ro졘wp:;;BV0/M8:D@*|sa  -F(7 "*=8&0!2  1-5$& 6:B4V^ (B\w.'%&'&"632%6767>54$2"'&'.546767" 767>54&'&'&'2 '&'&547676?'*&$ 1$-+h+-$F3782**?1 $&>>9|wbdabc`zwbda_f_zxspsnunˎspsnulwI_"2[$  "" gI $[2!v 55 55 31cd퍅caf31cd툍i`43d;8ro졘wp:;;8rown<;x,A-57'36%33#3#!2#!3#3##$'#7$@d5{sVd]F0 0F]dVs{5⒒d@( jPP,PP` 0 ")- !676762!"'&'&'&54!X$#R#+/RFF$#R#$1Sh,  k-"s!}P476?6763&'&'&547632676767654'&547632!54'&'&54'&&#"'&/&'&'&#"#"'&'&/&'&#"&'&'&?6'&'#"'&'&#"!'476='654'&545454'327654'&'&327654'&/%4-)"$0JK&  )7    %0'# #6 +-L __^/s4* 1( .266 |/(1   \   #:7  lS&   x71]/~[#<$  o_%@,: $";vR $X$+|!5DX&PY;9Do6 b'n2  83eF] 4T&  &  /50$?- 1@& 3l K  C"P1 :03<D:5XI.)D&[+-1:   q/A8   g+jl9Lp{7654'"'&#"+"'&54?67676763276323273#5%6767'&#"6"/67#"27632327654'73654'676547&p/l0&J!cS%YE]{@C"$4>-;% ,(6Y>m!N$X6"/,(4sS?X$U>"sJ?K(`./4+2K2.0>S Zp0+1^' ;cs  /^"|Y/ 428ۇϕl%%ot5oA='Y$ aT* ''G+- %_kj~r}jL`І|\gK@/.85c($ (2LS>54/##326?%%3254'654'3>7632#"&547>32'% ;66I   }g ?6qn   -> 9@ H67;  zh| 8 >6!q    B5>%+?F4&'&/76765'7! !'!654'!4'!!$467>2"&'&!654' 33 ^^^RXI#J2VlP# ~!88!~ Kppph,p<(##(#id (2LS.#"227654&'''%'654+.#"65.'&54632#"'.6#"%  I66; o |>?%6!q   9  ;76H   |h> 86qm    BX{[%G'23 %%.'&"27>7%$!"#232%"'&'.4676762%#"#2%k      A>>dIID`nS   SnGYn 5>5 n)(%$#"#64'232%%&'&'&"27676&22k**!n``n!##3W 2327632#"'&'&5476'( > !~GH ".4F+@xH )0$'*' 23277632#"'&'&54763'( e` }{*279HF`0@xJL 1 ,A  ' 7 Ɏ877Ɏ77ɍ8ɍ? tt7tt7t7tt7uB2632#"'&'#"'&54767'&54763267632676 Q   x L$3 z(   6X3  6*=P*> "#  R26#"'#"'&'+"'&'#"'&547&'&54767&&5476326763276T 디% $$YyX$ zc0 + j :  (̢1#: _$ #- Խ =1 '2ĺ pD #!!!!!%!!!!!!!!#!5!36HVBBXBBUHVPBXyBpD !!!!!!""p"p"#pD35#7!!#!5!3rrsrspD!!%!!!!!!r"p"#p"#Rb !!#!5!3ppEU l3!!'#'!!#!!3!5@,r,,_ r,,_>v #!!!!!'!!!!!!!!#!5!3hm_|P_H_pDK#";54&'&'&#'!326767657'&'&'.+3!76767>5{dIB,$2$*DEh{LGC_RQ|66R_CIJ{hED*$2$,BFd{LGC_RQ66R_CIJKIB`OT|87O\FGKzdGB+%2%+BIdzKGF\OT87O`BHL{dGB+%2%+BId  #!! !!! 373#'7#ZAA:Llحmllmzlmllm|}}|d d}cT`C54'&54762327632#"'&+"'&5476=#"#"'&476323(L,68x86,L zFvd0000dvFz L,68x86,L zFvd0000dvFz zFvd0000dvFz L,68x86,L yFvd0110dvFy L,68x86,LV^&'##"&'&'&4767>32367675&'&'.5467676236767>32#"&'&'&'#"'&'.546767675&   R.-R  R-.R "  *!""! ((\(( !""!#%   " R.-R  R-.R    %#!""! ((\(( !""!**!""! ((\(( !""!#%    R.-R  R-.R "   %#!""! ((\(( !""!*  " R.-R  R-.R   Sa4&'&'&'.546767622676767>32#"&'&'&'.#"'&'.54676767>5"#"&'&'&4767>32(,$ ((*& :.r06$&**& )'De!  'd8:b&$$&b:8d'  )a@/!  ')*&$6/r/6$&*)'  ')?c'  &d8:b&!$&b:=_& (bCc"  &d8:b& $&b:=_& (a?/!  ')*&$6/r/6$&*)'  ')De!  'd8:b&$$&b:8d'  )a@)' ((*& :.r06$&**& ((T`0267632#"'&'&'!&'&'&54676763267632#"'&'#"'&'&'&5476767!6767632#"'&'"'&'&'&54767#"'&'&'&5476767632!#"'&'&'&54767#"'&'&'&476767632&'&5476767632!#"'.'&5476767632&'&54767676Z   ( &            <   4          % (      (   2     6           %    <    %  (   W_2767653"4'&'&Wspsnullunsps;8rown<;;j>-'O^__^Oq44H4"hdd0!% %!-@jjjk**37'73 #'xxxx.xx.x..x  pD #'!5!73!GFdFGrEGdGErFGqFGdGFqGEd@L     - FOFc,OO,cFd,PO,dGOP T` '%%%%%% % -wD{wwe#w%f{wwy||y{xxe#w%f{wwxEy||y % %  Zp/AppA/}}ET`     - Zq NqqN  NrqN qrT`% % -ZyllylyyT`%% %% -ZtGcVGttGVcGGstGWcGtsGcpD/3%!!%#'''%!5!%777xo:U.cF.d;UǩoxoU:e.Ec.U9oE.f:UūoxoU9g.Ff.U:oxo9U. 54'&5476276767632+"#"32;2#"'&'&/"'&5476=&'&'#"'&'&547676;232?&547'&#"+"'&'&54767632676'K,68x86,L qA'C<4GW>L d  f L>WG4L d  d L>WG454&'&/54'&5476276767632+"#"32;2#"'&'&/"'&5476=&'&'#"'&'&547676;232?&547'&#"+"'&'&54767632676o**YK,68x86,L qA'C<4GW>L d  f L>WG4L d  d L>WG42#'"372"'&'&/"'&476="'&547>Q!//VZ *nN+G80j@6RR6@j0/P1N TP#00VZ ,lO@W+G80j@6RN6@j03L/N  ]H,`,H Yc!77\4OO4VA7gU3',H^ ]H,`,L&3c!77\4OO7VA7fV4&,H^67654'&"327632#"'&'&/#"'&5476=#"'&'&5476763232?'&#"#"'&'&5476763254'&5476276767632#"'&#"#"'&#"327676%32767654'&'&#"#"Z8%1T1%85e %ZF\ +m8BS/?JV@6RTXN6@VGB1QB8n* \FZ% e53e!&ZFZ *n8BS/?JV@6RR6@VGB1QB8m+ \FZ&!e3DA 5<; > +F$H$F+ > ;<5 AcJ2QD++DQ2J (5H,'9,J&0f) T|\`j4OO7g`\|T 'g/& H,9',I4( (3J,&9-H &0f) T|\`j4OO4j`\|T 'g/&J,9',H5(""'!$(:UJJU:($!'""nFw"2767>54&'&'767632"'"'&'.'"'&'.546767"'&'.546767632.546767632=>343343>==>343343>x>%85670-),(-%8/[0!-(,)-02y/8%0%)-02y/8%-(.'&$W/:#-(,)-02;>/;),)-02;>/8%-( 06{IF{6006{FI{605+'g>:c.&".c;=g'+&1N%&W'+&.c:>k#"$.c:>g'+,B:>g'+&.c;=?nF\v%"'&'.546767"'&'.546767632.5467676267632"'"'&'.27654&'&'&"67&'&'&'276767&5467'&'&#"32767>54&/76767>54&'&'&#"Z0%8/y20-),(-!0[/8%-(,)0-<1:3%>(-%8/|/8%-(>%85670-),(-%8/[0!-(,)-02y/8%0M=  H C# B/g H /*x#$  8## H g/B PP  $#x*/%N1&+'g=;c."&.c:>g'.5 ?=;c.&&.c;=? 5+'g>:c.&".c;=g'+&1N8GG$> >$ c.,bB$#>  Ir0C >'#> LM >#$Bb,.$ >#'> C0rI T`)T:e&'#"&'&'&4767>3267'&#"327%32676764'&'.#"7632#"#.4767676324676762>322##"&'"'&'.5#"'.'&467"&'&'&4767>&'&'.'&'>76?&'326767767>5&'&'.#"767>7.'&/32>7674&'&'67'&'.#"67'&'.'67676767"2767>54&'&'"'&'.54?&'2767>54'7654&'&'&"67'&54676762:    $4 4$ww4 4 xy   %" !()-+U$"! ((\(( !"&S+-)(! '7M"# V2% A()-.R$"! ((\(( !"(O-,*(A"#2P"# "M    ! *4 2 kk  4 2 uKK        i2 4* !== 2 4  `_  wR#$$#R#$$  8 < c !<>     8 < d!!<>   "%UV*) !!$3R  R3&!-(-%Z& "#%(.2$( &&S+,))A!$3R  R3'A))XT$""#%(`$( "      i3+!x== 3 _`        !+3 kk 3 uKJ   F)(GG()F$    %3 3%ww3 3 xy   V^3N^"2767>54&'&/2"'&'.4676762 '&'&547676% %-z35++++++53z35++++++5pWDM69?=;9JHDM69?=;9JHSspsnunˎspsnul}}(.h<;h.((.h; +F$$> +F$H ;<5 A~ ;<5 A+DQ2J (5H,'9,J&0f) T|\`j4OO7g`\|T 'g/& H,9',I4( (3J,&9-H &0f) T|\`j4OO4j`\|T 'g/&J,9',H5(G+DQ2J$(:U$(:U3!'""!'""A''7'753'75377537'7'#5''#5'7#5'7'7<B-DH2#"2767>5!"&54$3!57!#"'&'.5467676#_>I-743TP>CPNDG-2.1/&D9 88 '.* !-8D_2{j@F'%.3r@Md7+4V/2&'&54676762"'&'.546767Zy*,&''&%1]~|45,-++-,54|45,-++-,5(+&a4|d΃fz4a&$(F*.j=3"&'&'&54767>32rJ6464NN4646Jp`684F@NLBD64:866D@NLBD668^~* i654'&#"632327632!"'&5!267&'&#"#"'&54763247632327654'&547632#" 6+Jo.^V|;-˙it36?̺fQMeEJS?(*$ s]vh2K)*NL13^v:Mc*ZeC03N35%&-Kt\K%9S >BWN=!$?$8(F!5{^?Z Q67654 547&'&+327#"'#536767&'&'&5432&5476323254'&5432?-BO>=v06&%K`dC+(k$'eM?$#=Hb B=)+8=.m9eb PB>$3g:84!EB7WPfG+1KHP<Ff#&T'0P+A'<}DC/'"05276767654'&'4rceNS((((`hm@DDF/CD}>C/GFCG !&547>2; 0!!6P<:! !$ ! "#{! !{54&#">32!5!>??qq>0ţ=as;N_/>!RL}A?rFi:}$:&N?(U?"Mt 6+A]A)9IYiy ]1.+. + !'+!+9*'!901! 4$32%4&#">32+32#"&'32654&'26??qq|=_ky4[\XZcksuD}[X@v hA?rs ?<:32#"&'32654&#"75!5!??qqYe2hvvhDw_X@ϰ?A?r%aVUa/  23/4/3и/4ܸA]A)9IYiy ]A&6FVfv ]A] +  + +,&+,/&,901! 4$32#"&54632"32654&#"7>325.??qq\NN\\NN\qºN w/aTJjA?rZbbZ[bb*= P# + + 01! 4$32%!35!??qqlUA?rv]K 1=++ +A]A)9IYiy ]A&6FVfv ]A]A ]A ) 9 I Y i y ]/9;9;/A;;]A;);9;I;Y;i;y;;;;;;; ]5+ )+ +28+201! 4$32#"&5463232654&'>54&#"2#"&546??qq_TT__TT_⾭vijvkKRRKMQQA?rlHQPIIPPI\vSttSvB>=BB=>B &23/4/ܸA]A)9IYiy ]3'и'/-A-&-6-F-V-f-v------- ]A--]+ +  +*0+*# 901! 4$32254&#"326#"&'4632#"&??qq鿹ºO w.aUJk<\NN[[NN\A?rK < O$[bb[[bb $0Ӻ%+%+++A]A)9IYiy ]A++]A+)+9+I+Y+i+y+++++++ ]+ .+ (01! 4$32!5##7##"&5463232654&#"??qq$ŸuF?@EE@?FpA?r*'$ =$>  767654'&'!5%3!!  '&'&54767̆mommom4mommomP\|~{{~||~{{~|96oooo6996oo  oo6}9:݈@>}~Ա~}>@@>}~,,~}> =6P  767654'&'!!567>54&#"5>32  '&'&54767̆mommom4mommom)4 \=)N=kP`aF7I׺\|~{{~||~{{~|96oooo6996oo  oo6_A.Xx;_x55'(IZV@>}~Ա~}>@@>}~,,~}> =B\  767654'&'#"&'532654&+532654&#"5>32  '&'&54767̆mommom4mommomttLUDWx~zB\RGr=\|~{{~||~{{~|96oooo6996oo  oo6yt'(xrjw_Z\bd @>}~Ա~}>@@>}~,,~}> ='A  767654'&'!33##!5  '&'&54767̆mommom4mommomh*˪+\|~{{~||~{{~|96oooo6996oo  oo6 @>}~Ա~}>@@>}~,,~}> =7Q  767654'&'!!>32#"&'532654&#"  '&'&54767̆mommom4mommomz#G#KSLVAC\|~{{~||~{{~|96oooo6996oo  oo6c ۻ)%}|X@>}~Ա~}>@@>}~,,~}> =%>X  767654'&'"32654&.#">32#"32  '&'&54767̆mommom4mommomllm=|< /Vڵ =|^\|~{{~||~{{~|96oooo6996oo  oo6EKۼ>-O@>}~Ա~}>@@>}~,,~}> = :  767654'&'!#!  '&'&54767̆mommom4mommom\N\|~{{~||~{{~|96oooo6996oo  oo6`E#@>}~Ա~}>@@>}~,,~}> =#9E_  767654'&'"2654&%.546  &54632654&#"  '&'&54767̆mommom4mommoms慄htdthutԄ9tihvvhit0\|~{{~||~{{~|96oooo6996oo  oo6,{{|kl{Eggss\hh\]hh@>}~Ա~}>@@>}~,,~}> =2>X  767654'&'53267#"&54632#"&2654&#"  '&'&54767̆mommom4mommom=|< .Vڴ=}mmlJ\|~{{~||~{{~|96oooo6996oo  oo6DJټ@>}~Ա~}>@@>}~,,~}> =+8Ca  76767654'&'&'"32654'.  735733!  '&'&'&5476767̆mo5885om4mo5885omT,+VUVV++2QPPQΠP3p\|~-,g%&݈@>}~~}>@@>}~~}> = $!5!#%  '&'&54767{\|~{{~||~{{~|#:9q @>}~Ա~}>@@>}~,,~}> =6>7>54&#">32!5  '&'&54767I7ݺFa`Lk=N)\\|~{{~||~{{~| ZI('55x_;xX._@>}~Ա~}>@@>}~,,~}> =(B>54&#">32+32#"&'32654&  '&'&54767ir׸G\\Bz~xWDUL2\|~{{~||~{{~|db\Z_wjrx('°t=@>}~Ա~}>@@>}~,,~}> = '! !335#$  '&'&54767hno\|~{{~||~{{~|  @>}~Ա~}>@@>}~,,~}> =7>32#"&'32654&#"!5  '&'&54767CAVHSK#G#\|~{{~||~{{~|=|}'' %@>}~Ա~}>@@>}~,,~}> = $>2#"&546.#"32654&#">32  '&'&54767PmmlC|=ϵѴV/ <|=\|~{{~||~{{~|+޸KE@>}~Ա~}>@@>}~,,~}> = !35$  '&'&54767>h\|~{{~||~{{~|@fE@>}~Ա~}>@@>}~,,~}> = +E2"&46' 654&'>54& 74632#"&  '&'&54767Yt愄/tԃuhtt-tihvvhit0\|~{{~||~{{~|{lk|{{Essgg]hh]\hh@>}~Ա~}>@@>}~,,~}> =$>%32#"3267#"&'"&54632  '&'&54767!C}= дѳV. <|=Allm\|~{{~||~{{~|Q/=޸JDg@>}~Ա~}>@@>}~,,~}> =  :2#"&546$  !5##7  '&'&54767eddedddB¡\|~{{~||~{{~|>-/#&%q @>}~Ա~}>@@>}~,,~}>uPj !!5!!Pp#@pppt 7%FN4NGuP85 zD<22pJJt '-ZKFGNuP!!u\lE>~~>uu+"&'.546?!".4>3!'.5467>2p4,,$$,,42.p ,.".2."., puP8!5! %JZPJJuP8!5! %JHJJuP8 #3#3#3!!5 xx<<oJpppJJuP8 55!#3#3#3oPxx<<΄ΊXXXXuP8!!5 %JJJPD! 6>l>>PD ! DR>l>>P  BlvvuPb3!5 5! '&'.u$##+* ZJMM*+##$0U%!JJ!%UuP84676763!5 5! u$##+* ZJMM*+##$0U%!JJ!%U0!! ^r{VXeoouP855!Dq΄Ξ0uj%5!!53  !<9h9>uj%5!!53  !<9h9>+Z !73#57!!+ Id&+ъ2&+Z 5!'53#'!!!+dI|&22 !'!'!53 !Odcndh 2 3#5!7!!! ndnd;ch dd !53#'5!'! !]n2n22r-hJdc;dJdd 7!573#5!! !2+2n2nr-hLJd;cdJ<6767632"'&'&'! <'CZmo~yti^Z\X^Vqoti^?)X6nGCZ.//+]Y݀z_X0//+]>Iʞ BP "&*.37#37#37#37#5!!!!3'#3'#3'#3'#<<< 7&#"7'7 !%*BF8WU{FC*9oX:WubP 55!5!!'!XXddPRt '327'' !!iFB*8X:*CF9XUpt>*%&#">7'&'&">327&5467>7tBEH#&NKX$W/,0$" D5Hp*G6$"!0,0Y"W!F&'&#GGCuaP'467#"!4676?'&'.5!3!.5P5#$%"//"%X$# 5eeJ(0Y! "X0(Jet*.'.54?'#"&'2767.'32t)H5 X"$ #0,0X"KN&#EHEBCGG&'&KW"Y0,0$"E6GsPX'<6%"'&'.54676$4676762"'&'&&'.54676762$/+z >_ $#R#af#R#)>xbQu 88RK68# 88  vc<*676767632#"'&'&'&%.5467.546A ''+/54<3o8n23'9%%%%bb%%%&:?$ fLLf#&#/:&'X23X'rr'X32XV2c"'&'.54?654&'&'&#!"#!".4?64/&4676763!23!2767>54/&546767622 Z ;:td Z   c   uu  c  2c"'&'.54?654&'&'&+"#!".4?64'&4676763!2;2767>54/&546767622pW\xj IJ \W   8  uP^'#76767&'&/3#>7!5!!5!.'PSJl..&GG&GlHSi7*nK Kn**7OUnm'66'1U=Hd)dH=m'*'$&'&#"'67667 h7Hm^:-3 RE SRQO1̡LHO'57$'&54&#""OER 3-:^mH7hH܏1OQ S #u ! ! j.u-10 3%!#3!Zddd/ #3!53#5ddZd{3 #pph # 3hp&T&T[[ '#'#'##'x\xxjjxx\x,x\ehhP8\xYY73373737+.x\xxjjxx\x.x\8Phhe\x,OlD=072767>54'&'&'&"7#7676767632#"'&ew@RNJV !'7$"!3!&'&'&'!#!2767676wx !1cbbc1! "1cbbc1" `x]\LM&  &ML\;RR &ML\]]\LM&ZwxZQvcbddbcvQZ[RwcbddbcwR[xV''LM\7=e=7\ML'e;6\ML''''LM\6d 8   2@ @@ 00 ]1@   990@   <<@ <<KSX << Y5!!dx yxUZxxu 8   2@ OO __ ]1@  990@   <<@ <<KSX << Y'7!5!'7 wxy xZwxxd 8ڶ 22@ PP_ _O O]1@    9220@   <<@ <<@ <<@ <<KSX <<<< Y5!'7'7!dxxwxxUZxxwZwxxd 8!!5!! s]xwx]ix]xZx]xiu 87'!5!'7'7!5 ii]xwx]iix]xwZwx]xd 8!7'!!5!'7'XiiiI]xwx]h]xwxiii]xZx]]xwZwxd 8 !5!3# Y#xwxݪ-xZxYu 8 #3!'7'7xwx-\xwZwxd 8 !5!53#5! Y]xwx]Q7ii]xZx]Eiiu 8 !'7'7!#3!7'Q]xwx]iic]xwZwx]\iiu 8%77777773'7'7#'''''''uFFxwxcnFFFxwZwxnF,X@,,X ,,X@',,,X,,X@',,,X ',,,X@',',,@,@',,@',,@',',,@',,@',',,@',',,@',',', ,@',, ',,@',',, ',,@',',, ',',,@',',',@',@',',@',',@',',',@',',@',',',@',',',@',',',',@',, ',,@',',,',,@',',, ',',,@',',',@',@',',@',',@',',',@',',@',',',@',',',@',',',' ',@',', ',',@',',', ',',@',',', ',',',@',',','@'',@','',@','',@',','',@','',@',','',@',','',@',',','',pX,p,pX@',,p,pX ',,p,pX@',',,p,pX',,p,pX@',',,p,pX ',',,p,pX@',',',,p,p@',p,p@',',p,p@',',p,p@',',',p,p@',',p,p@',',',p,p@',',',p,p@',',',',p,p ',p,p@',',p,p ',',p,p@',',',p,p ',',p,p@',',',p,p ',',',p,p@',',',',p,p@'',p,p@','',p,p@','',p,p@',','',p,p@','',p,p@',','',p,p@',','',p,p@',',','',p,p',p,p@',',p,p ',',p,p@',',',p,p',',p,p@',',',p,p ',',',p,p@',',',',p,p@'',p,p@','',p,p@','',p,p@',','',p,p@','',p,p@',','',p,p@',','',p,p@',',','',p,p '',p,p@','',p,p ','',p,p@',','',p,p ','',p,p@',','',p,p ',','',p,p@',',','',p,p@''',p,p@',''',p,p@',''',p,p@',',''',p,p@',''',p,p@',',''',p,p@',',''',p,p@',',',''',ppp,p@',p,p ',p,p@',',p,p',p,p@',',p,p ',',p,p@',',',pp@'p,p@','p,p@','p,p@',','p,p@','p,p@',','p,p@',','p,p@',',','pp 'p,p@','p,p ','p,p@',','p,p ','p,p@',','p,p ',','p,p@',',','pp@''p,p@',''p,p@',''p,p@',',''p,p@',''p,p@',',''p,p@',',''p,p@',',',''pp'p,p@','p,p ','p,p@',','p,p','p,p@',','p,p ',','p,p@',',','pp@''p,p@',''p,p@',''p,p@',',''p,p@',''p,p@',',''p,p@',',''p,p@',',',''pp ''p,p@',''p,p ',''p,p@',',''p,p ',''p,p@',',''p,p ',',''p,p@',',',''pp@'''p,p@','''p,p@','''p,p@',','''p,p@','''p,p@',','''p,p@',','''p,p@',',','''p,p',pp,p@',',pp,p ',',pp,p@',',',pp,p',',pp,p@',',',pp,p ',',',pp,p@',',',',pp,p@'',pp,p@','',pp,p@','',pp,p@',','',pp,p@','',pp,p@',','',pp,p@',','',pp,p@',',','',pp,p '',pp,p@','',pp,p ','',pp,p@',','',pp,p ','',pp,p@',','',pp,p ',','',pp,p@',',','',pp,p@''',pp,p@',''',pp,p@',''',pp,p@',',''',pp,p@',''',pp,p@',',''',pp,p@',',''',pp,p@',',',''',pp,p'',pp,p@','',pp,p ','',pp,p@',','',pp,p','',pp,p@',','',pp,p ',','',pp,p@',',','',pp,p@''',pp,p@',''',pp,p@',''',pp,p@',',''',pp,p@',''',pp,p@',',''',pp,p@',',''',pp,p@',',',''',pp,p ''',pp,p@',''',pp,p ',''',pp,p@',',''',pp,p ',''',pp,p@',',''',pp,p ',',''',pp,p@',',',''',pp,p@'''',pp,p@','''',pp,p@','''',pp,p@',','''',pp,p@','''',pp,p@',','''',pp,p@',','''',pp,p@',',','''',ppd?8 !5!53#5!s]xwx]ii]xZx]EiiuP8 !'7'7!#3!7']xwx]siic]xwZwx]\ii 3'#'##-Z-x\xxx\.x\n #\733737#x\xxx\xZ'x\# n\xO'=%"'&'&'&767670327676764'&'&'&pk_V1..1Vbrx`Xk_V1..1V_kpIxXE?#!!';B]YQS@?#!!';BQ9.-\ZnllnZ_.x$-\ZnllnZ\-.)xF!F@RNJV>lmGСBk>DdW0Xdtsݓ.W@#.  -&.%)/K TX)8Y299ܴ]<<999991@ &$-/22907&54&'>5!2;#"#!532654&+CI02Kl>>l5UU5D>kB0GmstݔdXЎW2  5 1Vd22h' %#3 5' :' 73 ٪L^8bb:'B 7''ٛ>PNq'B '''ٛ>PNq^D'B ''>PN'B%  '''tNP'B5  5''bNP#u  u-3!3!!#!#!5 L3ͨ--Ӫ--333333#######5Ϩ---Ӫ---:k7!!  767654'&'$  $'&'&547676h08rtrrtr@rtrrtr VGFFGrGFFG;:rs죟sr:;;:rssr:Ŭɪ:k3?  767654'&'$  $'&'&547676!!#!5!rtrrtr@rtrrtr VGFFGrGFFGssB;:rs죟sr:;;:rssr:ŬɪKss:k3?  767654'&'$  $'&'&547676   ' rtrrtr@rtrrtr VGFFGrGFFG]x3w32x3B;:rs죟sr:;;:rssr:Ŭɪ3x23w3xuM %' io& i' i% iJuM327!5!>2&#"!!"&' ;E 2&#"!!!!"&' ;E $;E Ϊ@z٨zuM&#"%"&'73275%>2";EC;EJ綠mzzuM*3&#"&'67"&'7327&'&54767>2";EIq(P >6D;E]InoSu=,HK%)AH!+p$ z1IosV2";E+@/V]H6H\nUm;D [>wfP3,,I6x/Ur]HH]lVzM>wrN3 F4uM!3#!!>2&#"!!"&'732w~9F 9 }9Gr0}}uM+3#>2&#""&'73273264&c)~9GcBnnVs~9F (6o~ç|K|oU}uMp.3#327264&#">2&#"632#"'"&'z;E-8pƖqS;E;DܛWI3>6я]z!zuM 13#64&"327&'&767>2&#""&'˔֐;E]InoSu;EcBnnVszяϐ-1Io7sV2&#"!!"&'73273!#3;~9G9G ūI}ޭ{ tMm-&#"!2#567&'!"&'7327!5!>2";Ed_``!;D ܻ`;`*I6ƌebIz`:H:`*F4uM#&#"7'"&'7327'7'7>2";Exx;EzxXyxzyxإzuM*327#467>2&#"#4'"&' ;E-A 4yy;E Z>Vy|-2PIϼ+zEa82JzuM'&#"63"&'7327&'&53>2";E*y;E\?Vy~+&8'zLFaI1zuM>32&#"#"&'7327!5KL~9GALK~9G⧅}}gkb>32&#"#"&'73275!KL~9GALK~9G⧅}}Р? 5 5FѶeѦ 55FѶ///m' //& 0'' /'' 0' // ' 0N:A%#"'&'&'&#"5>32326#"'&'&'&#"5>32326 5jbn ^Xbh`n ^Vhjbn ^Xbh`n ^Vg@PNE;=LTNE;=KPD:32326#"'&'&'&#"5>3232655jbn ^Xbh`n ^Vhjbn ^Xbh`n ^VePNE;=LTNE;=KPD:327&#"56767326 5jbDS4WVhjbm\Y@/Xbh`ES3VXbhZmMp[Y@1Vg@PD4KUNE;@LTNE4LRN"*,@J^po_N5<#"'3267#"/'7&#"5>327&#"5>32732655jbDS4WVhjbm\Y@/Xbh`ES3VXbh`n[Y@1VePD4KUNE;@LTNE4LRND:@J^T 5!5!-5 !5!uu/0\^ҲЪ~T -55!55!usҲЪ᪪/0N%#"/&'&#"5>32326!! 5jan^Xbh`n^Vf@PD:32326!!55jan^Xbh`n^VfPD:323265-5ian^Xbian^VgsuOE;=LSNE; =KJ/0:ҲЪ !(#"/&'&#"5>32326-5 5ian^Xbian^VeuOE;=LSNE; =KJҲЪ/0, -55!55!us%ҲЪ᪪(/0٪, 5!5!-5 !5!uu%/0\~ҲЪ^6 5 5 -55uu/0V/ҲЪа/6 -555 5uuҲЪ۰/'/0K/& 55p/ѦѶ& 5 5p/om//&' /G&' H{ 5!5 5!@Ѫop9{ !5! 5 !5!@Ѫ555@pNpop 55 5@p pU(".#"#"&'5327>76325hV^ n`hbX^ nbj@TL>7632 5hV^ n`hbX^ nbj?TL>֪VJ<:DNTL<:DNDop$+5!5!.#"#"&'53276767632 5hV^ n`hbX^ nbj@>֪VJ<:DNTL<:DNDf $!!!5!676762!!&'&'&!!C.8d 6WYYV7 e8-;Z{+DD\93[2332[0<[EC,W7!!%5$$}y]]x|W%!5505%$}$y|]]W !!'7!5!%5$ZZ N$}qPP]]x|W !!'7!5!55%$ZZ N}$qPP|]] K75!5!%5$!:[]3֪k-QtXVv K75!5!55$%$][:!3֪kVXQ-qK!5!7!5!7!!!!'%5$&`ȉ)P"_=6!:[]ss1st-QtXVvqK!5!7!5!7!!!!'55$%$&`ȉ)P"_=6][:!ss1stVXQ-y:E#"'&'&'&#"5>76326#"'&'&'&#"5>32>%5$ian ^Xbib` ^Vgian ^Xbian g!:[](NE;=LTN9 A=KOE;=LSNE;C E-QtXVvy:E#"'&'&'&#"5>76326#"'&'&'&#"5>32>55$%$ian ^Xbib` ^Vgian ^Xbian e][:!(NE;=LTN9 A=KOE;=LSNE;C EVXQ-6A#"'3267#"/'7&#"5>327&#"56767326%5$jbDS4WVhjbm\Y@/Xbh`ES3VXbhZmMp[Y@1Vg!:[]$PD4KUNE;@LTNE4LRN"*,@J-QtXVv6A#"'3267#"/'7&#"5>327&#"5676732655$%$jbDS4WVhjbm\Y@/Xbh`ES3VXbhZmMp[Y@1Ve][:!$PD4KUNE;@LTNE4LRN"*,@JVXQ-7 5@pppo%5555òi ' '!]#\e#N\#]x#L   !77 ! \ݿ##N]##4 !7 7:\#]x#L]ݿ#\eL#1 4  %''' !]ݿ#\eL#1\ݿ#]j#7P~ % ! !!5 5!3!   7?~% !!3 *^V !!^*  ^V!!!^ ' '!##L  !  ##4%7 7#L4L#1 4  ! L#1#7P~ % ! !3!߆^V ! !! !ECuR #7!5!7Zxx/{xx:xu-R '!5!'xx vx:xH% 7!!7vx{/xxxƪxvH-% 3'!!'Zxx vxx$!%!!W7 r$!!!W7 $!!,7r32 &}f[_ &}f[, %$R/ %$R !2+##5332654&+!ʿ[qrqqϐђАfT$@  $ !? %29999991@&  B  $/999990KSX9Y"@&]@Bz%%%&'&&& &66FFhuuw]]#.+;#"&! 32654&#A{>ٿJxn?M~hb–m؍OH#(07#5#"''7&546;7&'&#"5>327354326=-?\g`n;) T`TeZx_958>cc3Vfa<}NV{ E..''rOs+Ax.ٴ) 3{ B333#;#"'&'##53w1ѪKsQ fև3͏oNP r>6!#4&#"#3676323#d||BYZucce22wxLj%3###3!E3A1wH33 3###%̟8ǹiEL#\ !!#!5!sP=g՚oAX` !!#!5!qjLl}e`R%sw-@ 221/053#5# !232#"MT+焀\\xEEf! !+53265##-}-MDnh %!#3!3҈R={0#3 632#54&"$\^TރQr)m`Tῆrr:T*D  # #3 3 67632#54&#"f:9:54'&'&s~&&~~ڢ~.]=@N\N\.]=zz❞zz}qa !SM!R}|pas?#-n@.  '&$ /$ .9999991@ .'& ) )./9999999046$327#"''7&7&#"4'32>s~&Ġn~ڢĠnՑꏧw֜\w֜\zvijޝzwkj!^`|g^` .@   <<<2221/03#3#3#3#):@  1/<0@22 # #3.]F; -@    1@  /<<03!#!#!"9q><@  9/1 ]@ /<220KBPX@     @     @ Y333 # # \Xds3{ 1@   <2<2??]1/<2<20%3#3#3#3#\ 7@  91/0@ BKSXY" !!!!&TdD՚ohh $@    1/<<2203#3#3#hhh7o !@  /221/220!!!!5!!o&.-ժo1/,@! ',01*$ 022122<20!"'53 !"563 676!2&# !27# '&%4rmyymrO4%%4Trmyymr4*B6!*:'(8) 6AB6 )*!6oP@   <<222<<<<21@   /<2<<22<<2203!3!!!!#!#!5!!5!!n""xxyyrr3@21/03!!!ժ,o7@   /<<2<<21@ /<2<203!!!!#!5!!5!CCPPxyr7@ KTX@8Y221/0@ 0 @ P ` ]73#3#>@ 10@ BKSXY"47!5!32654'3! $x˿ßwNetwc #/9@1E- !'E0<2<21@ 0*$002654&#""$54$322654&#""$54$32,,,,PIIPPIIPPIIPPIIPs'(@ ) (1@ #(046$32#"$&732>54.#"s~&&~~ڢ~\ww֜\\ww֜\zz❞zz}``}|``s,P@  ! #.# -9991@ ! ((-99046$32'#"$&73277654.#"s~&&~l~\wj\ww֜\zz➞ikwz|`^jI|``; -@   1@   /2203!3!#,dq9d (@   <<<<1/03#3#3#QIh ?@     <2<2??? ]1/<2<20#53#533#3#3#h+Is'+>@- )(( ,9//)]1@+(#,046$32#"$&732>54.#"3#s~&&~~ڢ~\ww֜\\ww֜\zz❞zz}``}|``s>,P@  %$#& !.! -9991@ #&$%((-99046$327#"$&732>54''&#"s~&Ġn~ڢ~\ww֜\pw֜\zvikzz|``|?l^`sr%1=G@8&,20><2<21@/; 5 )##>9//0! #"&547 !&54632! 32654&#"4&#"326sS_  _mz,,,,,,,,gs'O;H66H;O'sz<11<;22<11<;//d #@   <<1/<203!!#!5!IIjk=;;sr3?Kf@F4%+6:0L2<2<29/<<1@=(I C (7##11L9///<20! #"&547"333###3&54632! 32654&#"4&#"326sS_ ̻A;z,,,,,,,,gs'O;H6ߊ6H;OO4z<11<;22<11<;//;@   2<21/220]!!!33##!!!>ժFh);@ 1/<0)3!3;+y=@ B <1/20KSX@Y!# 5!!!8ks#O@%$!  /<<22<2<21@  /<<<2<<<2032653#2#4&##"#3"3ʊyʊy+VVF%F.@ KTX@8Y1/0!##u-s+f@- ,&'  #+ /<<<222<2<21@+*   #*'"/<<<2<<<29/<205!5"3332653#!!2#4&##"#35ʊAyʊy>FV>=VF=6-@ 1/20!3!3M-$36767#"&546?>7>5#53!Ya^gHZX/'-93B$BS #C98ŸLVV/5<4,5^1Y7:X!##:o#5!#&X3!3hXo!533oXKK'464';6;'769'96:'469&496'96;&9;6'468&456&;46';64&466'466&;:6&7;6'765'86:'56;&8;6'766&:66&:;6'76;'764&:46&:76&586&996&666&5:6&786':64&746';66&;66&866&656&9:6'967&:56&876&546&486&5;6&;86'965&986&566&686&776&::6&8:6&756&766&6:6&886&556&896&956&856&7:6&966'966rid{jXn`+v)4>@01, *$6E591@ $ *052220#"'&'&#"#"'&547673!27676323 4'&'3ft[na`zxz{n[tfCGo~[U]LKfdKJ]U[~oFCD@@DDDk63366336Fk!<@!  # E"91@  ! "2220!"$"# 33276762324rTRrƒ>IxddyI?ВP8[ 77 [8G<r&,>`&s   !3#!! ! H0x:;hLH+fabgp{ "326&33###" rhո  983#!#!#3! !9҈_:o%+kj{"-#5#"&547!#3!63!54&#"5>32"326=?/j`TeZ߬ofasP`A"..''f{bsٴ)e767!!3##!#!!&aO)p(?x4&A D+k`76765!!3##!#!![(bR-f}v(UԓR:d6T356765!!#!T:WO)fb0d+L`356765!!+!L3DS{X^}з3oP! !!+##-}) `! !!### >?h˸ʹ`3'Ps'y2qu{&Ry.se3#%3# '&76   1L  F<HqC{3#%3#"32654&' ! hJ IHn98s j&m'yryq{'yo'y.n:W '/7?GO%3#%3#3#%3#3#%3#"264"264$"264"264$"264"264$"2642+ '&' &547"#"&546;&546 676 3#J"{iihiihiihiihiihiihiihG4UU32UU4IF]97R̬\dfʬ\ʫZee̫ZҜf!!!2+5327654&#!#!qmL>87||ժFwrKK"9+32+532765||BuƣF1n!&edH08L*!!!2!"'&'5327654'&+5!#!^eicUQsj~cd\]ժ˚8+lhzy$1KKIJJ+7L402!"'&'5327654'&+5!;#"&5#533!AicUQ^cdjTmcd\[jKsբe8+lh%12KKKJN`>¨{Rg|1&'&547632&'&#";#"32767#"$546p<HmmFEMUUU8%~` !!!!#+`Ӕo{V 3 3#!+!# ! !J9҈_҈_%s%>+{'{ 5@M"326=%#5#"'#5#"&5463!54&#"5>3205>32"326=63!54&#"߬o?nQ?`TeZxeZ߬o5y`[A3f{bsٴ)Lfa' fa..''~D''f{bsٴ)hn< - 3676! ! '&'!# !  J-p;:xżP.g%H}[[Xr%H{{{"-82 '&'#"&5463!54&#"5>3 6"326="32654&y7!``TeZ*qO߬o{ǝ>REa..''f{bsٴ)nq !3!2653! '!#%{J®sv%_r\4h{{(3%#"&5463!54&#"5>3232653#5# "326=H`TeZ||Cu߬oߍo..''{fcPf{bsٴ) !!#3 3%Lj_:+{N{ ("326=5#"&5463!54&#"5>323߬o?`TeZ^\3f{bsٴ)ͪfa..''5 )!#!#333#%~gY_:gci5R{N{"-0!5#"&5463!54&#"5>32333#"326=!#u?`TeZxgƚÛ߬oGfa..''~mc3f{bsٴ)V !+53276?!#3 3%lKMJ|ثL*+2_:q?=$%2@{VN{'2!5#"&5463!54&#"5>323+5326?"326=u?`TeZ^N|lLT3߬ofa..''wj8zHB3f{bsٴ)s'{f 37!!_(^M*c37#xIS 33#!!#53ʨ_YQx 33###53YR j% 3#! '&#5376 !&'! 76;:~ ż ~HjiF wvҵCҤֆ {'23##"'&'#53676"!&'&!3276o ~~ oV?s?VLVVM{~͐~sUUu%gstgs j$. 676! ! '&'!     ':/##.;:xŽ.$#.yHH5==5[[4=<4HHHq{ 1"32654&!"32654&'267632#"'&'#",nn霜ǝ98 !#!5!)+Vy`3#\{V4&#"#367632#PQfeCBVd{#4&#"#3>32d||Bu\ ed#Ib !5!5!5!b>>I5:@ K TX8Y991@ _]0 P]3#5qeo7@ KTKT[X8Y10@ @P`p]#o+w #!5!!5Pp+ɪF #";##"$54$3@/+X 3333! +m3#mD U%3 3# # #3>:9w+: #'+/37ڷ/$0(7,48<<<<<#+ 3'<<<<< <<<<< <<<<<9̰XKRX8K bf TX30<32#4&#"#9`M1Cuȸ||MM 7BuƸ||e,'"xMfca?'Gzed\V5<!"'&76763!!32653#5#"&5#3!#"&5332765!"3ە^SWsv||CusCuȸ||WVۃ^SBWLa{fcBVfcf__{{V H!&#5#"&5332654/&763!6763232653#5#"'&=4&#"#9`M1Cuȸ||MM 7c%Zk>8nClbd||xe,'"xMfca?'Gz2XO{fcx{䟞[t`&-V 332673 &Vv aWV` v ޞKKJL[`&ASN~`6@  F991B /2<0KSXY%2767653)5!3$Wq2!dj±/8s4tVg` ##4673>=3|u˷d7<T "yX`#!5!e/я`!#3#4&#!5!2snJvy–X`35!26&#!5! #X-뒦yX4=!3!#T\[CLzl` 3!2%!4&#!Wn`–X` !#4&#!5!2nKy–X`!#4&#!+5265#5!2nã rLy–a;- 1 <05!3!----Ӫ&=&= && `&$u`&$`&$\X`&%BCZ`&&Xh`&'d`&(Q`')ZX`&*`&,&Q`&-ZXV`&.X`&/:X&0X`&2%X`&4X(`&5Vd`&7Id`&8{C!`&:nV`&;X`&<I`&=`&><t&)X&%X&/d&8X3>=3##67'#3x]GgG.i=dB`ԛ":T)C '9 '9 X& ~X' ' ' X&c ~X&c ' ' X&c ~X&c'9'9&L~&L'&&cL~&cL'I&I0a&I+p~a&I+p'x~\F&x?&,~ x&>'xx\F&x?&,x x&> (f'X >f'}D>\/&E 8>>/&F 8 (f'~X >f'~&D8\/&E~88>/&F~8 (f'X >f'2D>\/&E8>>/&F8 (f'X >f'2D>\/&E8>>/&F8 ' \ ~&P /&\I> ~/&PI>)7%#"'$47332767654'&54767;#"'&/cͷ?Ahž#62 #dGG&+@XA:g!axLn 6r'|>X %+53276=3+HZ#c,1VV,1jٻ~X%+53276=3;#"+MZ#c,11,c7nVV,1jj1,JoX&~c~X&~cpn"56$3=gi~wun52&$=Ԛuw~ig* '/&'&#"#67632O,$e5Fqp[?8WH7  $0GJI  '327673#"'&'O,$a9Gqp[?8W7  $,KJI Pq,l&fq,Pr,i,k ;#"'&=3!1,cK\WL71,\W+Ps,Pt,l't,fPu,l'u,fPv,l'v,fdw,l'w,f<x,l&fx,UL'yR&0yl9'zRl9&0z @'z>n 6&z>l '{Rl &0{'z>o&zXD&z+p~&z+pyR 3;#"'&1,cKPWskj1,\e'}9&}9X&}~X&}'~m^&~^ '~ &~&~cR~&~cR'&&cR~&cR (f'}X >f&D}\/&E} >/&F} (fX >f0%3#"'&'&'!27# '&5767"#"5$3 "(1{R=IrbJIԖ^` __&m3HZdP^vc–e4)?6 [_w\/&'&'&5672+5327676SSgURHKLXJKݣdht^#4b4bBPH:jV>/);#"'&'+53276767&'&'&5672~AI2hrBV~(;E)Kݣdht^eSgURHK 4b)N"w6a.%PH:jV# ('}?X >&D}?\L&E} >L&F} }RZ}GR &'3;#"'#"'532767654"9aRQS,cKa].-fgsT!"#?zNuIS,!&* 1p*D}'}EZ}G&L}E b&\ ~&3;#"'!5 767654x I*eK2D0# &pgM,>ꅗ:H~ b'}q \ ~&P}q ^ GF%7653323;#"'#"'&''&'#&'$473327676'&/3N0%@nS,cKvDm% I01_@8'TPxmil_Qb_y^@@$:|_2&aS,`[ F{GHܳ&%0l}=J<~ 1%+53276=3327653763#"'&'#"'&+8LcKc,P,+hm,%@n\Kf%#?70`DAbH<;!.,Pd@dczg2&q\ =!1(78#"'&'#"'&'+53276=3327653763;#"'%#?70`DAbH<)+8LcKc,P,+hm,%@nS,cKvD =!1(I;!.,Pd@dczg2&aS,`Z ' ^ G&T  &U 7&V ` <I)"'&5#&'$47332767654'367676;#"/"3276'&'&u&4-JXPxmil_Qf[+!' (s{lHX}a*=RKgL~큻%MGHܳ&%Dl7(2l^F"%GMF ,\v7Ql?[F2 .327654'&#"!"'&'+53276=36767632Ш큺%0LJNA'fKc,P-e_KUskl?[F*#=,PdrNP2T?!'Dmx+8)"'&'+53276=36767632;#"/327654'&#"JNA'fKc,P-e_KUqm*=RKg਑큺%0L*#=,PdrNP2T?!'DKH ,\vl?[F '} ` &\} 2&]} &^} b))5!3%632;#"/%3276'&'&#"@o\Dui*=RKg큻%0Pz\?c!'EMF ,\v?]DQx %3276'&'&#")5!3%6329큻%0Pzu \Duiʸ?]DQx\?c!'Emx))5!3%632;#"/%3276'&'&#"8 \Dui*=RKg큻%0Pz\?c!'EMF ,\v?]DQx'}Rb&d}R&e}R&f}Ru *du %+! '&7.54762;# '!2764"[b=D}a_[9^DU)k_1ocz2t*n@00@p[C+ @Mkl=v8`3$*727&'&5763"327%+5SF7J \X];d}M4F!Ť$/%+532767&'&5476762;#""654'v`kB;(aD hYYh MXD=p`vʨ4/gg/($'UZ'-)74--47)-'bM,(U __ u F'}wdu L&l}F&m}wL&n}'}~\L&}?&}~ ~&}kG'~R~k &~k?&~,~ ~&~8i!D#"'5327654'&'&7676'&'$54733276763;#"'J&P DfXRNB8D-<9_h$$EB|=Q#!v+6(  %{{qe))5!27654'&54767;#"'&/66-62 hGG&+@XA:g!a_h$$EB|=Q#!v+6(  %?+)x.j#$%653;#"'#"'$&733276N1,cKpNyUcE@A(IPmI~jkj1,3.(B"[\ss~B"5 +5327653WPKc,1se\,1j%+5327653;#"SMKc,11,cKVV,1jkj1,^kgt5%327654'&'&#"#"'&#4763&547632;#"bzL,5;(.;D K2KxAZM\HT((&iK*9:X DD(PNNOmf7*(?$GC,,m$%#"'+5326767632%327654'&#"dan@ht4W^Q[a>/4(*X.[4fb0G1P8TYNE5EK&)/4:''5)24fb0$#1P8S1>,E5EX !a%H'}?  +&}?&'}R~'}Rm^ $&'&'&'3;#"'&'#"'&5476 xRot$8pKZI-&8:m*12e CY>)2'+eO,3;I0D-=67654'&#"27&'&5476&'5#"'+5327654'&$"':A4N--0M,Q@(Jxb 41}! @H=.%4-+#%v iEN@TSZ 'D49g=ql)D%'i.C!v-3j  ;AWE L9P)8K6(S/VL_+Y9K1\SJo765&'&'&54767632;#"'&#"#"'$4733276L[/,4PT*uW ##rpl$-AIqYhu?AB[M!3!+ (;=A<^ĸ#0{bV` )gZZrN J'~ o '~ X&~c~X&~c.&y,.&y,&z,&z, &{ &{T#"'53273676537M͞jK`Uq%BUG FA+7T#"'5327367653;#"'&4;IʡjK`Uq%"@Pif<[A FA+7DT)TL* 35'5467676?67654&#">32,X\"$߸g^aOl39ZZ8L{4<+VZ@EL89CFnY1^5YVe !5!5!)5!S2SR7'XF: 'b:= ']C; '<b= ']bH'&'H'''H' ''H'&'H'''H''' H&&'H&&'H' ''H''&H'''H' ''H&''H&''H' ''H&''H&&'H&'' H' '&H&'' H' ' 'H''& H' &'H' ' 'H' '&H' '&H' &' H'''H'&'H''' H'''H'&'H'' 'H''&H''&H'&' H'&'H'&'H''' H'''H'''H''' H'&'H''&H'' 'H'&' H'' 'H'''  H'' &H''& H'''  H'&& H'&& H'' & H'&'H'''H'' 'H'&'H'''H''' H'&&H'&&H'' 'H'''H'''H'' 'H'&'H'&'H'' 'H'&'H'&&H'&' H'' 'H'&' H'' ' H''& H'' &H'' ' H'' &H'' &H'' &  #3 !!#!]W:\w98qq+_N  %*!2#!327&#363&#!3654/654'f;33;$ $#>]a{w DD663! )327&#!36'hPcp~qAA k{qS3%!!!!!!-x9vq dddsd !!!!!#3#oQn.ddqs&&$#"32767!5!# !2deVRuu^oRaG@;@&5dSUmnHFcIf3%!#3!53#.nXddddq dddd fY6765%!#!53265-V?O?nqd J^ dd0 !3 #!3pdw@1q 2 !!!3ddo o !#!! !3!3_Gbn}qR+q  r'( ! '&76 7& 676'&&:żGlllli$ #ab2222jT%%5$c$-6&/.4%&  %5 64&/.$ Pdo&nŢmngzoʷ-[ʚ)'NXd''pui$2Xf| / 3%!!!!rpq ddq $!&%! 65! X!!Y fqba@`|gd5\*$ 3%! 3!dq d+D 3!3%! ! 3! !D5D:9:9d|q  d+l 3%! 3 ! #(\~vbL:H|dq d22{ 3!! #3ndp29V{{",34&'3!5#"&546;54&#"5>3 5#">76/=Kd?Vu`Tw86/^b;:gCzӆ]YfaH..t''UNHGgwt-!>32#"&'!4'&'676763&#"327N:||:^,<<,9RKM_]daadt= z =OsKTdihtJq{#%#"!2&'&#"3276%M]-ULEmGJXHCQRHV,${z$d$$>:##dWS%&-!!5#"323327654'&'&#"N:||v9,<<,^(]_MK^daDDaZKsO=  =Td6Jthio}{!327# 32!.#"}K_mk)#i̩J@b]u-)8 CqzӾ/ 3476%#"!!!#5354763g.9:9|WX -8J_D8d97ddddTVqV{#.=65326=#"325!!"&32767654'&#"jlQR:||:Nry^,<<,9/KM_]=ʌo,*qdaDDad-w=  =OsKihtJH "34'&3'!>32!4&#"! GS5‡OIƁkk h@[:Lded\ПU5 33#!!JKOhV #676#532765!3#%G(=1l$%OQRaеT0Hd01``2 !3 #!3OHіmdi#L&5#"'&5!3J=(G%RQOLiH0T0Z``~J^d{"&1<!>32>32!4&#"!4&#"!3%34'&%34'&OIƁԝTށkkkkd[ GS5 GS5`edJv\П\ПUh h@[: h@[:H{ "34'&%3'!>32!4&#"! GS5‡OIƁkk h@[:hded\ПUqu{ #2#"27&"676'&s3x33x3d4'pp'3(pp({98  kp-$-R-ۀ-qV{-%!!>32#"&4'&'&'676#&#"32N:||9,<<,^؆]_MKdaaKsO= z =oHJthiqV{-%#"325!!3#32767654'&#":||:N<^,<<,9(KM_]daDDad=  =OsK2HHihtJ{3'!>32.#"!N:4I,hdfc˾zo{E67654'&/&'&5432654&/.54632.#"#"&'i'K&'q4=B%%U+.39GSOjqL4vfLJ\_opPx3Zl=vf03"3;@{R?Bsl37'*7CoT78^UNO,, z1$YXDL#/%%7%&7#!!;!"&5#53*\{KsբjU|7N(dUNdudTD` "%&'&5##!5#"&5!3265! GS5CIƁTkkTS hl[:hded0=` 3%! 3!YT^^d\hdTV`3!3%!!3! !bTNdhhdjjjL` 3%! 3 ! #U|p|[hd-s=V`7%! 3+53267>^]_lP|XQ+ۙdi8{dCYXb` 3%!!!5!\vwhddhddh$%s'&'(#)s*;+f-j.j/031s23s4T567)8h9D:=;;<\={-{DEq{FqZGq{H/IqVZ{JdKyLVyMN9{Pd{Qqu{RV{SqVZ{TJ{Uo{V7WX`X=`YV5`Z;y`[=V`\X`]   6/&"27 d3{44{3s s#Տ0,-k37!!5!5%6bJJgq ddd HdH(7!676'&'$32!!7676&#")`"LlDbZE0Q](=ymd͕@9\9pd9hbiddAbs$*0"'5327&+5327&#"56325654&'>54+!ĪeO6?;2:L uWEdJj D d <h@Ѳ|!ŐUl$yXZ#3 !!3#!!5Qpq3d\#66'&#"!! !"'532gd1jKEн܁\`I Kd# F32v cSRav 6978w{z9 j@ VV120K TX@878YKTX@878YKTKT[X@878Y332673#"&v cSRav 6978w{zfGd10KTKT[X@878YKTX@878Y3#@1<203#3#䙋N#!#ęę53#73#'3# 3#3#'3#}}d 3#3#'3#}}d3#3#d 3#3#3#3#dd&;#"'&'#"'$&733$767654'3F??7KX~X\,>%!$'$&73!2%7&'&547676323!!"'654'&'&#"xhn}@AQ+"R:4RQP ioh4"(=)1$+<'g\^sM6,|y$K2S%jAzG' <8BN?0654'&323276767'&54767632#!V)B,4((7(*HTO<?aNbNLZB`.NJ|m+M;3*)3P& ]027EW4,E$2Hf3Џ,' !5;#"'+5327&'&54767632"67654'&'&f$'و'$A??8 D?$ 9P2*I1C299(M.L,0W 5+5DE2.4! k .@%&'&'&547676323!!#'$'&5473!2766'&'&#"B.y9()Wp8c20-=^E>><l/"'"3 9Ld/  #+m=E2X:zFNV}`kL:DbZzWK# :<,; ? &}R~&}R %4'&"2>"'&4762<R8R8z?@?@@?@(8)*8@@@@@?? '~'&'~cRP~&'~cRP' &cL~&cL >&Dz8\K&EzX>K&FzX >&D?\F&E >F&F  >&D\F&E>F&F >&D'}?>\L&E'8} >>L&F'8} 3_+ 5__bV'J@!B  6991/<2990KSXY"]33+532765#ոRQi&&}``01}` 2@  F <<221/<20@  @ P ` p ]33###53ø`<ĤV.` 54!333##"3276!5R w{i&V`p?`3A0c3'q=rUa4'qr[^3'zPq=cZ'sdrUcZ'udrUaZ'sdqaZ'udqvj 3't\q=cZ'wbrUvj V'r}t\cW'wuz|vj0Z's@dt\c:'vus(Dcm:'uDvuvc u'vutvV Y'yPtpVZ'yPsdVZ'yPudV'yPc['vuPj&Z,,!!,,O=32653#"&[hq`=QQ, &&| &3;#"'!5 767654x I*e2D0# &pgM,>ꅗ:H~#'|`'|S'|SF'|8@'|+ '~c~@'|+ '~c ~r'|>P9 9F KSKQZX8Y1/0@  @ P ` p ]3;#"&5Li a^q%qqu `&JOw`73#!!dž$Nd`Vw`#676#732767!5ʆ#5H2K1i0/N)deеT0Hd01``vg`'`&3#3## +@     22221/220!#3!53#^ժ ?!5 ?8'qXw8 U'rXw8'wq8'tXw8 U'uXw8 'w,t$'tz$'uzN@ T1/0333N@T 1/20%3!533yոBy@ T1/0)533ysոBq8@ E EԶ0]991@  /0 6& #" 3 *NYh> éA@E E Զ0]91@    /<20 6& "'!53&54 3 *NNJhh> é!8@ E EԶ0]991@  /0 6& &54 #"'!5 hYNJ>z=x 4@   2291@  /290)33!x³j*]Qix 6@   2291@    /2290%!5!33xtj³瓓]Qi' 4@     2291@    /290#5!33j³]Q=q) #33mCq"q )5333!mm"q)533#mOq $@  1/2<0)3!33OkUq""Oq (@   1 /22<0)533!33OιUΓ""q $@  1/2<0)533!3kιU"Oq $@   <1/2035!!5!3ΓK"Oq $@   1/20#5!!5!3ΓK"q @ 1/0!5!!5kqKq:@!E E ܲ@]ܲ@ ]1@  /<0!&'.4> !2>4."RJr 惃sKR9[ZZ 1ũbbŨ1 p`88`p`88!>@#E E"ܲ@]ܲ@]1@  /2<0%!!5!&'.4> 2>4."RJr 惃sKRQ[ZZ{ 1ũbbŨ1 p`88`p`88O:@!EE ܲ@]ܲ@]1@  /<0#5!&'.4> 2>4."RJr 惃sKRQ[ZZ{ 1ũbbŨ1 p`88`p`88O &@    21 /03"3#!5!>k fO "  21 /03"3#!5!>c f $@   21 /03"3#!5!pk fq7@ E<21@  /<20!!##"&6 !354'&"3.Cf^v ]8mr^<Uf"qɃ]8ƃ;@! E <21@ /2<20%!##"&6 !3!554'&"3.Cf^v7]8mr^K<Uf"Ƀ]8ƃ7@ E<21@  /<20%!##"&6 !!554'&"3.Cf^v]8mr^K<UfɃ]8ƃ ,@   <<1@  /03!!!!!55Փ/ 0@   <<1@   /20#53!!!!!55B/D ,@    <<1@  /0)53!!!!ys55B/= ,@  <<1@  /0!!5!!5!355ߒѓ 0@  <<1@  /20#5!!5!!5!355ՓLѓ ,@    <<1@  /0)5!!5!!5!,55Lѓ *@  <1@   20!!27654'&3!23,R4,,=ٹUiXO]Oz}I_"_Ҥ.@  <1@  /220#533!23!!27654'&ιUiXO,R4,,=B_Ҥ]Oz}I_ *@  <1@   /20!!27654'&533!2#,R4,,= ιUiXXXl]Oz}I_"B_ҭ@@  ܲ_]9@  /999@  10!4'&'5!!5Mc4B_9V@9D@   ܲ_]9@  /2999@  10#5!&'&'&'5!! 5Mc4BX]9V@9$@@   ܲ_]9@  /999@  10#5!&'&'&'5! 5Mc4B X]9Vq=:@   91@ /̲]촍]0!533T9 >@  91@ /2̲]촍]0#5!533hՓL9 :@  91@ /̲]촍]0#5!53hL9+#1@%!$1@  #/2203432>3234&#"!4&#"!}x5%^qZHZlK--Xh|ŕnc%5@'#&1@  $/2220#53432>3234&#"!4&#"!}x5%^qZHZl[K--Xh|ŕnc#1@%!$1@  "/220#53432>324&#"!4&#"!}x5%^ZHZl[K--Xh&|ŕnc= -@   <<1@  /<<0!!5!3!!!KK?1@   <<1@  /2<<0#5!!5!3!!!KK? -@   <<1@  /<<0)5!!5!3!!@KK?=X>@ <<<<1@  /2<<<220%!!5!3!3!!!=KøL??XB@  <<<<1@  /22<<<220#5!!5!3!3!!!%!KøL=??>@  <<<<1@  /2<<<<<0)5!!5!3!3!!!0KøL=??Oq %@   1/203!3!$Uq"KOq *@    1@  /220#53!3!$U"Kq %@  1 /20)53!!kUޓK=C  1@ B/0KSX@Y!!!tFs0hB~ F  1@ B /20KSX@Y!5!!!tFlhhB~BC  1@ B/0KSX@Y!5!!tFlh0B~B+ 8@!  <<1@    /2<20327654'&+!!!2/!m]%i ; @ED\qQE=4."RJrCEoJRXErrJS9[ZZ 1SV/ { 2Ʀ1 "p_88_p`88*#5!5&'.4767675!5!!2>4."RJrCEoJRXErrJS9[ZZ 1SV/ { 2Ʀ1 "p_88_p`88O(#5!5&'.4767675!5!2>4."RJrCEoJRXErrJSQ[ZZ 1SV/ { 2Ʀ1 {"p_88_p`88Q %@   1/0!!#!3BQ *@  1@  /20#5!!#!3ԓ} %@   1/0#5!!#!+Q (@   <1 /0!!#3!3OQ -@   <1@   /20#5!!#3!3ԓ} (@    <1 /0#5!!#3!B /@   <<1@   /20!!!5!3z;  K"qѓB3@   <<1@  /220!53!!5!3z;7 K"ѓm /@    <<1@  /20!53!!5!z;7 K"ѓ+q &B@%(E# E'ܲ@ ]<<ܲ@]1@ # $ /<<02>4."&'.4767673! [ZZRJrCEoJRXErrJS"p_88_p`88~ 1SV/ { 2Ʀ1  (F@ *E#'E)ܲ@]<<ܲ@#]1@' (/2<<02>4."!5!5&'.4767673 [ZZlRJrCEoJRXErrJS"p_88_p`88 1SV/ { 2Ʀ1 O &B@(E# E&'ܲ@ ]<<ܲ@]1@ #  %/<<02>4."5&'.4767673!5 [ZZRJrCEoJRXErrJS0"p_88_p`88 1SV/ { 2Ʀ1 {q*!&'.4767675!5!!!2>4."RJrCEoJRNXErrJS9[ZZ 1SV/ 2Ʀ1 "p_88_p`88 ,%!5!5&'.4767675!5!!2>4."RJrCEoJRNXErrJSQ[ZZ 1SV/ 2Ʀ1 p_88_p`88O*)5!5&'.4767675!5!!2>4."0RJrCEoJRNXErrJSQ[ZZ 1SV/ 2Ʀ1 p_88_p`88 '' '' '' '' '' '' '' ''  :@   @ ? o ]9999991 2<0#'##'##'d2222222dddddV!#!3!3#3jժVV8`!##333#{}`9VVX` %5#"&5332653!"&'5326Cuȸ||aQQRjBfca{+,*X10!5!-ЈX'3I(sInhX#'3h'OW`4X#'3v5]dDZX#'3 |;d07!X#(ẌI$@h$An4$B`$CnhX#7OhWh$Eh@4AnB`4X#7]vDdn4$J4E4@dAdZX%#7d|!70`$OnJEd@0<0^X133ֈXJ=_<UU3 r Um Q rZf55q=3=dd?y}s)3s\\?uLsLsyD{={\{fqqq/q999qqJ+o#7=V;=3X55^R\sd5^5bs#5`b?yyyyyys\;\\\3 LsLsLsLsLsLf {{{{{{{fqqqqq9999qqqqqqH==y{y{y{sfqsfqsfqsfq)q3 qqqqqq3sq3sq3sq3sqTx\9\9\9\9\9r\9?u9u9uuFLsqLsqLsqs/qJJJ+o+o+o+o#7#7#7DV={\3X{\3X{\3X/ }}ssfq3 }qqLu3s~\ 9 =LsNgvsq7r+d#7#7N={\3XTT\h3qT]hX\] ` d <qKsday{\9Lsqqy{y{{3sq3sq?LsqLsqTX9 ` d <q3squy{{LfHy{y{qq\9\9LsqLsqJJ+o#7,Gqqq{\3Xy{qLsqLsqLsqLsq=79qqy f u +o3XPP}  yq\9@sq J qefqqqqq|SA4Pq9qq q``9t*KL:+#qqGpPPOJI>>t+o7#7#7q=V=f3X3XXmXXXXLsPqq;VVqXXqvqq77:7/ <66JO<u1ufu]H^H 6&:uuuuuu  3s3soouuuuddLhuTzuuu%q7]yq$U $ zw(j#Lcxh!c+qc3x+x.pppp*pw<.::3efqesDy}uy{\Ls\?yLsLs{=LsN\FqSFq qSZkq=xJvkqJqqdGp;Gpq?qWWGpAOpLsq0q@GGrwxssFqU-~Od$s6sq,J7Opfq9Lsqs5UsssJs\\\T\J#y}}@e(!TLss#y{=6|<}o{p4kq5FA33L ;q;fq<=p;rR>Qdqtqq/4dq+o9998L07/3=;xs*` D3 GLsk7sS[2Lsq@R2@R2s<qsq pv9xssfq;XXX.j}!&4fG8=(5F!A!=2*ISsqsfq<=={=;yt|||\(5F?56].I6r|29y{y{{qLuqLuq(5F!ATX33LsqLsqLsqodq#=#=#=|4QfG8{=;{=;}q -qn6.3sGq/STL ZTL'AtLsqDVT>LLXv&tuA&7\\S&eLsR\Lsuu^x"6^Zq"qDq;' qqF92 F &q/qzw`DDcc/NDdc\\fcXCX.X0.XsXXEX.XXN*CCMXwBS(?99l9lC91***}} ffuuXK5k1CCOOLLLRLLLLLUL<L<LdL\W5kVz*******KKK))*CC1LLLRLLLRLjLL<L<Ld9qd==;;q;q=x==D=;==p==q==.qqB[B[{d{d7]xlxr[")>WE_HHY"~h~h@rx2OsN~sxMn`P{P@@@@`NzBza\d>N c c]ccY]dji:~:~PZ"ZPZ|ZPZ}PPZPZXZPPP|ZPP*FZnP\ZZZFdZWPPWFZdZddDPGd.d#ddadd%dvd-d/Cd$d/dWd/?d1<Nd/dBd/d-d-d/d/F.Z#d{ddddddd.ndmdndyyyy'''''w'w'ww'w Xc^c^%H Ewyyyywwwwwwywwwwwwwwwyy^^^l4wl4w4y4yywyFFFF*F*F*FAA8F3F3FFFF*F*F*Fzzwuuuwwwuu&w&w&wwFF wwwFFGwyFyFFwFFFwwwFFGwy=Fy=FGwGw=F=FwFFFFFV+V+FFV+V+VY]YFFF"F"F"F"FGFGFGF F F F F wwww?w?w?wYSSwSwSSSFFFFYwyyyyMMwwdwSSyy4yFwwFFww```````FwFw     FFwwFF%w%!%!%w%w%!%!Y )#su` z    s 4 s 3  E p 2 O 3wq= {>fq$S9( 3qfyqyqy3/qqq222</=V3X5x=2ZLr u//SH||NYHG p+"M"M>G/Mmu>GVGVGTR>GnzhuuEuOGGOGOGmu\#=nnuV&7yGSG%nzu=nV&7yKySG%t9>GGGOGT_>G=nIzIIVz[quuIuEqOGOGFK\#^YGu@zV&7~77#7OG[[[[BBy{}}}sfq)q)q)q)q)qqqqqq/3sq\9\9???uMuMu9u9LsqLsqLsqLsqJJJJT+o+o+o+o+o#7#7#7#7y=y=DVDVDVDVDV{=;{=;={\3X{\3X{\3X#V={/&qy{y{y{y{y{y{y{y{y{y{y{y{qqqqqqqq\Z9D\9LsqLsqLsqLsqLsqLsqLsqNgvNgvNgvNgvNgv====FqFqFqFqFqFqFqFqyy'iSSSSSS0l7hx qqqqqqoE.k_FqFqSc<qqFqFqFqFqFqFqFqFqyy'i7hxk_FqFqFqFqFqFqFqyyy<pr\\D~{aNsVddddd%%%%9933W q q(()((()( 33?nn=V`Jd=n=dn8N(ffadp5Wnz5?5f5\5l5Y5S999og0u5W55^5b5?5f5\5l5Y5S999og"MVGOGuVGVs`u .;F_( ..D]1u!===P=&C&Cs#&<<oI HZ;jDN hR6nLsbBSV,y('y\XNND?yJ\}WJT9hgd(V FhZ $<|3uuWZ[O=;6Q^^b?fbfl\bya W{=w= =us)9~=}== ]=;;;9fqq y) ysedud    du,dudududvdvdd*ZZd-Opdduudwddxvxddddudud  dududuku7^H^^^@^^^uzz^uwududdud7u7y#_ZZ,dDX===,,ff+uPuuu+uPuuu+u+u+uyyy``>>**yyby*cc| a aXXJr;xxdxxd++* 8 8 P 8 x PFq 8#+7',,,,,,,,,,xxxxxxx||''''''''''''''''''''''q''''''''''llgg'''''''''''''''''pprppppppppp7p7Tpp''''3'''ppppp'''',h,d,,,,+,}}_}} ,,,B,d,,,,,,,,,,},,,dZd2E\,,,,,,,,,,,,,,,,,,,,,,,S,,,,,],,,,,m,,E,,,,A,,,U,,Q,0,,,U,,L,0,C,,X,,B,,X,,,x, ,,,,,,,,,,,,,,1,,,,,,,,,,,X,X,j,, T},y,},),,,,,d %6  dT YxEVIVVx+5X3ppppR >pTVSTWW/V0/0002p@TTTTpnnTVaaTT,f,z,z,z,z,xNNx>NnX~#9Uwlf,,,,,,,,,,                    uuuuuuuuuuuuuu++<uusumOss[YOO Bu xd xu xd xd xu xd xd xu xd xu xu,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,duwOwO::: u+u+u+u+u+u+u+u+u+u+u++u+u+u+u+k  77^^  7^uuHH''''$$"pMMu 9 u H#?{\3X@sy= DVh<GpPqbfr ,qssu@xC@~yyv{\{\ssg)?>8{\(oo:o\:o\csssss$d{=syNsNs6??,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,r+d pv9;<@>speKkT5L mLsqsq s&q:Bz<<|ff7S+o { { #{{{{seq#Sjxt  s&qu 9553wF\ Dq/ / ///}/o } <.VN1X?,XXuXXwwwwXCX.QX0QXsXXEXXXCMXwB.XsXX:j:j:j:j:j:jKH KH ************jj))k))k":jC:jp*XXXiXXXXXXXXXXX9p9lpl"9lplC:j9p:j1J:j:j*********}3}}3}jj 3# 3#  f^f^uBuuBu/KH 5kk kpSI:j1J8"CC:j..TT4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,c3s$f"=3LrDrlK{fqo/q5 "qqq+o7=HVhL=Xy}s)3s\?uLsLsyD{={\{fqqq/q999qqJ+o#7=V;=3XkZqAjds N:jH k :j:j:j********_9xxxxxxxxvxxvxxvxxxvxvxxxx,p:jj9Jqq9O99:::qd=dd=;;;;;;q;;;q=xxx==D=DD;;;====p==q======...qq,,,,,,,,.jn`#Zn`nn`n#Z`n3>?@<@<@ABPChDDFFGHIIJKPKLLM$MN0NO@OlOP$PQ|QQQRRSS0S`STUUUUVV8VPVhVVW|WWWXX<XlY4ZlZZZ[[[\\]]4]`]]____``$`<`T`x`bLbdb|bbbc chdde e,e\etffDfpffffgg,gDghggggghh,h<hii4iXi|iiiijj,jPjtjjjjjkk@kl lllm m0mTmxmmmn$nHnlnnnnoop$p<p`pxpppqdqrr<rTrlrrrsxtt@t`ttttu|vvvvww(wPwhwwwwwx$xHx`xxxxyLyzz4zdzzzz{{,{D{\{t{{{||$|<|T|l|||}}~~t8X|T`P0\t(@XD L@  8Ph0Pt4D\t<Tl $<Tl,D\t4Ld| $<Tl4xld| $<TlX8$ t,l,<äPňH,ȸhʤ<̌(x\ 0@\(԰ՔLLڐhܜX(ddL`lt` 0THX \hX, 0  T   < L d t   $      T $,@`|8L`|PpHT\,T(<Pd Lh@h<\ Dh$8  <    !!!,!@!T!!!""("t""##<#####$$$,$P$x$$%<%%&&(&(&h&&&&'L'`'t''( (4(\(l(|(() )<)L)\)))))*0*`*x*****++(+@+P+`+,,,$,4,,--h-x---...../t/00011(1@1X1p123t3456L6778`889:@:P:;`< <==>L?X?@@@@@@A8BBChCCDdEEFxG$GHdHtI$IIJhJKLMHMNOpOPXQ QRPRSXST`UU(U8UHUV$V4VDVTW$WX0X@XXXpXY(YYZxZZZZ[P[\P\h\\\]]]]^T^d_X```aabbb bhbxbbcccd8dde\eefhgghphiHijjjklhlmmnnnno@oPo`ooppqqqrHrrsLstu$u<uvvw<wLw\wlw|xxy<yTylyyzz{d{||}<}|}~0x<Tlxh4\,TXt8Ph<\8, ,`$pl84$`<Lx Dhx(8Ph,D\t4t$th8|8| 8DTdd0t ,<LP,\h x¨@tXŴTƜǤ<,ɠ<ʬ(˔̀(8ͬ͜dt$4P`ѴҨ4d՜ ֐׈$؜xl|h(8߈ߘdt\pH`L,T<dt,HtHh$Xx<,(08P,LH0Ld|,D ,D $@Lx\,HPdx D  D   D   @    < @Xp 8Ph @XhP4DTd@ dP\T@`<0h  \ !,!""`"##$T$$$%L%&&h&&''T''( ()*X++,\-8../T0012234845647789H::;>0>??|@@H@A(AB<BBClDDE8E`EFFFG0GHHIIJ@KKpLtLMNO\OP(PQQ|QRhRSSTT|ULUVVVWHXXXY(YxYZLZ[D[\d\]]\]^p^_$_`@`a ab bcctcd de(eef\fg8gghLhiHij(jk kkl@lm$mn no$oppxpqdqr4rrssssst0tHt`txtuuu0uHu`uxuuuuuvv v8vPvhvvvvww@w|wwx0xxxxxxyy,yDy\ylyyyzz(z@zXzzz{{{4{L{d{|{{{{{| |$|<||||}d}|}}~~,~~~~~ $<TlpLdx(@Xp0H`x t(@ 8 8Phh(@x0H`x 8tp @XpP8P(@ $<Tl4p4L(@Xp0H`x|`x 4Ld|h4L4Ld| $<Tl$`80,Tl$< 8 @X d|\t $<Tl48 8Ph 4L \t80H`x@,h0|\ d(HL€¸P`Ĩ0 0hxƈƘƨXȤX8Hʄ$˘˨˸8Hp̀ l8hΘ8Tτϸ@pьDlҰhX0Քp׬<(XDۀ۸\ܨpݘ0޸4ߌߠߴ`tX\pD\,,  pP Pd8X,D\t4Ld| $<Tt $<Tl,D\t$<Tl,D\t4Tl,D\t4Tt4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t $<Tl,D\t $<Tl,D\t4Ld| $<Tl   8 P h        4 P l        , H d        8 P h        4 P l     4Ld0H`x,D\t $@\x $4L\t $<Tl,D\t4Ld| $<Tl,D\t,DTd,D\t0H`x0H`x0Hdt(@\l,,,,,,,,,,,,,,,,,Xh,D  t !,!`!"<"|""##T#l#l#l#l#l#l#l$&&(&@&`&|&&&'H''(((()X)))* *<*l***+$+\+++,,,4,X,,- -T-|-..,.h../8/8/8/8/8/8/8/8/8/8/8/8/8//01@112343h3334 4444445 5 545H5\5p555555566$67H78899:l;<;>`>@D@AxB BxCCCCDD@DxDDEFGGHDHIxIIIJKL4LLMN0OOP PPRSTTTUdV|WxX(Y@YZZ[t[\\t\\]]]]^_` `adbDbpcdde<ef@ffghhhhiPiijj0jkdll<lm<mmmmn n,nLnlnnnno o$o4oLolooooop pp4pTpdptppppppqqq<qdq|qqqqqqrrtttuuvdwwxzz8zhzz{{`|||}H}}~(~t~~@LH8pd 4L 8TpdL4t4,l,l$|d8t<|4`4xpX |4pDx80(0448L\<\,`Lx ptT,`p4`HPt$  @` XLdüĈŤ,Ɣ@DŽ x ɌxˤX̜p4lϠ,М Pєlӌ xD\ռ@t֨tDڴۜ܀@DހX|ߤL|<Xd0X,`<<p ``TP$\h,xh4 0 d|P0 h@tP<\| 4\|4Tt$pt,   , < L |   D `    $ D `      p    T   $Px| |"X$$$%%4%h%%&&L&&' 'D'h''''((@(`(((() ),)P)x)))* *P*|***+ +L+t+++, ,L,x,,,- -H-l---..@.l.../,/`///040l0011D1x112202\2223303d33344H4|445585l5566T6667,7\778(8889 9T9t999::$:@:\:x::::; ;H;\;x;;;;<< <<$>>>>??h??@tA AB8BCDE EEFF0F\FFFGG0G\GH$HILJKL,MN4PR STTUXVpWLXLYdZ0[\\t]^$^_`(`abbcd|ddee0e`eff<ffg g<glggh,hXhhiilijjXjkktllplmmnnoxpq0r\rsHssttLtttttu$uxv<vwxyXzxzz{T{{||d|||}},}@}\}x}}}}~ ~(~L~p~~~~ (Lp,P| (Lh@l8\(\ 8l (Lh@l8\(\ 8l@l@l,`LxH| \ (Lh@l8\(\ 8l@l@l,`LxH| \@l@l,`LxH| \,X \,`H| \LL(txXP4|hLtXpLhlP4Ld|`@Dd$Ì,hĀĘ$lż4ưD`ȨPɰXD$Τ8πXд,XфѰLҀҸ$P|Ө0`Ԑ<`՜4p֔X$`l| Lۀ۴Lܜ ݐPxޠ l0\|$ | HD p8DT0D(h4p<L@<Ld| $<Tl,D\t4Ld| $<Tl,D\t4L\l|h0H(x`< d      d   T d   ,T|4X| L` P,dTth LtX8Xx<`!$`$%&(&'(H()*+4+,-H-`--.$.|../ /`//00T0l00000011,1D1\1t1111122242L2d2|222223 3$3<3T333344,4D4\4t4444455545L5d5|555556 6$6<6T6l66666677,7D7\7t7777788848L8d8|889P9`9p99999:X:p:::;;;0;H;H;H;H;H;H;H;H;H;H;H;H;H;H;H;H;H;p;;<(<<>,>D>\>t>>>>???0?H?`?x?????@@ @8@P@h@@@@ATAB@BXBpBBBCC,CDCTCCCCDELEFFF4FLF\G8GHhHHHHHI\IJLJdJ|JJJKDKL,LDL\LtLLLLLMMM4MLM\N(NOO,OOPP(PQ8QQQRR(R8RSlST TTUTUlUUUUUUVV,VDVWWWWWWWWWWXX4XPXxXXXYYHYpYYYZZ<ZdZZZ[[0[X[[[[\(\P\x\\\]]D]l]]]^^8^`^^^__0_X____`$`L`x```aa@alaaabb8b`bbbcc,cXccccd dLdtdddee@efffggghh\hhij jXjkkpkl lmLmn`no@pp|pqqTqr\rs\sttuvv|vwwlwxxx x0x@xPx`xpxxxxxxxxyyy y0y@yPy`ypyyyyyyyyzzz z0z@zPz`zpzzzzzzzz{{{ {0{@{{||}}\}~X~p ,<L\l|(<X D|t,| $<Tl<\| l,Hd (D`| Xp $Dd|(TXt4lt |DpdD@L$@\`pdl |LhtDhp|PPtT0DX| 0Tx \8\(<Pdˆœ°$8T|ZT+h h>2   : `   (Z4;b ;; 0    " F m " : %: h: ; ;Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. DejaVu changes are in public domain Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. DejaVu changes are in public domain DejaVu SansDejaVu SansBookBookDejaVu SansDejaVu SansDejaVu SansDejaVu SansVersion 2.29Version 2.29DejaVuSansDejaVuSansDejaVu fonts teamDejaVu fonts teamhttp://dejavu.sourceforge.nethttp://dejavu.sourceforge.netFonts are (c) Bitstream (see below). DejaVu changes are in public domain. Glyphs imported from Arev fonts are (c) Tavmjung Bah (see below) Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. Arev Fonts Copyright ------------------------------ Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the modifications to the Bitstream Vera Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Tavmjong Bah" or the word "Arev". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Tavmjong Bah Arev" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the name of Tavmjong Bah shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from Tavmjong Bah. For further information, contact: tavmjong @ free . fr.Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. Glyphs imported from Arev fonts are (c) Tavmjung Bah (see below) Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. Arev Fonts Copyright ------------------------------ Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the modifications to the Bitstream Vera Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Tavmjong Bah" or the word "Arev". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Tavmjong Bah Arev" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the name of Tavmjong Bah shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from Tavmjong Bah. For further information, contact: tavmjong @ free . fr.http://dejavu.sourceforge.net/wiki/index.php/Licensehttp://dejavu.sourceforge.net/wiki/index.php/LicenseDejaVu SansDejaVu SansBookBook~ZZ  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~        !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ sfthyphenAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019FOhornohornuni01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01C4uni01C5uni01C6uni01C7uni01C8uni01C9uni01CAuni01CBuni01CCuni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DDuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3uni01E4uni01E5Gcarongcaronuni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F0uni01F1uni01F2uni01F3uni01F4uni01F5uni01F6uni01F7uni01F8uni01F9 Aringacute aringacuteAEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccentuni021Auni021Buni021Cuni021Duni021Euni021Funi0220uni0221uni0222uni0223uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236dotlessjuni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0242uni0243uni0244uni0245uni0246uni0247uni0248uni0249uni024Auni024Buni024Cuni024Duni024Euni024Funi0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Auni025Buni025Cuni025Duni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268uni0269uni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276uni0277uni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294uni0295uni0296uni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2uni02A3uni02A4uni02A5uni02A6uni02A7uni02A8uni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AFuni02B0uni02B1uni02B2uni02B3uni02B4uni02B5uni02B6uni02B7uni02B8uni02B9uni02BAuni02BBuni02BCuni02BDuni02BEuni02BFuni02C0uni02C1uni02C2uni02C3uni02C4uni02C5uni02C8uni02C9uni02CAuni02CBuni02CCuni02CDuni02CEuni02CFuni02D0uni02D1uni02D2uni02D3uni02D4uni02D5uni02D6uni02D7uni02DEuni02DFuni02E0uni02E1uni02E2uni02E3uni02E4uni02E5uni02E6uni02E7uni02E8uni02E9uni02ECuni02EDuni02EEuni02F3uni02F7 gravecomb acutecombuni0302 tildecombuni0304uni0305uni0306uni0307uni0308 hookabovecombuni030Auni030Buni030Cuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320uni0321uni0322 dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334uni0335uni0336uni0337uni0338uni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0340uni0341uni0342uni0343uni0344uni0345uni0346uni0347uni0348uni0349uni034Auni034Buni034Cuni034Duni034Euni034Funi0351uni0352uni0353uni0357uni0358uni035Auni035Cuni035Duni035Euni035Funi0360uni0361uni0362uni0370uni0371uni0372uni0373uni0374uni0375uni0376uni0377uni037Auni037Buni037Cuni037Duni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosuni03CFuni03D0theta1Upsilon1uni03D3uni03D4phi1omega1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03E2uni03E3uni03E4uni03E5uni03E6uni03E7uni03E8uni03E9uni03EAuni03EBuni03ECuni03EDuni03EEuni03EFuni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F7uni03F8uni03F9uni03FAuni03FBuni03FCuni03FDuni03FEuni03FFuni0400uni0401uni0402uni0403uni0404uni0405uni0406uni0407uni0408uni0409uni040Auni040Buni040Cuni040Duni040Euni040Funi0410uni0411uni0412uni0413uni0414uni0415uni0416uni0417uni0418uni0419uni041Auni041Buni041Cuni041Duni041Euni041Funi0420uni0421uni0422uni0423uni0424uni0425uni0426uni0427uni0428uni0429uni042Auni042Buni042Cuni042Duni042Euni042Funi0430uni0431uni0432uni0433uni0434uni0435uni0436uni0437uni0438uni0439uni043Auni043Buni043Cuni043Duni043Euni043Funi0440uni0441uni0442uni0443uni0444uni0445uni0446uni0447uni0448uni0449uni044Auni044Buni044Cuni044Duni044Euni044Funi0450uni0451uni0452uni0453uni0454uni0455uni0456uni0457uni0458uni0459uni045Auni045Buni045Cuni045Duni045Euni045Funi0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0483uni0484uni0485uni0486uni0487uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0490uni0491uni0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni0514uni0515uni0516uni0517uni0518uni0519uni051Auni051Buni051Cuni051Duni0520uni0521uni0522uni0523uni0524uni0525uni0531uni0532uni0533uni0534uni0535uni0536uni0537uni0538uni0539uni053Auni053Buni053Cuni053Duni053Euni053Funi0540uni0541uni0542uni0543uni0544uni0545uni0546uni0547uni0548uni0549uni054Auni054Buni054Cuni054Duni054Euni054Funi0550uni0551uni0552uni0553uni0554uni0555uni0556uni0559uni055Auni055Buni055Cuni055Duni055Euni055Funi0561uni0562uni0563uni0564uni0565uni0566uni0567uni0568uni0569uni056Auni056Buni056Cuni056Duni056Euni056Funi0570uni0571uni0572uni0573uni0574uni0575uni0576uni0577uni0578uni0579uni057Auni057Buni057Cuni057Duni057Euni057Funi0580uni0581uni0582uni0583uni0584uni0585uni0586uni0587uni0589uni058Auni05B0uni05B1uni05B2uni05B3uni05B4uni05B5uni05B6uni05B7uni05B8uni05B9uni05BAuni05BBuni05BCuni05BDuni05BEuni05BFuni05C0uni05C1uni05C2uni05C3uni05C6uni05C7uni05D0uni05D1uni05D2uni05D3uni05D4uni05D5uni05D6uni05D7uni05D8uni05D9uni05DAuni05DBuni05DCuni05DDuni05DEuni05DFuni05E0uni05E1uni05E2uni05E3uni05E4uni05E5uni05E6uni05E7uni05E8uni05E9uni05EAuni05F0uni05F1uni05F2uni05F3uni05F4uni0606uni0607uni0609uni060Auni060Cuni0615uni061Buni061Funi0621uni0622uni0623uni0624uni0625uni0626uni0627uni0628uni0629uni062Auni062Buni062Cuni062Duni062Euni062Funi0630uni0631uni0632uni0633uni0634uni0635uni0636uni0637uni0638uni0639uni063Auni0640uni0641uni0642uni0643uni0644uni0645uni0646uni0647uni0648uni0649uni064Auni064Buni064Cuni064Duni064Euni064Funi0650uni0651uni0652uni0653uni0654uni0655uni065Auni0660uni0661uni0662uni0663uni0664uni0665uni0666uni0667uni0668uni0669uni066Auni066Buni066Cuni066Duni066Euni066Funi0674uni0679uni067Auni067Buni067Cuni067Duni067Euni067Funi0680uni0681uni0682uni0683uni0684uni0685uni0686uni0687uni0691uni0692uni0695uni0698uni06A1uni06A4uni06A6uni06A9uni06AFuni06B5uni06BAuni06BFuni06C6uni06CCuni06CEuni06D5uni06F0uni06F1uni06F2uni06F3uni06F4uni06F5uni06F6uni06F7uni06F8uni06F9uni07C0uni07C1uni07C2uni07C3uni07C4uni07C5uni07C6uni07C7uni07C8uni07C9uni07CAuni07CBuni07CCuni07CDuni07CEuni07CFuni07D0uni07D1uni07D2uni07D3uni07D4uni07D5uni07D6uni07D7uni07D8uni07D9uni07DAuni07DBuni07DCuni07DDuni07DEuni07DFuni07E0uni07E1uni07E2uni07E3uni07E4uni07E5uni07E6uni07E7uni07EBuni07ECuni07EDuni07EEuni07EFuni07F0uni07F1uni07F2uni07F3uni07F4uni07F5uni07F8uni07F9uni07FAuni0E3Funi0E81uni0E82uni0E84uni0E87uni0E88uni0E8Auni0E8Duni0E94uni0E95uni0E96uni0E97uni0E99uni0E9Auni0E9Buni0E9Cuni0E9Duni0E9Euni0E9Funi0EA1uni0EA2uni0EA3uni0EA5uni0EA7uni0EAAuni0EABuni0EADuni0EAEuni0EAFuni0EB0uni0EB1uni0EB2uni0EB3uni0EB4uni0EB5uni0EB6uni0EB7uni0EB8uni0EB9uni0EBBuni0EBCuni0EBDuni0EC0uni0EC1uni0EC2uni0EC3uni0EC4uni0EC6uni0EC8uni0EC9uni0ECAuni0ECBuni0ECCuni0ECDuni0ED0uni0ED1uni0ED2uni0ED3uni0ED4uni0ED5uni0ED6uni0ED7uni0ED8uni0ED9uni0EDCuni0EDDuni10A0uni10A1uni10A2uni10A3uni10A4uni10A5uni10A6uni10A7uni10A8uni10A9uni10AAuni10ABuni10ACuni10ADuni10AEuni10AFuni10B0uni10B1uni10B2uni10B3uni10B4uni10B5uni10B6uni10B7uni10B8uni10B9uni10BAuni10BBuni10BCuni10BDuni10BEuni10BFuni10C0uni10C1uni10C2uni10C3uni10C4uni10C5uni10D0uni10D1uni10D2uni10D3uni10D4uni10D5uni10D6uni10D7uni10D8uni10D9uni10DAuni10DBuni10DCuni10DDuni10DEuni10DFuni10E0uni10E1uni10E2uni10E3uni10E4uni10E5uni10E6uni10E7uni10E8uni10E9uni10EAuni10EBuni10ECuni10EDuni10EEuni10EFuni10F0uni10F1uni10F2uni10F3uni10F4uni10F5uni10F6uni10F7uni10F8uni10F9uni10FAuni10FBuni10FCuni1401uni1402uni1403uni1404uni1405uni1406uni1407uni1409uni140Auni140Buni140Cuni140Duni140Euni140Funi1410uni1411uni1412uni1413uni1414uni1415uni1416uni1417uni1418uni1419uni141Auni141Buni141Duni141Euni141Funi1420uni1421uni1422uni1423uni1424uni1425uni1426uni1427uni1428uni1429uni142Auni142Buni142Cuni142Duni142Euni142Funi1430uni1431uni1432uni1433uni1434uni1435uni1437uni1438uni1439uni143Auni143Buni143Cuni143Duni143Euni143Funi1440uni1441uni1442uni1443uni1444uni1445uni1446uni1447uni1448uni1449uni144Auni144Cuni144Duni144Euni144Funi1450uni1451uni1452uni1454uni1455uni1456uni1457uni1458uni1459uni145Auni145Buni145Cuni145Duni145Euni145Funi1460uni1461uni1462uni1463uni1464uni1465uni1466uni1467uni1468uni1469uni146Auni146Buni146Cuni146Duni146Euni146Funi1470uni1471uni1472uni1473uni1474uni1475uni1476uni1477uni1478uni1479uni147Auni147Buni147Cuni147Duni147Euni147Funi1480uni1481uni1482uni1483uni1484uni1485uni1486uni1487uni1488uni1489uni148Auni148Buni148Cuni148Duni148Euni148Funi1490uni1491uni1492uni1493uni1494uni1495uni1496uni1497uni1498uni1499uni149Auni149Buni149Cuni149Duni149Euni149Funi14A0uni14A1uni14A2uni14A3uni14A4uni14A5uni14A6uni14A7uni14A8uni14A9uni14AAuni14ABuni14ACuni14ADuni14AEuni14AFuni14B0uni14B1uni14B2uni14B3uni14B4uni14B5uni14B6uni14B7uni14B8uni14B9uni14BAuni14BBuni14BCuni14BDuni14C0uni14C1uni14C2uni14C3uni14C4uni14C5uni14C6uni14C7uni14C8uni14C9uni14CAuni14CBuni14CCuni14CDuni14CEuni14CFuni14D0uni14D1uni14D2uni14D3uni14D4uni14D5uni14D6uni14D7uni14D8uni14D9uni14DAuni14DBuni14DCuni14DDuni14DEuni14DFuni14E0uni14E1uni14E2uni14E3uni14E4uni14E5uni14E6uni14E7uni14E8uni14E9uni14EAuni14ECuni14EDuni14EEuni14EFuni14F0uni14F1uni14F2uni14F3uni14F4uni14F5uni14F6uni14F7uni14F8uni14F9uni14FAuni14FBuni14FCuni14FDuni14FEuni14FFuni1500uni1501uni1502uni1503uni1504uni1505uni1506uni1507uni1510uni1511uni1512uni1513uni1514uni1515uni1516uni1517uni1518uni1519uni151Auni151Buni151Cuni151Duni151Euni151Funi1520uni1521uni1522uni1523uni1524uni1525uni1526uni1527uni1528uni1529uni152Auni152Buni152Cuni152Duni152Euni152Funi1530uni1531uni1532uni1533uni1534uni1535uni1536uni1537uni1538uni1539uni153Auni153Buni153Cuni153Duni153Euni1540uni1541uni1542uni1543uni1544uni1545uni1546uni1547uni1548uni1549uni154Auni154Buni154Cuni154Duni154Euni154Funi1550uni1552uni1553uni1554uni1555uni1556uni1557uni1558uni1559uni155Auni155Buni155Cuni155Duni155Euni155Funi1560uni1561uni1562uni1563uni1564uni1565uni1566uni1567uni1568uni1569uni156Auni1574uni1575uni1576uni1577uni1578uni1579uni157Auni157Buni157Cuni157Duni157Euni157Funi1580uni1581uni1582uni1583uni1584uni1585uni158Auni158Buni158Cuni158Duni158Euni158Funi1590uni1591uni1592uni1593uni1594uni1595uni1596uni15A0uni15A1uni15A2uni15A3uni15A4uni15A5uni15A6uni15A7uni15A8uni15A9uni15AAuni15ABuni15ACuni15ADuni15AEuni15AFuni15DEuni15E1uni1646uni1647uni166Euni166Funi1670uni1671uni1672uni1673uni1674uni1675uni1676uni1680uni1681uni1682uni1683uni1684uni1685uni1686uni1687uni1688uni1689uni168Auni168Buni168Cuni168Duni168Euni168Funi1690uni1691uni1692uni1693uni1694uni1695uni1696uni1697uni1698uni1699uni169Auni169Buni169Cuni1D00uni1D01uni1D02uni1D03uni1D04uni1D05uni1D06uni1D07uni1D08uni1D09uni1D0Auni1D0Buni1D0Cuni1D0Duni1D0Euni1D0Funi1D10uni1D11uni1D12uni1D13uni1D14uni1D16uni1D17uni1D18uni1D19uni1D1Auni1D1Buni1D1Cuni1D1Duni1D1Euni1D1Funi1D20uni1D21uni1D22uni1D23uni1D26uni1D27uni1D28uni1D29uni1D2Auni1D2Buni1D2Cuni1D2Duni1D2Euni1D30uni1D31uni1D32uni1D33uni1D34uni1D35uni1D36uni1D37uni1D38uni1D39uni1D3Auni1D3Buni1D3Cuni1D3Duni1D3Euni1D3Funi1D40uni1D41uni1D42uni1D43uni1D44uni1D45uni1D46uni1D47uni1D48uni1D49uni1D4Auni1D4Buni1D4Cuni1D4Duni1D4Euni1D4Funi1D50uni1D51uni1D52uni1D53uni1D54uni1D55uni1D56uni1D57uni1D58uni1D59uni1D5Auni1D5Buni1D5Duni1D5Euni1D5Funi1D60uni1D61uni1D62uni1D63uni1D64uni1D65uni1D66uni1D67uni1D68uni1D69uni1D6Auni1D77uni1D78uni1D7Buni1D85uni1D9Buni1D9Cuni1D9Duni1D9Euni1D9Funi1DA0uni1DA1uni1DA2uni1DA3uni1DA4uni1DA5uni1DA6uni1DA7uni1DA8uni1DA9uni1DAAuni1DABuni1DACuni1DADuni1DAEuni1DAFuni1DB0uni1DB1uni1DB2uni1DB3uni1DB4uni1DB5uni1DB6uni1DB7uni1DB8uni1DB9uni1DBAuni1DBBuni1DBCuni1DBDuni1DBEuni1DBFuni1DC4uni1DC5uni1DC6uni1DC7uni1DC8uni1DC9uni1E00uni1E01uni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Euni1E2Funi1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3Funi1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E64uni1E65uni1E66uni1E67uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Auni1E9Buni1E9Euni1E9Funi1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1Ygraveygraveuni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEuni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200Buni200Cuni200Duni200Euni200Funi2010uni2011 figuredashuni2015uni2016 underscoredbl quotereverseduni201Funi2023onedotenleadertwodotenleaderuni2027uni202Auni202Buni202Cuni202Duni202Euni202Funi2031minuteseconduni2034uni2035uni2036uni2037uni2038uni203B exclamdbluni203Duni203Euni203Funi2040uni2041uni2042uni2043uni2045uni2046uni2047uni2048uni2049uni204Auni204Buni204Cuni204Duni204Euni204Funi2050uni2051uni2052uni2053uni2054uni2055uni2056uni2057uni2058uni2059uni205Auni205Buni205Cuni205Duni205Euni205Funi2060uni2061uni2062uni2063uni2064uni206Auni206Buni206Cuni206Duni206Euni206Funi2070uni2071uni2074uni2075uni2076uni2077uni2078uni2079uni207Auni207Buni207Cuni207Duni207Euni207Funi2080uni2081uni2082uni2083uni2084uni2085uni2086uni2087uni2088uni2089uni208Auni208Buni208Cuni208Duni208Euni2090uni2091uni2092uni2093uni2094uni20A0 colonmonetaryuni20A2lirauni20A5uni20A6pesetauni20A8uni20A9uni20AAdongEurouni20ADuni20AEuni20AFuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5uni20D0uni20D1uni20D6uni20D7uni20DBuni20DCuni20E1uni2100uni2101uni2102uni2103uni2104uni2105uni2106uni2107uni2108uni2109uni210Buni210Cuni210Duni210Euni210Funi2110Ifrakturuni2112uni2113uni2114uni2115uni2116uni2117 weierstrassuni2119uni211Auni211BRfrakturuni211D prescriptionuni211Funi2120uni2121uni2123uni2124uni2125uni2126uni2127uni2128uni2129uni212Auni212Buni212Cuni212D estimateduni212Funi2130uni2131uni2132uni2133uni2134alephuni2136uni2137uni2138uni2139uni213Auni213Buni213Cuni213Duni213Euni213Funi2140uni2141uni2142uni2143uni2144uni2145uni2146uni2147uni2148uni2149uni214Buni214Eonethird twothirdsuni2155uni2156uni2157uni2158uni2159uni215A oneeighth threeeighths fiveeighths seveneighthsuni215Funi2160uni2161uni2162uni2163uni2164uni2165uni2166uni2167uni2168uni2169uni216Auni216Buni216Cuni216Duni216Euni216Funi2170uni2171uni2172uni2173uni2174uni2175uni2176uni2177uni2178uni2179uni217Auni217Buni217Cuni217Duni217Euni217Funi2180uni2181uni2182uni2183uni2184 arrowleftarrowup arrowright arrowdown arrowboth arrowupdnuni2196uni2197uni2198uni2199uni219Auni219Buni219Cuni219Duni219Euni219Funi21A0uni21A1uni21A2uni21A3uni21A4uni21A5uni21A6uni21A7 arrowupdnbseuni21A9uni21AAuni21ABuni21ACuni21ADuni21AEuni21AFuni21B0uni21B1uni21B2uni21B3uni21B4carriagereturnuni21B6uni21B7uni21B8uni21B9uni21BAuni21BBuni21BCuni21BDuni21BEuni21BFuni21C0uni21C1uni21C2uni21C3uni21C4uni21C5uni21C6uni21C7uni21C8uni21C9uni21CAuni21CBuni21CCuni21CDuni21CEuni21CF arrowdblleft arrowdblup arrowdblright arrowdbldown arrowdblbothuni21D5uni21D6uni21D7uni21D8uni21D9uni21DAuni21DBuni21DCuni21DDuni21DEuni21DFuni21E0uni21E1uni21E2uni21E3uni21E4uni21E5uni21E6uni21E7uni21E8uni21E9uni21EAuni21EBuni21ECuni21EDuni21EEuni21EFuni21F0uni21F1uni21F2uni21F3uni21F4uni21F5uni21F6uni21F7uni21F8uni21F9uni21FAuni21FBuni21FCuni21FDuni21FEuni21FF universaluni2201 existentialuni2204emptysetgradientelement notelementuni220Asuchthatuni220Cuni220Duni220Euni2210uni2213uni2214uni2215uni2216 asteriskmathuni2218uni2219uni221Buni221C proportional orthogonalangleuni2221uni2222uni2223uni2224uni2225uni2226 logicaland logicalor intersectionunionuni222Cuni222Duni222Euni222Funi2230uni2231uni2232uni2233 thereforeuni2235uni2236uni2237uni2238uni2239uni223Auni223Bsimilaruni223Duni223Euni223Funi2240uni2241uni2242uni2243uni2244 congruentuni2246uni2247uni2249uni224Auni224Buni224Cuni224Duni224Euni224Funi2250uni2251uni2252uni2253uni2254uni2255uni2256uni2257uni2258uni2259uni225Auni225Buni225Cuni225Duni225Euni225F equivalenceuni2262uni2263uni2266uni2267uni2268uni2269uni226Auni226Buni226Cuni226Duni226Euni226Funi2270uni2271uni2272uni2273uni2274uni2275uni2276uni2277uni2278uni2279uni227Auni227Buni227Cuni227Duni227Euni227Funi2280uni2281 propersubsetpropersuperset notsubsetuni2285 reflexsubsetreflexsupersetuni2288uni2289uni228Auni228Buni228Cuni228Duni228Euni228Funi2290uni2291uni2292uni2293uni2294 circleplusuni2296circlemultiplyuni2298uni2299uni229Auni229Buni229Cuni229Duni229Euni229Funi22A0uni22A1uni22A2uni22A3uni22A4 perpendicularuni22A6uni22A7uni22A8uni22A9uni22AAuni22ABuni22ACuni22ADuni22AEuni22AFuni22B0uni22B1uni22B2uni22B3uni22B4uni22B5uni22B6uni22B7uni22B8uni22B9uni22BAuni22BBuni22BCuni22BDuni22BEuni22BFuni22C0uni22C1uni22C2uni22C3uni22C4dotmathuni22C6uni22C7uni22C8uni22C9uni22CAuni22CBuni22CCuni22CDuni22CEuni22CFuni22D0uni22D1uni22D2uni22D3uni22D4uni22D5uni22D6uni22D7uni22D8uni22D9uni22DAuni22DBuni22DCuni22DDuni22DEuni22DFuni22E0uni22E1uni22E2uni22E3uni22E4uni22E5uni22E6uni22E7uni22E8uni22E9uni22EAuni22EBuni22ECuni22EDuni22EEuni22EFuni22F0uni22F1uni22F2uni22F3uni22F4uni22F5uni22F6uni22F7uni22F8uni22F9uni22FAuni22FBuni22FCuni22FDuni22FEuni22FFuni2300uni2301houseuni2303uni2304uni2305uni2306uni2307uni2308uni2309uni230Auni230Buni230Cuni230Duni230Euni230F revlogicalnotuni2311uni2318uni2319uni231Cuni231Duni231Euni231F integraltp integralbtuni2324uni2325uni2326uni2327uni2328uni232Buni232Cuni2373uni2374uni2375uni237Auni237Duni2387uni2394uni239Buni239Cuni239Duni239Euni239Funi23A0uni23A1uni23A2uni23A3uni23A4uni23A5uni23A6uni23A7uni23A8uni23A9uni23AAuni23ABuni23ACuni23ADuni23AEuni23CEuni23CFuni23E3uni23E5uni2422uni2423uni2460uni2461uni2462uni2463uni2464uni2465uni2466uni2467uni2468uni2469SF100000uni2501SF110000uni2503uni2504uni2505uni2506uni2507uni2508uni2509uni250Auni250BSF010000uni250Duni250Euni250FSF030000uni2511uni2512uni2513SF020000uni2515uni2516uni2517SF040000uni2519uni251Auni251BSF080000uni251Duni251Euni251Funi2520uni2521uni2522uni2523SF090000uni2525uni2526uni2527uni2528uni2529uni252Auni252BSF060000uni252Duni252Euni252Funi2530uni2531uni2532uni2533SF070000uni2535uni2536uni2537uni2538uni2539uni253Auni253BSF050000uni253Duni253Euni253Funi2540uni2541uni2542uni2543uni2544uni2545uni2546uni2547uni2548uni2549uni254Auni254Buni254Cuni254Duni254Euni254FSF430000SF240000SF510000SF520000SF390000SF220000SF210000SF250000SF500000SF490000SF380000SF280000SF270000SF260000SF360000SF370000SF420000SF190000SF200000SF230000SF470000SF480000SF410000SF450000SF460000SF400000SF540000SF530000SF440000uni256Duni256Euni256Funi2570uni2571uni2572uni2573uni2574uni2575uni2576uni2577uni2578uni2579uni257Auni257Buni257Cuni257Duni257Euni257Fupblockuni2581uni2582uni2583dnblockuni2585uni2586uni2587blockuni2589uni258Auni258Blfblockuni258Duni258Euni258Frtblockltshadeshadedkshadeuni2594uni2595uni2596uni2597uni2598uni2599uni259Auni259Buni259Cuni259Duni259Euni259F filledboxH22073uni25A2uni25A3uni25A4uni25A5uni25A6uni25A7uni25A8uni25A9H18543H18551 filledrectuni25ADuni25AEuni25AFuni25B0uni25B1triagupuni25B3uni25B4uni25B5uni25B6uni25B7uni25B8uni25B9triagrtuni25BBtriagdnuni25BDuni25BEuni25BFuni25C0uni25C1uni25C2uni25C3triaglfuni25C5uni25C6uni25C7uni25C8uni25C9circleuni25CCuni25CDuni25CEH18533uni25D0uni25D1uni25D2uni25D3uni25D4uni25D5uni25D6uni25D7 invbullet invcircleuni25DAuni25DBuni25DCuni25DDuni25DEuni25DFuni25E0uni25E1uni25E2uni25E3uni25E4uni25E5 openbulletuni25E7uni25E8uni25E9uni25EAuni25EBuni25ECuni25EDuni25EEuni25EFuni25F0uni25F1uni25F2uni25F3uni25F4uni25F5uni25F6uni25F7uni25F8uni25F9uni25FAuni25FBuni25FCuni25FDuni25FEuni25FFuni2600uni2601uni2602uni2603uni2604uni2605uni2606uni2607uni2608uni2609uni260Auni260Buni260Cuni260Duni260Euni260Funi2610uni2611uni2612uni2613uni2614uni2615uni2616uni2617uni2618uni2619uni261Auni261Buni261Cuni261Duni261Euni261Funi2620uni2621uni2622uni2623uni2624uni2625uni2626uni2627uni2628uni2629uni262Auni262Buni262Cuni262Duni262Euni262Funi2630uni2631uni2632uni2633uni2634uni2635uni2636uni2637uni2638uni2639 smileface invsmilefacesununi263Duni263Euni263Ffemaleuni2641maleuni2643uni2644uni2645uni2646uni2647uni2648uni2649uni264Auni264Buni264Cuni264Duni264Euni264Funi2650uni2651uni2652uni2653uni2654uni2655uni2656uni2657uni2658uni2659uni265Auni265Buni265Cuni265Duni265Euni265Fspadeuni2661uni2662clubuni2664heartdiamonduni2667uni2668uni2669 musicalnotemusicalnotedbluni266Cuni266Duni266Euni266Funi2670uni2671uni2672uni2673uni2674uni2675uni2676uni2677uni2678uni2679uni267Auni267Buni267Cuni267Duni267Euni267Funi2680uni2681uni2682uni2683uni2684uni2685uni2686uni2687uni2688uni2689uni268Auni268Buni268Cuni268Duni268Euni268Funi2690uni2691uni2692uni2693uni2694uni2695uni2696uni2697uni2698uni2699uni269Auni269Buni269Cuni26A0uni26A1uni26A2uni26A3uni26A4uni26A5uni26A6uni26A7uni26A8uni26A9uni26AAuni26ABuni26ACuni26ADuni26AEuni26AFuni26B0uni26B1uni26B2uni26B3uni26B4uni26B5uni26B6uni26B7uni26B8uni2701uni2702uni2703uni2704uni2706uni2707uni2708uni2709uni270Cuni270Duni270Euni270Funi2710uni2711uni2712uni2713uni2714uni2715uni2716uni2717uni2718uni2719uni271Auni271Buni271Cuni271Duni271Euni271Funi2720uni2721uni2722uni2723uni2724uni2725uni2726uni2727uni2729uni272Auni272Buni272Cuni272Duni272Euni272Funi2730uni2731uni2732uni2733uni2734uni2735uni2736uni2737uni2738uni2739uni273Auni273Buni273Cuni273Duni273Euni273Funi2740uni2741uni2742uni2743uni2744uni2745uni2746uni2747uni2748uni2749uni274Auni274Buni274Duni274Funi2750uni2751uni2752uni2756uni2758uni2759uni275Auni275Buni275Cuni275Duni275Euni2761uni2762uni2763uni2764uni2765uni2766uni2767uni2768uni2769uni276Auni276Buni276Cuni276Duni276Euni276Funi2770uni2771uni2772uni2773uni2774uni2775uni2776uni2777uni2778uni2779uni277Auni277Buni277Cuni277Duni277Euni277Funi2780uni2781uni2782uni2783uni2784uni2785uni2786uni2787uni2788uni2789uni278Auni278Buni278Cuni278Duni278Euni278Funi2790uni2791uni2792uni2793uni2794uni2798uni2799uni279Auni279Buni279Cuni279Duni279Euni279Funi27A0uni27A1uni27A2uni27A3uni27A4uni27A5uni27A6uni27A7uni27A8uni27A9uni27AAuni27ABuni27ACuni27ADuni27AEuni27AFuni27B1uni27B2uni27B3uni27B4uni27B5uni27B6uni27B7uni27B8uni27B9uni27BAuni27BBuni27BCuni27BDuni27BEuni27C5uni27C6uni27E0uni27E6uni27E7uni27E8uni27E9uni27EAuni27EBuni27F0uni27F1uni27F2uni27F3uni27F4uni27F5uni27F6uni27F7uni27F8uni27F9uni27FAuni27FBuni27FCuni27FDuni27FEuni27FFuni2800uni2801uni2802uni2803uni2804uni2805uni2806uni2807uni2808uni2809uni280Auni280Buni280Cuni280Duni280Euni280Funi2810uni2811uni2812uni2813uni2814uni2815uni2816uni2817uni2818uni2819uni281Auni281Buni281Cuni281Duni281Euni281Funi2820uni2821uni2822uni2823uni2824uni2825uni2826uni2827uni2828uni2829uni282Auni282Buni282Cuni282Duni282Euni282Funi2830uni2831uni2832uni2833uni2834uni2835uni2836uni2837uni2838uni2839uni283Auni283Buni283Cuni283Duni283Euni283Funi2840uni2841uni2842uni2843uni2844uni2845uni2846uni2847uni2848uni2849uni284Auni284Buni284Cuni284Duni284Euni284Funi2850uni2851uni2852uni2853uni2854uni2855uni2856uni2857uni2858uni2859uni285Auni285Buni285Cuni285Duni285Euni285Funi2860uni2861uni2862uni2863uni2864uni2865uni2866uni2867uni2868uni2869uni286Auni286Buni286Cuni286Duni286Euni286Funi2870uni2871uni2872uni2873uni2874uni2875uni2876uni2877uni2878uni2879uni287Auni287Buni287Cuni287Duni287Euni287Funi2880uni2881uni2882uni2883uni2884uni2885uni2886uni2887uni2888uni2889uni288Auni288Buni288Cuni288Duni288Euni288Funi2890uni2891uni2892uni2893uni2894uni2895uni2896uni2897uni2898uni2899uni289Auni289Buni289Cuni289Duni289Euni289Funi28A0uni28A1uni28A2uni28A3uni28A4uni28A5uni28A6uni28A7uni28A8uni28A9uni28AAuni28ABuni28ACuni28ADuni28AEuni28AFuni28B0uni28B1uni28B2uni28B3uni28B4uni28B5uni28B6uni28B7uni28B8uni28B9uni28BAuni28BBuni28BCuni28BDuni28BEuni28BFuni28C0uni28C1uni28C2uni28C3uni28C4uni28C5uni28C6uni28C7uni28C8uni28C9uni28CAuni28CBuni28CCuni28CDuni28CEuni28CFuni28D0uni28D1uni28D2uni28D3uni28D4uni28D5uni28D6uni28D7uni28D8uni28D9uni28DAuni28DBuni28DCuni28DDuni28DEuni28DFuni28E0uni28E1uni28E2uni28E3uni28E4uni28E5uni28E6uni28E7uni28E8uni28E9uni28EAuni28EBuni28ECuni28EDuni28EEuni28EFuni28F0uni28F1uni28F2uni28F3uni28F4uni28F5uni28F6uni28F7uni28F8uni28F9uni28FAuni28FBuni28FCuni28FDuni28FEuni28FFuni2906uni2907uni290Auni290Buni2940uni2941uni2983uni2984uni29CEuni29CFuni29D0uni29D1uni29D2uni29D3uni29D4uni29D5uni29EBuni29FAuni29FBuni2A00uni2A01uni2A02uni2A0Cuni2A0Duni2A0Euni2A0Funi2A10uni2A11uni2A12uni2A13uni2A14uni2A15uni2A16uni2A17uni2A18uni2A19uni2A1Auni2A1Buni2A1Cuni2A2Funi2A7Duni2A7Euni2A7Funi2A80uni2A81uni2A82uni2A83uni2A84uni2A85uni2A86uni2A87uni2A88uni2A89uni2A8Auni2A8Buni2A8Cuni2A8Duni2A8Euni2A8Funi2A90uni2A91uni2A92uni2A93uni2A94uni2A95uni2A96uni2A97uni2A98uni2A99uni2A9Auni2A9Buni2A9Cuni2A9Duni2A9Euni2A9Funi2AA0uni2AAEuni2AAFuni2AB0uni2AB1uni2AB2uni2AB3uni2AB4uni2AB5uni2AB6uni2AB7uni2AB8uni2AB9uni2ABAuni2AF9uni2AFAuni2B00uni2B01uni2B02uni2B03uni2B04uni2B05uni2B06uni2B07uni2B08uni2B09uni2B0Auni2B0Buni2B0Cuni2B0Duni2B0Euni2B0Funi2B10uni2B11uni2B12uni2B13uni2B14uni2B15uni2B16uni2B17uni2B18uni2B19uni2B1Auni2B1Funi2B20uni2B21uni2B22uni2B23uni2B24uni2B53uni2B54uni2C60uni2C61uni2C62uni2C63uni2C64uni2C65uni2C66uni2C67uni2C68uni2C69uni2C6Auni2C6Buni2C6Cuni2C6Duni2C6Euni2C6Funi2C71uni2C72uni2C73uni2C74uni2C75uni2C76uni2C77uni2C79uni2C7Auni2C7Buni2C7Cuni2C7Duni2D30uni2D31uni2D32uni2D33uni2D34uni2D35uni2D36uni2D37uni2D38uni2D39uni2D3Auni2D3Buni2D3Cuni2D3Duni2D3Euni2D3Funi2D40uni2D41uni2D42uni2D43uni2D44uni2D45uni2D46uni2D47uni2D48uni2D49uni2D4Auni2D4Buni2D4Cuni2D4Duni2D4Euni2D4Funi2D50uni2D51uni2D52uni2D53uni2D54uni2D55uni2D56uni2D57uni2D58uni2D59uni2D5Auni2D5Buni2D5Cuni2D5Duni2D5Euni2D5Funi2D60uni2D61uni2D62uni2D63uni2D64uni2D65uni2D6Funi2E18uni2E22uni2E23uni2E24uni2E25uni2E2Euni4DC0uni4DC1uni4DC2uni4DC3uni4DC4uni4DC5uni4DC6uni4DC7uni4DC8uni4DC9uni4DCAuni4DCBuni4DCCuni4DCDuni4DCEuni4DCFuni4DD0uni4DD1uni4DD2uni4DD3uni4DD4uni4DD5uni4DD6uni4DD7uni4DD8uni4DD9uni4DDAuni4DDBuni4DDCuni4DDDuni4DDEuni4DDFuni4DE0uni4DE1uni4DE2uni4DE3uni4DE4uni4DE5uni4DE6uni4DE7uni4DE8uni4DE9uni4DEAuni4DEBuni4DECuni4DEDuni4DEEuni4DEFuni4DF0uni4DF1uni4DF2uni4DF3uni4DF4uni4DF5uni4DF6uni4DF7uni4DF8uni4DF9uni4DFAuni4DFBuni4DFCuni4DFDuni4DFEuni4DFFuniA644uniA645uniA646uniA647uniA64CuniA64DuniA650uniA651uniA654uniA655uniA656uniA657uniA662uniA663uniA664uniA665uniA666uniA667uniA668uniA669uniA66AuniA66BuniA66CuniA66DuniA66EuniA68AuniA68BuniA68CuniA68DuniA694uniA695uniA708uniA709uniA70AuniA70BuniA70CuniA70DuniA70EuniA70FuniA710uniA711uniA712uniA713uniA714uniA715uniA716uniA71BuniA71CuniA71DuniA71EuniA71FuniA726uniA727uniA728uniA729uniA72AuniA72BuniA730uniA731uniA732uniA733uniA734uniA735uniA736uniA737uniA738uniA739uniA73AuniA73BuniA73CuniA73DuniA73EuniA73FuniA746uniA747uniA748uniA749uniA74AuniA74BuniA74EuniA74FuniA780uniA781uniA782uniA783uniA789uniA78AuniA78BuniA78CuniA7FBuniA7FCuniA7FDuniA7FEuniA7FFuniF000uniF001uniF6C5uniFB00uniFB03uniFB04uniFB05uniFB06uniFB13uniFB14uniFB15uniFB16uniFB17uniFB1DuniFB1EuniFB1FuniFB20uniFB21uniFB22uniFB23uniFB24uniFB25uniFB26uniFB27uniFB28uniFB29uniFB2AuniFB2BuniFB2CuniFB2DuniFB2EuniFB2FuniFB30uniFB31uniFB32uniFB33uniFB34uniFB35uniFB36uniFB38uniFB39uniFB3AuniFB3BuniFB3CuniFB3EuniFB40uniFB41uniFB43uniFB44uniFB46uniFB47uniFB48uniFB49uniFB4AuniFB4BuniFB4CuniFB4DuniFB4EuniFB4FuniFB52uniFB53uniFB54uniFB55uniFB56uniFB57uniFB58uniFB59uniFB5AuniFB5BuniFB5CuniFB5DuniFB5EuniFB5FuniFB60uniFB61uniFB62uniFB63uniFB64uniFB65uniFB66uniFB67uniFB68uniFB69uniFB6AuniFB6BuniFB6CuniFB6DuniFB6EuniFB6FuniFB70uniFB71uniFB72uniFB73uniFB74uniFB75uniFB76uniFB77uniFB78uniFB79uniFB7AuniFB7BuniFB7CuniFB7DuniFB7EuniFB7FuniFB80uniFB81uniFB8AuniFB8BuniFB8CuniFB8DuniFB8EuniFB8FuniFB90uniFB91uniFB92uniFB93uniFB94uniFB95uniFB9EuniFB9FuniFBD9uniFBDAuniFBE8uniFBE9uniFBFCuniFBFDuniFBFEuniFBFFuniFE00uniFE01uniFE02uniFE03uniFE04uniFE05uniFE06uniFE07uniFE08uniFE09uniFE0AuniFE0BuniFE0CuniFE0DuniFE0EuniFE0FuniFE20uniFE21uniFE22uniFE23uniFE70uniFE71uniFE72uniFE73uniFE74uniFE76uniFE77uniFE78uniFE79uniFE7AuniFE7BuniFE7CuniFE7DuniFE7EuniFE7FuniFE80uniFE81uniFE82uniFE83uniFE84uniFE85uniFE86uniFE87uniFE88uniFE89uniFE8AuniFE8BuniFE8CuniFE8DuniFE8EuniFE8FuniFE90uniFE91uniFE92uniFE93uniFE94uniFE95uniFE96uniFE97uniFE98uniFE99uniFE9AuniFE9BuniFE9CuniFE9DuniFE9EuniFE9FuniFEA0uniFEA1uniFEA2uniFEA3uniFEA4uniFEA5uniFEA6uniFEA7uniFEA8uniFEA9uniFEAAuniFEABuniFEACuniFEADuniFEAEuniFEAFuniFEB0uniFEB1uniFEB2uniFEB3uniFEB4uniFEB5uniFEB6uniFEB7uniFEB8uniFEB9uniFEBAuniFEBBuniFEBCuniFEBDuniFEBEuniFEBFuniFEC0uniFEC1uniFEC2uniFEC3uniFEC4uniFEC5uniFEC6uniFEC7uniFEC8uniFEC9uniFECAuniFECBuniFECCuniFECDuniFECEuniFECFuniFED0uniFED1uniFED2uniFED3uniFED4uniFED5uniFED6uniFED7uniFED8uniFED9uniFEDAuniFEDBuniFEDCuniFEDDuniFEDEuniFEDFuniFEE0uniFEE1uniFEE2uniFEE3uniFEE4uniFEE5uniFEE6uniFEE7uniFEE8uniFEE9uniFEEAuniFEEBuniFEECuniFEEDuniFEEEuniFEEFuniFEF0uniFEF1uniFEF2uniFEF3uniFEF4uniFEF5uniFEF6uniFEF7uniFEF8uniFEF9uniFEFAuniFEFBuniFEFCuniFEFFuniFFF9uniFFFAuniFFFBuniFFFCuniFFFDu1D300u1D301u1D302u1D303u1D304u1D305u1D306u1D307u1D308u1D309u1D30Au1D30Bu1D30Cu1D30Du1D30Eu1D30Fu1D310u1D311u1D312u1D313u1D314u1D315u1D316u1D317u1D318u1D319u1D31Au1D31Bu1D31Cu1D31Du1D31Eu1D31Fu1D320u1D321u1D322u1D323u1D324u1D325u1D326u1D327u1D328u1D329u1D32Au1D32Bu1D32Cu1D32Du1D32Eu1D32Fu1D330u1D331u1D332u1D333u1D334u1D335u1D336u1D337u1D338u1D339u1D33Au1D33Bu1D33Cu1D33Du1D33Eu1D33Fu1D340u1D341u1D342u1D343u1D344u1D345u1D346u1D347u1D348u1D349u1D34Au1D34Bu1D34Cu1D34Du1D34Eu1D34Fu1D350u1D351u1D352u1D353u1D354u1D355u1D356u1D538u1D539u1D53Bu1D53Cu1D53Du1D53Eu1D540u1D541u1D542u1D543u1D544u1D546u1D54Au1D54Bu1D54Cu1D54Du1D54Eu1D54Fu1D550u1D552u1D553u1D554u1D555u1D556u1D557u1D558u1D559u1D55Au1D55Bu1D55Cu1D55Du1D55Eu1D55Fu1D560u1D561u1D562u1D563u1D564u1D565u1D566u1D567u1D568u1D569u1D56Au1D56Bu1D5A0u1D5A1u1D5A2u1D5A3u1D5A4u1D5A5u1D5A6u1D5A7u1D5A8u1D5A9u1D5AAu1D5ABu1D5ACu1D5ADu1D5AEu1D5AFu1D5B0u1D5B1u1D5B2u1D5B3u1D5B4u1D5B5u1D5B6u1D5B7u1D5B8u1D5B9u1D5BAu1D5BBu1D5BCu1D5BDu1D5BEu1D5BFu1D5C0u1D5C1u1D5C2u1D5C3u1D5C4u1D5C5u1D5C6u1D5C7u1D5C8u1D5C9u1D5CAu1D5CBu1D5CCu1D5CDu1D5CEu1D5CFu1D5D0u1D5D1u1D5D2u1D5D3u1D7D8u1D7D9u1D7DAu1D7DBu1D7DCu1D7DDu1D7DEu1D7DFu1D7E0u1D7E1u1D7E2u1D7E3u1D7E4u1D7E5u1D7E6u1D7E7u1D7E8u1D7E9u1D7EAu1D7EB dlLtcaronDieresisAcuteTildeGrave CircumflexCaron uni0311.caseBreve Dotaccent Hungarumlaut Doubleacute arabic_dot arabic_2dots arabic_3dotsarabic_3dots_aarabic_2dots_a arabic_4dots uni066E.fina uni066E.init uni066E.medi uni06A1.fina uni06A1.init uni06A1.medi uni066F.fina uni066F.init uni066F.medi uni06BA.init uni06BA.medi arabic_ring uni067C.fina uni067C.init uni067C.medi uni067D.fina uni067D.init uni067D.medi uni0681.fina uni0681.init uni0681.medi uni0682.fina uni0682.init uni0682.medi uni0685.fina uni0685.init uni0685.medi uni06BF.fina uni06BF.init uni06BF.mediarabic_gaf_barEng.altuni0268.dotlessuni029D.dotless uni03080304 uni03040308 uni03070304 uni03080301 uni03080300 uni03040301 uni03040300 uni03030304 uni0308030C uni03030308 uni030C0307 uni03030301 uni03020301 uni03020300 uni03020303 uni03060303 uni03060301 uni03060300 uni03060309 uni03020309 uni03010307 brailledotJ.alt uni0695.finauniFEAE.fina.longstart uni06B5.fina uni06B5.init uni06B5.medi uni06CE.fina uni06CE.init uni06CE.medi uni0692.final.alt uni06D5.finauni0478.monographuni0479.monographiogonek.dotlessuni2148.dotlessuni2149.dotlessuni1E2D.dotlessuni1ECB.dotlessdcoI.alt arrow.base uni0651064B uni0651064C uni064B0651 uni0651064E uni0651064F uni064E0651 uni0654064E uni0654064F uni07CA.fina uni07CA.medi uni07CA.init uni07CB.fina uni07CB.medi uni07CB.init uni07CC.fina uni07CC.medi uni07CC.init uni07CD.fina uni07CD.medi uni07CD.init uni07CE.fina uni07CE.medi uni07CE.init uni07CF.fina uni07CF.medi uni07CF.init uni07D0.fina uni07D0.medi uni07D0.init uni07D1.fina uni07D1.medi uni07D1.init uni07D2.fina uni07D2.medi uni07D2.init uni07D3.fina uni07D3.medi uni07D3.init uni07D4.fina uni07D4.medi uni07D4.init uni07D5.fina uni07D5.medi uni07D5.init uni07D6.fina uni07D6.medi uni07D6.init uni07D7.fina uni07D7.medi uni07D7.init uni07D8.fina uni07D8.medi uni07D8.init uni07D9.fina uni07D9.medi uni07D9.init uni07DA.fina uni07DA.medi uni07DA.init uni07DB.fina uni07DB.medi uni07DB.init uni07DC.fina uni07DC.medi uni07DC.init uni07DD.fina uni07DD.medi uni07DD.init uni07DE.fina uni07DE.medi uni07DE.init uni07DF.fina uni07DF.medi uni07DF.init uni07E0.fina uni07E0.medi uni07E0.init uni07E1.fina uni07E1.medi uni07E1.init uni07E2.fina uni07E2.medi uni07E2.init uni07E3.fina uni07E3.medi uni07E3.init uni07E4.fina uni07E4.medi uni07E4.init uni07E5.fina uni07E5.medi uni07E5.init uni07E6.fina uni07E6.medi uni07E6.init uni07E7.fina uni07E7.medi uni07E7.init Ringabove uni2630.alt uni2631.alt uni2632.alt uni2633.alt uni2634.alt uni2635.alt uni2636.alt uni2637.alt uni047E.diacuni048A.brevelessuni048B.brevelessy.alt uni02E5.5 uni02E6.5 uni02E7.5 uni02E8.5 uni02E9.5 uni02E5.4 uni02E6.4 uni02E7.4 uni02E8.4 uni02E9.4 uni02E5.3 uni02E6.3 uni02E7.3 uni02E8.3 uni02E9.3 uni02E5.2 uni02E6.2 uni02E7.2 uni02E8.2 uni02E9.2 uni02E5.1 uni02E6.1 uni02E7.1 uni02E8.1 uni02E9.1stem@%2%%A:B2SAS//2ݖ}ٻ֊A}G}G͖2ƅ%]%]@@%d%d%A2dA  d   A(]%]@%..%A  %d%@~}}~}}|d{T{%zyxw v utsrqponl!kjBjSih}gBfedcba:`^ ][ZYX YX WW2VUTUBTSSRQJQP ONMNMLKJKJIJI IH GFEDC-CBAK@?>=>=<=<; <@; :987876765 65 43 21 21 0/ 0 / .- .- ,2+*%+d*)*%)('%(A'%&% &% $#"!! d d BBBdB-B}d       -d@--d++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++munin-2.0.75/master/DejaVuSansMono.ttf000066400000000000000000011624541451614574100176260ustar00rootroot00000000000000 FFTMOp,GDEFnSywHGPOS\8GSUBSUT:OS/2?`Vcmap_ωU?zcvt  U40fpgm[kWdgaspX glyf薥X$head>@6hheax$hmtx ,rlocazX1hmaxp 1G name`G!postЩhuprep:>rmrm $%kllmno|}       X arab&cyrl4dfltFgrekPlao \latnj SRB 4ISM 4KSM 4LSM 4MOL 4NSM 4ROM 4SKS 4SSM 4mark mark,mark4mkmk:rtbdB &.6>FPX`Rx | \447:" p#h&0  c"c" ]j]jbj $6HZl~ P|H 0 P|H <P ,  T`  P|H    #hhhn  "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\Xd|dh0`tXtX <|00hL( Tplx`xDJPV]j]j]j]j]jbj]j]jh|  &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| dt hd8|$|`HLD \t@4H(|XT(lt<0xx_` iFWW@TT[:B`LLL[Z[IWTL4K]l8@^  @bXXXXoM!TxxTT&~O[ XPXD,aDf\Ph< \\\0,0@, d `|tL`00@\  T`@L845D80H <D<x(LX$Tt0TLh0pLh<pl$d`0h8< PLdhx8TR m$v2z`M;)RDhp_)I R|o~4>>#AD$  K( N Qd T Wh g gl j mm p q M M R R T T V W  !"$ &,28>DJPV]j]j]j]j]jbj]j]jh&  &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~DhxzOhDh*hDhhhhDhhhvhhVhYDxhVVhxhhsDVhDDDDDDzzzzhhhh6hhhhhhhhhhh46vvvvhhhhhhhhhhhhhhhVVVDD6h6hzvzvzvzvhVhVhVhVhhhhhhhhYDxxxx9hVhVhhhhhhDDhhhhhhhxhxhhhhhhhhhhshVhhhhhhhhhhh 66hhhVVh"VhVhVhhhDDxVhhXVVjhhhhDDVh$=D]4:IWfoqr "#&36k  |4pb  "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~      & , 2 8 > D J P V \ b h n t z     " ( . 4 : @ F L R X ^ d j p v |     $ * 0 6 < B H N T Z ` f l r x ~      & , 2 8 > D J P V \ b h n t z     " ( . 4 : @ F L R X ^ d j p v |  $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^hhhh66zzhhhhhhhhhhhhhhhh66hhhhhhhhhhhhhhh`hhh`hhv`vhhh`hVhhhhV22h`hh`hh`hh`hVh`hVh`hh`hhhh`hh`hh`hh`hh`hVh`hhhhhhhzzzzzzhhhh66hhhhhhhhhhhhh6666hhhhhhh`h`vvvvhhhhhhhhhhhh`hhhhhhVhhVhVhhhhh`6h66hhzvzvzvzv`zvhVhVhVhVhhhhhhhhhhhhhhh`hhVh`2222222hhhh`hhhhVh`hVhhhhhhhhh`h6h6h`6h`hhhhhhh`hhhhhhhhhhhhhhhhhhhhhh`hhhhVhhhhhhhhhhhhhhhhhh  `6666hhhhh`hVVVhhVhhVhhVhhhhhhhh22hhhhhhVh`hVhh`hhh`hVh`hV66Vhhh`hhhhhh`hVhh66h`hh`hh`hh`h)`)`YYhhv`vv`v`v`vZ`Zr`rv`v`V>>Vh`hV`n`nVh`hh`hVooooVD`Dh`h88PP>>Vh`hh`hVh`hV`Vf&Vh`hh`hh`hh`hhhVh`hhh6`6Vh`hVh`hVc`ch`h``h`hVssVssVx`xVssV`VhhVh`hh`hh`hh`hh`hhhh`h`Vh`hh`hVh`hV$$$$h`h ` >`>h`hh`hVh`hhhV$$h`hh`h`h`v`h`h`h`h`h`h`h`h`h`h``h`h`hh`h`h`h`h`h`h`h`5`h`h``h`''}{}hhRREE^^rrhhhh??aaiihhhhhhGGhhhhhh6\aadd55g'ee``66LhViihhh`hh $=D]4Kjsxk 4;9=AaCGffnkpt   J J"+12 $*06<BHNTZ`flrx~h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`hhhhhhhhhhhhhhhhhhhhhhhhhhh`h`h`/-3 ntz "(.4:@FLRX^djpv|Oillo|>DJPV\bhntztwwwwt`~~`~`/llo| Parab&cyrl:dfltVgrek`lao llatnv SRB 4ISM >KSM >LSM >MOL JNSM >ROM JSKS >SSM > ccmp>dligDfinaJinitPligaVlocl\loclbloclhmedinrligt   (08@HPX`hXjn  ~  ~  !$% > $$XLMLM&   gp5 i k m o q u w { } %    !  5 1 9 = C A ) E I O U M 4D# U U4T' s y  S '    #  7 3 ; ? + G K Q W 4=@D"T' r x ~ R &    "  6 2 : > * F J P V 4=@D"   u  u > $  o  k  i  o  k  i  O LI LM33f  "(PfEd@ m`,p, ~!AEM?CXauz~_s  :UZmt{ .<[ex{-KcwEMWY[]}  # & 7 : > I _ q !!!!!!!"!$!&!+!.!_" """" "-"="i"""""""####!#(#+#5#>#D#I#M#P#T#\#`#e#i#p#z#}#####$#&/&&&&'' '''K'M'R'V'^'u''''''))*/+,d,o,w,z,}..%..t $DLPCXatz~r  !@Z`ty~,0>bw{0Th| HPY[]_   & / 9 < E _ p t !!! !!!!"!$!&!*!.!S!"""""'"8"A"m""""""#####%#+#5#7#A#G#K#P#R#W#^#c#h#k#s#}#####$#%&8&&&''' ')'M'O'V'X'a''''''))*/+,d,n,u,y,|.."..RpvnfTPMHGFED6$xUnlcNM {kih]_YMH87530.%$"  BA?>=<986ށxsrqcccH jX  ~b!$ADELMP_fjlpy?CCXXaatuzz~~_&rs    !:@UZZ$`m%tt3y{4~7:<>?@ABCDEOQRTUVZadefhuw} ,.0<>[bewx{{ -!0K7TcShwc|s EHMPWYY[[]]_}1fu     # & & / 7 9 : < > E I _ _ p q t   !!!!! !!!!!!!!"!"!$!$!&!& !*!+!!.!.#!S!_$!" 1"""""""" "'"-"8"="A"i"m"""""""$""&""'""7##8##?##M##!O#%#(U#+#+Y#5#5Z#7#>[#A#Dc#G#Ig#K#Mj#P#Pm#R#Tn#W#\q#^#`w#c#ez#h#i}#k#p#s#z#}#}##########$#$#%&/&8& && 2&& ?&& A'' C'' G' '' K')'K g'M'M 'O'R 'V'V 'X'^ 'a'u '' '' '' '' '' '' )) )) */*/ ++ ,d,d ,n,o ,u,w ,y,z ,|,} .. .".% ....     R  @ L N R Tpt Xv ] p֣     !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpkvjsgwl|cnTm}b x:   yfvqrstzwugf 7X!uu9!{Xm{o{RoZ!=fs +b#1N {T\q#w`j#fy```{{w`b{!{RNNfffHF?{L'oo5jo{-{3=foD7f}s, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-,%%I%%I` ch #:e:-hh/10!%!!hsr) @ <2991/03#3#qeR@1<20###Ѯ++J@0     91/<<<<<<<2220333!3!###!5!!5!#3hiiThiih)T/hTaabbNZ /d@9($)%/%$(!$, ( 0<2<1/299990>54&'#.'5.546753.'n|phumdfbdcӿdOTUPDNtd]gp^Vd-.)>B+/Qš ! *9V@/7(" "7(.+  % 4  + :99991/9999032654&#"4632#"&'%32654&#"4632#"&iNMklLNi@s..2H#)iOMllMMk@u--1?NjkMMljO0./t?``OikMMkjN0--uA9*7@b  -,.+2345617B7 1 +"1"!% (! 7+!(!(! .899999991/9990KSX999Y" >54/3#'#"5467.54632.#"3267>7#'&JKNSj抋20ǭAF;}Eap:6\[ț*\,#1h F'XmFD ̉dHG'%[M;I\ 10#+u @  29910#&547u;:\' @  299103#654\<<J+N@,       <2<2991<22990 %#'-73%+f9s9f9PsPbybcyXqy '@    <<1<<0!!#!5!CDDD/@ 103#Śc/dm10!!d 11/03#1fB7@ 103#ymL # @  $!"!$10@////////// / / ?????????? ? ? OOOO O ____ _    F////////// / / __________ _ _  $]]4632#"&"32'2#"M68PO98K7PP78NL0670xx~F &@ ## 1/20%!5%3!!:P6ȪuLJժ#Q@)%%B   "$91/20KSX92Y"%!!567>54&#"5>32uu5dF[pga Yd8ժ.>zO}BC12`tA7(G@)  #)&" )9190#"&'532654&+532654&#"5>32ggfbYhyI'Ǖ&$54zms{(( ۵{fo B@   B    $<291/<290KSXY" !33##!5)!3d-=@"   "190!!>32#"&'532654&#"+W,wN\aQFժ 21%%L$=@#  %"& "%190.#">32# !2"32654&?M0n#J݁%'dkuzl75@%%B"991/0KSXY"!#!5V+N #/C@% '-'0 $*$ "!0991990"32654&%.54632#"$54632654&#"hʁ򑁖Myz{yŗT!Ѳ!!ȟɠbx~~xzF $;@" ""%"  &%1902654&#"532#"543 !"&T?M/nI%'!dk  os'@ <21/03#3#'9' %@  103#3#Śc /Xyw!@('29190 5yR!÷X`y@ <210!!!!X!! BXyw!@('<919055X!R^^="{@B  %%B !    ) #99991/9990KSX99Y"#546?>54&#"5>323#=TZ>/mNb^hC^XE&bRY;X1YnED98L\VBT=/s 4p@1(+$ 4 '$+1+5' ( + . !+ -.5<991999990@ ]4&#"326#5#"&5463254&#"!267# !2kkkk%RӡP$J6l90?{:]x<!o?DF=?z% @A%%%% % % %  % B   / 91/<90KSXY"]@    ]]!3#!#hnl#+{q =@#   21 0!29991/9032654&#32654&#%!2)qﰖ뒃JF{f>p}qdƵϠ1.@  2 10210%# !2.#"32671M[?[MJVXI5))pn))=@@=R(@  2 1099991/0% 6&!# )`dVDѦHKw/N )@  13 21/0!!!!!!vTrwժFX $@14 21/0!!!!#o\eժH7fP<@!   6251990%# !2.#"3267#5!PQv@^PQ_ſCe){KMon56MI!H &@ 1 0 221/<203!3#!#)d+9 %@ 77 221/220!!!!5!!=99ժm,@    51990753265!5!#"&m[hqG`=QQD, @!% %B  0 291/<290KSXY"]@L&&6FUWX dzy{ ',+&:IGG[WXXWkzx]]33 ##wVhs@ 141/03!!dժVy @,  B    / 0 91/<290KSXY" ]@$  &)&) 6968  ]]! !###V+'F m@B10 991/<2990KSXY"]@&)&8Wdjuz &)FIWgh]]!3!#3+3u\ #@ 2 62510#"32#"32IIz~u+@  2 8 3291/032654&#%!2+#ꌝL/ϔu\=@   2 625999919990"#"32#"32ȗyHdIj@8  %%B     21  0299991/<9990KSX9Y"#.+#!232654&#NnRٲM{cݑohy]ҔYJ'@=  %  %B %( &919"0(9999190KSX99Y"]@ ]].#"#"&'532654&/.54$32\^mjikshulм V;53#"&'.  yVWx! 9FBjiCE:= m];<<;\lh?;::;>9L@)%%%%B/091/290KSXY"%3#3h_KKѪ++ @D    %%% % B    /91/<<90KSXY" ]@^ //+ ??8 ZZ  &*%*(+ % & 5:5:;: 4 6 TTZXWV[[RW X ] gh yvy v #]]333# #ŏӬ߿ʿD"+w @K % % % %%%% % B   ;/; 0 91/<290KSXY"7]@8  '()& X xyw !%+% 5UYX es]]3 3 # #VHNAu3B}%Y@.%%%%B<< 9991/290KSXY"3 3#%lk!mb E@%%B/0 991/0KSXY"]]!!!5!" ՚ow@=210!#3!XfB7@ 10 #%mZ@=210!53#5XޏH@ 91290 # #Ȳu-m/10!5/mPPf%@ <1K TKT[X@8Y0 #fx#{ )n@*  ! $   D >*22991/99990@00 0!0" ]#"326757#5#"&546;5.#"5>32=zl;^[fX=& 3qpepӺ)Ld_y64''RR2X 0@  G F221/9904&#"326>32#"&'#3,fd./xRXWS%{/@   F210%# !2.#"3267%JR%QNI]`A9++88*,A:;>{0@G H221/9903#5#"3232654&#"Z.deCSW;7W {X{E@&    IH991990!3267# 32.#"X㿮Xmi[ ^Z89++9 @Ţ'4@     <<2991/22990#"!!#!5!5463'cM+Qgc/яN{H{ )H@' '  $(*' G!H*221999904&#"326#"&'5326=#"3253ZLSbC,ml/9.,}^\::VZ,@  J  F21/<990#4&#"#3>32jq1sJ`cD .@ L LK <<1/20!!!5!!3#mnm`/BCV 8@   <2991990!5!+53263#XZZӜ} @:  B  DE 291/<90KSXY"]@R546Ffuv ('(;;797JIYYkiiiyxyy]]33 ##Gb{ZFB?  &@   L 991/990;#"&5!5![Y饵|~mo{"@'  MNMNME#K TKT[X8Y<91/<<<299990@G000000 0 0 ????????? #]>32#4&#"#4&#"#3>32"iJo5FP;9JI9!c?LeHEws{p{``32jq1sJ``cH{ #@  D>10"32654&'2#"hڜ-.VT{3@ GF221990%#3>32#"&4&#"326w.df SWWRw 3@   G>22199032654&#"#"3253#L-ed.+SY7:WSj{O@   21/990@%  0030@@C@PPPP].#"#3>32;zI.Dv6y.*`w"${'u@@    B %( OI"E(99991990KSX99Y".#"#"&'532654/.54632OS}{\JSjgTzEZ9..QSKJ#}##55cY1!1@  <<2991/<2990!!;#"&5!5!f^^uϪ+|b`>^,@    JF21/2990332653#5#"&økp1qJyaddm`e@)BIE91/290KSXY"']@%]]3 3#dEFr`T` @E      B    /91/<<90KSXY" ]@      &&)&))#, 96993< EI F J VX W Y fifij e vzx| r -   ++>>< H Y jih {yz|  ]]333# #àö`wBfL` @H      B  IE 91/<290KSXY" ]@ fivy  :4 ZV ]] # # 3 ^oo)'`?HkhV`@E       B   IE9129990KSX9Y"8]@v  &&8IIY ]]+532673 3Z.Gc".\mQ\GOLGhu:NN^Nlb X@BIE 2991/0KSXY"8]@68EJWXejuz ]!!!5!-}bܖ%$f@5 %   !  % $  = %<<29999999199999990#"&=4&+5326=46;#"3@k>>j@FU[noZUtrݓWV10#$j@7%   #%#= %<2<9999999199999990326=467.=4&+532;#"+DVZon[VD>k@@k>XXrtݔXy &@  '1<2990#"'&'.#"5>32326yKOZq Mg3OINS5dJ t]F ;73 !;?<6 7= @ <2991/0533)eq%!N@*   " E"<<<2212<990.'>7#&5473%C??BI9gg9ބ5(,-("9="+` 츸X>@     <<1/2<2990.#"!!!!53#5354632D>Cs3A,,ُ/яLB /@ (-  * -'! @') -0)$ !'$* xyx( $02299999999912299999999904&#"3267'#"&''7.5467'7>32d|[Z}}Z[|Z^.[20`0\^.[3.^Z{{Z\}~t]1]02[-^Z3].2]-_%@D% % %%B  < e e<<2<299991/2<2<290KSXY"3 33!!!#!5!5'!53%lkVoqZmo#o o#o!<210##  = 2>j@<#$93 $*0?#54&S9akԂ[]=:̭IWW9fqր][<;ȧH>=><''PGZsweZ54m@''TLf{xf[1,pE-Z/L-Z/L?F@aa1<203#%3#?}N1ID@'  &>>2J\ ^,8 8YD/210.#"3267#"&54632'"3267>54&'.'2#"&'.5467>`:o:u8g24r=г=rjKKMMKLijLLKLKKkZZ\[[[~}[[[\ZZ/lhȬJKKjhKLLLLLijKKJgZZ[~}[[[[[[}~[ZZ %)d@6  (&&  #*& (' j kji*22999199990"326=7#5#"&546;54&#"5>32!!|WHi1Rwu3}CD?kdPDN@Ms=@pABtZ\#! {w# /@    v v<2991<2990 5 5L-+-+#RRXsy^@ '10!#!X!^?dm10!!d }N4L@I  ] ] B   A)5)M  \\ [G#X;#Y//29999129990KSX9Y"2#'.+##32654&2#"&'.5467>"3267>54&'.XXP:&rk1=-7ffZJJDZZ\[[[~}[[[\ZZ~jKKMMKLijLLKLKKLbeG]C;P*T6?>5VZZ[~}[[[[[[}~[ZZgJKKjhKLLLLLijKKJ=b10!!=V+u @ STS 102#"&546"32654&hAu,-/OomOPqp1.-rBoPPlnNOpXy.@    <2<21/<<07!!!!#!5!X!dCDLIB}a@WWBA     @9991990KSX9Y"!!57>54&#"5>32eQdR1q?Ay;Jwrnaz3=L$$}k9wuF(\A          @#) & )99190#"&'532654&+532654&#"5>32^c:r;Eq-evnmBJ]b`W,p;Eu2X`lP|yQDJLl?<8?yvcG]f%@ <1K TKT[X@8Y03#fT` L@*  !   JF!99912<9903326533267#"&'#"&'øxo ! &D">K .Y\,T H;8 OOPNLPj; #@WW1 9120!###.54$FfNݸ/`103#`u)X 9A      @  aW}a 12035733!j c)t'+n 3@   jkji9910"32654&'2#"&546!!hfssfeusgʫ˫\{u༻߻`{\# /@   vv <<991<2990 5 %5 +-:+-#^R^  ^R^  Z{'V'{ /Z{'{& /tVZ'V'u /!%@G  %%B! "$ $&# # )"#&999919990KSX99Y"33267#"&546?>54565#53%=TZ>/mNb^hC^XC&DbRY;X1YnED98L\V@T?%k&$ ,u@O ]1%k&$ *u@O ]1%m&$ -u  +@ /  ]1%^&$ +u# +@O#@]1%N&$ )u +@p0? /]1%m !@W % %%% %!%! %!! % !B     !  PPK/K!"2299999991/<9990KSXY"]@  ]]4&#"326!.54632#!#Y?@WX??Y:Arr@;nlZ?YWA?XXP!yIrrIv${g@7 % %%%B    c /<291/<20KSXY"!!!!!!#!3eex5ժFժu1&d&Nk&( ,uNk&( *uNm&( -uNN&( )uk&, ,uk&, *um&, -u  Ic:1N&, )u+1N ;@!    21 0 0<291/220 )#53 6&!#!!VD}}/`ŕ{HK+Fb&1 +y"+@O"@]1u\k&2 ,u@O]1u\k&2 *u@O]1u\m&2 -u +@ /]1u\^&2 +u 0!+@O0@!]1u\N&2 )u +@p0? /]1;T .@     <91<290 7   ^t^_t\t%\^u^uw^ +k@:+)&  *&&, #* #)+262#5,999999991/9999990324&' .#"#"&''7&5327sT sV)+y=g %s9d/NZIn-QUPeQzQQFIRPJ=k&8 ,u@O]1=k&8 *u@O]1=m&8 -u $+@ $/ $ ]1=N&8 )u$!+@p!$0!?$ !/$!$]1%k&< *u@ ]14 @  28  32299991/032654&#33 !##ꞝL!󄃃}/V@1-'!  **.  !' $'$-DF099991/9904632#"&'532654&/.5467.#"#7C:oEBL;lAxC\[yqyrq|d1M*%]taQG_J'8O#kr#f&DC#f&Dv#f&Df#7&Dv#&Dj#&Dt){ C@I=70 6 %C "76. 3@:("D%=/.M/u MCM6+sD299912<2<2<999990@ 05060708]5#"32654&#"!3267#"&'#"&546;54&#"5>32>321xYS\JMWWLepO27Gn 'aȿuc^8>M<[|%!YHZqYa4+#"33)+RNPPXx+'#!?@=Bu%{&hF{Xf&HC{Xf&Hv{Xf&Hf{X&Hj@@]1Df&CDf&vDf&f @@ 0 ]1D&j +1H)@O B $ *'! !'D! >*999999199990KSX9Y"#"32.''7'3%.#"32654&Ŷ"#!H&!!#R-:/(  (-Y,\bPȑ^b n7&QvHf&RCHf&RvHf&Rf+@]1H7&Rv. +@ 0 ?. /. .]1H&Rj +@ p_PO@]1Xyo '@ w <<103#3#!!j!/ +s@>+,&  )&  *&& &,+,* # )#D>,99999999199999990 32654&'.#".5327#"&''m1$eA H#cC')d<]*,g9\ //4o0.0tGGq.78MBz;32#"&4&#"326w.dfSWWhV&\j%0& 6$ +@ @O /]1#&D%m& 1$+@ _PO@/ ]1#H&D%u'u$ur{'uYD1k&& *Zu%f&FvZ1t' -~|&%f&fZF1P& 2K&%&KF1m&& .Zu%f&FgZRg&' .o{ ' (:G @8@]1N{$H@ "  @"   GH%<<1/<2990!5!533##5#"3232654&#"Z1.de5yySW;7W N0& 6({X&#HNm& 1({XH&HNP& 2({X&HuN'u1({uX{'uHNg&( .$o{Xa&Hg#fPm' -u*{Hf&fJfPm& 12*{HH&JfPP& 22*{H&JfP'*{HN'.JHm' -u+ +@ /]1m' -uKKQX@8Y@p`O]0?@!     1 0<22<221/<2<<2203!533##!##53!5*ʇʆ*9QF?@"   J  F<221/<<2990#4&#"##5353!!>32jq}}a1sJzz`c^' +u, +@ O@ ?0 / ]1D7&v0& 6,+@O@]1D&m& 1,+@O@]1DH&u&,uFuD&LuPP& 2,D` "@LLK 1/20!!!5!!mnm`/B =@!   "!221/2<220%532765!5!#"'&!#3!53#=Ga'&HHAA@-]@QQJKDuuêK I@&  ! <<<<1/22<220!5!+53263#!!!5!#3#ZZi,+뗗Ӝ}/BCmm' -0u-Vf&f&j.'N` @9  B  DE 291/<290KSXY"]@R546Ffuv ('(;;797JIYYkiiiyxyy]]33 ##Gb`/ZFB?sl' *v/@ ]1 l' *vOKQX@8Y@O]0s&f/ &Os' (m/' (Os'y`/'yOs 7@   1 4<2.9991/903%!!'7;NwdPo;jnL >@!    <<2999991/9990;#"&5'!5!%[Y饵P{;Pu|~$o/nFk' *!u1m&vQF&*1{&0QFm&1 .*uf&Qg&Q{`IV=2@  1021/90+5327654&#"#3>32=YZͧZ-,t|6~ij>>WotV{ 2@  JF!21/90+5327654&#"#367632YZ͹Z-,jqFE1TTsTU6ij>>~ʗ[\``21qpu\0& 62+@ O@/ ]1H&R+1u\m& 12 +@/ ]1HH&R#+@]1u\k& 32Hf&RH;@     -299991/220%! )!!!";(RH=MKF{ 8i@92/ & 8   #5/)#92& MuMCM,s9299912<229999904654&#"265&#"!3267#"&'#"32>32PVWMfRPhgPPcpP/;}Jb04TY/%W & +#T53+)CBDA88>A>Ak' *u5jm'vU&r5 {&Ug&5 .ojf&UgZJk' *u6m&vVJm' -u6f&fVuJ&6u{&VJm&6 .uf&Vg/u&7u&yW/m&7 .u +1~&W (/-@  : : <<1/2<20!!!!#!5!!/s-  +ժA@B@!    <<<<2991/<2<2990!!3#;#"&=#535!5!f^^uϪ+|b>=^' +u8/ +@O@ ]17&vX'+@/ ]1=0& 68+@ O@/ ]1&X+1=m& 18+@/ ]1H&X+@]1=U&8t O&Xt=k& 38f&Xe=&8uu^&Xut' -|:+1m&fZ+1%t' -|< +1hVm&f \%N&< )u +1k' *u=m&vV]P& 22=&]m&= .uf&]g',@   <991/990#!5!546;#"+cM=яNQFX%>32#"&'##53533!!#&#"32y,fd.{{aRXWSzzc)?@$   2 !$'+9291/9032654&#32654&#%!2)"#546xxtB>d{f>p}qdƵϠ/Wp1FqGX>32#"&'#!!&#"32y,fd.RXWS0 327654'&#'3)'KKOO{e5Fq>=DEdgh=< !&#"32>32#"'&'#'҈FDDFl,fttttdLL.zYmnnnRRX랝+,S|1/@21 <1@   0>3 !"&'532#"M[?[MIXVJ))gj))=@0230@=<g"%# !2676;".#"3267M[?ZO*ZT3,JVXI5))p*32j>5F=@@=^s!%# !2676;".#"3267JR%FC=ZZ-,I]`A9++8(8rGj>>~A:;>N3 !#"#546 6&!#FVD6<0c//&r1FHKwN#";5!! $5476%3羽EF5e{ɉ{+ˡd4 32654&#"5!#5#"32_.df,T}SW;7XR=G{ 7%2654&#"#"/532376?654'&'&'&'&32h(>vwf2BFKI I<' )iy|{L (=\RR $+.! -&N +@ 1 3 21@  /0!5!!5!!5NwrT+u\=@ 26 25991@  9905!54#"5>32#"327uVJM[׌~ S@=))yz~#7(>@  22&0)1@ )) #)90.54$32.#";#"3267#"$546IyhYbfgg"{ (({smz45$&Ε?V!!!!+532765| YZZ-,ժH#ij>>~V'$+"!!+532765!5!547676;'a'&QRF1i&&(W%6MI!RI ! 5 3 3325D`H&0tt?uo+EA&#767653#"'&54&#"#367632"&76/JI_BG}MKUv14IBe``  1:!5!!;#"'&5=,-Z٪\Y+z=>jf!!3!!#!!5!!5!!= 99ժi@n67632'&#"##3i~\/j!-<BmV c3r%3sh5476;#"33 ##YZ͹Z-,Gbij>>~ZFB? ;#"&=#53!5!;+[Y饵 |~ĎɎ1m% # #''3C\P"Pn@Jo |mo"%#"&33265332653#5#"&8"iJo5FP;9JI9!c?LerHE! s{ p{+`=R{#4&#"#3>32jq1sd``cu\  32'&#"32767\:DC; 9CD8 z~{{vu'y2 {'R-%63273# &#"327-N5>o毴o8=yy=Y+͠v~^VR{ 763273##"'&7327&#"VPeo~簵noVAA( /s%+n+͝u"mmB8!+#"#54763! 27654&+Vn]6<0``~'R@]M//&r1FRQE8DVT*3265'"#"67232#"&'#76;#"w" . [f,?N͹ /2FIWS.O#.+#33232654&#N76SٲM{cݑ77hy]ҔYJ'>323267#"$546?>54&#"iV luhskijm^\''Ƞ/ vp{DI--յ1#hcq<;{'>323267#"&546?>54&#"PZڒEzTgjS썉J\{}SO9!!1Yc55##}#JKSQ..xmvV[!&'&#"3;#"'&5# 54!238!n|wx'%dQW/R5-0A3=g)(V\`@oV !!;+53276=#"&5!5!f^^uYZ͹Z-,(Ϫ+|bij>>~`>/&#5463!!#ŃF1-/7&r1F+!!;#"&5!5!5476;#"f^^uϪ*YZ͹Z-,`|b`ij>>~/V!!;#"'&5!/s-,-ZߥZY+ժ~>>ji? '8v'q'|XdJ##"47#5! 54'5{no{x4xn8!L IL*!"'&533254'3\Z,,Zxznjf?~>> ILɸ#367632'&#"kSHcm.-PKG "(3t*b7m*9 Vm+5326?3 67632'&#"nQGJ|lLT3!;^2Q+31705+h:=HTN~) )!3!#!!5#5!!uP" "v՚i@5b!!!#!!5!5!!-8YbҖg 7654'&+5!5!2! $5dc{dd\^rhbVPKKKKIJG8+lh3! $54767635!!#" 76RUci r\\dc{dd݊hl+8GJIKKKK}LT` 5!!#"3267# $547676peje]\dcmTjdc^QVbܨ JKKK21%݊hm*8V$` 2767# 4%$54#0!!5! TMOQWPVa ejo0, 5%b|8d1a# 6323#!!5!5!67654'&#"п -"BP8u~i9Dc``JU?T<>< % 7654'&#!!!!2! &53h<= \^G/"icUPRz,ʞ[+3IJ I8+le[tG)}LT` 7654'&#!!!76!"'57?\]ȨgcUQԪ-5IJ,39+lhJc 4'&+#5333#!"'53276MJY>lunc9rO_}nw~FVrA}Vg{#36763254'&#"64QҸMNr98xܭz BR1pqWBA3#+9'6-3!!!!#!5!5!5!^^``l%m&$ .u#f&Dgm&, .uDf&gu\m&2 .uHf&Rg=m&8 .u#+@ /##]1f&Xg=' )&8i2&&Xq<=' )&8 *&&X<=' )&8 .&&X<=' )&8 ,'<zW{%' )'i$#2'q<%'i& 2$#2'q<0' 6)&fPm' .u*{Hf&gJm' .u.m' .uNue\&u2eH{&uRue\0& 6eH&m' .uy}LTf&g7Va&g#fPk' *Zu*{Hf&vJ=%2763#"'&5!#3!3m6!h9Hն+ROf'MSb9du'Fk' ,u1f&CQk' *u)f&vk' *u/f&v%k& 4$#f&D%m& 0$#H&DNk& 4({Xf&HNm& 0({XH&Hk& 4,Df&m& 0,DH&u\k& 42Hf&Ru\m& 02HH&Rk& 45hf'Um& 05jH'U=k& 48f&X=m& 08H&XJ&6{&V/&7&YW}RT. 56$>54&#"57>54.#"5632 4o1\}p_s54&#"57>54.#"5$32Fp>!BlJc(v];?"AW?-1CA#E ptgDZX%KlaF='.`[b[3XpVU 32=t|6~kWotl(1%7276"'676#"'#7&/'&'&3232654&"m B{ rhG0wD &8Md\]P{#looԐ>>t#O9 Y%Z5H7WSCTV!!3+53276=!5!"YZ͹Z-,՚o*ij>>~Vb!!#+53276=!5!-}YZ͹Z-,|bܖij>>~%%P& 2$#&DuN&2({uX{&2Hu\' )'i2H2'q<u\&2' +iH2'q<u\P& 22H&Ru\'i& 22H2'q<%0& 6<hV&\l %7276"#7&'&5!5!676#" B{wD3' rhF>O9aJwt#jlg{.%7276"'676"'#7&'&=4&#"#3>324 B{zgG1wD3'5ZI9!c?98>>r.O9aJs{``>t#O9aJ;>V` ,@   991990!5!+5326XZZӜ}xY12654&"&#"32>32#"&'#5#"323QSSQPQRRQP]=zz<\o\32#"&'#QSSZQPPSSPP]=yz<\o\GJR\D%QM((_]q"! ^`A7S]++ L8 rMq;> 3!!!!!5! d iA!#!5!7##'-.d'Jug[|BJ8jF{5.#"3#"'&/&/5632654/&'&54632OS}{\Jvh*L'TrGY3e2{zD>zEGIZ9..QSKJ#}^R ~$=&[5#`cY1!GJ!b!;#"'&/&#=!-j1 *L4[TrGY=Zb ~$=&[?%7"#5463!2##326&#v6<0~'ʌ//&r1F )533!33##"&'.=)3267>5~9FBjiCE:  yVWx! A?;::;>`m];<<;\l9 #3#i++!!2#.+##5332654&#LN76SٲM{cmˉҔ77hy]w{.#"!!##533>32;zI[M ܹ.Dv6y.*l\<Ĥw"$8{ )32654&#"3>32+3267#"&'.>zl<^\fX<& +qpepӺ)Ld_y64''RR2{{0@G H221/99053#5#"3232654&#"Z.deэSW;7W Wy 0@  G F221/9904&#"326>32#"&'#3.de,-xSWWS^X $9@  !G! F%22991/04&#"326>32#"&'#46;#",fd.̸ZZ/xRXWS~Ӝ}}{0@ <1@   0>3 !"&'532654&#"JR&PNH\`@%++*,A:;>s:{!)47&'&!2.#"63 !"'3254#" 90%QNI]cU-RG+>jiáS,+;7W{7 &32654&#"%5476;#"#5#"32;mjkookjDGH8H$#${PϳQ{#T ij>>~SW;7WSzW{432!"&'5326=!7!.#"z [imX^^"++98ȷzW{?@ I H991@  9905!54&#"5>3 #"73267zXmi[&Z89++"Ƣ{ )%654'2273;#"&5 '&'&'& 56BL+>w9P!1jcGF:“֊>8E#ZuaQ`vg("chV({09@ 22&%!*2 &.F110&'&54632&'&#";#"32767#"'&546wA@Q[\ihWVLHHZ[c[[MaZ[V_A@^  VJ=+,nQb54"[\m({(<@! #)) )& )190#"&'532654&+532654&#"5>32U`LdKhiP_m"#ibQnW=JV^8{B#"&'5327654'&+5327654'&#"56763273;#"'&55c88hh@H9DDKCE@?qv|f6688h8AANODE<[KO 0A=1_JJm\["45bQ77,+=J++  OAf10`ZȢA"y( !27654'&+532764&%632#"'$Sv<;;!!;yUkbbkˠU^A;;LL67ss.gg=XVq^!+53265!5!!5!qZZh(Ӝ}}ؤg {H4&05476;#"#"&'5326=#"43#"326HH1:hh>CO6xn#{XЮmssngn^ ij>>~.,}^\:<bH4^ $!"326#"&'5326=#"4763!|LSbC,muq9b.,}^\:ᥟzX %#5!#"!2&'&#"32BHXN,>hhxpVHdbbd">:KMrqfqrfQk^".5 3 3265+]ܞ^+ r.$a32#4&#"#5476;#"}2rjrZZκZX `cJij|~V(>32+5327654&#"#5476;#"}1rX[̸[-,jrZZκZX `c6ij>>~ʗij|~23#!!!!!5!!5!! bnnl~ ˏ5i ^!#"'&5!5!; ̦ZXXZjf;8~|2^ !!!!5!!nnl^BXy&;#"&=&#"5>32!5!3267#"'.H>ꤶ./OINS)&r\FJKOUi(? ;?<65=>;7-4 ;#"&=# 5432!5!3#'&#"3[Y襵>5*GN\|~ܽ󠠄K9V  ;#"&5!5![Y饵|~(L-;#"&5#5!!2#"'&'532654'&+5!HHTgOE@KOPUCWJJX|~A$8+lh%12KJhj{!%#"&3326532653#5#"&2"hJn4FP<88 d>LfHE!s{p{`LfHE!s{p{)32+532654&#"#4&#"#3>32"iJoSN5FP;9JI9!c?S,5HEcԜ|~s{p{^^@;#+VM{+532765367632#4&#"{5D1DCWVV\^oA@01re22wx\__V{ 4'&#"#3>32;#"'&./]p@A2XVV?4<>OO__^edwxH10``A{ !3!##{yyH{  #"%"!&'&!3276ߌH?7?H4HH4{-m__mOmmOE`!!!!!"'&763#";d~~~~ KKKK`hghi&{.4'&#"3276=332#"'&'#"'&57!29PHJ|")u) }07_CC_vD8@jxZqosO++Oz2ee2z| VH&/#5!#3!535&'&76767654'&_|{_hd{{dE,HH,O1HH1ouu{{BnmBHImnI^732653#5#"&';zI.Dv6.*+w"$732653#5#"&';zI.Dv6.*w"$fV^;#"&=#"&'532653YZNb.Dv6;zI}}w"$.*+jV{.#"#3>32;zI.Dv6y.*)w"$jV{;#"&53>32.#"#,,[.Dv6;zI[[}?>rw"$.*ll2{476;#"!!5!RRҼj&$pnhb`022{!!5!4&+532jnnHlдRRV``bzW^!#&'&+#!2327654'&#7545â?;;alkpw?@@?w 66^q$%'^NMi++ST**zW^!#!3327673327654'&+jpkl|a;;?î545(w?@@?wSiQP^)%$q^667**TS++V{8.#"#"';#"&=327654'&/&'&54632N[DF20@RRz|hj&"nfdbbFF24@LLf?((**T@%$!*MLZ[705-,QK((%$JK}VT+5326546;#"ӳZZcMӜ}}¸Qg}VT!+53265!5!!5!546;#"4ZZ=cMh(Ӝ}}ؤiPQg}VT^;#"&54'&+532'&cݳ--Zg() |@>vV[!#"327673## 54!3476;#"8tn!ʷ5RWQîd%'3A0Ǜo@`\V()g+`!5!4&+532!!H^^uϪ+>`|bW!!;#"&5!5!f^^uϪ+|b >`!533!33##5#"&=)3276:CYYu>>|WMĤ45wEioabdo?ܤdqnܑkmhAw` !+"'&5#5!?27654'&'&UBr86FRQS&(g3XXBO\Ldqn``;612abdw7,H`!# #3T`` !# # #33”­jj`jH >;#"# #4N|lLT2"zHTlfk}3 3#f%.]}8 V`!!;#"&=!5!;4z `ۧ10%*`!#47!5!5!332!'3254#ejL<FX3<;4% 6[}LT` 2!"'&'5327654'&+5!5!ajbVQ^cdjTmcd\]ej8*mh%12KKKJiLh`$- 76654'&+5!5!2#4'07&#"327* \^ejeidTQ'd( }ŃcL;*1JJ$8+lgqUeR8y*K/K327654'&#"56763 #?W::fPONNLQQUmlprLbAr+#}swt#&'&5476!2&'&#"3ʪplnUQQLNONPc9:Vws}#+rAbLr3!"'&'5327674'&#˪plmUQQLNNOPc9:Vtws}#+rAbLrJ#476!2&'&#"32767# '&5nUQQLNONPc99cPNONLQQUn>}#+rAAr+#}_-sB (47632 7 654'&#"47632"'&_ԚO̵eddede"!/."!B^!"5Ԝ0ٍccƍffff.""""./B!!} -@   F!21/9032654&#32654&#%!2#!]_Z^UTTVeb`sci?\dU?.Vi}u"y+";#"3$''"'&5467.5476322s9@< <@7uTxVddjbbjdd6=76NJ@6sx>WVggWV6l0%#5!#"'&76325476;#"#&'&#"32t:{}|3H >ws}#+rAbLr #26&"3!!!+5#"32deen a^!;8NN8:j+^Lۓa31DD10ML C3276'&#"%#5#"'&76323!2#"'&'5327654'&+5!22XW3223WX2o#55JzLMMLzJ55#o ?M;319;<@3xAr;<78chqjtssttss_3d0110d^L$8*mh%12KKKJ6 ,326&"!5!332+#47'#5#"3233254#dees ai$\\#jKyyKj#n0 h*5j 3.#"#"'&'#"&5#5333#;532654'&/.54632/e6RR;Y%w026:8>qaQQo-Ek>v;NTj&g[{=l?((TT@I!,KL!&`>NM55YQK($)$V4%.!5476;#"+53276=#"'&5#53!3A &'p.6@3632&'&#"632#"'47&'#"&5#533254#"&57#3uZ310.///0l;;;2v1+\!raQQ$-WO=MT-E‚#+qrfr9DhT"2`>9KiNVH3+5327654&#"####53546;#";67632G11l?KJYhonjjhqij;/m(56Ft<;H``01/яNPhce22wx%7&'&#"#"'&'#367632327654'&/&'&;>ABgf$&n/<>][ALODKTLDCKEJIa54%"092?)TT?&$!,KL\[&:MV3-+RK(#*$JA 3!!!+ۊqvLۓB 333# #333# #ttttU=B!#!#!#!#kkUXrXJ 4&+53232653#9O%5zpcęaBþybV "32653;#"'&'5#"4'&+532Zgo0>*m/2O?*mbþyf\gę10A @ 32tNN^luu)qJy}wYYk\g88A3>32#4&#"#5476;#")qJy}tNN^lu43rB98wYXj\1Sw66WU 3+532653#wtgr,B0ttxlX6Vr8.#"#3>327.bjtt%uT  qksa97832653#5#"&'.bjtt%uT  qkJa97Q32653;#"'&=#5#"&'Q.biuB-r33$vS  qkJH VX66x a970!+33276?3327654'&+CGDDuj=%%(f{n!!!|K((((K|N;[--s?5/.B 333# #tt+5326?331]O\D05 {{bpEW(K/itf--452654DŽ@XX@rPPPP=>X@?X=>POPP"'&4763"3tPNNPt@XX@PPOP>=X?@X>^s327654'&#"567632#(y6$$>q31210336WEDGkM@*7K$@ ` XFh_@C^s#&'&547632&'&#"3kGDFV63301213q>$$6yMAmC@_hFX ` @$K7*@)f7@  91290K TKT[X@878Y3#'#f)f7@  91<90K TKT[X@878Y373x$@1@0#+=b$@1@0%#+=/#!!heJ'#!heJ#b#c>U 533##5#5uJu!5!J>ߖ/)HDV{ W @  P{P10K TK T[X@878YK TX@878Y#"&546324&#"326{tsst{X@@WW@@Xssss?XW@AWXu"  @  |1/90!33267#"&546w-+76 >&Dzs5=X.. W]0i7@!   PP99991<<99990K TK T[X@878YKTX@878Y@?       ]'.#"#>3232673#"&d9!&$|f['@%9! '$}f['@Z7JQ!7JQXfs%3;!"'&5k&&iWRd10`ZȢ% '&73733256/MMV| ;#"&5#5!88hr.EGWwl:Q[v/).#"#"&'532654'&/.54632P1j8WV>](}248{D@}=RX o)k`@q a//$)*+MWfk2-*SIXa #'#37 ͉H+^s#&'&547632&'&#"3kGDFV63301213q>$$6y[AmC@_hFX ` @$K7*@,X!!5!yЈ,X!!5!34,X3#!5hh,X3#!54,X%3!5 DfCfv)ff7v=b10!!=V /)H @ PP1<0332673#"&/w `WU`w HLJJLD@ a103#?Fj82#567654#"56J24C1xZ@Vƪ@$C!Xl05^ V{tXf%@991<203#3# fx)fg"#DVv'4f#!#͇fxx/)'sr/)H >32#.#"/ w`UW` )LJJL" #3ﻒY#55#53pp{53#3"pp{f3# qfyX0CbyX0v53#5#5L:#33T걈s^p!5!#.q532654&'3#"&=X.. W]0ihw-+76 >&Dzs5  "&463"]]3GGlbbG23GbL3!5353:^H#5!##걈c_I #53533##sc^5!tcV %+53276=YZ͹Z-,ij>>~V 73;#"'&5,-ZͥZY~>>jic/@a103#?d.@ aa1<203#%3#@ D  1  0#"&546324&#"326D]\\]bG33FF33G\\\\2GF34FG;@103#ﻒu)8  @ |1/90@ IYiy]!#"&'532654&'85xv-W,"K/:=,,>i0Y[ 0.W=u#u $s/#DU|/#5!#|J9X#"4533273273"h;tv gfv ifvtR)@  91<90373x)@  912903#'#m/8 @ PP1<0332673#"&/w `WU`w LJJL/: #.#"#>32w `WU`w LJJL5@!   PP99991<<99990@?       ]'.#"#>3232673#"&d9!&$|f['@%9! '$}f['@X7JQ!7JQ=/10!!>VєmB]Xy a&h!5&hhh5!Ĥ/'\ ]`LM'ogDdFFJ  26544#3GG3]]lG32GbU|3!53UJU |/!!|&b9X632#&#"#&'"#72h54'&' RJ 6"RH 0PQn +0PQn  &Ds7"#4%62#&%n~vv<<tf3AntVH%#AnHV #"=3;X3Vh'fv?F&jrf&>/`yNf&DHf&f&D\f&pf&f&6&%$q%sI%1/01/0@%%%%% !3yyN(=H+u\6@ 26 25IJ]1@ 0!5!#"32#"320qYǪIIz~,.%0/01/<0@%%%%3#3#+#Vy0F1H (1  <<1@   /0!5!5!!5HA)Aժ9u\2HUu3xm < 1 <21@ /220@% %%% !!5 5!!9" :A@/7%<uZ&/M@"2  &,2(0<<2<<21@&( '   /<<2<<203!535&'&547675#5!67654'&'Ͱa|{aa{{bL+CC+LL*DD*+v[ssZttZss[v*DD*7*DD*;uZ9@  <<1@   /22<<2067633!535&'&3I.K{bb{L-I["WDWx ֪ WW"J@@qqro prol 991@ /<20353&5323!5654#"J{n !o{1xx 7oȼ߅LI LN' )u%N' )uFf&(f&Vf&6f&3i& Fz *'&'&3273;#"'&''&'&767,-b=MJMUHi;c( #) Xn^T).\-rv~ oik*%1)0T*XmY*)Va#%#54'$QQ 0kEb6=q'0  Vm`&+532 3#-^1FAF[D~S]VH" 4"32654&%&'&54632&'&#"76hY(>f2BFUR I<' )iy|{L (=\ $+.! -&({R&%#457654'&# !5!OTJPE* :Lf.,KOxsPWKL,#%5,*3eaZiV{.@ J  F21@    /90#4&#"#3>32jq1s```cH9@ D >221@ @]0&'&#"!32762#"?HH?5@HH@<⇙8wyvs6`@ 1@ /0;#"'&'#5"$lYoRQ`+.0`b;`D # #'&#5~J/1Fe<2T`wtB`!367676'&'31!xdLjE.*{`T|p5dwY|rNįtR8&%#457654'&# 4%$47#5! OTJPE* 9MKOxsPWKL,#%5,*p$Rݿ &H{RP`!#3267#"&5!##P117,#J%q\T`PH? XVT|.@ G  F21@  906#"&'#764&#"326ttf,n䇅{WS<R%{$%#457654'&# !2.#"OTJPE* :%QNI]]^KOxsPWKL,#%5,*8(8*,A:nok` 2@  D>ij991@ 20"32654'&7'"763!aFH<Ηr{sPSے-- 2^!@  1@ /20%;#"'&5!5!!$lYoRR0`b3i`%27676'&'31%"'&5#5!tZ;jF-*RR"#vfwZ{s`b;+.0LVh )O@ '#* KQXYԴ0'']<<Դ0]1@' *2<220"27654'&'2##"'&7673A\VMMG*w|~~hA1LLNeˑRh]c[斘n,mKseg.YVx`#&+53 3;# t/1FC /1Fz ~,~VN`%67653#&'&533?>TyyT>?@WxؑoW@F`&#"&'#"'&37676376' 2KUXK2~)@V""V@)~`{gLHk{A>oRyRo>6&j3i& jHf&3if& Ff&C$ # 76'&%$'&763 '7676] NI5|utf M2C6R6WpA{z Ʋ irS$ $6'&'&'&7!2#"'&32765JgNn-R Nr^ydPpw{A K~}Sj~"#4''&5676'&qO**d\txLJso@z8 vVOv~*+40r51_Tpf&"N& )umVd'#&'&7673567654'&'ĸh]i^V5RR*aW4QQ(VyvaxxGnռFCImֹD9` !32376&7%&# 67#5!'TQ0'( 0A_+T3 3[g/&'&7'&7676'&#"56776327'5!`ȍ=`[+9[R~!*`ȍ=`[+9[&͘7 cl|YDT|˩hl="pl |YDT|˩hlfMZuV\ #"32&'&32_{|^"y|•"jVH{ "32654&#&'& h1`{{_ mw.vRL#"32#457654'&#"'&76)MbzYTJPE* 9{2e+wTOxsPWKL,#%5,*˞nͱR)`#!"#457654'&#"76))I]]_bNTJPE* 9ۓ eUlnoJOxsPWKL,#%5,*8(X)V#!47632.#"!!#"&'53276`1c3$R,x:KAb9f.1d0W@Rd>Qoɏ?s!K_`7"'&76'&52n 'BQ_'BQ_[~,`*l#FR`*l#FRM #!3M&pM]!V!#56#'0#0?&'&bTB9[@[`7"7>9[@[|"O z:6hl0%[Ml |"Oz:6hl0%?[MVT| 7636#"&')! $&#"32nttf,hՇ<WSs)%{FVMu\&'&#"!3276 32 5DC6 >BCD@qopުՈOz~ {!&'&#"!!32?# '&76!2 %%cjf_[_fMJOhk en(' c\\c(  {"056763 !"/532767!5!&'&#"'(ne khOJMf_[_fjc% ؜c\\c VT1&Vy ! !###V{+'`VO` !!###`{`UVT|##5#537636#"&'!&#"32wiinttf,;pp>WS17532#"5>3 !"&IXVJM[?[5=@0230@=))gj)1&/y1'yc3Nk' ,uKNN' )8uK*o/32654&#"##5!!676767632#"'4=N qjqjW.E~C%0"@X UkiS*\o-* % T pFwusk' *HuI1L@!21 0221IIPX@8Y0327# !2&#"!^?s}RooR}J6,N&< )u+1m-%326&+32+##526!Z}y^ϬvH+W"%32654&+!#3!332#!Z}x_uwg)9dqco"676767632#4&#"##5!%0"@X UjqjW.E~-* % T pΗ*\o-k' *uPFk' ,uNhm& 1YH 33!3!#)v++B%$q2@   21 0291/032654&#!3)!qﰖE{e5F{f>dghq%s141/03!!/ժ!0@  1@ /2220%3#!#32!!3!7yŪM0'TB /ѪBL ҪN(x@   <<91@ B /<<2290KSX@ % %Y@ I:I:I:I:I:I: <<<<33 ##'# 3 0YY0S0кv{Z7F <@  B 10 991/<2990KSXY"33!#3+3Fm& 1N.F$@  1@  /<0#526!#v.+W++Vy0H+u\2H@ 101/<0!#!#++u31&/7h?@ B120KSX@%%%%Y+532767673 3;E,LE\mQ.-"X74oJ+'/.M *5>Bg@2  2 <<<<1@ /<2<20 KTKT[KT[KT[X!  @868Y3#5&%>54&'II Ǹ ˥II<{z WS ;P $@   1/22033!33#P)ˆ+BD @   021/20332673##"&nmuz[v~PEx+:r` &@   1@ /2<<0)33333`++</@   1@   /22<<03333333#<ຆ++B u*@ 2 2/1@  /0%32654&+!5!32#ϊ)+An ,@  22/1@  /<20!3%327654&+332#[fN+1@"#( +0! 3 632#"6/&4767676%67䐌x$[3#F#3bJ/P{3-wRIUA   +t` -@   F!21/9032654&#32654&#%!2#!_eUkUTTVe_cmcpPO^UCCVpou`@ 1/0#!6`ih`0@  1@  /2220%3#!#325!!3y-C7 "Ld64d!{X{H;`x@  <<91@ B /<<2290KSX@  Y@ I:I:I:I:I:I: <<<<33##'#3hh`Pl4_P({` =@ F 221@B /<2990KSX@ Y #33#b縸)`)H&n``"@ 1@ /<0!#!+53265 _7#U^`v=` N@B      221/<290KSX@  Y3 3###=ww`M` $@   F 221/<20!#3!3#b縸`9H{R`@F1/<0!#!#bW6`VT{S%{F`@ 1/<0#!5!и&6ʖhV`\cVec@    <<<54&xjjx޸ܸxjj ++ gsL`[|^` $@   1/220%#!3!3^渖L`66b!@   F21  /20332673##"&øknXrE5od]'+}U` $@    1/2<0)33333U(`66P`-@   1@  /2<0)333333#".𨐖`666L`)@  2/1@  /0%32654&+#5!!2#|y֜XZZZʖ;hi` +@   2/1@  /<20!3%32654&+332#S|yS[`Y[[[`;8`*@    F291/0%32654&+3!2#{֙YZ^X`;%{K@  <21@   IIPX @8Y07532767!5!&'&#"5>3 !"&A`^S E^]INQ%R9>;qdRp:A,*윜+N{ ?@#    221/904&#"726332#"'##3pLLqjUUe9ҝ暙?``B@  291@ B  /<90KSX  Y;#".5463!##r78r5ܖaUmV9{Xm&kC{X&kj##VT533!!>32564&#"##@1|yj{яLs`c. m&ivQ%{L@    F 221IIPX@8Y0%# '&!2.#"%!3267%JR%QNI]]E S^`A9++8*,A:pSdq;>{VDLD&j +1VM `%2+##+53265!327 RC'#U^5iрiv;A`%254+32+!#3!3 褽l`9#9533!!>32#4&#"##@1sjqяLs`cBm&pv0m&nC@hVH&y` 33!3!#ø`6u\aH{s@141/03!3!/2$@ 1/0#!38X`:Us 3!!!!##U/#˂>` !#53!!!!`¸ fs#!!!2+5327654&#/7qohfL>87||9ժFwr|zKK"VR`#!!3 +5327654'&#HRRQn!&&1`GQ``07 )3 3333###'0:YY{ZSS/B0кv;`33333###';M4hhPPlL_u7&Mu({&m%3###33VrwBh`%3###33,bG*B?`/Z%3##!#3!3)˪B9db|`%3##!#3!3ø縸*`Cu1&dWu%{&hw/ %3##!5!!+s-B+` %3##!5!!ø&ɸ*%<\Vt`3 3#\IIT`lD%3 3!!#!5!5%lk! mP\P\Vt`3 33##5#535\IIT`l55%3## # 3 3XfuPHNAB}3BL`%3## # 3 3o)'o*?HkG#4&#"#36?6?2GjqjW.E#20@0X U ڗ*\cU'% T pK,m& 1L;H&lf32+5327654&+#33stohfL>87||wwqwr|zKK"hVm`3 +5327654'&+#33j:HRRQn!&&1'wGQ``07 )&?`/fH%+532765!#3!3HhgL>87)hzzKK_9dV`+532765!#3!3RQn!&``07 `CG %"'&'&5332767653##|#3/@0X TjqjW.Fժ$'% T pI*\z)b%6#"&53326=3##c<1Tsjqø~3$2 #%m& 1F+@ _PO@/ ]1#H&f%N&F )u +@p0? /]1#&jf){Nm& 1K{XH&ku\QzW{u\N' )uzW&jN' )uL;&jl7N' )uM(&jmy}LT`7F0& 6N&nFN' )uN&jnu\N&T )u +@p0? /]1H&tj+@ pO@]1u\*H{u\N' )uH&j1N' )uc%&jh0& 6YhV&yhN' )uYhV&jyhk& 3YhVf&yDN' )u]&j}s %3##!!/Bժ` %3##!!øȸ*`AnN' )uahi&j7R({u\4RwT:`Z1"$/#4'&'3767653653#"''##53 ra{ .r &q,bS !/#12j{@E#$]|0q!<"5DAb1)*5"32767#"'&54767&'&'&76'##53|LAV:218UG&/=8O6-N@?_F?D(7-"Gq/#)^ %$ \*$@.!* n F?\KH* #TH#5DAbZw %3#%3#3#%3#ô ^ %3#%3#%3#3#%3#̠&!#53ӤR@dm 327654'+53367657M593pQf$h?FA@6b !eI(R[2* #53 3#ӤR%@-$%#5754&'./.54632.#"'/XZH߸g^aOl39ZZ8{4<5/VVL89CFnY1^5YVeU-"%56767&'&54767632&767/SD435gcbnZdF31`9:H:ZU!LOTAKv?=0ps2# &#\&"r 3# E' <, K' =qE ' = KE' > KX g' < X g$$27'&5767&X$JԖ`e_'@5 ^vbĘe4)X ' <j%67654'&'3#"'532T! D' <U !?%#"'&7673327676'4/37653323#"'&' &!UNBAE3I0<^yM\dsቬ+;H2zm^\꜑#P}g£x&R" C~m8(!' >c  =%327654'&#"67632+"'&5#"'&5473767654'&'3HIj($@GgLK1ZX%5,0.3cM[|dh<2=B%A !  .DF-%!mNH7(M' <O  %327654'&#"!#53367632,Ij($?GhKL1[W~.DF-%!mNظ\wLR! Xn*' X &/.Q&+8O\79LK5:,]-#4BK%63#"'&&733276u2lecw@A(IiTcI9(jzG1H*V\ss~B")T.327654'&'&#"&#4763&547632#XzL,5;(.;Dn2KxAZM\MObxX'*9:X DD(NOf7*(?$S-8AP6`' < U"327654'&'2#"'&5476B!799[]KB{ƶ`Q%T*WE{R,,9.UMAx|KU#JNL 3 &"4'&!5 767&'&'&547632?,3/V%.-js1v-3t9>YH9!$7+(;ڮ.TVLh+bZ3[f5%#"'$47332767654'&'&767632#4'&y]H?BKSxlkA;"b^M`72'#}[7 0&huqc-##NG".*3:,=2IB="9), g:^M ' =r D5%5%DHHnnnnD&567&'&54763233"/#"'&5332767654&#"t$!lD?I'8 .4LT^s7Z $08 " ,d$* 9^W4'6O'&n=NV)qaK" %D5%%5%DHHnnnnnD5%DHnnD/&'&54763233"'&'#5276767654'&#" lD?I'8" +EɓV  , 8_W4'6O -n=*{nmp" %D5%DHnn0('&54737676537654'3'&x!9EO)"a 2=`KG g&ZGM'DA2omb}8T"RY$6s9It6X !Vz 4&"2>#"&4632XXXXztrrt?XW@AWX栠732767#"&'gC*6:*kWZZB6"D6{S )L}@"Fx3 3\v4373ŠF3#< !#'3<&1yI !n8#'337673#" &1CRz6 *boajr!nUPymL%#'37676537653#"' &1/(0H/<(F!34.5WY9!nr|> @2%,*;l>3  *"2767#"'&54767&'&'&76`yg\NNYp0' >p  S ]*%267654/&54767#"'$473&64_>.VhG hRcpl?AOXj<9U9iDGTOA7.?#ou\N/ b \^xH a8' GA&6F('&5473327&'&5476&'5#"'767654'&#"%327654'&Cv-(;G--0M,Q;(J$"':AGb 41~!$@K5:,+  iEN@TSZ 'C49g=ql@H=.%4-+#%v%'r.C!0B7,g`o6oU%m`m!3/AbM3))I~R,~R-0.>d{*>@ #% +BA?>pпQQ9l,"2"54767$32632&#"# '4%7654DҼJPi?3k]KM?oabu;\SfaƎ:F78UyP8327&'"'&#"%47&76$#&67 #"'632Y60I616*, !*:9u`0'"/6OAK %[9.ȵ!463 #"&'7325#'&&7'6et !yCBBquЍ h! ACBB|U )"32654&24''&5432#5476n$ % *|{e6Lj` %"%:yx~)RhKK>a 9"32654&&5456767$ 3276320! 54-6546$ % #vdz]#x.>r>>$o %"%}@~Y9peDQcFlWNn-7Vr\ ,2654&'&! 3%$4567&7'7$!% /@l~.ZA $! $?=Qm.uG4 {V|,@ //1@ <0%"32544$#"54$76f@@@)@@@@Pyxo *%"32654&"#" #"5323272#4#"$ % zw6^"$J7f %"%&PW0>|#$"2"22#"5#&567663 #4#"&퀀!7y{^܀ݹIedm%jh|'>@ $/<@ )1@")<@ & )<0&7'64323254#4%$7"6CBCbRBCACKؼJjeh0KT 24#"53265$54767653!"'#@>z]U]xTrs@0egu/ss|T}"247&76% 3%$Զ%< &Ѩ'LB|T"247&76! 3%$Զ%< &Ѩ'hBc J"32654&"32654&+&'#"'&5432'3253765&7465&'7$ % $ & cge~PLfrtгJwT\U &"$ %"%IJTObo;4ˋP6A^g[oPIcb$0+&'#"'&5432'32537653"32654&b\U{cge~PLfrt$ & ,PIIJTObo;4ˋ %"%el~ ,"32654&2537653%&'# 47&76b$ $ 0nuhigeޘ %"%4ˮ/IJ=%ۏel ,"32654&2537653%&'# 47&76b$ $ 0nuhigeޘ %"%4˯IJ=%ۏ``3323!"'#"543225cиx)3Ɯ)`,88{r\ ,2654&'&! 3%$4567&7'7$!% /@l~.ZA $! $&=Qm.uG4 dm +"32654&&! ! &%$&7676=$ $ W6tm2JX $ $8${{N&`p .%"32654&%&'&'&7!2765!"'676F% $ WD NbfP'G!$ $TrJco<ob{"326! %&$'423 54! P@@<)"FTY=Ia# 2:%"32654&%!$76! 6'&'7%&'&'&7!276 54! H& $ JF r cXD NbfPt!$ $=1TvTEmMd 6"327$"327$7&76365+&7632676#4#"A?A?`ƾ5@VC?/@@@L@@@E>/} cbWZwj %-"32654&7$%&'&763 54\$ %  ʪ=A8)SUB<S %"$36H9G)#$67T 6"32654&$'&%$76!232'&#"%$'&762$ % T\oEh@MqKUPn^ %"%+DYl0yP^d6Qqt^}]F0&$"2''&'$! '&5"32?6*ʁ+=x #esZh N)=mdZe'llu ! &7623$54'74"m#!AVB8?kP$U.F~>=!{ ##"2#"5324#j=;C>{jVnn!r&m|/S~i! ! !5 74! $&>%~?>~Si! ! 3!= 74! %&>%~?>wJ~S~i! ! !5 74! #5$&>%~?>~NSi! ! 3!= 74! #5%&>%~?>wJ~T3"36654'#"5432AA\(DeN[̼o[$N[ux"325"547&5423253,r>Jm,Ws> [yu?{EBXFD '656%"'&76! 4"3VA!. {x9f>.`h>4A?~= h\$kb8:;-F_Zkf2)I 53533##5J؎؎؎vP;r 432#"324ЄLT3z! 473! 4'$331=PU~iibcWOJf34! %56'&53!! 4<1~k  !TxY9vwsknWb!%! %674#"&5! % %a lշ._z-FH+,S.+RLo ۤTnB7W !,6752363 ! 54+"&$54+"32(TX[P<fI+yhZtԕGw, bb+]mLVW3! 473! 5 &5 3I%$&>8 wjs oeKrdW2'! 673! =4+5374# #&5! 24:ip II-<-]mxRbXxR~X{\+2 ! 75&7!  4'"6258TW\F ;IJ:QU-\p!7 %#65+"! 5!2363 6#&32Ԅ!S}GJtAFWHCBuccyi3#! #&! 2Ɣ#cZY4! 473! =+53254!5 4C(pbAAZZwfq211W2 #&$'6?! &65l_$^M>p v\Y1xOh_[ )euG4! !234!#5!  ! 4E%D˼  }>>&T3! )!"363! %22:M#@͹$[ڀ g7 ##654#"#4+"#&=!2363 K@BM{hIF`fhh&4!! 473! !#53274%$534D'e[?RܬW&Z~IJȕ!6G))=iY32! 3! 5 '%5%30>'Mhko 4>>HS2>+3|'! %!5!$! 3#3%! ! 5)54!  I8<rrr OfkQؔc7X!,!"'#!52'4#&3$5!23634+"32~ Mas1D>3LIvjt| ٔxukYf!! %$54#"'! ! 4'7fGD `U6I@bYsrg8A:ԃM){6\lY4(3! 4%7%#"'#% 3! #>U&;3̿0?7YpmWc$!6=3! 47$$5! nڞòd?;kHuLL8TWJ&)*y354&#"'675&%'% t_CCty`^q|ytJfI8=\ ۣb*#2 3#3#3##Ѻ/㰽<2"4;%"4#"32lѹF|pux$LRQ´){ B32654&#"26=%!>54&#"5>32>32+3267#"&'#"&1xYS\JMLepO27Gn 'aȿuc^8>M<[|%!YHZqYaq4+#"33)+RNPPXx+'#!?@=B2({0#"'&'532654'&+532654'&#"567632wA@Q[\ihWVLHHZ[c[[MaZ[VA@^  VJ=+,nQb54"[\mPDd %!!5!!!#53)Ḹя{ 6326="326&!54&#"5>32>32#"&'#"&PVWMZfRPhgPPTcpP/;}Jb04TY/%W & +ݮyT53+)CBDA>A>A2/H{  #4&#"Ð/.G/  33265G.Ð/.+[%!5!2654&#!5!#J^adlp2r?W75353!5!2654&#!5!#?idxEvDFzlp2r+")5!2654&#!5!2654&#!5!#HEws{p{``pU?32654&#%!2+#XcbYSJKR]#'.+#!232654&#1E4p1M>ze\Y_[' ?]Z4D|vShPIKHM!!#!ڀ_A33267>53#"&'.A L67K $,*kCBk*,$=4!!!!4<8l$! !#n 333# #|ZkmZ|xyY>E )#"326757#5#"&546;5.#"5>32&ffMD_ntt&pQlT];y:Av7X|&??8?vh+]85l[hmKDg..RE )32654&#"3>32+3267#"&'.&&ffMD_ntt&pQlT];y:Av7X|&??8?vh+f]85lZimKDg..RG53#5#"&546323264&#"tt`??aVTSXXSTNO/00z{{ B32654&#"26=%!4654&#"5>32>32+3267#"&'#"&5kK84:/0n06@F2Q #S-E^T=bg~yI>;#T'1S&9NT8m\)3?26JUJLXP ZQ`.+-,`\`d1CH^#$"%G4&"2>32#"&'#3VWWaA?`tt]z{{.10/OgG3#5#"&546323264&#"tt`??aVTSXXSTDO/00z{{1!3267#"&54632.#" xn7yEB{9t\UTm 2gp fnZ_cW15!54&#"5>32#"&732671xn7yEB{9t\UTm 2gp fnZ_cWO(.54632.#";#"3267#"&546KQ3sCBm0W][Uhd^jrm>s0=r6]H5KX ]0*"0Q>-7;af]=SO(#"&'532654&+532654&#"5>32KQ3sCBm0W][Uhd^jrm>s0=r6]H5KX ]0*"0Q>-7;af]=SG '4&#"32#"&'5326=#"&5463253UQUZZVPɖ0i4>d+]V_E||D^tgxxz)f[bF5302QI !#5!#3#53W?浵ss#PP-8 33##8x0BVxyDI%">32#4&#"#4&#"#3>32B/UFj",2%j$/.#jj?'0@)&ug@Eg?Es6"#'[v+5327654&#"#367632v89hu9CGR,+tt54Ilj!pm;32#"&'53264&#"X.c43a1.\;muvl=_)ʮl$!~~!#: 46 #4&#":&{[YX[ՠxzzx:  &533265ڜ{[YX[ՠxzzxG#3>32#"&$4&"2uu`?@`8UWWbP/00z{{M!!;#"&5#535};JkPF7R]rTP[v332653#5#"&[tCGRWttkGlj{TPg`b^68~}!5!2654&#!5!#K`Yslqj=?g32#"&'#3LSbC,ml/#.,}^\VZ:5`!!!5!!5!!5!!5nnlphˏ5iV ;+53276=#"&5!5![YYZ͹Z-,0|~ij>>~G#3>32#"&$4&#"32tt`??a9VTSXXSTNqO/00z{{Xy#"&632.#"3267y.c43a1.\;muvl=_)6l$!}ut~!#QI+325&#"47&'&547632.#"632#"d&/\R@5a$^`^63302b3q>>>5|4 * &:/ZXX `@?@bj:)#"&54632.''7'37.#"32654&|s .sPm4\a^UV^%wp237,pQ57vonwwn=rO(#"&'532654&+532654&#"5>32T]5sQ0"*0] XK5HWz#"3###535463z>1tkqU.98P#P,gabo53#5!3#+53276=Ι<98h9\P\ m;d+]V_E|~4wuxzzf[bF53[v332653##"&[tCGRWttkGlj{TPg`bO68~C3#!3#3!535#535#4tt)r\PP\ap #"&5#5!;phq)99uun@PpFFI !#3!53#I?P-PPG#3!535#535#5!#?\PP\PPdm3#"54;33#'0#"3276ttdytrx !3rJMB ,|ssW?#5$ U| ;#"&5#5!98dv.FFXtp(QU| ;+53276=#"&5#5!9889ht9hr.EGbm;32+53276=14&#"#4&#"#3>32B.V"#23_uj3",2%j$/.$ii>(0@)&:;Sm;32#4&#"MU!1$XT8[]V;;FQxlX6V~a88wYYk\U|$54'&#"#367632;#"'&5:G()WW*+7[//$1!U&'H/Y,-56\sa8BDH V6X66x? 33##?-{{~: #"'&547"!&'&!3276&NNNMMNNX-(e(-W !-XY-!TUTTTTU=5cc5=J,==,:&/#5!#3!535&'&5476767654'&3fwx=%; )=xw>)[v<.#"#"/;#"'&=32654'&/.547632P1j8W*,]({44MN8> 0Br34@>?=RX!k)k`FG@98b/$+*MW33 V6X66x"192-*TIX00xY46;#"+5326 j{mo>1gr,B0]MecU-:JxlX6M!!!;+53276=#"'&5#535}J88hu956PF]m;T_^s!!#;#"'&=!5!jG$2!V&'G^=R V6X66x ^M^#47#5!5!3632#'03254#a\'Ln& m,8!!^R^=jR332#"&'532654&+5!5!dCP>i;}C5~Dx~uhn\' xM|mTPJS]R^: .#"!326 #"&54UYXUcVXYV&l~~g~]% &$ #{&DqP& 2%X&2Ecq&%cX&Eq&%X&Eu1k' *Zu&d&u%f&vZ&hFRP& 2'{&GcR&'{c&GR&'{&G}uR''{u&GR&'{&GN&({X{&HN&({X{&HuNm& 1&(2{uXH&&H2XP& 26)'P& 2IfP0& 62*{H&JHP& 2+P& 2KcH&+c&KH5' )\+X'jHKuH'+7u'KH&+&K&,D&Lk' *u.k' *%uNc&.c&2N&.&2Ncs&2/c &Ocs0& 6=c 0& 6>s&2/ &Os&2/ &OVyk' *u0mof&vPVyP& 20mo&PVcy&0mco{&PFP& 21&QcF&1c{&QF&1{&QF&1{&Qur' *w|3VTf&SuP& 23VT&SP& 25j&Uc&5jc{&Uc0& 6Y=c&Z&5={&UJP& 26&VcJ&6c{&VcJP& 2&6c&&V/P& 27P& 2W/c&7c&W/&7&W/&7&Wd=&8d^&X=&8^&X=&8^&X9E' +\9dm&vY9c&9dcm`&Yr' ,|:m&CZr' *|:m&v@Z4'j$:&jZP& 2:&Zc&:c`&ZP& 2;L&[5' )\;L&j[%P& 2<hV&\t' -.|=m&f]c&2=cb&]&2=b&]&K'jW&tZhV&t \'P& 2AH"%c&$c#{&D%ct' -|c#m&f%& 1&$ ,#'<%cm& 1c#&rcN&({cX{&HN^' +*u({X7&vHcNt' -|{cXm&f"c&,cD&Luc\&2cH{&Ruc\t' -|cHm&fk' *ub f&vck' ,ub f&Cc^' +ub 7&vcc&b c{&cc=&8c^&X k' *vuq'f'vdr k' ,vuq'f'Cdr ^' +vuq'7'vdr c'vq'cq'dr%r' ,|<hVm&C\%c&<hV`'\%^' +u<hV7&v\Fr&oFr&Fr&|Fr&Fr&}Fr&F&~F&%r&o%r&pkr&|vkr&vr&}r&&~&p(r&o(r&(r&|(r&(r&}(r&~Nr&o~Nr&Nr&|Nr&Nr&}Nr&Vr&oVr&Vr&|Vr&Vr&}Vr&V&~V&LHr&o]LHr&]?Hr&|J?Hr&JHr&}|Hr&|cH&~DcH&D6r&o6r&r&|r&'r&}r&&~&~r&o~r&r&|r&r&}r&&~&Hr&oHr&Hr&|Hr&Hr&}Hr&\r&o~\r&\r&|\r&\r&}v\r&v3ir& o3ir& 3ir& |3ir& 3ir& }3ir& 3i& ~3i& r&?r&JDr&1&Fr&oFr&Fr&|Fr&Fr&}Fr&F&~F&r&oer&vr&|r&r&}r&&~&Ff&CFf(f&C(fVf&CVff&C6fHf&CHf3if& C3ifFf&CFfFVr&ʜFVr&ʜFVr&ʜFVr&ʜFVr&ʜFVr&ʜFV&ʜFV&ʜ%Vr&n%Vr&nkVr&nkVr&nVr&nVr&nV&nV&nVr&Vr&Vr&Vr&Vr'Vr'V&V&LVHr&nLVHr&n?VHr&n?VHr&nVHr&nVHr&ncVH&ncVH&nFVr&FVr&FVr&FVr&FVr&FVr&FV&FV&Vr&neVr&nVr&nVr&nVr&nVr&n V&!nV&"nFH&F&FVf&#ʜFVz&ʜFVf&ʜF7&pFV7&fʜ%m& 1%0& 6f&pf%V&nroVr#525#53d7vF&jpTVf&'V{&Vf&V7&pV7&uNf&vNf[Hf&DHfVH&nr'o'r'o8d&op/H&6&&67&p&qm& 10& 6f&fr'r'$d&p3iH& 3i& 3i& 3iVTr&oVTr&3i7& p3i& q%m& 1%0& 6[f&Dpf~ur&F&jr?FfCFVf&/FV`&FVf&F7&pFV7&\f&\ff&fJV&nfvr53#3"ïddm10!!d dmy/10!!/yy/10!!/yy/10!!/yy/10!!/y]&BB-@ 10#53Ěb~-@ 103#1řb/103#Śc/-#5b %@   1<20#53#53Ěb5Ǚb~~ '@   1<203#%3#řb5Ěb/ * @  1<203#%3#řb5Ěb/ #5!#5bb;/ '@  RQ R <<1<203!!#!5!nn\];/<@  R Q R <<2<<212<220%!#!5!!5!3!!!/nnnn\\?!   V 104632#"&?}|}||{|?q?P1 #@   1/<<2203#3#3#P3f111'3?Kt@%1= 1%+C@&7IF:4(:PFz4P@ PzP"P.zP@(/99991/<22299990'32654&#"4632#"&32654&#"4632#"&32654&#"4632#"&H%'H_EDbcCE_yxxwyLaEEacCEayyxxy aEF`bDEayyxxy7a`JGacECcaEyxyEaaECcaExxy"GaaGCcaExxy DP\h4632#"&62654&#"'4626763267632#"'&'#"'&'#"&732654&#"32654&#"32654&#"yxxyyaacCE%'E FedE  FeddeF  EdeF FceeO:8RR8:OxQ::PR8:QzQ::PR8:QyxxyaaECca`JyS  SS SxyT TT  T{GacECcaEGaaGCcaEGaaGCca`$3`u`'j`P','`$#3$V`u`'j`P&',Z/#@ v29190 5/-+#Ry#@ v<9190 5 +-#^R^  '4%#56763253767654'& Yb^`_hon"!^XE&->B% #D9``LAB\VBT=BR-;,,1Y7 Bw !#3#3!XEFZ !53#53#5Xޏ!' 5 5!' 54' 5= ,47632""327654'&'2#"'&5476"%F$W+,,+WX+,,+XLLLLJKKL @ !UUUUUUUUYnmnmmnmnH !3!53#3#z(洴ttPPD  5   @  W <291<29033##5!5 !wtt}oyc?}!!!>32#"&'532654&#"f6TTXYJz04?9= 25DIJLL...P\\PS****hQQ;JJKJ hh2112=!#!=HCD0;.="327654'&'&'&547632#"'&54767327654&#"hT-../RU-../P--KKKK--P]12PPPP210'(KL('NMK(')+*++*+NM*+/23Gc;::;cG3288Yq?@?@pZ88C#$$#CDH$$0.27654'&#"532765#"'&547632#"'&SP-..-PS+***(X/x==jDHIKLKKZ[-..44]\4421ab21hQP854&e_]]_eTSS}~A @ 32tNN^luu)qJy}wYYk\sa88=TdXC{dB}TtdFTud Cd?}CdITd=Cd;Rd0Td?d8d difdifdEd1d:ds|d1d ##"32.#"3267!!!!!!;JܾL:9II9^o78?*?77IG8GI`{c9'.473&'3267#"'#7&'#7&'&76?3&',;8+$"5:lUXn;4";τPqJ8=0;i<)^_HH?WgjιKp(_Y,%6767# !2.#"3>32.#"YQbUYoHqWUnrV,e#7!v'/_HGghGG_^ٜu]\YC!!!!3###5ZpP~WHE9El#!!53#535#535632.#"!!!?-쿿=OL=tyB_))HmBo)632#4&#"#5#4&#"#3>323 0?o5FP;]i9JI9!c?L3!Bjbws{Ep{``N#55YQKP%$((TT@I!*##` E326&##.+#!232654&/.54632.#"#"'&]``]z/YM$TP*N(:?>?>SZAm)naAt02k:WX?^)}j9>/b؍$~3YQKP%$((TT@I!* *-037#!3!73!733#3#####53'#53'33ٹpg1 2CYȿYD2FIn$uumuuwugu* %2#4&#!#)"33!3*ԕ|aԕ~V*$oN{&kz%%3p@< 1& (# #43('1)-&- 2'-4229999999999122<2032.#"!!!!3267#"#73&'&54767#70TJBN1Fi1OCHU,1u1!(*=Dl-.&nC>*( n -/ l*33!!###5<~rTws1s/!5!!77#'%5'+s-PPMMo؈onوn9-bw'67>32#"'&'"326767654'&'&67'>7632#"'.'&/#"'&54632326767654'&'&&#"32fbU!O3'A"+0.!. !  _ \5#?\k2,,#2!$(2( 4" )1>((E8&^ ,9Q F 9)ЗiRm:3Xwdg7? 2j7#=5(6$ 629T/ (2M !:5S}$@{mbq~Es/4 -& "TAB`]|@8nRkcd]aC".)5'632327&547632#527654'#"'&#"%654'&#"o|@X"07PYtaTk~j[IwmqJ2530D#24!`NkBX``S㫣†qJ`R{{{{{A667654&#"5>323#!!3267#"$547#536767!5? 7^\iV ^':,hski HE 4cq<;''K={[/ {9b{DI--N@{ O/{O!,&'&#2767#&'&576757O[TUeeUT[Y\Y[dsye]Y\[CvlCi----iH$"u9Bt"#BuflC1 %3267# !2."_|dT ȄE}=[~oi 7@,L]r4*:0N̾ (2.#"3267#"&54632%3#"326.2"&54:F#KVVK#F:-Q.~*Pʇ=II=323]W!{/tKb J G'QWab ^TH632#64&#"#'?3%Ǘŋ]W!{ 1$ÑEmJHWEbOYbcJ %# !3!# GHMZMd q+  #  "32!!3463"##526eb223b WU&WU&  1Q~>;\>}N*3>"32>54.'2#".5467>32654&#%!2+#hjMMKLijKLkZZ\[~}ڶ[\ZZ&RXXRuJjhKLLLijJgZZ[~}ڶ[[}~[ZZICBISqmopB 33!27&#%!2+!67654'&` `s1:+YX*q jdZ)VV) (%#'#  %27&"676'&\ӿ,F E]]]][{ab[ 2222jT%%5$c$% &.2&'&+3!.+!!2!27&#676'&%3A::f&AVy-`5?vfAd)7%LK$201/O~hbb)j)V>U)- fh@6    B     ` `_`_/91<<2<<90KSXY"###5!3###r}r7q^^-B0 %#!!!5!bJZCJ]d qddd J.%m -)7 7673 $54$32!"53!25&'&#"6Ky {U>ZLtࠢ""38M{{M7M3TT<`xGZAEIpP3RQ4Oe{'uV& /{e'uV& /tZ{'V& /{Z'V& /tZ'V& /u Z{'V& /j{'V& /{j{'V& /_{'V& /{_'V& /u_{'V& /_{'V& /Z{& /{B} 5!!B#ZpZR#ZZM '#'"ZZ$MZpZ#B} '7!5!'7ZpZ#ZZM !737@ZZ#ZpZB}!5!'7'mZ#ZZ#ZߠZ#R#ZZRZM%7#7'3'ZRZZ$R"ZݠZ#ZZ#Za 7!##:nt':tna #5'#5!tn'dtna )53753dtnntda 733!ntd:ntB}3!'7!5!7ѓc}Z#Z㔎RZ#R#ZRB}#5!7!'7'7!'/cZ#ZߤRZRZRYxa532767676767632&'&'&#"#"'&/#7!$f ! +!3-68+2",j!!!3 .6+85.0$m: w '07)(6;C+ : ,:'+:Yxa5!5!#5#"'&'&'.'&#"'6767632327676:m$0.58+6. 3!!!j,"2+86-3!+ ! f:d+':, : +C;6()70' wB}!!'#537i&ڠZZ#ZZZZ#R#ZZM'75'3''#ZZ$R"ZZ&ZZ#ZZB}'73'7'7#'7!5hZZ#ZZZZRZZM77#75'73ZZRZZ'ZZ#ZZ&B}'!5!7ZZ#ZZ1ZZ#R#ZZB}'7!'7'7!'4ZZ#ZZ1ZZRZZB} 53#5!5뤤4Z#ZhZ#R#ZM %'3'3!5Z$R"Zh̠Z#Z4B} !'7'7!#3̠Z#Z4ZRZM 7#7#5!ZRZ4Z#Z̤M%'7'3'73!5ZZ$R"ZZhZZ#ZZB#(276767654'&'&'4#!5d >b-*,%:0Z#Z  *+(54<852.&Z#R#ZB#)!'7'7!"'&'&'&547676763"mEZ#Z0:%,*-11> ZRZ&.258<45(+  B#$>2+#5!5!54767676"3276767654'&'&'&l>b-*,%:0ΠZ#Z2)-019 o #*+(54<852.&ՠZ#R#Z};47(+ }  A#%?!'7'7!#5#"'&'&'&54767676";54'&'&'&e910-)2Z#ZΤ0:%,*-11> o #+(74;}ZRZ&.258<45(+  } B}X3267676767632267676?'7'7#&"'&'&'&'&'&""'&'&'&#5! ! Z#Z  > >  Z#Z" *!#$' * ZRZ %  '%  %' " Z#R#ZB!'7#5!3'7'<2Z#Z<2Z#Z Z#R#Z ZRZq` %7'7]JQgz=Zӄh PJV}e 5!#Z"ZǠZ#R#Ze !#!'7'< Z$Z9kZRZe !3!5zZ"ZZ#R#Ze '7'7!354'&/#7!J%%%'HD_SlhX[HJ%%%%Jw422-A8;>112-!:zJZ[ghX\HC+%%'GKY[eg[WMs2=>FD{2,/2{DF>H':Xy6#5!#52767>54'&'7#"'&'&'&54767<:!-211>;8A-224wJ%%%%JH[XhlS_DH'&&&Iz:d'H>FD{2/,2{DF>=2sMW[ge[YKG'%%+CH\Xhg[[IB}5!B#Zp{#ZB!!BMZZ#M3'#|"ZMZM#'Z$MpZ#B}!5!'7pZ#ߤZB'7!5Z{ZM!37ZMZGM!#73{Z#ZpB|  '7!5!'7 5!!ZpZ##ZpZZZR#ZZ*M !737 3'#'2ZZR"ZZ#ZpZMZpZB| '7!5!'7%!!ZpZ#ZpZuRZZ#ZZ#B|'5!!!!5 #ZppZ>R#ZZ#R*M73'#'#'3hR"ZZ$RZppZ#B|'7!5!'7!5!'7ZppZ#>RZZR*M%#73737#hRZZR#ZppZBA! '7!=!Z#Zp{Z{#ZBA! !! !5!'7BMZMpZ#ߤZ#ZB}!73!!!'7#5!!qVa6ZEV`6NZ#Z">RRjը;mRR:lNZ#R#ZRRB!!373'7'7#'7#537!7'!RRȚNZ#ZN|NZ#ZN.9#!RRRRNZRZN ~NZ#R#ZN RRB}!'7#5!7!5!73'7'%!7'!`]Va6.ZxV`6NZ#ZRR;mRR:lNZRZRRB}!!5!RRpNZ#ZNRRRNZ#R#ZNRM#'3'#'RNZ$R"ZNRSpNZ#ZNpRB}!5!'7'7!5!7NZ#ZNpRRNZRZNRRM%37#73RNZRZNRRpNZ#ZNRB}!!7/7'7!5mRRRNZ#ZNNZ#ZNRRRNZRZNNZ#R#ZNM'77#7'3SRRSQNZRZNNZ$R"ZpRRmRRANZ#ZNNZ#Z6a##7!#tn::n3:t:5p::6a '#5!#5'5C:3n::n:4:dp:nt6%753!5373:4:dp:ntn:nd:4:6%3!'3n:nd:4:n::p5:tB}5!!!!!Z#Zwgw"?Z#R#ZRwRwRB}!5!7!5!'!5!70"wgwZ#?RwRwRZRB}37773'''#5:;!\[`Z#ZCCjjZ#R#ZB}'7'7#'''53777Z#Z`[\!;:ZRZjjCCM%#5#535#535'3'3#3Z$R"ZtZ#ZtM533#3#7#75#535#5ZRZtZ#ZtB} !553353!Z#Z{Z#R#ZM '3'#7#7Z$R"ZnZ#Z}ʻB} !'7'7!+53#53Z#Z}ʻZRZM 7#77'3'3ZRZZ#Z}6B} !!#3#Z4ZݤZ#ZZ#B} 3#'7!5!'7뤤Z4̠ZZ#h#ZZ 5!5! !!? Ou]%uuv 333'#!#\^vtP uB !!75!!5 t]]Xv ###3!3,^\X& v 3'335%!!# #^\XtvpFguv %3'3#!5%# #3!^\^$tv~Fuv #3#!5#3/# #3!J\^^|HGetvJ~{GGMuv 3#!!5#3# #3!F\ F ^tvW~uv 3'333'37# ##!#^\fd^tv ^u9v #!5#3'%3'37#7# ##3!3^^ fd^tvJ^uB '#35!7'!!!5 5~t]]EF 7!##!#*:ntaI':tnIFEF %!53753!5!ldtn~ntd&Iv #7#3'# #3 3\^^tvP*OutuB}'0#"'&'#53676323'7'7%&'&#"!32764RvxN1kk2Ow9g' Z#Z 0GD2 & +JD5@3PO2BB4R,( : ZRZ11/0*M !#737'#'RZZ"ZZ$#ZpZ*ZpZ#Ba7!5!'7!5!'7'7!5!ppZ#Zp?ZRRRZB}#5!5!53!Z#Z[qZ#R#ZB}!5!53!'7'7!#p\Z#ZߤZRZB}#53533'7'7##Z#ZZ#ZߠZ#R#ZZRZB}#5##5#53533533ҤtZ#ZtZ#R#ZB}#53533533'7'7##5##tZ#ZtߤZRZB}53533533'7'7##5##5Z#Z8Z#Z8ߠZ#R#ZZRZ !! ?OuuuB 7% !5uzR##7 ! ?S:uuzRuu##% %!3!3hV[7l n7R{+u\ #&'&#"327673 u B!OO!B ocI7͙7IcL 0"'&547632654'&#"563 3276767&#" \m`cu\6% GGnth r5?,/H@3H5,Y:$UeI+HQ\N,tqzSd69->eSY׮l 7!!5!!5!!LLk+5!#7#53!5!!5!733!ZD2/+^^``kIb!0?"'&''7&'&54767>2"&'2767>54'&&cv-'''OO_@8vcu-'''OO_A:GE:;9($(#&GFF:;9cv8@_pm__ONP(-vcu:A_mp__OOP(-9;SPF($(9;PSF'O@*iiiiB91/90KSXY"#3 !q!#7!hqqP3!!"&63!!"!0",Z(膆(\JN*"f_QQĪKM_fOPi%+%3!!"''7&'&6;73#!!#"!#L(0,:CyEB航6'|>v\JK-".4"$: 1cQı2#KK_ff_lFO]B/ 3 3ް2ް2201!3!!".>3!!"N=c(憆(c=֪I9[[9IP&'.#!5!2#!5!276767!5 ,Z(؈膆(\JL, 1f_rĪKM_fOPi%+&#!5!27+'7#53!5!3276767!73&'&'(/-9CyDD舫6'{rx\JJ. 4 %:  1crı2ݪyKK_ff_lFO]5!&'&#!5!2#!5!2767>b(؈憆(؆b>,I9[[9IL9@ 120!#!L^L= 7   @  <91990!!5 5!!LR%# Չ\P_X-y10!!X!תXy!5!!5!3!!y!DCmILfB7+U e+Gr?; /@     99190'%3##d)#Ӕ/}b%9;v'ue;e'e %.#"326"&'#"&54632>3"38\32#"&'#"&546329[=G[TFBi8\=G[SDCj~/[w~SNAU}^sdlkutcjmvu۠d|k֥s}T!3!T*,}T!3!T*p,33# NM^T,3 3#T^,$476767632#4'&'&'&#"#;9_UijB9 KGLV32326yKOZq Mg3OINS5dK t]F;73 ";@<7  6<Xy32767>32.#"#"&'XJF]t Kd5SNIO3gM qZOK?<6  7<@;" 37;XyG&'&#"5>323267#"''43OINS61-NSXIFJKOQdSP  ;@<7 W"323326X!!KOZq!Sc1NJOR`!t]D;83$777=X`y!!#"'&'.#"5>32326X!!KOZq Mg3OINS5dK t]F c;73 ";@<7  6<Xbz'767#"'!!'7#5!7&'&567676ǧfYUE5kIQ%\n*xrYQMoIF\<[ETFR q$"B2(d%(9L5XXy$!!!!#"'&'.#"5>32326X!!!KOZq Mg3OINS52'V t]Fجϯ;73 ";@<7 " 6<X1y0%#5!7!5!73!!!'#"'&'.#"5>32326Qu{hq,gqTKOZq Mg3OINS52'V t]FR=R ;73 ";@<7 " 6<Xy.1%!5!7!5!7&'.#"5>3273267#"'!!!!'hMEnK Mg3OINS523J:VQ FJKO!8!E$F";@<7 832326#"'&'.#"5>323326yKOZq Mg3OINS5dK t]FJKOZq Sc1NJOR` t]Dï;73 ";@<7  6<а;83 $77 7=X0y8&#"5>327&'&#"5>323267#"'3267#"/'00NJOR:G67'43OINS520N]a91FJKO?J4r[DKKOdgb 7 ;@<7 !7)32326#"'&'.#"5>323326!!yKOZq Mg3OINS5dK t]FJKOZq Sc1NJOR` t]D*!;73 ";@<7  6<а;83 $77 7=Xy7S#"'&'.#"5>323326#"'&'.#"5>32326#"'&'.#"5>323326yKOZq Sc1NJOR` t]DKKOZq Mg3OINS5dK t]FJKOZq Sc1NJOR` t]D;83 $77 7=;73 ";@<7  6<а;83 $77 7=Xy$!5!53276767632.#"#"&'y!JF]t V'25SNIO3gM qZOKج#?<6 " 7<@;" 37;Wy' %52% $'"51pZV(IٜXDz;%76767!##"'&'&'#5!!5367676323!&'&'&i1*+V WJRNMR  W,!::!,\HSLPM% +*%'H:^2:A<336G84^:H'@'H?Y L=@33/N0<^:H'%X`z!!5367676323!&'&'&!!i:!,\HSLPM% +*!#'H?Y L=@33/N0<^:H'%X`y& $Xy& '$'$$Xy'$& $oWz'$& $nJ. 3#3#!5!5J=>𹬬J. ##!!!!>7BX`y 365&'!!5!&547!5!!%43448>!0IG00GG2?8>;_8X`y !!!!"264&'2#"&546X!!IdddeH:l'**z{ BbFE``bq+((d:svvX`yK!!!!2&'56X!! BS X`yD!!!!73# X!!鏫 BZVX`yD!!!!33#X!!֕ BLVX`y!!!!!!'X!!߰ TU UT BX`y !!!!!3!X!!-e Bz(iE`07GO!!!!#"3###535463!3267#"&54632.#"'53#5#"&4632264&"X!!4@#mmC???DJB&G$$K&aqk[Q_B;18BCC?-I\\I-?p`ctiF6A?9i=$#tu#gSSSX`y*!!!!>32#4&#"#4&#"#3>32X!!."]?T\Y88EQY7:DQYYU;;R B=:xoHOM]QHPL^P%U20=X`y ,!!!!3#7#546?>54&#"5>32X!!ffc`--A6(Y1/a3\p$-, BiN2A+,/-7#!^aO&E++ X%y<@     <291<2<<990!3!!!'7#5!7!X}y}J;fժhӬXyB !!!!!!X!!!جX y%#5!7!5!7!5!73!!!!!'G=XkXU7Y Z:wSAw@Xy 7!!!!!!!!X!!!!߬Xy? (@( ' <2291/905!5y!!LK Xy? (@ (' <<291/90-5!!X#!!VVTw 3!!5!5V!!!!߬¶LK VTw 3!!-5!5V!!!!߬VVw#5!7!5!73!!!'5 p[5m{*[y~!߬`u,`vLKVw#5!7!5!73!!!'-5 p[5m{*[y!!߬`u,`vWy&%5767$'5674[šzآb|۠M)Ig#M(Jh#Xy %5%%%'w2rK/dtm0x0oVXy '75%%5%'rKnd.t'o0xEu0#oVX y!5!%5%%%!!'XC_^?sMN#N+PJ>`5Yd|5X y!!'7#5375%7%57'NEO>:fLNtt5\h}a5H<Vw?#%#"'&'.#"5>323265wKOZq Mg3OINS52'V t]FJ!;73 ";@<7 " 6<LKVw?!(%#"'&'&'&#"5676323276-5wKHGOZq M343OFGINIIS52'V t]FDE)!!;3 " @< " 6V w+.%"5>327%5%%%3267#"'&'&''}QINSE^AsMP#Bt]FJKOZq _4O;@<7փ_5Xc|6V w27'732767#"'&'&''5676?5%7%53;L t]FDEJGLGOZq P32326&%&%5$7$7wKOZq Mg3OINS5dK t]FJl#a;73 ";@<7  6<RO]ɗ9=}Vw*%#"'&'.#"5>3232655%$wKOZq Mg3OINS5dK t]F)a#l;73 ";@<7  6<R˖}=9"]OV[w67&%'&'5$774hmU֠Gc _eT2wnw2"O0Bj%V[w'567&'567&hmU*c _eT2Vwnw 2O0BDj%Xy_%!"'&54763!!"3!yɊD_`Dƍ^`Xy_75!27654&#!5!2#XD`_DȊɣ`^ȋXy> #"&'&5476;7!!!!"#'J_+30TD~K9# K^+#Eƍ5p5Xy> 32654'&'7+'7!5!!5!237RJ_+30TD~K9FC K9^+#Eƍ55Xy%!5%!"'&54763!!"3!y!ɊD_`Dƍ^`Xy%!=!27654&#!5!2#yD`_DȊɪ`^ȋX,y&%!!'7#5!7&'&5476;73!!!#"$UrG6:qYȲG5^_=R5 Yƍ5p&`=X,y!++!!'7#5!7!5!&#!5!27327654'&'92D4VqF53 D&#I`__ 2ȋ559`^`X0y!%!'7!5!7#"'&54763!!"3!!yR|ɊD_aDAQjfƍ^`5eX0y"%!'7!5!7#!5!27654&#!5!2yR|Da_DȊ]zTQjf`^nj^DeXwy1/3ް2/301!!!!X!w@Xwy1/3ް2/301!5!!5ywXy H/3 ް 2 ް2/33 3017!!!!!!X!!w߸Xy J/3ް2 ް 2 / 301%!5!5!!5y!w54&'&'3!!#!5!ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:FތPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9Oi372"&'&'&547676"2767>54&'&'!5ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:FMPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9ՌOi3?2"&'&'&547676"2767>54&'&'77''7ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:FBccccPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9ccccOi372"&'&'&547676"2767>54&'&''ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:F,ccPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9KccOi73#2"&'&'&547676"2767>54&'&'ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:FPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9Oi2L2#"&546"326542"&'&'&547676"2767>54&'&'h7b%&'qqnNL88OݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:F)'%`8nqqMpLM77POO__pm__ONPPNO__mp__OO=9;SP;99;PS;9Oi!'/7=E2"&'&'&547676%&'&'& 654'67676-ݾOO''''OOݾOO''''OOf:F-T1-F::E.S1.E:POO__pm__ONPPNO__mp__OOAϚ9FPQ9.9떖EQPD19Oi!;!!!!2"&'&'&547676"2767>54&'&'+{{ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:F;gZfPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9Oi372"&'&'&547676"2767>54&'&'!5!ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:F2mPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9IPi%!!!3!!#!5!3Ҍ8Ȍ7nj6Pi %!!!!53rM_Pi%!!!7   '3ͬc  ccc #c ccc Pi 3#!!! 3/`103#`7 !!'  TS TS8X`y!532767>32.#"#"&'yJF]t Kd5SNIO3gM qZOK ?<6  7<@;" 37;XAy 755%5!5X!#!!ʶLK XAy % 5 -5!!y#!!!!KL VVw?  55!5!w!!!KLVXy? 55%5!X!#!Vw $75$&%&%5$7$7"nWlܜ86s˖}=9]OVw $'$'5%$5)n˱#lݷW680O]"ɗ9=}Vw)%*67&'&%&''&'57&%5$?7dMjTVʥ3˱!3a4m"cjX)3S][e﹏3N@%HZ-=}k$Vw)$(6%'56?56%7$'57&%D>WwZN(۷+/m")33 +Si063hiyje˖X[y3!!!'7#! !PYBzrYh?ݪ@?@X[y3!'7#5!!5!!PYzrY(s??ݪ@X>y!!!!!!'7!5!7!X!w R`RgfjfX>y%!'7!5!7!5!!5!!yRgw! RjfhDfVw?%%&'&#"5>327%5 %3267#"''43OINS:Z0!!x2XIFJKOQd>3  ;@<7 ҧK{"327V!!?E>XIFJKOQd>C43OINS:Z0"323267#"''&%&%5$7$743OINS61-NSXIFJKOQdSl#a  ;@<7 W"323267#"''55%$43OINS61-NSXIFJKOQdSa#l  ;@<7 W" # #h֣ͣG9> 3 3h*338> !!# #g֣ͣrcG9> !!!!# #gg֣ͣrrcG9w!##mZ##5w33ϸ"mZ!533X%C!!3#CrrCr[  C!!3# CrrCr[ %~!!3#CrrCr  ~!!3# CrrCr Xsy^!#y^ap$%%$~  %6 %!&'&"112*zz`XXroGGnY  67" ,J5PP5JX*77*#L8P"2642#"''7&546Ċnji56؝]QBɉLJo3NEQ\|G+-7AJT35#"&546;5#"&46235462+32"&=54&#"3#"2653264&"2654&#ςYxxYςZxxZE1/EE0uu0EE`Ev/EDaEEaDE/wZ\ZЂZwwZЂZ\Zwu0EE`E`E/1EE0E`EE0u0EE1/EXsy^!3!yߨys+~!#!r ~5!#r rS;+;!!3vrr;)3!rv;SL4732#"'&'.#"0 Pd@7* h$TA6?&H|( #"&546323250 Pd@7* h(V$DTA6?&Hk- hi !!!#%!!h\roa`޾"(I  !! #37!#3'Q''Ho99Ƀo!p=⻻}(TI #!!7!#3'l)okkɃ=r!r⻻+2" #/;GS_kw+7CO[gs!2#!"543!254#!"+"=4;2+"=4;27+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2%+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2'+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2'+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;22+"=4"=43+"=4;2+"=4;2"=43!2#\\K\=Kl]\\\\]ii\][]\\\\\\\\]\\]\\\\\\x::f>]Y"\I\\\\I`LLMK\y>>(I !! 3#)%3!'-''96=ûHJ7 hHH--JJ4!!!rrZm4 !%!!5!5!5!!mr4 3#3#!%!!5!!omrX4  ! !! !ZS;Z$m4& :4 '3276'&#"!#"'&'!!67632!zzzzzzzzIwwHS;HwwI΍GG2GG$_EXXE^m_DXXE_3#%673#&'%676'&1rsrs2srsrTTTT@TTT|B B@B)B)΍11@4121tD& : y4 !%!!Ybm5A4 !!!!;rbmY5A4 !%!5!mrXae4 !%! !2mrx1x4 !%!!5!!ZZmrϠZnZ4 !!'7!!!'7;rZZmZ1X1Z'327#"'$%'3632# 6'&#"zz=>lWWsPYX2灾vzz>GjX`OXG%3 33!#!!3/ .^+^kk4 7!!#xr;rc/Krmu4 % !%!50!53!ymrZ[z4 !!%!!''!;GZZHrZEZw%3 !#!3!###.77/^+kk4! !! 3!xS;ZK{mu4 ! !%!#5!5!"~~+mrz[Z4 !7!)!7ZbZmEZwZ4& 9 4& 84?& 8 u4& 9 :4D& 8 y4& Z^' %4&' ;%?H' ;8 :t' ; yXyH' ;8aXyH' ;8!% *!%#567!676&'&'&'&|z*(2J E<iKH#&GIJE E|-2 M3 +O  ! r; ?9y?p  ! Xy*= 67 '&5677%"632327'&32767#"'&'.#"y{h p{"zi piE5 5dJ t]BFZCEF4 Zq Mg3ħĨ1P$s 7%ےs3 !Xy3#"'#&'&#"5>323326yKOGU43OINS52^NFz;7 (  ;?<6 '='3#3!!#7#537 `?+0'7ϺrSSr4!!!!3!!!'7#5!7!xrddeZmn;fժhӬ4&"6`VT|F`4z& 845{& 7+4`& 94`& 8Fz. ! //1 /<20#5!!!#oo.ڭ%ۭ&H:07 %#"326=7#5#"&546;54&#"5>32hHH#[]E>Vcii!fHatLT5m4:j2O85%--JJ??8?vh+]85l[hmKDg/R: 935#35#35#!5!#"326=7#5#"&546;54&#"5>32assssss;#[]E>Vcii!fHatLT5m4:j2Op"r??8?vh+]85l[hmKDg..R:1=[%5!!5!#"326=7#5#"&546;54&#"5>32#"326757#5#"&546;5.#"5>320;0#[]E>Vcii!fHatLT5m4:j2Op""[]E>Vbii"eIasLS5m4:j2Op"rrrr??8?vh+]85l[hmKDg..R}??8?vh+]85l[hmKDg..Rk*.26:>B#5#"&546;54&#"5>32#"326=%=!!%-!!%qi!fHatLT5m4:j2O85%#[]E>Vc,,,,,]85l[hmKDg/R}??8?vhyyrDyyyYrJyR8~4<DLTZ`%#5&'&'&''7&5475'7676767537'5676767'7&'&'&'5'7'%654d3/D9229D/3d4.E9229E.42*  *22*  *2*88fWfEOPDfWg88fWeDPOEeWf8g * apa *  * aa * ba.43ab-45F^'04>2".33&'."#67>76#FVʓVVʓ"vv"Z83Pv"Υ"vP3ʓVVʓVVnh<8PvDDvP8"vP9~?9Pv"F^&0!4>2".7!&'."67>4'&hVʓVVʓDvP479;;74PvD"MʓVVʓVVv"i c;DD;g"vP F^ %7'32>4.#52".VFoDvvDDvYoʓVVʓVSwVF_YvDDvvDoVʓVV4!!!xrZm #53àZ03#s #5ˠАWeE%3 53Zz i#0s  3#àР˓[m#!!# q3#s!!`N um!5!#z3#zz3!5!`z m #4763!!"ƺoyeD9uߑfW#'&%'53 763:*enK==Mne( =C _AEc H<  3!!"'&59De{oVfd #3ƺ m 4'&#!5!2 9Deyo}Wf &'&3!3#76<(enM==Kne*!<McEA_I= 3#!5!2765 o{eD9ᏞfV(3(v% !!!!55!#uX ̼uu]]e! !!;bc;$<.: 1/<03!3T.%y5!!X3 2!@ 2 5!!5!!5!4)4𬬬 !!!!!4)4XXX 333 Nf  !!!@@@ Nf  53353353353𬬬 3333333XXXX 333322s's' !!!!@@@@22s's'!!!!\!!#!!#\!5!Z!!X!5!$Z!!$X3!-Ԭ3!-.*!!@Ԭ!!@.*5!3,,(!3,X5!!@,(!!@X3!!- 2Ԭ3!!- 2* #!!!P@ZԬ 33!!P-#,Ԭ!!!@# 2Ԭ #!!!P@.* 33!!P-#\*!!!@# 2*!5!3,Z,!!3,X !5!!#@PZ,( !5!33$,PZ,!5!!$@Z, !!!#@PX !!33$,PX*!!!$@X!5!!Z !!!!-XV !5!5!!,ZV!!!X!5!!$#Z !!!!$#XV !5!5!!$#ZV!!!$#X5!3!,-,Ԭ !3!!,-XԬV 5!3!!5,-3,*V!3!,-X*5!!!@,Ԭ !!!!@#XԬV 5!!!!5@,*V!!!@X* #!5!3!,-Z,Ԭ !!3!!,-XԬ !5!3!!,-Z,* !!3!!,-X* !5!!!!@Z,Ԭ !5!3!!$,-#Z,Ԭ !5!!!!$@#Z,Ԭ !!!!!#@#PXԬV #5!5!!!!P$@V,* !!33!!$,P#X*V !5!533!!$P-#ZV* !!!!!@X* !!3!!$,-#X* !!!!!$@#XԬ !5!!!!$@#Z,* !!!!!$@#X*5!35!,-𬬬!!!-,XX33*!!@@*DH5!5!xX333x 2 2H !!!!-Rx !!##xmsZxH !!3!!xm3-sZRH !5!5!5!,NX 5!###lZZXH !5!!!5!4l t,ND 3!!!--Dx 333!x,ԬxD 3!3!,(D 5!5!5!3,,D|X 5!333,,(DX 5!35!3̠| 3!!!!-- 2Rx 333!!xs 2 2Ԭx 3!33!!-s, 2ZR !5!5!5!3,,X !5!333xtZ, 2X 5!3!5!33t, 2H !5!!5!4R 5!!###sZZH 5!!5!3!!t,-sZRD 5!5!3!,-DX 5!333!,,ԬD 5!5!333!DX,!5!5!5!3!!!!,,--R5!333!!###s,,ԬZZ !!!!5!5!333!-s t,ZR, 4763!!"Q[yY[`~| 4'&#!5!2.-Yx[Q`~=?x 5!2653#xY[Q[~|2Ψx !"'&533![Q[Yyx2|~>3m 2>#3> 2> # # 3 3>ݲ}#$cc|5!F3F~|5!|iF3P|!XF!@F~|!|iXF!@P5!5!!5iVV333PP~P!!!iXVV#!#P@P~P( 2! ! !!!!#!#(!(F(!Z((!((!(h(!|((!(*(!>((3(i( :} F( #'+/3!33!33!33!33!33!3䟟䟟䟟mnmnm( '/7?GOW_gow3##3#3#3#;##;##;##;##;##;##;##;##;##;##;##;##>>>(!%)-13#3#3!3!##!#3#3#3#3#3#3#ޟޟ#|ŸŸ|Ÿm#( /#E( =Zh!|i D}h( D(& D& E F(& E F(& D& F K(& E& F Ki( D}(& D K(& D& E Kw!N<w7!!!xr$<w 3!254#!") ) xrVVVw& X Ow !%!5!5!5!5!5!5!5!5!5!N?:IILII޸[["[[w !!!!!!IIN< w !%!!5!!!I) N"-?33 #&'&+"'&#"/573;2?"#'57#&'#"#567635a)8)kOkaKA-'= //G),Y=  !H$ /+HDH)+) $., fYYx !=Z Lx73&'37&'67&'67&'67'32654'&'7654&#"3672 $54767&'&47'&27632#"/#"?#"54?'&5432'&327632#"/#"?#"54?'&5432'&327632#"/#"?#"54?'&5430'&327632#"/#"?#"54?'&5432&5432&56327&5432'&327632#"/#"?#"54?'&5432'&327632#"/#"?#"54?'&5432PO~ )*+')+(@&'$||e?/A}]\B-71SLoWj\vLLr%%,* #$ )*n$ % +) $ #*+    ? '+&()&(+&p   % % +) $ $*+*EC*Z*,)-)-*,%&%&fБfU 5HhfeefhH2pu^QFs棥sKQG4 4  22044 22 9       L%('*%)(*%)(*t     144 22 0r!2CTev+&'&54?6?6/&2#"/547672#"/547672#"'=47672#"/54762#"/54762#"'=4762#"/547672#"'=47672#"/54762#"/547672#"/547672#"/547672#"/5476l=.%G\&#- Lj.N 0d&K4i    }    g    s            &                            H5-V"*2-.T<:U'EGE'DN-ֈU]\`CDcbF]WWZA@ZZ@AZZA@[[@AZKPrqqrPGeޝdMP䠠P }2ٛk A4&#"26%4&#"326#"547&'&4632 $54'&'&4632XP79NNqO.N97OO79N']EacDC_\n\U>DbcEXFDbbDEaaEEaaEDaa+G詄UUSj멏i LVV6 "32654&7#"32?ɏǾ/`TcȐɎ;P12Y.1"264&"3264#"54327&5432#"'&'@KjjjiOiiLKirqrtPssrqQܩZTdIU )5AMYdp{3/&76'!'47653!476=332654&#"#"&54632'#"&54632#"&54632&'&676&'&676'.7>'.76$6&'&%6&'&6>'.>'.f<;.=+,>/;Kyz~LZ|WX{{XX{IE11EE11ET    m       ;   R       s@dd@s}>}=/NnN/=}>@MllMNkk& % I% % "!$# "! "!! & % % & %-5AMYdp|5#!4'&'5#2#"&546"264&"264"2647>'.7>'.%676&'&>&'&7>'.%7>'.676&'&676&'&753!476=3''676%27/&76'77&'&/#?6'&7liilYz{XW|{bEEbEd      8    @     .HxttxH%?%5E$6  6$D5%?%-5!!1(~(1 5,4t4(4N4(4t4;hhh%%#%% $ %_ $ $!"!$!/!!!" $ $ $ %:-,GtG,-: XLRqqRLX ![$n[ii[n$[!ob !!'!tKZGkcn "!!'!##&+572367676hNn_5, S Grj3#-EmDJ~o.(*!4\tR~UL !!'!  ##' CI3Z  > << 5DCX << ; YD36273 ##'5&< +Z@\\DC ZY\5#,5>~3+&=4%3+&=4%3+&=43+&=4%3+&=43+&=43+&=4%33 #&'&+"'&#"/573;2?"#'57#&'#"#567635@)A({@(@){A)A(@A(^)4 'iOj_J@,&< //F(0'&&'ܐ'J&(lN5  >! )&V?<?$&$ '& ZN N />Eqw!674#!!6?676'4#'323276767654#3#&'&'&6%67!672!&=75$/563&43!32+'!67#>54&53 *,  3)="(&)09$) L&TE` MPA[MH Y $ ;&&e=O%/ N ,8(.7L1Rf~H8SQ,zH%9D6 )jGP@4Rjd_*KfsDIR 9! O  -]&C+/3#"'43727&'#"$472776725676&5&U8)$ tJ .; d3f,"3' VD ( GL/7;;,g t^F$< LD&?>X4R !/# I ? P?D!)Mv>/z2!"&54676737#&'&54>;7#"&546767!7!"&54>3!6763!2h!.)g$'30!/&j ! /:(/  )/ 9)/  9)0:*/z2463!2!2#!!+32#3#i9/ ! j&/!03'$g).!*:0)9  /)9 /)  /(:!!C4&#!"!&3!!"3!#";#"3&'6737#&'6737!"'67!7!"'63!67!2e;'+pCCo CCCC2CCKK<LLKK%JJ60"2=2).=<==<@=:>=;TT USUT UT83$QE!D72654'6#"'4#"'54#"'54#"'675674767#%$4:JILLHOKHLKIhghgighgD>-sJ1 b6'SS cRR SS?SS\\K\\;\\]]!A*>K!!D!254+'3254+'!254#!'!254!&#!"0!463!!2#!!#!3#3lCC2CCCC oCCp+'q=2"06JJ%KKLL:=@<==<=.)g$38TU TUSU TTE!C32=732=7325732'654&#'%2&'&5&'5&'IKLHKOHLLIJ:4$N->DghgighghSS=SS SSb SS'6a!0J)K>*B \\]]:]]J]] O!%)-1523656;2#'7+"/#"'+"5&54775'"'5476;25'7&567635&56;374765'75'76=4'&+ '"'4!#"'&36365&5&#%#754'&5&&547'5367&7+&'&'735&2?"5775537'7'3533553535'32767&5%2?&#%55'575775775uo,Mz"060D/5I:2'5:6&" *:D:S46$.e QN5  u4MDa 6bUP+ ,H;`I23N5( (#I0M '^5%#!:X+ "*  6W}W:uW4 5vT & /H3V XD9\SL+&31.d+%X!Q $2``KPPPG[6%# Qy- 6[[3GK[O`_A[-)$t7 L-$ L6=" (CJ#R"0 :~GB{~Eoj<4S[Za LC5 ) .U%+Z&)͢ 7e<ILAaMoK33K@G6 $$(& (''&1/----2)( (-((d.'-T?OK8T$ !T3(-<((')))())( &2%2#"'&=477654'#"'5473t\*e O@UCXq P S. P ӍMOb>YaYƮ58l7P P@ $0<FX + &=%6&#"3 6=%&#"';27!54767%!&'&'2+"'&=476^7\Pg㑵Hr'.)%sM M#fC-7!%A.; ӎw:kKqz +H*G;M tu/&((AA&:+C;."/ 8Pi>'67&&&'6.7#"'&'#"'676'773.#'6'5676&&5476'&'67&&07 ^< 1x,B5@2 JVMv!#uA+UBDX[f*;-10)..C,sB#HKU P]12<0VQ }%'H6-T}^$k7 R2'7f!A\;y?1!50BEt"!zkQ;0qu0\oi:5oPZjsXFaPJGl;4ejN^1F[q7&&'7'6&'$#&7'&#"'5&767#&''5$'67'6'6'5$'67'656&'67&'6'&'''5$7676'&&'6'63&7"7&'7&'7&'7&'6'6%676767&77&77&''5&"'6%35&'.54>23#67#&8 p +WDTc'H @XO`= ;*)8 kDv/Pk-J KDhGa D`gBD6DDD =3dTDW, :g j)Yi#'WtI-9w18$^8;./7-I)jS)'#i\-IM91D;8%a7/.D=uRNBR&'%QBNRq d2 D s98C ["|44&3, '2^3R T(B?#'9C- !y ~#Z10>N?$%Y4 )%FN? ({ usis< 3(&^T05<>7;,#4[:O(vAfGEtYB z^~4j #,;b:['~Av@~EQ Bak4~_H#T2 $$$$ 2T"`q$&'6&'67327&#!65#&3jjdnh wWVݱqZre[c7 7 cyX ,35'533#3!'#'5!5!5#53!5!5#!!ʶ~~ blvF F A<<3ffX苜qXGccGap 3264&#!2+73 #'#5# 3m`hh`2`Ĉѳh|;vvʷ}f33#!!#'!'57!5#'5735 64pzp7d+!#!573#'5!3!'573!#'73!#'5IxOOTxSVVdY\yvVPPvIyY',32#' 37+ &5%6323'#57'53mJl{~m@+ݼh4144'0>,_ vNknmmnObs32732753"'#"'432364'5;+"'#"'53275'&'&54?5572'#&'&547634%476='4&#68$$B )Z>&A_;i88u-o1bFGfQ_M5mwLbkjI,K=''8 0##Rm4 ڹ+ܴ5!PP"4\=ѻ"8Qý32#"&546324&"26%#"5432itvxsq1"00" 0/B//B/#a`ir|H!//!"00""00"!/0 _b 9>DJPV\bhn27654'&#"&7367'67675673#''5&'&'7&'%67'7&'67'%7&'&'%6767%&'&$h%$%%34$&1++XSA N@`==k>P CRX++XYC P>k==l?L ?Q oL+ Nn;P?;@  nMNn3%%%%34%&&%s==`?J >PW,,WW? K?_==f?H?PW,,WU?H?^<=Ke+cL mCP`k<<!4(0847632#"'&7327654#"&#%#&7&'67&'67!󫪪vӤ=6 5N'V[S.U[R󫬬񫪪񿉊 ʯX[V[X[V[!4(0847632#"'&7327654#"73$3&'67&'67!󫪪vѦ=63QNV[S.U[R󫬬񫪪񿉊w  'X[V[X[V[!4!)47632#"'&%#$''&'6%&'6!󫪪4>;D@KDzcngk?dnhk󫬬񫪪I kpinipi !4 "*2:AIX3#''%#&'52#"'&5476!!'5%!!'53'5%3'5%3#'32765'&#"M==,/0#H 8&O6 |7iY06./==e6a&i1r4z012+KN2HQ>>>>f^2"/1]8`1"Y 4f2y5+ +"'5$76%&'547327676=&#; hz0/O{[(*TQ~`NO =tR[\ 8d<+% &56;2'5$%75#"3vh0.P~N^(8P,VRZycOpO >S\^ f`1B7#5#53'&'&54767&'&=33676=3#327654'&O&"}|fzg}}"&&"}UQn$mQU}"$nQUVV{xVVUQ<"{u^^\ _u{"#| zUOOUz |#YOT{zQPPQz{TO@>)4'&#"3276&5476327#'#53'&`____`oŠqk]^^]YYňÁhgf@> '"3276'&'7#5373'#"'&5476j___``_ߓqŊqYX]]XYfhhĈÁj0 '&'&376&+"'&5'476%7!Z{z[ZZ[~\YWmpN#ZX[[YZ[PQmp#TG*52764'&#"#463233#!5sPQPPtrQPyzg֏LQQQPPQr{{t|g*#"#53533#632#47654&#"#ddiqqCBigIIugzyUr}ppDtPQs_CS 7"27654'&7#"&54767##53#533333#3##h. @\ ! 2(>>?ZW~>'3|}}! -/@ /- !^'?XY??~YX?(F}R}hh}}hLS<#5#535&'&'5'73'3#'73'676=35'73'13|e{vw}wwUATwx|xxS@Wwx}vv|d|re{Eus~~suE|VAKtrrt@X{Ius~~suI{dr|*! #!!!'!27674'&#_82V)3{D#MHZW{s{?zK8! %#"#&5463 67!2#6#";z\)MaBuh __ itBaM(]y tt[+##+tt\5."264&'67>3"#"&54767&'&#52hq៝rd:BJ|^d#!p⡠q $c]7A;{26XY "zz" YX62 &'5 %$ 56?6'.j拈|*xIIz'&|JJx, F42$8"3264,'5'&54632264&" &$#"&547>ȜmmNMm} lyzU<Mnnnm+}7 lyzU<|||,&(uO#eaHG||||Q'(sO#e‹`IH=! <>'.463227#"&5454&#"&'&5476766&D9BB8Ğv?W:pbW~tp) "-ff)-gtpQ@3AA:ACj›GmN?ijbvr56WGe((Wi0154d)-?/6?2>32>32#&'567'6'#4&&#4'3>64&"-S5,9"\0+Fgv!4u|W")^,k ikdS!eb[_[H|NYC:RHB=G`SnU|#!!!53&54632!!5#67654&"U't00Z =yy= :]ZssZ JjkkjJ 2f4%353'5#"'&''#&&#4'3>32>32YE;<<-!&Y*dx cf_Oz.*O2)7Ze``b<`WuALh`8!5!1##'!5!_drrPk^K{U_W{'/27632#"'#576&#"4'5267>327&'"2XCZd}uud$gq~dV)40tlx!&%"dLk}:Uwma4 sOHK{wY@x A63276327632&"'&#"'&#'6327627632&#"'&#"'&#'YR #{=('%{XNCEz>O&z>'(#&R #{=O&{YNCEz>'(%{=('#&ee22ee$l66kd23dEPdd33dd$l76kd34eE^s#!5!37!!'  L34((C $Td67&'&"!3!67>54.#"!5&'.54>325467675#53533#63232>54.#"3'8xpA?9l9>@q<;9'D} 5RTP=: SSPSS ;r>>p  p>>r> !A% )RSQ1 )6BB6) 1QSR) p  ""V{zHNRh|&'4>32"'4>32&'4>32&54>32&54>32#!5!'!567>54.#"32367>4.#"323732>4.#"327>54.#"732>54.#"I )),(?)(#!3()3$))BG!(( K{mg,;h IXI L$  P   H''1|G''#s%'')7$ ''A  ''HTݬ9.%~~ rF)~ wpa!'-23353#3!53573#'5#5335!75!!5'57!ePPeeQQeDpH>H@A~}}~00mrTTreppe-!7CQ^&54767&'&'5676767&'&54>32!535#5##3654."!2>4.#" 1""#@%@#!@% ?$##0 ܍a1%?E?%4,/--+D,/1+ 4;AB<>"  "#>"">#"  ">#10$ITNnVB, n ?%#Naji-/4^t&AYcgb3%' + ((NV8OQĿ>:<uyg**5 k5h P[32>4.#"732>54.#"!5&546767&'&546767&'&4>32'&'.#"+L)+L*+M)(LH     > |n @: !:;! 8An} E04`a30TL**LTM((     ++x: 8>>q ?9 9? q>>8 :x++c^UZbbZU^jg% $Tdhy47&'&";67>54.#"!5&'.54>325467675#53533#63232>54.#"!57#&'.54>3234'67632#7$5oh<:5d4:;i865%1MNJ96 MMJMM 68JNM0v    +0 +/0U-,,+,.T1/, 9j9:h  h:9j9a &LMK- '2==2' -KML& 1  V//X//X//V6HLP&'4>32"'4>32&'4>32&54>32&54>32#!5!5!M ,,.*C,+%#7+,7%,+ FK#++ PDNAM**4d;K))$'**,dY&"**E #**L:ƥ??@@=%)5!5!3353#3!53573#'5#5335!mD^JJ^W^KK^׋LLZZ,}}uz%yuu{{u}--4@4767&'&'5676767&'&54>32!&7535#5##3 1!!#?%?#!?% >$""/ _1+ 4:AA<="  !#=""=#!  "=![1=%T e >6.HC'L"'G 12h[FH`[$%ok+*8d .Ncv[.7&546767&'&546767&'&4>32 w "E> #@!!?% =E!w ./@ =CDz" E>"">E "zDC= @/.QO##"'##565'##"/547?kM ,4N"DF &Fi?JO/FB!O {|Im<&=M2227632#&547636=4'&#"#4'&#"=` ]d2 cBU;/G;SXMB:@B ս;7hf% #>|\@9@O &&5 iC n:^O G  %2O7236;2"'##'65##"'&5476;235&'&=476jS c1=EO ;SCFRʝT6*F@E1;O+.`162V Yi8/D ;8[B VRP"<B+"'##565#+"'&575477;2732;276=4'&3&'"ih;F(wQ"DG".FWCNfBy" bODUq5u4  Pro@ |S`64 '4'&'~ v '  w (  w ' $k=F F>jG3~Pjb^*IerN{̑?qJAe}Ωv6\~x(ONPPNO(!8?|EE|?8!r!_3#"/4?23D-!]UF+}{<!/3#'654'&'#"547326Rs9W5[S%3;B[/OBC'*|<j_g#"=4?2%#"=4?23ɧ%QM?ˠ)TK7(w7џ5s ?|O"'4723!#"5472!5YA>RHIOq 1 ӫg 4D% 3363'$6'"I+4 puoS^*  3%#'#3%#';&2 IʗHj7*(,377#'#'547#5773%%,ppsr,'zzxz'984?/99e5>:_`qE#&#"'5654'5673;54'56732733273+&##&"#&'565*G1 VV2Is3'{'3sI1VV 0Gs3'{'3sP3+1='3sH1WW1Hs3'=1+3PH2WW2H. ;G7567&'&'3#6737'#&'7#&'6735'67#3335#5*)SR))&*&';((:'&)'ȶkkn\\[[nȶ kk n[[\\n`ff/ee.((&(;((:(&((@))SS**n\][[o jj |o[\\\n jj e(P( /N#.6CMhw!2732!'5675&'&=32#&'567637&/7&+"+&'532?4/%32#'#&'&=4?#'57335'3!273+#&='#"/547354;2?!&=35-,;K> #WU* y "њHV ηz/;@"q=o )we)$IY'L ALaXwH >X%CII$PC/DN6g+  b% #  jnN :3 O+5{bQ< ,d-  X] f '^ JJA!< 8 2E35733!&54?'7'7!!"'&%#'73676'77'7'&'676}]} =--HW(7*! >y*1c{F=.,H-.'d(#Y+GC8957jN})%%tGl5nm3(,H:0/(_kiN}!N920K 1DW3!5>7>54&#"5>32&54?'7'7!!"'&%#'73676/77'7'&'676@.#5*"I?6O"[m" c<,+GU 5) <|w)/ayD<,+G,,&a(!>B<#q'%NG91 M7835hL{'$$qEh3kj2'+G8/.&HghL{ L8*/D *(=Pc#"&'532654&+532654&#"5>32&54?'7'7!!"'&%#'73676/77'7'&'676D|q%N24H'CB=9PS3464E>6O#]o 32EX!#2632#"&'532654&#"&54?'7'7!!"'&%#'73676'77'7'&'676Hevyn$L27C'y*1c{F=.,H-.&c)"ERUHHS S /(*. 8956jN~(%%tGk5nm3(,H:00'\jiN}!N91/K "7J]"3264&7.#"632#"&54632&54?'7'7!!"'&%#'73676/77'7'&'676]'00'*//l+2>AB(S`dT^dyg7<,+GU 5) <|w)/ayD<,+G,,&a(!.T--T.H D&RECSukf{7835hL{(#$qEi4lj2'+G8/.&HhgLz L8*.- X.A!#!&54?'7'7!!"'&%#'73676'77'7'&'676n!?/.JY08+"(@},2fH?/-J.0'f*#&K:;68nP*%'wIn7rp5).J<21(vmmQ"P;:1-K':7&54?'7'7!!"'&%#'73676'77'7'&'676N!?/.JY08+"(@},2fH?/-J.0'f*#:<68mP*&&wHn7qp5 ).J;11)wnlP!P;;199'9HR!273!567&#2&'676+&'67'#'6765'533!273+#/#"/47$,7Jv I MO $p%|I ^ [T<K"(~GW$?8?])( EAs#L, T 0 ` +WVۄ`$$a.|%2<J\e3 + &=762367#&'&#367&#&#"3274/"34?3'35732?5#+'535^-J|@h'\-e@<r2&H); uZJM =9jl:jgb.Qi2Q|酝:*}( dpR!h j `]_i$x:-(^%,3"ؿEa HMP E7g /:BR`j # &5%6; 65%&# 327#57&/#2#&'676+'%3#'#&/47'3327##'%3#"/6j1M{ǮG&z v$ExݨE(+=R:n:D!s Y!gQKum;} uA;>e=g¯Cy??ԢB|*>w4I ' 5@` bC$ j$H?iM!%.|7H27&' # &5%6367&#'.7&67263'#%; 65%&# mJB|e6O}°I+o|BJn^jaygwaaygxaj^w$FyتFG퇢D{C?` B]ww]B JХC}.?yP%.232#!7&!"4#".54767267p   {u*_ Jcllm8*#I%<($|ʀX#{Nwt7mnld4)5:IIIB,<_4767632#"'&'&!%!!  >W$`4 Z|b<_/374767632#"'&'&4767632#"'&'&!%!!    UW$`H    Z|b<_/GKO4767632#"'&'&4767632#"'&'&4767632#"'&'&!%!!      UW$`H      Z|b<[/G_cg4767632#"'&'&4767632#"'&'&%4767632#"'&'&4767632#"'&'&!%!!    /    UW$`  K     Z|b<_/G_w{4767632#"'&'&4767632#"'&'&%4767632#"'&'&4767632#"'&'&4767632#"'&'&!%!!    /      >W$`  L      Z|b<V/G_w4767632#"'&'&%4767632#"'&'&4767632#"'&'&4767632#"'&'&%4767632#"'&'&4767632#"'&'&!%!!  0      /    UW$`+    .      @  Z|b.t )2 $$ >54.#"4>32#"&h..--t*Ƅ2..2/.y )62 $$ >54.#"4>32#"&$2#".46h..--1.-.y*Ƅ2..2//2..2/.t 2 $$2>4.#"h-..-t*f/2..2/.j '2 $$2>4.#"$32>4."h-..-q.-.1-j*f/2..2/y2..2/R7!!R-ӖR7!!%!!RMzM; 67'&/#'3#67$#%ׯP==Ͱ̼bN+#!f"K++!|o554.#"##"'5##"&'&'0!5!5&'.4>32!!676767'7' :!9!"9 :! F GF;kY_1278e56d:81)RLk<GG E~^ : : ; ;NG 5 e4G( Li) enf77fne )i (G4e5 G( Pm 9Y%&'%67&673&/'67'&'"&'4?&'37' '7 &/7&'#>7$%88EFu/- 6uNDL22LENu/80uFD8jU45B%y\A@Yy$F 0=/0 ,-X70 ;~*2 %% 2*~697X-,oo  +F9d1 ) ( 1d9C1*CT'&#"'5&767#&$'&%'6'&'''$'676'&5$'6%'.54>32D$ "@F,NNNvF8p^Lb2  N**+ B@0"AR/0?wA%od/D&3.YaQ/5#3$"uI' @3/u= =#n- .... w3% % 32+#".7!"&'&'#&=4;73737D*$#GFHH%#Ι+(&aa'm99m9 3.055_4i4_550.3k  #tttk"632&'.'#####֊v)%8 _^>:k{ZG_?g@`H,>|:=+,j,,<6O/233<bbJ 132>4.#"367#&7&$735&'.4>2,P*+P,.N+)PƗd"/%(MM~95DLMNMD2)WN,,NWP**g!ʇw֜s~  &JJ&?GO277''"/&'&'7&'&'7&47'6767'676?  6"&462EG#96\>42(p __ p(24>\69#G#:5\>42(p __ p(24>\5:'NmNNm U%4m+3 EJ5:6JE 3+m4%T  T%4m+3 EJ6:5JE 3,l4%T '\nMMnM* ? !&+05:?DP3&7"7&'7&'7&'7&'6'6%676767&77&77&'"32654&'5&'.4>323#67#&#"'5&'&547&"'6%6761a$O` "NiB*4l,4"U47),3($aM#"aT*BF 4,=44#Y3,)0BB0/CBO"!-$F$FJF1.#- -#-2MJF$G# 8<g7*!2U6J%n=_CBnT> rYw0d "*7]6U$u=n;wBLz >\e0wZ3C.1BB1.C(N "%""%" M#p.PA.$;QW$.AP-{ "R &.FR2#".54>&'767&%76'&''67&'&'&'67676547676'&7>3263'##"'&'&'&54767&'&547676&'&#"6&%6767&'&'&676&5467&'&6732767&h@9h),)RP|  |PR-*g:>/**Y&()((')&&)')(()% @9f+.TR"`33`\_ .np, 00441/ ,pn, ]]&&()&&EEEJ032WyQT.,d9@.**..1230IDE%&**%&F+.SEFE.IMMI."#FES. !  ";-0.--.0IM+.REF$$1.%2S_`Q2%-1OQQO2-$3Q`_R3&.>GIIG"" 7447#.$$FER/+L"  !75/57%"IJJI* )p~67&'67&'4&6%67.'4'6&&'6767&54?67&'&#&'#&'5&'"'67&'&47632>4.#"72#".4>"0'-, )*#'05%"*%%, ),,"GNYI'+""$(JYNO21, 9,4=SM:7,: -12-[[Z[]WXIOMKLMN2 Y{\ bCWDJgABcp7L^BML0b \u]! @R%KlhhO+ww+O hhlK$PZX'@D 0:)ww*;0 EA&XZw[[[[GJMMJ"( %3!'#!52#"62#".54>o:5(67%'$(n H0L*I"33'554#$/* PR 6h"&>I > >A>!!ua!&5476'#5!+{h_a66mHHm.rZy'#"'&#"'&'&'&547676763232767676'&'&'&/&'&'&547676762!2!%3276767654'&'&'&#"&#"3276767654'&'&˗Pz  ,D@   7;+  23  M98G ):               r         0   L:5U        .\ r26767654'&'."#"'%"'&'&'&54767676;27>764'.'&+"'&'&'&547676762%632$"26767654'&'&#  #  @!R763276;%326767654'&'&'&#"6767654'&'&'&#"32ɓ E79E"  21 +96  >B+  # zOo              49D   /    "    :           =JZx-4H67&'&'&+"'&'&'&4767676327632 #"/#"'&'&'&54767676;276276767654'&'&'&"276767654'&'&'&""'&'&'&547676762"'&'&'&547676762'&'&'&547654'&'&'&";276-&#"+"276767654'&5476%327%&"'&'&4767628?.  !  !a=?^'_)\?=a! !# "!.8?""  "  "  "   f  2 .?E S@6f G=. 2  ŕ6@  B   )_>9 9>_)  % I        ? *        ;d.      ?P   !-  @( ,#%>  NpNM&_*# (! &) ,,f&  ! (K_  Z0-  Yi D   cp-)L &gK1 [N3$ n/ "!0{I"H#fmt2>,7HBI.;/8[, Q[z)  .)S9L *E   '+(4%(4  *X >  7A) 0'-570+I;-% *#%(0  ]'5.  U-9Lp{7654'"'&#"+"'7&54?67676763276323273#5%6767'&#"6%"/67#"27632327654'73654'676547& t!M#l5G;@\ 2BX-0%-m * '?,N ?'!&R;-> <\-R5-6E!"$b$6$!q",; t@P"#C  *FS "DX@! %z$(`]jMP   &O/+@ p_u<  3  DMKZRdYL6D_YBI5.!!''kGWz")3SZ67654/##3276?7%754'654'36767632#"'&54767632'0,,; (| w| ki5.U,\\    %g .  ;,-0j{w {w3V. T, \[^     -5& '-EL4'&'&/767675'7! !'7!654'!4'!!$4767>2"&'&'!654'$$CCC||]V|V#u    9Z(f(Y7%$66%"'&'&'&47676762%'b&I    )^tN/  /dIW?    @ViDV /  V%&%$64'%%&'&'&"27676@))< "  " ]NO]    9|23277632#"'&'&5476"# 6v>? (-=%P8j?  #j<  y"$"JrB23277632#"'&'&5476"" YTo k%,02?=V8jiA{C {u+'qP?  ' 7 sssssstsXrsrtsssr@Q  ' 7 5NB2632#"'&'#"'&547677&'&54763267632676   Bt  ah>) c!  ,Hs *ܡ   },"2A "  {3+Q26#"'#"'&'#'&'#"'&547&'&54767&'&54763267632676  ΂    NjM  rkW* & \ *3 #ﳎ*3 Tv! ( 5+" , @V #!!!!!%!!!!!!!!#!5!3 ;E;JEJJJ<;E;EJK!IKV{ !!!!!!||uv9f !!#!5!335#*+մ*ִw0r!!%!!!!!!/0``1/`1) !!#!5!3^^^~S3!!'#'!!#!!3!5LDʃDMA #5!#3!3'3#!#35!3###5353;9nj#5AI##0vQ#"#3;54'&'&'!"3276767653#4'&'&'&+3!52767>5/]LED73!&&54GBO]63H>SkS>H388]OBG45&&!35FEL]63H>SS>H38882I32367675&'&'.5467676236767>32#"&'&'&'#"'&'.546767675&% >#"? ?"#>    G   >#"? ?"#>  G     F  >##> >##>   F    ?#"> >"#?4'&'&'&'.54767676322767676767632#"'&'&'&'&'&#"'&'&'&5476767676765"#"'&'&'&5476767632B ,#,+%) 3!, &&*-#''#-*&& &$0 )$W$) 0$' L+,$&&$,/"&&$b3") M*,%&&%,."'%%0 )$W#) 4!, &&+,$''$,+&& &$1 ,#,+$)0267632#"'&'&'3&'&'&54676763267632#"'&'#"'&'&'&5476767#6767632#"'&'"'&'&'&54767#"'&'&'&54767676325##"'&'&'&54767#"'&'&'&476767632&'&547676763235#"'.'&5476767632&'&54767676h             -  (                  '    *             .         +j276767653"4'&'&'&+sidUS+*+'WPihtthiPW'+*+SUdi),)URhexuhbXR,,,,RYaitwfgSU),%t?247676763"'&'&'&5!276767653"4'&'&'&LEA86:4DDMMDD4:68AEtjdVT,*+(XQjhvvhjQX(+*,TVdj-76DCOME@:66:?FLOCC67-*UShgyvjbYS,-,-RZbjvyghTU*,(8 %%! !)ttJHcdecH]F]~]^C5 )!%%!2#"'&'&'&54767676hzt@z@Az@t{ne_RP)((&SNcdome_RP)((&SMdd0x}*(QObbrle]TP)**(QObbooe]TN+*(.'"276767654'&'&'! !_)(""""()_)(""""(Y$(*/.*(#  #(*./*($]^#< '1%%2"'&'&'&5476767! !#xxa)(#""#()a)(#""#(YDgghgD^I^W $(*0.+($  $(+.0*($ YZ(8 3'7'3!%%!! !hE۱CCDe g  g f ҁссi:]^= 3'7'3!%%!7!7'7!hTDEDDTNPPIQ2P11P2#mm(? -5%7'%!! !] P  gfeer­696ƌ]^^. /'%!!%!77!yrryyqm"_^^l%%tu%ߴ߳!63% %#'-7:|:||9|kֵֵkֶWz`37'%7% %#'ZZZZZ]^Z^ZZ˛ʜm˜˜mʜ0o #'!5!73!P6M6P$6PMP66R#6QLR6$Q6L$z     - h<_K <; L_zK <; J`;<_  '!'/7'?!7% % -[9^[[ZG^ZZz'}*}zy}*}'q^\\ZG^ZZ:\O}zy}*}'yz(}2 % %  h_y(_^(zFGs% % -hVHzVUzHrVU{HUVH% % -hhhႁhhhႂhhh$h7% %' 7-'hX5 5XV6 6g5VW6 6WV5 0t/37%!!%'#''7'%!5!%7'77;[TA:#T8#AT[TA#9T#8AT T8#AT[U@#7S#9@U[TA8#154'&5476276767632#"#"#"327232#"'&'&/"'&5476=&'&'#"'&'&54767632332?&547'&#"#"#"'&'&54767632676?>$,.c.,$> ]5 71+: H3> kR  Sk >3H :+17 7Z  >$,.c.,$? Z7 71+: H3> lR  Rk >3H :+17 9X  ib9@R'))'R@9dg  8d< +$;)01):$* \570+9 F3= kQ  Sj  =3F 9+077Y  >$,.a.,$? Y770+9 G3= kR  Qk =3G 9+079W     > h`9@Q'(('Q@9bf  7c<+$:)/0(:$+HH#:.'W4,CEH@,4W'*>&DL:Z##KGW,f ',;[;;+*Q--}KOW*AA*WSGu5-U&+;;[;,)  '+;[<>**Q--}KNW+@@-USFu5-S(+;>Y;+* !67654'&"327632#"'&'&/#"'&5476=#"'&'&5476763232?'&#"#"'&'&5476763254'&5476276767632#"'&#"#"'&#"327676%32767654'&'&#"#"i/)F)/,UK:M $\/8E(5>H6-EFJA-5H;8)D7.\# L;KU,*UK;K #\.7F'5>H5-DE-6H<7*C8/\$ M:K U+:6-21 4 $:<;$ 4 22-6 O;(A7##7A(; !*:#.#;&Rm!CcJMU)??,RMJcCoS%9#.#;)!  );#-$:'Qn!DcIMU*??*UMIcD oS%;#.$:* f /D;;D/ $i"276767654'&'&'767632#"'#"'&'&'&'#"'&'&'&5476767#"'&'&'&5476767632&'&5476767632o00'))'00o00'))'0]0+)*+%# #+%0%##&&.0%+%   #%'.0$,#0%-# #%'.0$.  #%'-1$,#$%*/0961/*%%*/1690/*%) "*&0-(%$$$)-0&*!&"*!$$)-0&-#%(-0&*"" (-0&*"$$(./& n%#"'&'&'&5476767#"'&'&'&5476767632&'&54767676267632#"'#"'&'&'&27654'&'&'&"67&'&'&'276767&54767'&'&#"276767654'&/?676767654'&'&'&#"h &,&1/(&#!$&1%-$!&$/'.)2$-%c%-$2-*++&$!$-%1&$!#&(/1&,& =s0 9 55%R 9 !_  , 9 R%5s  _!#'"+'0/)&$%%).2'+$ * '1.*%%%%*.1' * "+'2.)%%$&)/0'+"'#L% %L %#M L:2(&6  _ M#%   6&(2: -[3b &'#"'&'&'&547676763267'&#"327%327676764'&'.#"7632#"'%&'&54767676324676762676322##"'&'"'&'.5#"'&'&'&54767"'&'&'&54767676&'&'&'&'&'67676?&'32767677676765&'&'.#"7676767&'&'&/326767674'&'&'67'&'&'&#"67'&'&'&'67676767"276767654'&'&'"'&'&'&54?&'276767654'7654'&'&'&"67'&547676762    (  b  (       #!"G"!# * " ' ## G!""  '  Y m    ( y   ( O k  w  m Q (  O (     ? ?   + / L* / *   +. M+ .*   !!!! '? ?' "#& #'"!!  '? ?'  !"!  $&  m P    O        m     y    O k         b       %j<\l"276767654'&'&/2#"'&'&'&47676762#"'&'&'&54767676% %-[''!  !''[&( !! (TB39)+,+76?A3:(+,+76>tjeVT,++(XRiiuskdVT,**(XQijtuz"z!uv!z"z#&(,-''""''-,(&#e)*:6?;97,+)*97z88,+,*UThgyricYT+,,*USigtvjbZR-,zvvz"z vv!z29"327632#"'&'&/#"'&5476=#"'&'&5476763232?'&#"#"'&'&5476763254'&5476276767632#"'&#"27654'&%&'&#"327676%327632 654'&'&#"#"i"(-+S I9K #Y.6C&4<F4,CDH?,4F96(B5-Y" K8IS*)RI8J "Y-5D&3<F4,CC,4F:6(A6.Y# K9I R*"(-62 #9~3 #9; 01+56 00,5`%;G,A $.?'!3&@!*Yx$ ImPT]-EE0ZTPmI "zZ)!?&3!'@-$ #,A'!2'?!*Yx$ImQT\.EE.\TQmI#yZ)!A&2"'?.#~&41%%14>3t-3>41%%14>3f^CC^B%@#@@%@#-4>41%%14>4-3>41%%14>3+  V  ++  V  !r +?Sg"&46277''"'&'&476762"'&'&4767622"'&'&4767$2"'&'&4767eeeBABA#U##U##U##U#V%**%V&**KV&**&V%**~ffeAA$AAV%**%V&**V&**&V%**#U##U##U##U#  &3@MYam+%5%32476;#"'&'?632&54?#"632/&54#"/72#547"&462"'&=3?_?6 6  6] 6'?&M&C_CC_?&M&< 'L&&L'!6 6^6!6 >_CC_D<>"l267632%632#"'%3#"'&'"'&547#"'&54727%#"'&47632%&'&54763&5476h!#;'&1'h 9##8)'1!, ;#A#; '&1')8##9 h'12;# 4%.&! 6 = 6%".% 3 3 G%.56 = 6 %".G 4 $8 ! 54."#"54$32632#"_ ɀ~~a>E  %!#!3!p EE?p9E=V %!%!35!cE:d FF8 %!!![:F:\;[0q %!!7!N]<N;)G+t  ,o9; #q !rQk!k`!733}b>v!#7#)iC~'  #'  #g]jOS2#"327676765#"'&546;57!##"'&'&'&54767676%#     42;%-n`Ԯrr#26A@:V7:$)&7.Yq   % $.277g[(dVDQ49%*,04?()-#52&'&547676762"'&'&'&5476767hc"$njln(Lfe*+$$$$+*e*+$%%$+! #'(*dRjjSc*('!"%*,20,+%""%+,02,*%"%C&'&547676762476767622"'&'&'&5476767hcn(%X%&&&W%(nؖe*+$$$$+*e*+$%%$+,Dj*('(&,,&('(*kC"&*,11,*%##%*,11,*&".i%%&%&54767676247676762hhÔ*(42u24)(()42u24(*i\=97,*+*96@@69*+*,79=Zr_'#"'&'&'&547676763"'&'&'&5476767632_dA=;0-/.=:DD:=./-0;=Abx1.=8DC9() 1F="%".4"tNa5&$4! /.r<@6B2L_0>Q#kI|"rz7&)?),%=^K=.C26F@13.!9+cM313N676 547&'&327#"'#536767&'&'&5432&5476323254'&543253%5@26`', =NR6#!vWR>4 2:O t51"".1&X.RO A5ȏ )T/186,FAS :#(=:tA0 9SD 'A#5}11BO9 "'&'&'&547676763"3ᗊpm8884qlYTN! !C@RP]e:6pltm9:'62~~jf77"05276767654'.'4]PR@C! !NTYlq4888mpe'67fj~~27&:9mtlo7:fkR !&547jljjlyyxzQqpnc$0!!676n wu;;vi43f$lcC}U# 3tD}U 3 DutV.! !JV. ! JA! !m^\GHB ! ^^HHv!'7DWWWW|'7'7WWbW>W^$#"&=4&+5326=46;#"3xMe,,fLx1d=AOOA=dƂ׈ihDŽOߍOi(326=467&'&=4&+532;#"+5nCFVU$#Cn5BB*)p//oTBB¥P⎁AAPDBۇ45iDCS/~ #!5!3}t]} 7%d^=]d>S~5 /%0~##t] '-f\=]d]>-!'7!. (``I)=2"&'&'&5476?!".'&47>3!'&'&54767>2 '!  `!!  !' ,&   &   S~&!5! F78-x!5!5 V(Mr6u #3#3#3!!5 鴴ZZ---I(,,,,S~  55!#3#3#3F9UU**b]^bUUUS~!!5 F7.`tq!%  qR{V$%! S%@{V t%226=3!5 5!"'&'&'&6  $hI$  h$   6<47676763!5 5!"6  $hI$  $   $O!! e 6n55!lMlTwccwekl!5!!53 ' !_[y"kd""e/l5!!53 ' !_["/d""5 !73#57!%!6UcGjbzbdǩ""ap 5!'53#'!!!7%acߎA[؁(ZqZ{{{ĒҒ}TM %'!'!53 !;qKRnKa26wwIw22wT}> 3#5!7!!! ZQtZQ0L>ssjLK2Nu '!53#'5!'7! !pSn%R&%Ua2wKJ,Lw22w)1 '7!573#5!7! !r&j&St&SpWl6qM,LLyy77y@!6767632#"'&'&'!  6IYZgb^UMI%&&"LF\Zfc^UM3!t:6I&&&#LHZZhc\UMH'&&#L2<tt XNy "&*.37#37#37#37#5'!!55!!3'#3'#3'#3'#r+qr*rr+rr+rV{{*q+*r*+r++r+9Ɔ\]t] 7&#"7'7 #%5#t69.wZY96t".*X/S~k 55!5!!7'!nnUVGG8:ȏu\j '327'' #395t".Y/Y"u69.xXXN2%&#"6767&'&"67632&'&547676767}:"  s %*&*(&"!#!"O>>;*E/4767!"!47676763"'&'&'&5!3!&'&5v  5 $ %% $  H vgMME %!#"!% EMu\2&'&'&'&54767#"'&'276?&'&'32\":  #'$'$#Y@I:86s6::I  #&'#'" X  :5*+B67"'&'&'&547676$47676762"'&'&'%&'&'&547676762$[ /  H =a=   / ZI=X  q> d(*c     XJn.676767632#"'&'&'&%&'&54767&'&54765 #&+*1)F-Y)) .EOO/3S>>S&/ #$))%#]]#%))$#&e"'&'.54?654'&'&'&+"#!".4?64/&4676763!2;276767654/&54676762 I ]]I    Q      Q  %eg"'&'.54?654'&'&'&+"#!".4?64/&4676763!2;276767654/&54676762 GKa u~iKG E     2 +#76767&'&/3#6767!5!!5!&'&'g?j7R=y66y=R6k?VO S+ +Sd _8=eyu'&utj>jiVVy ##! !+532765YZͷZ-,'ij>>% %!#3!3iVO7n lѲR{H3!!# 9`#3!{`9Lh '"27654'&'2#"'&7673A\VMMG*w|~~hA1LLNeˑRh]c[斘,mKseg.!#5#"&'532654&+532.Dv6;zIMcݳw"$.*gQH{"264   676326^\`s-!\[^[]-=P@7'"]_$5hWd3#3h݄D%36767#"'&5476?>5#5% Yb^`_hon"!^XE&->B%DS #D9``LAB\VBT=;-;,,1Y7:w!##Z:#5!#J&w3!3XZ!533XK,X3#3,X0X,X3#3,$dX,X3#3,X,X3#3,X,X%3#3#,񈈈X,X3##3X0X,X3##3$dX,X3##3X,X3##3X,X%3#!#3X,X!#!!yX,X!#3!!Ẍ,X!#3!!X,X!#3!!Xd,X%!!3XT| 3'#'L:LjLCUCT| #'737:LjLCUC( 3#3#'( f*D( #53#73 fRo(D d'IJ !5!5!5!J>>I3#qe10#+H %"32654&'6#"467.5463%!"h{1PAž(rڜ-*/1|I5#7N@*        JEE<2<2991/<299903#'#"!#!##535463wcM%ɩQge/яN#7B@#    JE E<<991/<29990#!"!!##5354637cM%۸ɩ{Qge/яNE & K EE & ? E X& R E  X& S E E & K DE & ? Dx X& R D X& S DE & K FE & ? Fr  X& R Fz X& S FE& K E7'E& ? E-x& R Em& S EE& K FE' F ?y& R F|& S FEW& KLfEH'4W ?& RK& S]r& S >tj' >v C?' >,~ @' > A~& S Fj ' Ft C?' F,x @ ' F AX g& E{X g&  E/}>\/' Ea8 >/' E 8 X g& =X g&  =&8\/' =8 8/' =8 X g& D*X g&  DB\/' D >/' D8 X g& F2X g&  F^\/' F >/' F8 a7& >` D' >\ 'U` ' ]A2%#"'$4733267654'&54767;#"'&'0Qcpl?AOL64)>.VhFd((&*=#>2(I=/ b \^xHj<9"1B,f%TOA7.N?+  cG&A GJ' GT E+9' H F9' H G6 U  &#"'&47332767654'3;#"'G{579T?:!e#"#V4^Wt<;?xC3^w3UT8@0C&6D%3!"''&5473327&'&5476&'5%67654'&#"%3276'&'&bJDv-(0g:-0M,QG$"':AG 5' 343%@K5:,+ CfN@TSZ 'AO@H=.%4-+#%v_U[1C "&_ ,:%&'&5476&'53!"'+532767654'&#"%3276'&'&\:-0M,QG JF$"':AG 5' 3CfN@TSZ 'AO844U@H=.%4-+#%v_U[1C "&_X %!#53 76=3`H՞,1VV,1jٻX%#!5!276=33!!"^L,02,*VV,1jj1,{ X' = RX' = SD&D"k ;#"'&=31,cK\WL71,\W+DD&D&D&0&Vz & U-&!'! u :&":'"' uL 3&"`>Z '"2>   &# &#V uW&"Y{`'" '"p R'"p S L 3;!"'&L2,PXskj1,\eE& K <E' < ?X& R <X& S <& =j' = E & K =E ' = ?#' =R R' =R SE& K >E' > ?,& R >R& S >,X g& <X g' <p \/' <  /' <  X gX g.%3#"'&'&'27'&5767&5$(1{R=IrbJԖ`e_$m3HZdP]vbĘe4)@5 [_w\/&'&'&5672+5327676SSgURHKLXJKݣdht^#4b4bBPH:jV/);#"'&'+53276767&'&'&5672~AI2h6:H~D& <] ` ' <  !F"'&''#"'&7673327676'&/37653323;#"'4A@E2J/1%=Gq_evh+&%*TdOvG#%,9?#@*8m~C "RVxgt7323;#"'#"'&'#"'&'+53276=32765!5d*,""@e;6:HO7* F35C%&F:DF*7@`*%Rx&),`X I6[m8*6759,%#GvOdTtg!& >6 ' >  ' >  ' >R   G%327654'&#"%;#"/+"'&5#"'&5473767654'&'367632Ij($?GhK=.';4f&4-//3fJZ}eh<2H7(28`@%(jm=vbw $A7. *727&'&5763"327%+5=K4X}ں>SF8I \Y];d}M4F!Ť$/%+532767&'&5476762;#""654'hkB;(aD hYYf MXD=pʨ4/gg/($'UZ'-)74--38)-'bM,(U __ z F&  <w L' < F' <w L' < & S <*jL' < C?' <~ @~' <" A|t& L =]~' =Nk B?& @ =,~~& A =!D#"'5327654'&'&7676##"'&547#76767;5#"'&5J&P DfXRNB8D-<9_h$$EB|=Q&v+6(  %z|qe))5!27654'&54767;#"'&/P/62 hF F&,@XB:f"``h$$EB|=Q&v+6(  %?+)x.BK $##"'&547#3276767;5#"'&5EcTiI(A@wc}^h3N8K+,19)"B~ss\V*"/~?]3,1j )5!27653WP,1se\,1j%)5327653;! hL,02,VV,1jkj1,TGt4%327654'&'&#"#"'&#4763&547632;#" zL,5;(.;D Kp#IxAIM\HT(RfV*9:X DD(PNKOmf7*(?$G@^m*%'+53276767632%327654'&'&#"d`p@ht4,+^]EE>/4:''5)24ed0$#1P8O$*ME5EX !a%m/%#"'+53276767632;#"%327654'&'&#"|an@h4,+^]HB3&id>/4:''5).4fb0$#1P8S1>/E5EX d%6& U < ' <w M& R <R& S <R $&'&'&'3;#"'&'#"'&5476 xRot$8pqZI-&8:m*12e CY>)2'+eO,3;I0D-=67654'&#"27&'&5476&'5#"'+5327654'&$"':A4N--0M,Q;(Jxb 41~! @H=.%4-+#%v iEN@TSZ 'C49g=ql)D%'r.C!v-3j  ;AWE L9P)8K6(S/VL_+Y9K1\S{7%&'&'&54767632;#"'&##"'&'&73327676 ,)MW,.@"##C@"*5NhLy $Eq:<3, $.=N/[ EW3235huFX^"!ݺh^bNm/>ZUb=*(DV\BAL89DEnY1X;YTBEb%$q%1&R'N(X)fP*H+,m-.s/Vy0F1u\2u3u\45J6/7=899:;%<=#{DXE%{F{G{X{H'I{H{JKDLVMN Omo{P{QH{RVT{SRwTj{U{VW^Xdm`Y`ZL`[hV`\b]LF#7fo-L7NFf@103# qf?Q@ aa1<20KTX@878YKTK T[X@878Y3#%3#?Zk10K TX@878YKTX@878Y@&  //// //]]3#@   99991<<990KTKT[X@878Y@t        !      ]]'.#"#4632326=3#"&d9 #(}gU$=19#(}fT"<9! 2-ev 3)dwyi10K TX@878YKTX@878YKTX@878Y@ //]#1Ś7]@ 91<90K TX@878YKTX@878Y@ //, ]3#'# ӌ7i@ 91290KTX@878YK TX@878Y@ //*//]373 ӌ Zj@ 9910'3$ll/{m > #.#"/  waSRd {xz{w8796/{m @ PP120332673#"&/w dRSaw m6978w{zPa103#PXck@ 1<203#3#\zkcyk#!#uŚŚkf$(#5476?>54'&'&56763#7:)*%>8'9@B9Q[~CG=9. bEBTY;X1I*%#D>"Y`LAB\VE'*==010!!=V045!5!5gr4!5!;r4!5!/r^s 2"&46"264hrL&'◚vURyVP%`8n|TyRTv?F3#%3#?53#73#'3# 3#3#'3#}}dE&"'&547332767654'3;#"'&')+ubEW-7!K4/+X`t8A>|0l7%5A8>7KZd3(,*OQ/9?0654'&323276767'&54767632#!V)B,4((7(*HTO<?aNbNLZB`.NJ|m+M;3*)3P& ]027EW4,E$2Hf3Џ,' 3;!"'#!5327&'&5476762"67654'&'ȷ$&ň($28 D@$ 8P2*I1C299(M.L,0W 5+5DE2.4!.@%&'&'&54767632;+'$'&547332766'&'&#"B.y9()Wp8c2X]0Lh^E>>:lu{/"'"5 9Ld/  #+m=E2X:SmJN}`kI="'&4762<R8R8z?@?@@?@(8)*8@@@@@??V<D@!B  10291/<2990KSXY33+532765#5YZ͹Z-,˹iij>>iE'&5473327654'ߚ)+ubj{G{yo9;>|0lALj@G|t8654'&32676#"'&54767632'&'&5473)B,4((7(*H[b?zKbNLc9g'!.9ΊMRVV+M;3*)3P&;f4KCW-3E$2Zwfj}ػH(E  ' I & K =E' D  KX '"8X ' EX ' >T&$>x9654'&"32#"'&&733276767#"'&54767632)B,4P7&,Hir$$xZT0A&?zKbNLZB`.,L95T2R$T&()X\^-"2(hLDV,4D$2Hf3B'$6#"'&47332767654'3dG{579T?:"FHt<;?xC3b`L 3H'$p'$(J=w{_<UUUmhR!9\XdffXXX%fmVuu/9%fZH{{{mjdLhX%?wXd=+XBFjX%%%%%%uuuuu%){{{{X/hh%%%{{{{{{{f{f{f{f{FmLuuuHj j///%h%F0<<^u?4An1mu -V8xv// 'J}}}9%uz%%)f{uu}f{=)/%%{{uuhj/}%{uuuu%hjxx/!79{{{zz8{zffX(hhgEfjjzz}}}v_AHHf}i_6w#6AAQ0^^))$=$=>/VX%V[^,,,,,)=/?VX)"V//yysb^_s?$U))//=X/UU?p6%%u%Vux/%uuJ%F63F 6DtP3LYF633Fr"p"m93u`!Y4uVUm"h%!Vu/hBPr< A<L}i{;=hcL|}PhN{{# A#huU;b/%\%\L;%%){uzuz;}uuuhhhhhhAhu11ZdL EEEXXX"" zzB6LDDDDDD0V\~~6EEEEEEXXXX ~~>ly|ar{jK||cceerd`oawT0ZSSSSxDV_In7777-7777Vv7km#)+?+77LL,:d^E<<.?AEEGGG11OOGI8%[:X::GM[%#H[#{:GXQ:OWbG[CaIGdUU~%%UU?::[xM Fa^M3:%{{{}{{{{{f{7VmVmVmjj==////9d9dLL%hh%%%%{{{uu    ' ' ' '%h%h%hFFFFFFFF%%llMM@@cc66'33333333@EFFFFFFFFfFF633FFFFFFFFFF%%llMM@@ccFFFFFFFFfFFFFFFF%%%['/66333333%%[p?FFFFFJdd??PZZ!!=H ?I=;0A=XBF ?I=;0E1:1 {_m *{%*/.j5':TTJ B%0J%  BBBBBYYBBBBBBBBBABBq?QQ2BXXBBBBGB*BB*B*BBBBBBBBBBBBBBBBBBEEB*BBBBBBBB%uIXXf+?;;;)}}?5XJWXXXXXXXXXXXXXXXWXXXXXWJJXXXXXXXEXXXXXXXXVVVVWXXXXVVVVVVVVVXVVVVVVXXXXXXXXXXXXXXOOOOOOOOOPPPPXXXVXVVVVXXXXVVVVPIrZZ% % XaGX++|h}2H%%?XX%XX6FFHRFFF    xxxxxxx||iEiiDDu777777?aa"2"Y::;+6.ocN8/&/// "N`ya7g!!!!GCL54=/U28Y^CVpj6gv2 1.rjD7`./<KD$>K--9.7.P<<<<<<....RRs'J*R*"..:=4%=?D-W&W;%J@3@V90~0G+%(C(#(=(.6W0$2$017+!$%2 02)!"$E=80+Qg.rA1fnCDStSt-IS-6SS`{{66O6ee5a}T2)XtSuN*u5&%277uuXXV%LZZ,,,,,,,,,,,,,,,TT(((##EEEEEEEEEEEEjjXXXXXXXX`` 6|DD"DDDD0VLZ| LEEEEEEXXXXXX``""  zzjB G6LZ||@S__R%fmVuu/9%{{{mjdLhf?y77//Xf=^?EjEEEXXXB6LLLLL,x 8d x ( d  4  ,(PD  !!T!!""@"#p$$%%&8''p'(P)L)*+ +,,,-P.<./ /0123X4$4T5$55567L8d989::<<=`==?,?X?@<@ABBCC@CPCDHDDEE FF8F\FFFH$HHI I$I<ITIlIIIJlJJJKK<KtKLMM4MdMMN@O$O<OTOlOOOQQQ4QLQdQQQQQS S$S<STS|SST8U<UTUlUUUVpVVVVWW,WDW\WtWWWWWXXXDXTYY(Y@YXYpYYYYYZZZ0ZHZ`ZxZZZZ[$[\D\x\\\\]](]@]X]^P___0_H_``\`````aa,aDabHb`bxbbbbbcddDdddddde|fffffg g$g<gTglgggggghh8hPhiLixiiijj<jTjljjjjjkk4kLkpkkkkklldlmmn ndno`op@pPpqqlrrtsst txtuXuvv\vw wTwxxLxxyy,yyzlz{\{|L|\|},}h}~~ ~<~~TH d0 dt4Ll ,DTt $<Tl, 8Ph(@Xp0H`xLTD,Ld|``\XxDX(H|pLt<,txP@8h<p`H < 4p< |0x d<Hxp0(HlH(pP D$t@X$\œ¬¼Tð h4DlňŜŰƼ 8HȄ Ɉ<`ʈʰ 0\lHX̸̨4l͈Ͱ<`΀<hτϴPмdtѐѴ<x ,<XpԌԨ(pդ,@րּ֠4D\t׌פ׼ |،؜جL\lDTdtܜD\t݌ݤݼ|,H`h@D8p( $4Th4(dtx4< 8P(`H`pHXhxHh\x(8pPhxd ,pXh t L  8   ( @ X       @0`p<p,T $<l \L\l`XH`p0H`pDTd| $<Tl ,<L\l@`\  , D ` x    !D!\!!" "$""##$,$D$$$% %$%&<&&'H'(l(()P)|)*(*D*+ +P+`+t+++,@,-@--.D.//H///080P0h00000011(1@1X1p1222333 3034444445667D78x9 9::;H;<h>?D?@pA A|B<BCD<DDE8EEEF,F|FGGlGH$H@HI<IhI|IJJdJKlKLDLM8MMNPNOOOPPPQ Q|RRSSSTlTUTUV$VWWtWXYYYZ|[[x[\L\\]4]^^_`__` `h`aaLaab$bTbbccPcccdd@dde,eTeeflfg<ghLhii\ijDjjk(kkl<lllm@mmnnpnnnnnodotoppdpq8qr,rlrs sdsst tHttu(uHuv8vvw(wx,xy yyzHzzz{@{{|P|h||||||}}0}L}d}|}}}}}~ ~$~<~T~l~~~~~4Ld| $<Tl,D\t4Ld| $<Tl,D\t $<Tl,D\t4Ld| $<Tl $<Tt4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| 4D\l $<Tl,D\t4Ld| $<Tl,D\t,<Lp(8P`x 4Ld| 8H`x0@Ph0@dddddddddddd H`8P`d$Dh|00`Hp((\0DXl  4H\p8x\0X4DX@Ĉ<|ŌŜŬ4TtƔƴ4TtǔǴ0dȔ$TɄɰ$l˼L̔\͘LΘ Ϭp8PҰ,dӜ D`՜֬Txנ@d؈0ل\ڤ ۄdܤ$d\ޜ\ߜH0|L@|$<Dx$hT @tX TX|hp$0@h|\tX l|l`$Tl\$l8dt,(  h  4   D    H 8|d`XL$xD8TPHD , d  !,!|!""<"# #$8$$%(%t%&t''''((@(h(())$)D)d))))*$*D***+0+,p,,,,--p-..d.3h334 4d455(5686P6677T778899h9::P::;; ;8;P;h;;;;;;<<,<==>8>>>>>???0?H?X?@@@ABCDE<EEF F<FtFFGG G<G`GGGGHLHHHI0IdIIIJ<JXJtJJJKKPKKLLLLLLMM(MLMpMMMMN NDNdNNNNOODOpOOOPPDPlPPPQQHQpQQQRR@RlRRRSS<SdSSST T<TpTTU U@UtUUVVTVVVW$WXW|WWXX(XPX|XXYY4YhYYYZ ZLZZZ[([X[[\\0\p\\]]\]]^0^h^^^__H_d_____` `(`L`t````a a(aDa`a|aaaabb$b@b\bxbc@depeeeeeeff,fHf\ftfffg,gDggi,ij<kkl$l@lllllmm mHmdmmmmn n<ndnnnno o8oTo|oooppXpq8qrsHst4ttu4uuv@vhvvwDwwxxDxpxxy$y@y\ytyyzz0zXzz{{4{\{||<|||}0}~~~~ 8T 0h0\,L<Hx<x h8\(TT(0@t8H(hLh|ŌȐTʬ˼̤hͼ 0ЌPѠ҄D( <(8X4(thp( `@`0D8l(DDP HTd 8   PLxX@,hLPX  !!"`"#T#$4$$%D%&L&&'0'(D)+-8/h139;(=x>8>?A4BDETFGGDGxGGH0HLHhHHHHIIJK|KLxMN|NOTOOOP PHPpPPPQQtQRR@RlRRSlSST T\TTTUU\UUVVLVVW,WxWXXlXY|YZZt[[\X]4]^`0`a(aaabb4bxbbcc4c\cccddDdedeeff<fg gggh8hXhxhhhhii@ihiiijj0jTjtjjjkk,kXkkkkkl lPlmhmnn n8nPnhnnnnnnoo(o@oXopoooooppp0pHp`pxpppppqq q8qPqhqqqqqqrr(r@rXrprrrrrs|sssssst ttudutv(v\vvvvvwww(w\wlw|wwwwwwx xx4xDx\xtxxxxxyyy4yLydytyyyyzz z8zPzhzzzzzz{{({@{X{h||h|}}}4}L}\}}}~~\~t~~pd0H`pL $48(@Xp0<th|d Td 8PhLDTdt$4DTdt$4DTdt$4DTdt$PP|LDp <Xt P`x$D t$D\tl$ Y +kW_B]  @  4     S  b     " :R & hhCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. DejaVu changes are in public domain Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. DejaVu changes are in public domain DejaVu Sans MonoDejaVu Sans MonoBookBookDejaVu Sans MonoDejaVu Sans MonoDejaVu Sans MonoDejaVu Sans MonoVersion 2.29Version 2.29DejaVuSansMonoDejaVuSansMonoDejaVu fonts teamDejaVu fonts teamhttp://dejavu.sourceforge.nethttp://dejavu.sourceforge.netFonts are (c) Bitstream (see below). DejaVu changes are in public domain. Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. http://dejavu.sourceforge.net/wiki/index.php/Licensehttp://dejavu.sourceforge.net/wiki/index.php/License~Z Y  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z sfthyphenAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019FOhornohornuni01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DDuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3Gcarongcaronuni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F0uni01F4uni01F5uni01F6uni01F8uni01F9AEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccentuni021Auni021Buni021Cuni021Duni021Euni021Funi0220uni0221uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236dotlessjuni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0244uni0245uni024Cuni024Duni0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Auni025Buni025Cuni025Duni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268uni0269uni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276uni0277uni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294uni0295uni0296uni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2uni02A3uni02A4uni02A5uni02A6uni02A7uni02A8uni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AFuni02B0uni02B1uni02B2uni02B3uni02B4uni02B5uni02B6uni02B7uni02B8uni02B9uni02BBuni02BCuni02BDuni02BEuni02BFuni02C0uni02C1uni02C8uni02C9uni02CCuni02CDuni02D0uni02D1uni02D2uni02D3uni02D6uni02D7uni02DEuni02E0uni02E1uni02E2uni02E3uni02E4uni02E5uni02E6uni02E7uni02E8uni02E9uni02EEuni02F3 gravecomb acutecombuni0302 tildecombuni0304uni0305uni0306uni0307uni0308 hookabovecombuni030Auni030Buni030Cuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320uni0321uni0322 dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334uni0335uni0336uni0337uni0338uni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0343uni0358uni0361uni0374uni0375uni037Auni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosuni03D0theta1Upsilon1uni03D3uni03D4phi1omega1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F7uni03F8uni03F9uni03FAuni03FBuni03FCuni03FDuni03FEuni03FFuni0400uni0401uni0402uni0403uni0404uni0405uni0406uni0407uni0408uni0409uni040Auni040Buni040Cuni040Duni040Euni040Funi0410uni0411uni0412uni0413uni0414uni0415uni0416uni0417uni0418uni0419uni041Auni041Buni041Cuni041Duni041Euni041Funi0420uni0421uni0422uni0423uni0424uni0425uni0426uni0427uni0428uni0429uni042Auni042Buni042Cuni042Duni042Euni042Funi0430uni0431uni0432uni0433uni0434uni0435uni0436uni0437uni0438uni0439uni043Auni043Buni043Cuni043Duni043Euni043Funi0440uni0441uni0442uni0443uni0444uni0445uni0446uni0447uni0448uni0449uni044Auni044Buni044Cuni044Duni044Euni044Funi0450uni0451uni0452uni0453uni0454uni0455uni0456uni0457uni0458uni0459uni045Auni045Buni045Cuni045Duni045Euni045Funi0472uni0473uni0490uni0491uni0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni04A2uni04A3uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04BAuni04BBuni04C0uni04C1uni04C2uni04C3uni04C4uni04C7uni04C8uni04CBuni04CCuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni0510uni0511uni051Auni051Buni051Cuni051Duni0606uni0607uni0609uni060Auni060Cuni0615uni061Buni061Funi0621uni0622uni0623uni0624uni0625uni0626uni0627uni0628uni0629uni062Auni062Buni062Cuni062Duni062Euni062Funi0630uni0631uni0632uni0633uni0634uni0635uni0636uni0637uni0638uni0639uni063Auni0640uni0641uni0642uni0643uni0644uni0645uni0646uni0647uni0648uni0649uni064Auni064Buni064Cuni064Duni064Euni064Funi0650uni0651uni0652uni0653uni0654uni0655uni065Auni0660uni0661uni0662uni0663uni0664uni0665uni0666uni0667uni0668uni0669uni066Auni066Buni066Cuni066Duni0674uni0679uni067Auni067Buni067Euni067Funi0680uni0683uni0684uni0686uni0687uni0691uni0698uni06A4uni06A9uni06AFuni06BEuni06CCuni06F0uni06F1uni06F2uni06F3uni06F4uni06F5uni06F6uni06F7uni06F8uni06F9uni0E81uni0E82uni0E84uni0E87uni0E88uni0E8Auni0E8Duni0E94uni0E95uni0E96uni0E97uni0E99uni0E9Auni0E9Buni0E9Cuni0E9Duni0E9Euni0E9Funi0EA1uni0EA2uni0EA3uni0EA5uni0EA7uni0EAAuni0EABuni0EADuni0EAEuni0EAFuni0EB0uni0EB1uni0EB2uni0EB3uni0EB4uni0EB5uni0EB6uni0EB7uni0EB8uni0EB9uni0EBBuni0EBCuni0EC8uni0EC9uni0ECAuni0ECBuni0ECCuni0ECDuni10D0uni10D1uni10D2uni10D3uni10D4uni10D5uni10D6uni10D7uni10D8uni10D9uni10DAuni10DBuni10DCuni10DDuni10DEuni10DFuni10E0uni10E1uni10E2uni10E3uni10E4uni10E5uni10E6uni10E7uni10E8uni10E9uni10EAuni10EBuni10ECuni10EDuni10EEuni10EFuni10F0uni10F1uni10F2uni10F3uni10F4uni10F5uni10F6uni10F7uni10F8uni10F9uni10FAuni10FBuni10FCuni1D02uni1D08uni1D09uni1D14uni1D16uni1D17uni1D1Duni1D1Euni1D1Funi1D2Cuni1D2Duni1D2Euni1D30uni1D31uni1D32uni1D33uni1D34uni1D35uni1D36uni1D37uni1D38uni1D39uni1D3Auni1D3Buni1D3Cuni1D3Euni1D3Funi1D40uni1D41uni1D42uni1D43uni1D44uni1D45uni1D46uni1D47uni1D48uni1D49uni1D4Auni1D4Buni1D4Cuni1D4Duni1D4Euni1D4Funi1D50uni1D51uni1D52uni1D53uni1D54uni1D55uni1D56uni1D57uni1D58uni1D59uni1D5Auni1D5Buni1D62uni1D63uni1D64uni1D65uni1D77uni1D78uni1D7Buni1D85uni1D9Buni1D9Cuni1D9Duni1D9Euni1D9Funi1DA0uni1DA1uni1DA2uni1DA3uni1DA4uni1DA5uni1DA6uni1DA7uni1DA8uni1DA9uni1DAAuni1DABuni1DACuni1DADuni1DAEuni1DAFuni1DB0uni1DB1uni1DB2uni1DB3uni1DB4uni1DB5uni1DB6uni1DB7uni1DB9uni1DBAuni1DBBuni1DBCuni1DBDuni1DBEuni1DBFuni1E00uni1E01uni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3Funi1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Buni1E9Funi1EA0uni1EA1uni1EACuni1EADuni1EB0uni1EB1uni1EB6uni1EB7uni1EB8uni1EB9uni1EBCuni1EBDuni1EC6uni1EC7uni1ECAuni1ECBuni1ECCuni1ECDuni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE8uni1EE9uni1EEAuni1EEBuni1EEEuni1EEFuni1EF0uni1EF1Ygraveygraveuni1EF4uni1EF5uni1EF8uni1EF9uni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEuni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni2010uni2011 figuredashuni2015 underscoredbl quotereverseduni201Funi2023uni202Funi2031minuteseconduni2034uni2035uni2036uni2037 exclamdbluni203Duni203Euni2045uni2046uni2047uni2048uni2049uni205Funi2070uni2071uni2074uni2075uni2076uni2077uni2078uni2079uni207Auni207Buni207Cuni207Duni207Euni207Funi2080uni2081uni2082uni2083uni2084uni2085uni2086uni2087uni2088uni2089uni208Auni208Buni208Cuni208Duni208Euni2090uni2091uni2092uni2093uni2094uni20A0 colonmonetaryuni20A2lirauni20A5uni20A6pesetauni20A8uni20A9uni20AAdongEurouni20ADuni20AEuni20AFuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5uni2102uni2105uni210Duni210Euni210Funi2115uni2116uni2117uni2119uni211Auni211Duni2124uni2126uni212Auni212B estimatedonethird twothirdsuni2155uni2156uni2157uni2158uni2159uni215A oneeighth threeeighths fiveeighths seveneighthsuni215F arrowleftarrowup arrowright arrowdown arrowboth arrowupdnuni2196uni2197uni2198uni2199uni219Auni219Buni219Cuni219Duni219Euni219Funi21A0uni21A1uni21A2uni21A3uni21A4uni21A5uni21A6uni21A7 arrowupdnbseuni21A9uni21AAuni21ABuni21ACuni21ADuni21AEuni21AFuni21B0uni21B1uni21B2uni21B3uni21B4carriagereturnuni21B6uni21B7uni21B8uni21B9uni21BAuni21BBuni21BCuni21BDuni21BEuni21BFuni21C0uni21C1uni21C2uni21C3uni21C4uni21C5uni21C6uni21C7uni21C8uni21C9uni21CAuni21CBuni21CCuni21CDuni21CEuni21CF arrowdblleft arrowdblup arrowdblright arrowdbldown arrowdblbothuni21D5uni21D6uni21D7uni21D8uni21D9uni21DAuni21DBuni21DCuni21DDuni21DEuni21DFuni21E0uni21E1uni21E2uni21E3uni21E4uni21E5uni21E6uni21E7uni21E8uni21E9uni21EAuni21EBuni21ECuni21EDuni21EEuni21EFuni21F0uni21F1uni21F2uni21F3uni21F4uni21F5uni21F6uni21F7uni21F8uni21F9uni21FAuni21FBuni21FCuni21FDuni21FEuni21FF universaluni2201 existentialuni2204emptysetgradientelement notelementuni220Asuchthatuni220Cuni220Duni2213uni2215 asteriskmathuni2218uni2219uni221Buni221C proportional orthogonalangle logicaland logicalor intersectionunionuni222Cuni222Duni2238uni2239uni223Auni223Bsimilaruni223Duni2241uni2242uni2243uni2244 congruentuni2246uni2247uni2249uni224Auni224Buni224Cuni224Duni224Euni224Funi2250uni2251uni2252uni2253uni2254uni2255uni2256uni2257uni2258uni2259uni225Auni225Buni225Cuni225Duni225Euni225F equivalenceuni2262uni2263uni2266uni2267uni2268uni2269uni226Duni226Euni226Funi2270uni2271uni2272uni2273uni2274uni2275uni2276uni2277uni2278uni2279uni227Auni227Buni227Cuni227Duni227Euni227Funi2280uni2281 propersubsetpropersuperset notsubsetuni2285 reflexsubsetreflexsupersetuni2288uni2289uni228Auni228Buni228Funi2290uni2291uni2292 circleplusuni2296circlemultiplyuni2298uni2299uni229Auni229Buni229Cuni229Duni229Euni229Funi22A0uni22A1dotmathuni22C6uni22CDuni22DAuni22DBuni22DCuni22DDuni22DEuni22DFuni22E0uni22E1uni22E2uni22E3uni22E4uni22E5uni22E6uni22E7uni22E8uni22E9uni22EFuni2300uni2301houseuni2303uni2304uni2305uni2306uni2308uni2309uni230Auni230Buni230Cuni230Duni230Euni230F revlogicalnotuni2311uni2312uni2313uni2314uni2315uni2318uni2319uni231Cuni231Duni231Euni231F integraltp integralbtuni2325uni2326uni2327uni2328uni232Buni2335uni2337uni2338uni2339uni233Auni233Buni233Cuni233Duni233Euni2341uni2342uni2343uni2344uni2347uni2348uni2349uni234Buni234Cuni234Duni2350uni2352uni2353uni2354uni2357uni2358uni2359uni235Auni235Buni235Cuni235Euni235Funi2360uni2363uni2364uni2365uni2368uni2369uni236Buni236Cuni236Duni236Euni236Funi2370uni2373uni2374uni2375uni2376uni2377uni2378uni2379uni237Auni237Duni2380uni2381uni2382uni2383uni2388uni2389uni238Auni238Buni2395uni239Buni239Cuni239Duni239Euni239Funi23A0uni23A1uni23A2uni23A3uni23A4uni23A5uni23A6uni23A7uni23A8uni23A9uni23AAuni23ABuni23ACuni23ADuni23AEuni23CEuni23CFuni2423SF100000uni2501SF110000uni2503uni2504uni2505uni2506uni2507uni2508uni2509uni250Auni250BSF010000uni250Duni250Euni250FSF030000uni2511uni2512uni2513SF020000uni2515uni2516uni2517SF040000uni2519uni251Auni251BSF080000uni251Duni251Euni251Funi2520uni2521uni2522uni2523SF090000uni2525uni2526uni2527uni2528uni2529uni252Auni252BSF060000uni252Duni252Euni252Funi2530uni2531uni2532uni2533SF070000uni2535uni2536uni2537uni2538uni2539uni253Auni253BSF050000uni253Duni253Euni253Funi2540uni2541uni2542uni2543uni2544uni2545uni2546uni2547uni2548uni2549uni254Auni254Buni254Cuni254Duni254Euni254FSF430000SF240000SF510000SF520000SF390000SF220000SF210000SF250000SF500000SF490000SF380000SF280000SF270000SF260000SF360000SF370000SF420000SF190000SF200000SF230000SF470000SF480000SF410000SF450000SF460000SF400000SF540000SF530000SF440000uni256Duni256Euni256Funi2570uni2571uni2572uni2573uni2574uni2575uni2576uni2577uni2578uni2579uni257Auni257Buni257Cuni257Duni257Euni257Fupblockuni2581uni2582uni2583dnblockuni2585uni2586uni2587blockuni2589uni258Auni258Blfblockuni258Duni258Euni258Frtblockltshadeshadedkshadeuni2594uni2595uni2596uni2597uni2598uni2599uni259Auni259Buni259Cuni259Duni259Euni259F filledboxH22073uni25A2uni25A3uni25A4uni25A5uni25A6uni25A7uni25A8uni25A9H18543H18551 filledrectuni25ADuni25AEuni25AFuni25B0uni25B1triagupuni25B3uni25B4uni25B5uni25B6uni25B7uni25B8uni25B9triagrtuni25BBtriagdnuni25BDuni25BEuni25BFuni25C0uni25C1uni25C2uni25C3triaglfuni25C5uni25C6uni25C7uni25C8uni25C9circleuni25CCuni25CDuni25CEH18533uni25D0uni25D1uni25D2uni25D3uni25D4uni25D5uni25D6uni25D7 invbullet invcircleuni25DAuni25DBuni25DCuni25DDuni25DEuni25DFuni25E0uni25E1uni25E2uni25E3uni25E4uni25E5 openbulletuni25E7uni25E8uni25E9uni25EAuni25EBuni25ECuni25EDuni25EEuni25EFuni25F0uni25F1uni25F2uni25F3uni25F4uni25F5uni25F6uni25F7uni25F8uni25F9uni25FAuni25FBuni25FCuni25FDuni25FEuni25FFuni2600uni2601uni2602uni2603uni2604uni2605uni2606uni2607uni2608uni2609uni260Auni260Buni260Cuni260Duni260Euni260Funi2610uni2611uni2612uni2613uni2614uni2615uni2616uni2617uni2618uni2619uni261Auni261Buni261Cuni261Duni261Euni261Funi2620uni2621uni2622uni2623uni2624uni2625uni2626uni2627uni2628uni2629uni262Auni262Buni262Cuni262Duni262Euni262Funi2638uni2639 smileface invsmilefacesununi263Duni263Euni263Ffemaleuni2641maleuni2643uni2644uni2645uni2646uni2647uni2648uni2649uni264Auni264Buni264Cuni264Duni264Euni264Funi2650uni2651uni2652uni2653uni2654uni2655uni2656uni2657uni2658uni2659uni265Auni265Buni265Cuni265Duni265Euni265Fspadeuni2661uni2662clubuni2664heartdiamonduni2667uni2668uni2669 musicalnotemusicalnotedbluni266Cuni266Duni266Euni266Funi2670uni2671uni2672uni2673uni2674uni2675uni2676uni2677uni2678uni2679uni267Auni267Buni267Cuni267Duni267Euni267Funi2680uni2681uni2682uni2683uni2684uni2685uni2686uni2687uni2688uni2689uni268Auni268Buni2690uni2691uni2692uni2693uni2694uni2695uni2696uni2697uni2698uni2699uni269Auni269Buni269Cuni26A0uni26A1uni26B0uni26B1uni2701uni2702uni2703uni2704uni2706uni2707uni2708uni2709uni270Cuni270Duni270Euni270Funi2710uni2711uni2712uni2713uni2714uni2715uni2716uni2717uni2718uni2719uni271Auni271Buni271Cuni271Duni271Euni271Funi2720uni2721uni2722uni2723uni2724uni2725uni2726uni2727uni2729uni272Auni272Buni272Cuni272Duni272Euni272Funi2730uni2731uni2732uni2733uni2734uni2735uni2736uni2737uni2738uni2739uni273Auni273Buni273Cuni273Duni273Euni273Funi2740uni2741uni2742uni2743uni2744uni2745uni2746uni2747uni2748uni2749uni274Auni274Buni274Duni274Funi2750uni2751uni2752uni2756uni2758uni2759uni275Auni275Buni275Cuni275Duni275Euni2761uni2762uni2763uni2764uni2765uni2766uni2767uni2768uni2769uni276Auni276Buni276Cuni276Duni276Euni276Funi2770uni2771uni2772uni2773uni2774uni2775uni2794uni2798uni2799uni279Auni279Buni279Cuni279Duni279Euni279Funi27A0uni27A1uni27A2uni27A3uni27A4uni27A5uni27A6uni27A7uni27A8uni27A9uni27AAuni27ABuni27ACuni27ADuni27AEuni27AFuni27B1uni27B2uni27B3uni27B4uni27B5uni27B6uni27B7uni27B8uni27B9uni27BAuni27BBuni27BCuni27BDuni27BEuni27C5uni27C6uni27E0uni27E8uni27E9uni29EBuni29FAuni29FBuni2A2Funi2B12uni2B13uni2B14uni2B15uni2B16uni2B17uni2B18uni2B19uni2B1Auni2C64uni2C6Euni2C6Funi2C75uni2C76uni2C77uni2C79uni2C7Auni2C7Cuni2C7Duni2E18uni2E22uni2E23uni2E24uni2E25uni2E2EuniA708uniA709uniA70AuniA70BuniA70CuniA70DuniA70EuniA70FuniA710uniA711uniA712uniA713uniA714uniA715uniA716uniA71BuniA71CuniA71DuniA71EuniA71FuniA789uniA78AuniA78BuniA78CuniF6C5uniFB52uniFB53uniFB54uniFB55uniFB56uniFB57uniFB58uniFB59uniFB5AuniFB5BuniFB5CuniFB5DuniFB5EuniFB5FuniFB60uniFB61uniFB62uniFB63uniFB64uniFB65uniFB66uniFB67uniFB68uniFB69uniFB6AuniFB6BuniFB6CuniFB6DuniFB6EuniFB6FuniFB70uniFB71uniFB72uniFB73uniFB74uniFB75uniFB76uniFB77uniFB78uniFB79uniFB7AuniFB7BuniFB7CuniFB7DuniFB7EuniFB7FuniFB80uniFB81uniFB8AuniFB8BuniFB8CuniFB8DuniFB8EuniFB8FuniFB90uniFB91uniFB92uniFB93uniFB94uniFB95uniFB9EuniFB9FuniFBAAuniFBABuniFBACuniFBADuniFBE8uniFBE9uniFBFCuniFBFDuniFBFEuniFBFFuniFE70uniFE71uniFE72uniFE73uniFE74uniFE76uniFE77uniFE78uniFE79uniFE7AuniFE7BuniFE7CuniFE7DuniFE7EuniFE7FuniFE80uniFE81uniFE82uniFE83uniFE84uniFE85uniFE86uniFE87uniFE88uniFE89uniFE8AuniFE8BuniFE8CuniFE8DuniFE8EuniFE8FuniFE90uniFE91uniFE92uniFE93uniFE94uniFE95uniFE96uniFE97uniFE98uniFE99uniFE9AuniFE9BuniFE9CuniFE9DuniFE9EuniFE9FuniFEA0uniFEA1uniFEA2uniFEA3uniFEA4uniFEA5uniFEA6uniFEA7uniFEA8uniFEA9uniFEAAuniFEABuniFEACuniFEADuniFEAEuniFEAFuniFEB0uniFEB1uniFEB2uniFEB3uniFEB4uniFEB5uniFEB6uniFEB7uniFEB8uniFEB9uniFEBAuniFEBBuniFEBCuniFEBDuniFEBEuniFEBFuniFEC0uniFEC1uniFEC2uniFEC3uniFEC4uniFEC5uniFEC6uniFEC7uniFEC8uniFEC9uniFECAuniFECBuniFECCuniFECDuniFECEuniFECFuniFED0uniFED1uniFED2uniFED3uniFED4uniFED5uniFED6uniFED7uniFED8uniFED9uniFEDAuniFEDBuniFEDCuniFEDDuniFEDEuniFEDFuniFEE0uniFEE1uniFEE2uniFEE3uniFEE4uniFEE5uniFEE6uniFEE7uniFEE8uniFEE9uniFEEAuniFEEBuniFEECuniFEEDuniFEEEuniFEEFuniFEF0uniFEF1uniFEF2uniFEF3uniFEF4uniFEF5uniFEF6uniFEF7uniFEF8uniFEF9uniFEFAuniFEFBuniFEFCuniFEFFuniFFF9uniFFFAuniFFFBuniFFFCuniFFFDu1D670u1D671u1D672u1D673u1D674u1D675u1D676u1D677u1D678u1D679u1D67Au1D67Bu1D67Cu1D67Du1D67Eu1D67Fu1D680u1D681u1D682u1D683u1D684u1D685u1D686u1D687u1D688u1D689u1D68Au1D68Bu1D68Cu1D68Du1D68Eu1D68Fu1D690u1D691u1D692u1D693u1D694u1D695u1D696u1D697u1D698u1D699u1D69Au1D69Bu1D69Cu1D69Du1D69Eu1D69Fu1D6A0u1D6A1u1D6A2u1D6A3u1D7F6u1D7F7u1D7F8u1D7F9u1D7FAu1D7FBu1D7FCu1D7FDu1D7FEu1D7FF dlLtcaron DiaeresisAcuteTildeGrave CircumflexCaron fractionslash uni0311.case uni0306.case uni0307.case uni030B.case uni030F.case thinquestion uni0304.caseunderbar underbar.wideunderbar.smalljotdiaeresis.symbols arabic_dot arabic_2dots arabic_3dots uni066E.fina uni06A1.init uni06A1.medi uni066F.fina uni06A1.finaarabic_3dots_aarabic_2dots_a arabic_4dotsarabic_gaf_bararabic_gaf_bar_a arabic_ringEng.altuni066Euni066Funi067Cuni067Duni0681uni0682uni0685uni0692uni06A1uni06B5uni06BAuni06C6uni06CEuni06D5]A GA% } % 2  %%@Y}2}Y&Y@&//2G@Gddkߖږ؍ }:Ս :  ϊ̖ˋ%}Ś   ]%]@%AA dd@2(-}-d   ..A]%]@%%%A  %d%BSx~}~}}|{zwvut uu@t tss@rqponSonm(nSm(lk2ji2hgfedcbcbba`_^Z ^]d\[Z [Z YXWVUU2TSRQ}PONM-MLK(JIJ7ICIHEHGCGdFEFEDCD7CBCC@@ BABB@ A@AA@ @? @@@ ? ? ?@@d>=-=<;(:9B9d818K76-65K404K3032B21-10/-/. .-,--@ ,,,@@+*%+* *%):)('&%B%E$#""! -!} -KBBF-B-B-B@  @   @    @  @7    -:-:-d++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++munin-2.0.75/master/MasterBuilder.pm000066400000000000000000000001621451614574100173350ustar00rootroot00000000000000package MasterBuilder; use base qw(Module::Build); use lib '../common/blib/lib'; use warnings; use strict; 1; munin-2.0.75/master/_bin/000077500000000000000000000000001451614574100151455ustar00rootroot00000000000000munin-2.0.75/master/_bin/munin-cgi-graph.in000077500000000000000000000345271451614574100205000ustar00rootroot00000000000000#!@@PERL@@ -T # -*- cperl -*- =begin comment Copyright (C) 2004-2010 Jimmy Olsen, Steve Schnepp This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =end comment =cut use strict; use warnings; use IO::Handle; use Date::Parse; use POSIX qw(strftime locale_h); use CGI::Fast qw(:cgi); use CGI::Carp qw(fatalsToBrowser); use Time::HiRes qw(gettimeofday tv_interval); use Munin::Master::GraphOld; use Munin::Master::Utils; use Munin::Master::Logger; use Log::Log4perl qw( :easy ); my $GRAPHER = "$Munin::Common::Defaults::MUNIN_LIBDIR/munin-graph"; my $conffile = "$Munin::Common::Defaults::MUNIN_CONFDIR/munin.conf"; my %period = ( "day" => 300, "week" => 1800, "month" => 7200, "year" => 86400, "week-sum" => 1800, "year-sum" => 86400 ); my $logfile; my $scale = "day"; my @params ; push @params, "--config", $ENV{'MUNIN_CONFIG'} if (defined $ENV{'MUNIN_CONFIG'}); push @params, "--no-fork"; # FastCgi forks for us push @params, "--skip-locking", "--skip-stats", "--nolazy"; push @params, "--log-file", $logfile; my $config = graph_startup(\@params); logger_open($config->{'logdir'}); logger_debug() if defined($ENV{CGI_DEBUG}); # BEGIN FAST-CGI LOOP: setlocale (LC_TIME, 'C'); my $nb_request = 0; my $nb_request_max = 0; while (new CGI::Fast) { # 1rst thing is to validate the URL. Only a subset of chars are allowed. # Return 404 if not compliant, w/o logging. # This fixes http://bugs.debian.org/668666 and closes a lots of other potential bugs. if ( has_offending_chars($ENV{PATH_INFO}) || has_offending_chars($ENV{QUERY_STRING}) ) { # If parameters are not valid, just pretend we didn't find anything. print "Status: 404 Not Found\r\n", "Content-Type: image/png\r\n", "X-Munin-Pid: $$\r\n", "X-Munin-Request: $nb_request/$nb_request_max\r\n", "\r\n"; next; } my $pinpoint = undef; my $path = $ENV{PATH_INFO} || ""; DEBUG "Request path is $path"; # The full URL looks like this: # Case 1: # http://localhost:8080/munin-cgi/munin-cgi-graph/client/\ # Backend/dafnes.client.example.com/diskstats_iops-week.png # $path should be # /client/Backend/dafnes.client.example.com/diskstats_iops-week.png # # Interesting bits about that url: Nested groups! # # Case 2: # http://localhost:8080/munin-cgi/munin-cgi-graph/client/\ # Backend/dafnes.client.example.com/diskstats_iops/sda-week.png # $path should be # /client/Backend/dafnes.client.example.com/diskstats_iops/\ # sda-week.png # # Interesting bit that url: Nested groups at the start and multigraph # nesting bits at the end. # # Case 3: # http://localhost:8080/munin-cgi/munin-cgi-graph/client/\ # dafnes.client.example.com/if_err_bond0-day.png # $path: # /client/dafnes.client.example.com/if_err_bond0-day.png # # Simplest (old munin 1.2): No nesting at any end, fixed number of /es # # Despite the slippery structure of the $path this expression works with # the rest of the code. To make a more scientific try we would need to # split on / and traverse the $config to determine what kind of part # (domain, nested domain, host, service/plugin, or nested service) # we're looking at. # # Scale will in any case work out since - is only used before the # day/week/month/year/pinpoint part, and the next part is always .png. # # Note: $serv *may* have some "-" inside (See #1218) my ($dom, $host, $serv, $scale) = $path =~ m#^/(.*)/([^/]+)/([\w-]+)-([\w=,]+)\.png#; ## avoid bug in vim DEBUG "asked for ($dom, $host, $serv, $scale)"; if ($scale =~ /pinpoint=(\d+),(\d+)/) { $pinpoint = [ $1, $2, ]; } if (! &verify_parameters ($dom, $host, $serv, $scale)) { # If parameters are not valid, just say we didn't find anything. print "Status: 404 Not Found\r\n", "Content-Type: image/png\r\n", "X-Munin-Pid: $$\r\n", "X-Munin-Request: $nb_request/$nb_request_max\r\n", "\r\n"; next; } # Environment variables are cleared each request # so we must set RRDCACHED_ADDRESS each time $ENV{RRDCACHED_ADDRESS} = $config->{rrdcached_socket} if $config->{rrdcached_socket}; my $filename = get_picture_filename ($config, $dom, $host, $serv, $scale, $ENV{QUERY_STRING}); my $time = time; # If a "Cache-Control: no-cache" header gets send, we regenerate the image in every case: # Removed $pinpoint from the $no_cache expression - janl 2010-09-29 my $no_cache = defined($ENV{HTTP_CACHE_CONTROL}) && $ENV{HTTP_CACHE_CONTROL} =~ /no-cache/i; # Be able to deactivate the cache with the url if (defined(CGI::param("no_cache")) && CGI::param("no_cache") eq "yes") { $no_cache = 1; } # Having some QUERY_STRING disables the cache. if (defined($ENV{QUERY_STRING}) && $ENV{QUERY_STRING} ne "") { $no_cache = 1; } # Compute the cache values # FIXME: Take the plugins update_rate into account here, at least for # the day graph. update_rate should be in $config # my $graph_ttl = $pinpoint ? 1 : $period{$scale}; my $graph_ttl = $period{$scale} || 1; my $graph_last_expires = $time - ($time % $graph_ttl); my $graph_epoch = (! $no_cache) && file_newer_than($filename, $graph_last_expires); if ($graph_epoch) { # The graph is fresh enough. Sending either IMS if asked, or # just skip generation # Check for If-Modified-Since and send 304 if not changed: if (defined $ENV{HTTP_IF_MODIFIED_SINCE} && ! rfctime_newer_than($ENV{HTTP_IF_MODIFIED_SINCE}, $graph_epoch)) { my $headers = get_headers_for_file($filename, $graph_ttl); print "Status: 304\r\n", "Content-Type: image/png\r\n", "X-Munin-Pid: $$\r\n", "X-Munin-Request: $nb_request/$nb_request_max\r\n", "Content-Length: 0\r\n", "Expires: $headers->{Expires}\r\n", "Last-Modified: ", $headers->{"Last-Modified"}, "\r\n". "\n"; # We replied, continue with the next request next; } } else { # Should generate it my $scale_options; if ($pinpoint) { $scale_options = "--pinpoint=" . $pinpoint->[0] . "," . $pinpoint->[1]; } else { $scale_options = "--$scale"; } # Try to generate the graph my $generated_file = eval { draw_graph_or_complain($dom, $host, $serv, $scale_options, $filename); }; # handle exceptions if ($@) { if ($@ =~ m/^Could not find FQN/) { # Unknown graph asked print "Status: 404 Not Found\r\n", "Content-Type: image/png\r\n", "X-Munin-Pid: $$\r\n", "X-Munin-Request: $nb_request/$nb_request_max\r\n", "\r\n"; # Next item next; } # Generic error # .. we DO NOT DIE, as spawn-fcgi doesn't like it. ERROR "[ERROR] $@"; print "Status: 500\r\n", "Content-Type: text/plain\r\n", "X-Munin-Pid: $$\r\n", "X-Munin-Request $nb_request/$nb_request_max\r\n", ""; next; } # draw_graph_or_complain return 0, but already displayed a message next unless ($generated_file); } # Now send it: headers print "Status: 200\r\n", "Content-Type: image/png\r\n", "X-Munin-Pid: $$\r\n", "X-Munin-Request: $nb_request/$nb_request_max\r\n", ""; my $headers = get_headers_for_file($filename, $graph_ttl); foreach my $header_name (keys %$headers) { print "$header_name: $headers->{$header_name}\r\n"; } print "\r\n"; # ... and graph data send_graph_data($filename); # If $no_cache, remove the file. No need to keep it anyway. # And it handles http://bugs.debian.org/668667 unlink($filename) if $no_cache; } continue { $nb_request++; if ($nb_request_max && $nb_request > $nb_request_max) { # Cycle last; } } # END FAST-CGI LOOP - Time to die. Nicely. exit 0; sub get_headers_for_file { my ($filename, $graph_ttl) = @_; # At this time the file exists and should be served my @stats = stat ($filename); my $mtime_epoch = $stats[9]; my $last_modified = get_w3c_date_from_epoch($mtime_epoch); # "Expires" has to use last modified time as base: my $graph_next_expires = $mtime_epoch - ($mtime_epoch % $graph_ttl) + $graph_ttl; my $expires = get_w3c_date_from_epoch($graph_next_expires); return { "Expires" => $expires, "Last-Modified" => $last_modified, "Content-Length" => $stats[7], }; } sub get_w3c_date_from_epoch { my($epoch) = @_; return strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime($epoch)); } sub send_graph_data { # Serve the graph contents. my($filename) = @_; my $buffer; if (! open (GRAPH_PNG_FILE, '<', $filename) ) { ERROR "[FATAL] Could not open image file \"$filename\" for reading: $!\n"; # We don't send anything... # .. we DO NOT DIE, as spawn-fcgi doesn't like it. return; } # No buffering wanted when sending the file local $| = 1; while (sysread(GRAPH_PNG_FILE,$buffer,40960)) { print $buffer; } close (GRAPH_PNG_FILE); } sub get_picture_filename { my $config = shift; my $domain = shift; my $name = shift; my $service = shift; my $scale = shift; my $params = shift; # XXX - hack to fix cgitmpdir default $config->{cgitmpdir} ||= "$Munin::Common::Defaults::MUNIN_DBDIR/cgi-tmp"; my $cgi_tmp_dir = $config->{cgitmpdir} . "/munin-cgi-graph"; $params = $params ? "?$params" : ""; $params =~ tr/\//_/; # / are forbidden in a filename $params = $1 if $params =~ m/(.*)/; # XXX - Q&D untaint return "$cgi_tmp_dir/$domain/$name/$service-$scale.png" . $params; } sub has_offending_chars { my $url_part = shift; return 0 if ! defined $url_part; # "." and ":" are for ip_ in IPv4 & IPv6 return $url_part =~ m:[^a-zA-Z0-9_/.,=&\:-]:; } sub verify_parameters { my $dom = shift; my $host = shift; my $serv = shift; my $scale = shift; if (!$dom) { WARN '[WARNING] Request for graph without specifying domain. Bailing out.'; return 0; } if (!$host) { WARN '[WARNING] Request for graph without specifying host. Bailing out.'; return 0; } if (!$serv) { WARN '[WARNING] Request for graph without specifying service. Bailing out.'; return 0; } if (!$scale) { WARN '[WARNING] Request for graph without specifying scale. Bailing out.'; return 0; } else { if (!defined $period{$scale} && $scale !~ /pinpoint=\d+,\d+/) { WARN '[WARNING] Weird pinpoint setting "'.$scale.'". Bailing out.'; return 0; } } # Checks the image size requested. if (( CGI::param("size_x") || "") =~ m/^(\d+)/) { my $max_size_x = ( $config->{max_size_x} || 4000); if ($1 > $max_size_x) { WARN "[WARNING] Asked image size x too large : $1 > $max_size_x. Bailing out."; return 0; } } if (( CGI::param("size_y") || "") =~ m/^(\d+)/) { my $max_size_y = ($config->{max_size_y} || 4000); if ($1 > $max_size_y) { WARN "[WARNING] Asked image size y too large : $1 > $max_size_y. Bailing out."; return 0; } } return 1; } sub file_newer_than { my $filename = shift; my $time = shift; if (-f $filename) { my @stats = stat (_); # $stats[9] holds the "last update" time and this needs # to be in the last update period my $last_update = $stats[9]; if ($last_update > $time) { return $last_update; } else { return 0; } } # No file found return 0; } sub draw_graph { my $dom = shift; my $host = shift; my $serv = shift; my $scale = shift; my $filename = shift; # remove old file if present if (-f $filename and !unlink($filename)) { ERROR "[FATAL] Could not remove \"$filename\": $!"; } $serv =~ s{[^\w_\/"'\[\]\(\)+=-]}{_}g; $serv =~ /^(.+)$/; $serv = $1; #" # . needs to be legal in host names $host =~ s{[^\w_\/"'\[\]\(\)\.+=-]}{_}g; $host =~ /^(.+)$/; $host = $1; #" # FIXME: Make "root" implied! my @params = ( '--host', $host, '--only-fqn', "root/$dom/$host/$serv", $scale, '--output-file', $filename ); # Sets the correct size on a by_graph basis { use Scalar::Util qw(looks_like_number); # using a temporary variable to avoid expansion in list context and fix CVE-2017-6188 my $size_x = CGI::param("size_x"); push @params, "--size_x", $size_x if looks_like_number($size_x); my $size_y = CGI::param("size_y"); push @params, "--size_y", $size_y if looks_like_number($size_y); my $upper_limit = CGI::param("upper_limit"); push @params, "--upper_limit", $upper_limit if looks_like_number($upper_limit); my $lower_limit = CGI::param("lower_limit"); push @params, "--lower_limit", $lower_limit if looks_like_number($lower_limit); } # Sometimes we want to set the IMG size, and not the canvas. push @params, "--full_size_mode" if (CGI::param("full_size_mode")); # Sometimes we want only the graph. Nothing else. push @params, "--only_graph" if (CGI::param("only_graph")); # XXX - the debug param is sticky. It really should be per request. push @params, "--debug" if (CGI::param("debug")); graph_main(\@params); return $filename; } sub draw_graph_or_complain { my $t0 = [ gettimeofday ]; # Actual work done here. my $ret = draw_graph(@_); my $graph_duration = tv_interval($t0); if (! -f $ret) { my ($dom, $host, $serv, $scale, $filename ) = @_; WARN "[WARNING] Could not draw graph \"$filename\": $ret"; print "Status: 500\r\n", "Content-Type: text/plain\r\n", "\r\n", "Could not draw graph \"$filename\"\r\n"; return 0; } else { print "X-Graph-Duration: $graph_duration\r\n"; return $ret; } } sub rfctime_newer_than { # See if the file has been modified since "the last time". # Format of since_string If-Modified-Since: Wed, 23 Jun 2004 16:11:06 GMT my $since_string = shift; my $created = shift; my $ifmodsec = str2time($since_string); return 1 if ($ifmodsec < $created); return 0; } # vim: syntax=perl ts=8 munin-2.0.75/master/_bin/munin-cgi-html.in000077500000000000000000000104171451614574100203330ustar00rootroot00000000000000#!@@PERL@@ -T # -*- cperl -*- =begin comment Copyright (C) 2004-2010 Jimmy Olsen, Steve Schnepp This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =end comment =cut use strict; use warnings; use POSIX qw(strftime); use CGI::Fast qw(:cgi); use CGI::Carp qw(fatalsToBrowser); use Time::HiRes qw(time); use Munin::Master::HTMLConfig; use Munin::Master::HTMLOld; use Munin::Master::Utils; use Munin::Master::Logger; use Log::Log4perl qw( :easy ); use Data::Dumper; my @times = ("day", "week", "month", "year"); my $config; my $lastchanged = 0; my @params; push @params, "--config", $ENV{'MUNIN_CONFIG'} if (defined $ENV{'MUNIN_CONFIG'}); # grab config html_startup(\@params); while(new CGI::Fast){ print header("text/html"); $config = get_config(1); show_page(); } sub show_page { my @path = split(/\//, $ENV{PATH_INFO}); emit_page(\@path); } sub get_next_part { my $path = shift; my $part; do { $part = shift(@$path); } while(defined $part && $part eq ""); return $part; } sub emit_page { my $path = shift; my $group = $config; #process groups $group = traverse_groups($path, $group); update_timestamp(); if(!defined $group->{"depth"} || $group->{"depth"} == 0){ #root url my $problems = get_next_part($path); if(defined $problems && $problems eq "problems.html"){ emit_problem_template(1); } else { unshift(@$path, $problems); (my $category, my $time) = get_global_category($path, $group); if(defined $category){ emit_category_template($category, $time, 1); } else { emit_main_index($group->{"groups"},0,1); } } } elsif(!$group->{"ncategories"}) { # group my $cmp_time = get_comparison_group($path, $group); if(defined $cmp_time){ # comparison template emit_comparison_template($group, $cmp_time, 1); } else { #group page emit_group_template($group, 1); } } else { #node my $service = get_node_service($path, $group); if(defined $service){ emit_service_template($service, 1); } else { emit_graph_template($group, 1); } } } sub traverse_groups { my ($path, $group) = @_; my $part = get_next_part($path); while(defined $part && (defined $group->{"groups_hash"}->{$part})) { $group = $group->{"groups_hash"}->{$part}; $part = get_next_part($path); } if(defined $part){ unshift(@$path,$part); # put the unprocessed part back on } return $group; } sub get_comparison_group { my ($path, $group) = @_; if(!$group->{"compare"}){ return undef; } # group is not a comparison group my $part = get_next_part($path); if(defined $part && $part =~ m/^comparison-([a-z]+)\.html/i){ if(grep /^$1$/, @times){ return lc $1; } } if(defined $part){ unshift(@$path, $part); #put the unprocessed part back on } return undef; } sub get_global_category { my ($path, $group) = @_; my $part = get_next_part($path); if(!defined $part){ return undef; } foreach my $category (@{$group->{"globalcats"}}) { foreach my $time (@times) { if($category->{"url" . $time} eq $part){ return ($category, $time); } } } return undef; } sub get_node_service { my ($path, $group) = @_; my $part = get_next_part($path); if(!defined $part){ return undef; } foreach my $category (@{$group->{"categories"}}) { foreach my $service (@{$category->{"services"}}) { if($part eq $service->{"node"}.".html"){ return $service; } } } return undef; } # CGI in perl 5.20 is now seriously broken as it doesn't import into the namespace. # So we have to delegate explicitly. It's easier than prefixing with CGI:: each use. # This workaround is applied only if "header" is undefined (i.e. for perl >= 5.20). if(!defined &header){ *header = sub { return CGI::header(@_); }; *path_info = sub { return CGI::path_info(@_); }; *url = sub { return CGI::url(@_); }; *script_name = sub { return CGI::script_name(@_); }; } munin-2.0.75/master/_bin/munin-check.in000066400000000000000000000101531451614574100176760ustar00rootroot00000000000000#!@@BASH@@ # -*- sh -*- # Copyright (C) 2008 Matthias Schmitz # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; version 2 dated June, # 1991. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # #### # prints usage function usage() { echo "Usage: munin-check [options] Options: -h|--help Show this help. -f|--fix-permissions Fix the permissions of the munin dirs and files. Needs superuser rights. Please don't use this script if you are using 'graph_strategy cgi'! It doesn't care about the right permissions for www-data yet... " } # Get options from the command line TEMP=$(getopt -o fh --long fix-permissions,help -n 'munin-check' -- "$@") if [[ $? -ne 0 ]]; then echo "Terminating..." >&2 exit 1 fi # Note the quotes around `$TEMP': they are essential! eval set -- "$TEMP" while :; do case "$1" in -h|--help) usage ; exit 0; shift ;; -f|--fix-permissions) PLEASE_FIXME="true" ; shift ;; --) shift ; break ;; *) echo "Internal error!" ; exit 1 ;; esac done #### # sets owner to "@@USER@@" function fix_owner() { fix_object=$1; shift fix_owner=$1; shift if [[ $(id -u) -eq 0 ]]; then # -R is wrong, in case we're not recursing, and if we are # recursing then fix_owner will be called again. OK, that's # slower, but still more correct. chown $fix_owner $fix_object; else echo "Fixing the permissions needs superuser rights. You should run \"munin-check -f\" as root." exit 0; fi } #### # check if "@@USER@@" is owner, if PLEASE_FIXME set it calls fix_owner() function owner_ok() { object=$1; shift || exit 1 correctowner=$1; shift || exit 1 owner=$(ls -ld $object | awk '{print $3}') if [[ "$owner" != "$correctowner" ]]; then echo "# $object : Wrong owner ($owner != $correctowner)"; if [[ "$PLEASE_FIXME" == "true" ]]; then fix_owner $object $correctowner fi fi if [[ -d $object ]]; then if [[ -n "$norec" ]]; then # The $norec variable cuts off recursion return 0 fi case $object in "lost+found") return 0;; esac # ... and then dive into it for subobject in $object/*; do owner_ok $subobject $correctowner done fi } function perm_ok(){ object=$1; shift || exit 1 correctperm=$1; shift || exit 1 perm=$(perl -e 'printf "%o\n", 07777 & (stat $ARGV[0])[2]' $object) if [[ $perm -ne $correctperm ]]; then echo "# $object : Wrong permissions ($perm != $correctperm)"; if [[ "$PLEASE_FIXME" == "true" ]]; then chmod $correctperm $object fi fi if [[ -d $object ]]; then # check the owner of the dir ... if [[ -n "$norec" ]]; then # The $norec variable cuts off recursion return 0 fi case $object in "lost+found") return 0;; esac # ... and then dive into it for subobject in $object/*; do perm_ok $subobject $correctperm done fi } #### # main echo "check @@HTMLDIR@@" owner_ok @@HTMLDIR@@ @@USER@@ for dir in @@DBDIR@@/*; do # Do not check the plugin-state directory case $dir in */plugin-state) continue;; esac echo "check $dir" owner_ok $dir @@USER@@ done echo "check miscellaneous" norec=yes owner_ok @@LOGDIR@@ @@USER@@ norec=yes owner_ok @@DBDIR@@ @@USER@@ norec=yes perm_ok @@DBDIR@@ 755 for dir in @@DBDIR@@/datafile @@DBDIR@@/limits @@DBDIR@@/*.stats; do norec=yes owner_ok $dir @@USER@@ norec=yes perm_ok $dir 644 done norec=yes owner_ok @@PLUGSTATE@@ @@PLUGINUSER@@ norec=yes perm_ok @@PLUGSTATE@@ 775 norec=yes perm_ok @@CONFDIR@@/plugin-conf.d 755 echo "Check done. Please note that this script only checks most things," echo "not all things." echo echo "Please also note that this script may be buggy." echo munin-2.0.75/master/_bin/munin-cron.in000077500000000000000000000011661451614574100175710ustar00rootroot00000000000000#!@@GOODSH@@ # This used to test if the executables were installed. But that is # perfectly redundant and suppresses errors that the admin should see. @@LIBDIR@@/munin-update $@ || exit 1 # The result of munin-limits is needed by munin-html but not by # munin-graph. So run it in the background now, it will be done # before munin-graph. @@LIBDIR@@/munin-limits $@ # We always launch munin-html. # It is a noop if html_strategy is "cgi" nice @@LIBDIR@@/munin-html $@ || exit 1 # The result of munin-html is needed for munin-graph. # It is a noop if graph_strategy is "cgi" nice @@LIBDIR@@/munin-graph --cron $@ || exit 1 munin-2.0.75/master/_bin/munin-datafile2storable.in000077500000000000000000000003231451614574100222110ustar00rootroot00000000000000#!@@PERL@@ use strict; use warnings; use Data::Dumper; use Storable; use Munin::Master::Utils; while (my $file = shift) { my $config = munin_readconfig_raw($file); Storable::nstore_fd($config, \*STDOUT); } munin-2.0.75/master/_bin/munin-graph.in000077500000000000000000000204541451614574100177320ustar00rootroot00000000000000#!@@PERL@@ # -*- cperl -*- =encoding utf8 =head1 NAME munin-graph - generate graphs from RRD files =begin comment Copyright (C) 2004-2010 Jimmy Olsen, Steve Schnepp This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =end comment =cut use strict; use warnings; use IO::Handle; use Date::Parse; use POSIX qw(strftime); use Time::HiRes qw(gettimeofday tv_interval); use IO::File; use Munin::Master::GraphOld; use Munin::Master::Utils; use Munin::Master::Logger; use Log::Log4perl qw( :easy ); my $GRAPHER = "$Munin::Common::Defaults::MUNIN_LIBDIR/munin-graph"; my $conffile = "$Munin::Common::Defaults::MUNIN_CONFDIR/munin.conf"; my %period = ( "day" => 300, "week" => 1800, "month" => 7200, "year" => 86400, "week-sum" => 1800, "year-sum" => 86400 ); my $logfile; my $scale = "day"; my @params = @ARGV; push @params, "--no-fork"; # We do *not* want to fork. Perf -> FastCGI push @params, "--log-file", $logfile; my $config = graph_startup(\@params); logger_open($config->{'logdir'}); logger_debug() if $config->{debug} or defined($ENV{CGI_DEBUG}); if (! graph_check_cron() ) { # Should not be launched from cron. INFO "[INFO] graphing is cgi, do nothing"; exit 0; } INFO "Starting munin-graph"; my $graph_time = Time::HiRes::time; # BEGIN FAST-CGI LOOP: my $nb_request = 0; my $nb_request_max = 0; my $graph_fh = new IO::File($config->{dbdir} . "/graphs"); while (my $path = <$graph_fh>) { my $pinpoint = undef; DEBUG "Request path is $path"; # The full URL looks like this: # Case 1: # http://localhost:8080/munin-cgi/munin-cgi-graph/client/\ # Backend/dafnes.client.example.com/diskstats_iops-week.png # $path should be # client/Backend/dafnes.client.example.com/diskstats_iops-week.png # # Interesting bits about that url: Nested groups! # # Case 2: # http://localhost:8080/munin-cgi/munin-cgi-graph/client/\ # Backend/dafnes.client.example.com/diskstats_iops/sda-week.png # path should be # client/Backend/dafnes.client.example.com/diskstats_iops/\ # sda-week.png # # Interesting bit that url: Nested groups at the start and multigraph # nesting bits at the end. # # Case 3: # http://localhost:8080/munin-cgi/munin-cgi-graph/client/\ # dafnes.client.example.com/if_err_bond0-day.png # path: # client/dafnes.client.example.com/if_err_bond0-day.png # # Simplest (old munin 1.2): No nesting at any end, fixed number of /es # # Despite the slippery structure of the $path this expression works with # the rest of the code. To make a more scientific try we would need to # split on / and traverse the $config to determine what kind of part # (domain, nested domain, host, service/plugin, or nested service) # we're looking at. # # Scale will in any case work out since - is only used before the # day/week/month/year/pinpoint part, and the next part is always .png. # my ($dom, $host, $serv, $scale) = $path =~ m#^/(.*)/([^/]+)/(\w+)-([\w=,]+)\.png#; ## avoid bug in vim DEBUG "asked for ($dom, $host, $serv, $scale)"; if ($scale =~ /pinpoint=(\d+),(\d+)/) { $pinpoint = [ $1, $2, ]; } if (! &verify_parameters ($dom, $host, $serv, $scale)) { ERROR "Invalid parameters!"; next; } my $filename = get_picture_filename ($config, $dom, $host, $serv, $scale, $ENV{QUERY_STRING}); my $time = time; my $no_cache = 0; # Compute the cache values my $graph_ttl = $period{$scale} || 1; my $graph_last_expires = $time - ($time % $graph_ttl); if ($pinpoint || ! -f $filename || (stat(_))[9] < $graph_last_expires) { # Should generate it my $scale_options; if ($pinpoint) { $scale_options = "--pinpoint=" . $pinpoint->[0] . "," . $pinpoint->[1]; } else { $scale_options = "--$scale"; } next unless draw_graph_or_complain($dom, $host, $serv, $scale_options, $filename); } # Now send it: headers DEBUG "X-Munin-Request: $nb_request/$nb_request_max"; } continue { $nb_request++; if ($nb_request_max && $nb_request > $nb_request_max) { # Cycle last; } } # END FAST-CGI LOOP - Time to die. Nicely. $graph_time = sprintf("%.2f", (Time::HiRes::time - $graph_time)); INFO "Munin-graph finished ($graph_time sec)"; exit 0; sub get_w3c_date_from_epoch { my($epoch) = @_; return strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime($epoch)); } sub send_graph_data { # Serve the graph contents. my($filename) = @_; my $buffer; if (! open (GRAPH_PNG_FILE, '<', $filename) ) { ERROR "[FATAL] Could not open image file \"$filename\" for reading: $!\n"; die "[FATAL] Could not open image file \"$filename\" for reading: $!\n"; } # No buffering wanted when sending the file local $| = 1; while (sysread(GRAPH_PNG_FILE,$buffer,40960)) { print $buffer; } close (GRAPH_PNG_FILE); } sub get_picture_filename { my $config = shift; my $domain = shift; my $name = shift; my $service = shift; my $scale = shift; my $params = shift; my $cgi_tmp_dir = $config->{htmldir}; $params = $params ? "?$params" : ""; $params =~ tr/\//_/; # / are forbidden in a filename $params = $1 if $params =~ m/(.*)/; # XXX - Q&D untaint return "$cgi_tmp_dir/$domain/$name/$service-$scale.png" . $params; } sub verify_parameters { my $dom = shift; my $host = shift; my $serv = shift; my $scale = shift; if (!$dom) { WARN '[WARNING] Request for graph without specifying domain. Bailing out.'; return 0; } if (!$host) { WARN '[WARNING] Request for graph without specifying host. Bailing out.'; return 0; } if (!$serv) { WARN '[WARNING] Request for graph without specifying service. Bailing out.'; return 0; } if (!$scale) { WARN '[WARNING] Request for graph without specifying scale. Bailing out.'; return 0; } else { if (!defined $period{$scale} && $scale !~ /pinpoint=\d+,\d+/) { WARN '[WARNING] Weird pinpoint setting "'.$scale.'". Bailing out.'; return 0; } } return 1; } sub file_newer_than { my $filename = shift; my $time = shift; if (-f $filename) { my @stats = stat (_); # $stats[9] holds the "last update" time and this needs # to be in the last update period my $last_update = $stats[9]; if ($last_update > $time) { return $last_update; } else { return 0; } } # No file found return 0; } sub draw_graph { my $dom = shift; my $host = shift; my $serv = shift; my $scale = shift; my $filename = shift; # remove old file if present if (-f $filename and !unlink($filename)) { ERROR "[FATAL] Could not remove \"$filename\": $!"; die("[FATAL] cannot remove old graph file \"$filename\": $!"); } $serv =~ s{[^\w_\/"'\[\]\(\)+=-]}{_}g; $serv =~ /^(.+)$/; $serv = $1; #" # . needs to be legal in host names $host =~ s{[^\w_\/"'\[\]\(\)\.+=-]}{_}g; $host =~ /^(.+)$/; $host = $1; #" # FIXME: Make "root" implied! my @params = ( '--host', $host, '--only-fqn', "root/$dom/$host/$serv", $scale, '--output-file', $filename ); graph_main(\@params); return $filename; } sub draw_graph_or_complain { my $t0 = [ gettimeofday ]; # Actuall work done here. my $ret = draw_graph(@_); my $graph_duration = tv_interval($t0); if (! -f $ret) { my ($dom, $host, $serv, $scale, $filename ) = @_; WARN "[WARNING] Could not draw graph \"$filename\": $ret"; return 0; } else { return $ret; } } sub rfctime_newer_than { # See if the file has been modified since "the last time". # Format of since_string If-Modified-Since: Wed, 23 Jun 2004 16:11:06 GMT my $since_string = shift; my $created = shift; my $ifmodsec = str2time($since_string); return 1 if ($ifmodsec < $created); return 0; } # vim: syntax=perl ts=8 munin-2.0.75/master/_bin/munin-html.in000077500000000000000000000053071451614574100175750ustar00rootroot00000000000000#!@@PERL@@ # -*- cperl -*- =encoding utf8 =begin comment Copyright (C) 2002-2009 Jimmy Olsen, Audun Ytterdal, Kjell Magne Øierud, Nicolai Langfeldt, Linpro AS, Redpill Linpro AS and others. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. =end comment =cut use warnings; use strict; use Munin::Master::HTMLOld; $|=1; html_startup(\@ARGV); html_main(); # ### The End 1; =head1 NAME munin-html - A program to generate html-pages in an Munin installation =head1 SYNOPSIS munin-html [options] =head1 OPTIONS =over 5 =item B<< --service >> Limit services to those of EserviceE. Multiple --service options may be supplied. [unset] =item B<< --host >> Limit hosts to those of Ehost. Multiple --host options may be supplied. [unset] =item B<< --config >> Use EfileE as configuration file. [/etc/munin/munin.conf] =item B<< --help >> View help message. =item B<< --[no]debug >> If set, view debug messages. [--nodebug] =back =head1 DESCRIPTION Munin-html is a part of the package Munin, which is used in combination with Munin's node. Munin is a group of programs to gather data from Munin's nodes, graph them, create html-pages, and optionally warn Nagios about any off-limit values. Munin-html creates the html pages. =head1 FILES /etc/munin/munin.conf /var/lib/munin/datafile /var/log/munin/munin-html /var/cache/munin/www/* /var/run/munin/* =head1 VERSION This is munin-html version svn-trunk-r4007 =head1 AUTHORS Knut Haugen, Audun Ytterdal and Jimmy Olsen. =head1 BUGS munin-html does, as of now, not check the syntax of the configuration file. Please report other bugs in the bug tracker at L. =head1 COPYRIGHT Copyright (C) 2002-2009 Knut Haugen, Audun Ytterdal, and Jimmy Olsen / Linpro AS. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program is released under the GNU General Public License =head1 SEE ALSO For information on configuration options, please refer to the man page for F. =cut # vim:syntax=perl:ts=8 munin-2.0.75/master/_bin/munin-limits.in000077500000000000000000000072041451614574100201300ustar00rootroot00000000000000#!@@PERL@@ # -*- cperl -*- =encoding utf8 =begin comment Copyright (C) 2004-2008 Jimmy Olsen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. =end comment =cut use warnings; use strict; use Munin::Master::LimitsOld; limits_startup(\@ARGV); limits_main(); # ### The End exit 0; =head1 NAME munin-limits - A program to check for any off-limit values =head1 SYNOPSIS munin-limits [options] =head1 OPTIONS =over 5 =item B<< --service >> Limit services to those of EserviceE. Multiple --service options may be supplied. [unset] =item B<< --host >> Limit hosts to those of Ehost. Multiple --host options may be supplied. [unset] =item B<< --contact >> Limit contacts to those of Econtact. Multiple --contact options may be supplied. [unset] =item B<< --config >> Use EfileE as configuration file. [@@CONFDIR@@/munin.conf] =item B<< --always-send >> Force sending of messages even if you normally wouldn't. The can be a whitespace or comma separated list of the values "ok", "warning", "critical" or "unknown". This option may be specified several times, to add more values. Use of "--always-send" overrides the "always_send" value in munin.conf for configured contacts. See also --force. =item B<< --force >> Alias for "--always-send ok,warning,critical,unknown" =item B<< --[no]force-run-as-root >> Force running as root (stupid and unnecessary). [--noforce-root] =item B<< --help >> View help message. =item B<< --[no]debug >> If set, view debug messages. [--nodebug] =back =head1 DESCRIPTION Munin-limits is a part of the package Munin, which is used in combination with Munin's node. Munin is a group of programs to gather data from Munin's nodes, graph them, create html-pages, and optionally warn Nagios about any off-limit values. Munin-limits checks if any values are above or below the set limits, and saves these notes to a file. This file is later used by programs like munin-nagios (to warn nagios) and munin-html (to incorporate them in the web display). If a service has fields with "warning" or "critical"-options (e.g. "load.warning 10"), and the munin-server configuration file contains the necessary configuration options, munin-limits will check its value. =head1 FILES @@CONFDIR@@/munin.conf @@DBDIR@@/* @@STATEDIR@@/* =head1 VERSION This is munin-limits version @@VERSION@@ =head1 AUTHORS Knut Haugen, Audun Ytterdal and Jimmy Olsen. =head1 BUGS munin-limits does, as of now, not check the syntax of the configuration file. Please report other bugs in the bug tracker at L. =head1 COPYRIGHT Copyright (C) 2002-2006 Knut Haugen, Audun Ytterdal, and Jimmy Olsen / Linpro AS. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program is released under the GNU General Public License =head1 SEE ALSO For information on configuration options, please refer to the man page for F. =cut # vim: syntax=perl ts=8 munin-2.0.75/master/_bin/munin-storable2datafile.in000077500000000000000000000003071451614574100222130ustar00rootroot00000000000000#!@@PERL@@ use strict; use warnings; use Storable; use Munin::Master::Utils; while (my $file = shift) { my $config = Storable::retrieve($file); munin_writeconfig("stdout", $config, \*STDOUT); } munin-2.0.75/master/_bin/munin-update.in000077500000000000000000000114151451614574100201100ustar00rootroot00000000000000#!@@PERL@@ # -*- cperl -*- use warnings; use strict; use English qw(-no_match_vars); use Getopt::Long; use Pod::Usage; use Log::Log4perl qw(:easy); use Munin::Master::Update; use Munin::Master::Logger; use Munin::Master::Config; use Munin::Master::Utils; # TODO # # - Include data from Munin::Master::Config in config dump? # - nested groups Getopt::Long::Configure(qw(auto_help)); my $globconfig = Munin::Master::Config->instance(); my $config = $globconfig->{'config'}; sub main { exit_if_run_by_super_user(); configure(); logger_open($config->{'logdir'}); logger_debug() if $config->{debug}; my $update = Munin::Master::Update->new(); $update->run(); return 0; } sub configure { my %args = parse_args(); # Uses default file if config_file is not defined by arguments. $config->parse_config_from_file($args{config_file}); if (defined $config->{'includedir'}) { my $dirname = $config->{'includedir'}; my $DIR; opendir($DIR, $dirname) or WARN "[Warning] Could not open includedir directory $dirname: $OS_ERROR\n"; my @files = grep { ! /^\.|~$/ } readdir($DIR); closedir($DIR); @files = map { $_ = $dirname.'/'.$_; } (sort @files); foreach my $f (@files) { $config->parse_config_from_file($f); } } # Arguments overrides settings from config file. Note that # this only handles settings that are on the base level, not # anything within groups or hosts. $config->set(\%args); } sub parse_args { my $do_usage = 0; my $do_version = 0; my %args = ( "version" => \&print_version_and_exit, ); GetOptions ( \%args, "config_file=s", "debug!", "fork!", "host=s@", "service=s@", "timeout=s", "version!", ) or pod2usage(1); delete $args{version}; $args{limit_hosts} = { map { $_ => 1 } @{$args{host}} }; delete $args{host}; $args{limit_services} = { map { $_ => 1 } @{$args{service}} };; delete $args{service}; return %args; } exit main() unless caller; 1; __END__ =encoding utf8 =head1 NAME munin-update - A program to gather data from machines running munin-node =head1 SYNOPSIS munin-update [options] Options: --config_file= Use as configuration file. --[no]debug Enable [or disable] debug messages. [--nodebug] --[no]fork Query hosts in parallel (--fork), or sequentially (--nofork). [--fork] --host Limit graphed hosts to . Multiple --host options may be supplied. --service Limit graphed services to . Multiple --service options may be supplied. --timeout= TCP timeout when talking to clients. [$timeout] --help View this message. --version View version information. =head1 OPTIONS =over 5 =item B<< --config_file= >> Use EfileE as the configuration file. [@@CONFDIR@@/munin.conf] =item B<< --[no]debug >> If set, log debug messages. [--nodebug] =item B<< --[no]fork >> If set, will fork off one process for each host. [--fork] =item B<< --host >> Limit fetched data to those from Ehost. Multiple --host options may be supplied. [unset] =item B<< --service >> Limit fetched data to those of EserviceE. Multiple --service options may be supplied. [unset] =item B<< --timeout >> Set the network timeout to . [180] =item B<< --help >> Print the help message then exit. =item B<< --version >> Print version information then exit. =back =head1 DESCRIPTION Munin-update is a part of the package Munin, which is used in combination with Munin's node. Munin is a group of programs to gather data from Munin's nodes, graph them, create html-pages, and optionally warn Nagios about any off-limit values. Munin-update does the gathering. It is usually only used from within munin-cron. It contacts each host's munin-node in turn, gathers data from it, and stores them in .rrd-files. If necessary, it will create the rrd-files and the directories to store them in. =head1 FILES @@CONFDIR@@/munin.conf @@DBDIR@@/* @@LOGDIR@@/munin-update @@STATEDIR@@/* =head1 BUGS For a list of bugs concerning munin-update, see FIX. Please report bugs in the bug tracker at L. =head1 AUTHORS The Munin Team. FIX =head1 COPYRIGHT Copyright © 2002-2009 Jimmy Olsen, Audun Ytterdal, Tore Andersson, Kjell-Magne Øierud, Linpro AS, Redpill Linpro AS This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program is released under the GNU General Public License. munin-2.0.75/master/doc/000077500000000000000000000000001451614574100150035ustar00rootroot00000000000000munin-2.0.75/master/doc/munin-check.pod000066400000000000000000000016541451614574100177160ustar00rootroot00000000000000=encoding utf8 =head1 NAME munin-check - A program to fix permissions of Munin directories and files =head1 SYNOPSIS munin-check [--options] =head1 OPTIONS =over 5 =item B<< -h|--help >> Display usage information. =item B<< -f|--fix-permissions >> Fix the permissions of the munin dirs and files. =back =head1 DESCRIPTION munin-check is a utility that fixes the permissions of the munin directories and files. Note: munin-check needs superuser rights. Please don't use this script if you are using 'graph_strategy cgi'! It doesn't care about the right permissions for www-data yet... =head1 AUTHOR Matthias Schmitz =head1 COPYRIGHT Copyright (C) 2002-2008 Matthias Schmitz. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program is released under the GNU General Public License =cut munin-2.0.75/master/doc/munin-cron.pod.in000077500000000000000000000024561451614574100202130ustar00rootroot00000000000000=encoding utf8 =head1 NAME munin-cron - A program to batch other Munin master programs =head1 SYNOPSIS munin-cron [--options] =head1 OPTIONS =over 5 =item B<< --service >> Limit services to EserviceE. Multiple --service options may be supplied. [unset] =item B<< --host >> Limit hosts to EhostE. Multiple --host options may be supplied. [unset] =item B<< --config >> Use EfileE as configuration file. [F<@@CONFDIR@@/munin.conf>] =back =head1 DESCRIPTION Munin-cron is a part of the package Munin, which is used in combination with Munin's node. Munin is a group of programs to gather data from Munin's nodes, graph them, create html-pages, and optionally warn Nagios about any off-limit values. Munin-cron batches all the above tasks by running their respective programs. All parameters are passed directly to the underlying programs, so only use parameters that are understood by all the other programs. =head1 AUTHORS Audun Ytterdal and Jimmy Olsen. =head1 COPYRIGHT Copyright (C) 2002-2008 Audun Ytterdal and Jimmy Olsen / Linpro AS. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program is released under the GNU General Public License =cut munin-2.0.75/master/doc/munin.conf.pod.in000066400000000000000000000314041451614574100201700ustar00rootroot00000000000000=encoding utf8 =head1 NAME munin.conf - Munin configuration file =head1 DESCRIPTION Munin is a group of programs to gather data from hosts, graph them, create html-pages, and optionally warn contacts about any off-limit values. The hosts are divided into three groups: One master (could be more, but Munin is not cluster aware so they'll likely be independent). The master contacts a number of machines running munin-node, these are called nodes. Each node has data from one or more hosts that is monitored by Munin. F is the configuration file for the Munin master server. The programs using it are munin-update, munin-graph, munin-limits and munin-html. There is also quite extensive documentation of this file at L The format of the file is simple. A minimal configuration looks something like: [machine1.your.dom] address localhost The default location of F is F<@@CONFDIR@@/munin.conf>. If your placement deviates from this norm, use the "--config EfileE"-option when running the munin-* programs. Munin-update will expand all node-entries in this file, and save them to F<@@DBDIR@@/datafile>, which is used by all programs in the package together with this file. Any directives in this file will override directives of the same name in F. E.g., if you want to change the title of the "load"-graph in the above minimum configuration, you would modify the two bottom lines to: [machine1.your.dom] address localhost load.graph_title Edited title of the load-graph This will override the C attribute of the C field/data series while keeping all the others at their default. =head1 GLOBAL DIRECTIVES These directives should appear in F before any host or group definitions. =over =item B I (Default: F<@@DBDIR@@>) Directory for generated database files. Required. =item B I (Default: F<@@LOGDIR@@>) Directory for log files. Required. =item B I (Default: F<@@HTMLDIR@@>) Directory for HTML pages and graphs. Required. =item B I (Default: F<@@STATEDIR@@>) Directory for files tracking munin's current running state. Required. =item B I (Default: F<@@CONFDIR@@/templates>) Directory for templates used to generate HTML pages. Required. =item B I This directive determines whether munin-update fork when gathering information from nodes. Possible values are C and C. Default is C. If you set it to C munin-update will collect data from the nodes in sequence rather than in parallel and this will take considerably more time. Affects: munin-update. =item B I Choose palette between the very nice "C", and the good old "C". =item B I This directive sets the resolution of the RRD files that are created. Possible values are C and C. Default is C. C is really huge, it saves the complete data with 5 minute resolution for 400 days. This will probably increase the I/O load on your Munin master, and currently has very little benefit. Affects: munin-update. =item B I Deprecated. (Graphs are now always drawn via CGI.) =item B I The local address to connect any node from in case the master has several IP interfaces. This can be overridden by a group or global directive. Without this directive Munins traffic will originate from the master server according to the IP routing table. =item B This directive specifies the maximum number of processes to be used for gathering information from nodes. If left blank, munin will use as many processes as necessary. Affects: munin-update. =item B This directive specifies the maximum number of concurrent rrdgraph proesses started by munin-graph. The default is 6. A setting of 0 disables concurrent processing. Affects: munin-graph =item B This directive specifies the maximum number of concurrent munin-cgi-graph jobs. The web server can start a high number of munin-cgi-graph jobs which we can't stop, but munin-cgi-graph will throttle down how many rrdgraph calls will be running at the same time to this number. Affects: munin-cgi-graph and munin-fastcgi-graph. =item B I This directive will set the maximum amount of time in seconds the munin-update task may run. So we'll make sure the update ended within the 5 minutes timespan needed to have complete graphs without gaps. You should probably not increase this value, unless all nodes are using ssh with munin-async. Otherwise you may expecience gaps in graphs, if C takes longer, than the default period (5 minutes). Munin-async can retrieve historical data, and if there is a big backlog, we could need more time depending on the size of the data generated by the plugin and the size of the backlog. This would also mean that we wouldn't care to skip an update. So munin-async will get more time to retrieve the backlog data. Afterwards new data will incrementally be fetched. Default is 240. Affects: munin-update =item B I This directive will set the maximum amount of time in seconds the munin-update task may run for a single node. This value can't be bigger than C. Default is 180. Affects: munin-update =item B I The name of the secure shell command to use. Can be fully qualified, or looked up in $PATH. Default: C =item B I The C command line options. Defaults: C<-o ChallengeResponseAuthentication=no -o StrictHostKeyChecking=no>. If you need per-host ssh configuration, add these to F<~/munin/.ssh/config> =item B Can have four values. C, C, C, and C. C and C require a TLS connection, while C will not attempt one at all. The current default is C because C is broken. C causes bad interaction between munin-update and munin-node if the node is unprepared to go to TLS. If you see data dropouts (gaps in graphs) please try to disable TLS. Affects: munin-update. =item B This directive can be C or C. It determines if the remote certificate needs to be signed by a CA that is known locally. Default is C. Affects: munin-update. =item B This directive sets the location of the private key to be used for TLS. Default is @@CONFDIR@@/munin.pem. The private key and certificate can be stored in the same file. Affects: munin-update. =item B This directive sets the location of the TLS certificate to be used for TLS. Default is @@CONFDIR@@/munin.pem. The private key and certificate can be stored in the same file. Affects: munin-update. =item B This directive sets the CA certificate to be used to verify the node's certificate, if tls_verify_certificate is set to C. Default is @@CONFDIR@@/cacert.pem. Affects: munin-update. =item B This directive sets how many signings up a chain of signatures TLS is willing to go to reach a known, trusted CA when verifying a certificate. Default is C<5>. Affects: munin-update. =item B This directive, if defined, searches a dump of the certificate provided by the remote host for the given regex. The dump of the certificate is two lines of the form: Subject Name: /C=c/ST=st/L=l/O=o/OU=ou/CN=cn/emailAddress=email Issuer Name: /C=c/ST=st/O=o/OU=ou/CN=cn/emailAddress=email So, for example, one could match the subject distinguished name by the directive: tls_match Subject Name: /C=c/ST=st/L=l/O=o/OU=ou/CN=cn/emailAddress=email Note that the fields are dumped in the order they appear in the certificate. It's best to view the dump of the certificate by running munin-update in debug mode and reviewing the logs. Unfortunately, due to the limited functionality of the SSL module in use, it is not possible to provide finer-grained filtering. By default this value is not defined. Affects: munin-update. =item FIXME: This section MAY be complete, it may be missing a directive or two. =back =head1 HOST DEFINITIONS Host definitions can have several types. In all forms, the definition is used to generate the host name and group for the host, and the following lines define its directives. All following directives apply to that node until another node definition or EOF. Note that when defining a nodename it is vital that you use a standard DNS name, as in, one that uses only a-z, '-', and '.'. While other characters can be used in a DNS name, it is against the RFC, and Munin uses the other characters as delimiters. If they appear in nodenames, unexpected behavior may occur. The simplest node definition defines the section for a new node by simply wrapping the DNS name of the node in brackets, e.g. C<[machine1.your.dom]>. This will add the node C to the group C. The next form of definition is used to define the node and group independently. It follows the form C<[your.dom;machine1.sub.your.dom]>. This adds the node C to the group C. This can be useful if you have machines you want to put together as a group that are under different domains (as in the given example). This can also solve a problem if your machine is C, where having a group of C makes little sense. Multiple groups can be specified by adding more Cs, e.g. C<[servers;local;mail;mail.foo.net]>, if you need a more hierarchical structure. =head1 NODE DIRECTIVES These are directives that can follow a node definition and will apply only to that node. =over =item B
The IP address of the node. Required. =item B The local address to connect to the node from. This overrides a group or global directive. =item FIXME: This section is incomplete. =back =head1 PLUGIN DIRECTIVES These directives should appear after a node definition and are of the form CvalueE>. Using these directives you can override various directives for a plugin, such as its contacts, and can also be used to create graphs containing data from other plugins. =over =item FIXME: This section is (obviously) incomplete. =back =head1 FIELD DIRECTIVES These directives should appear after a node definition and are of the form CvalueE>. Using these directives you can override values originally set by plugins on the nodes, such as warning and critical levels or graph names. =over =item B The graph height for a specific service. Default is C<175>. Affects: munin-graph. =item B The graph width for a specific service. Default is C<400>. Affects: munin-graph. =item B The value at which munin-limits will mark the service as being in a warning state. Value can be a single number to specify a limit that must be passed or they can be a comma separated pair of numbers defining a valid range of values. Affects: munin-limits. =item B The value at which munin-limits will mark the service as being in a critical state. Value can be a single number to specify a limit that must be passed or they can be a comma separated pair of numbers defining a valid range of values Affects: munin-limits. =item FIXME: This section is incomplete. =back =head1 EXAMPLES On all the examples below, all the 'top-level' parameters (dbdir, logdir, htmldir, tmpldir) are not present. They are only skipped for brevity - they are needed. =head1 EXAMPLE 1 An example with three servers on two domains: [machine1.one.dom] address machine1.one.dom [machine2.one.dom] address 10.33.32.123 [machine3.two.dom] address localhost This will appear as two groups (one.dom and two.dom), having respectively two and one node. =head1 EXAMPLE 2 Summarize the 'load'-graphs of the two servers in one.dom, in a 'total load'-graph. [one.dom;Totals] update no load.graph_title Total load load.sum_load.label load load.sum_load.special_stack machine1=machine1.one.dom:load.load machine2=machine2.one.dom:load.load =head1 AUTHORS Jimmy Olsen, Audun Ytterdal, Brian de Wolf, Nicolai Langfeldt =head1 COPYRIGHT Copyright (C) 2002-2008 Audun Ytterdal, Jimmy Olsen, Nicolai Langfeldt, Linpro AS and others. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program is released under the GNU General Public License =head1 SEE ALSO For more information, see the man pages of the individual munin-* programs or the Munin homepage L. =cut munin-2.0.75/master/doc/munin.pod000066400000000000000000000022671451614574100166440ustar00rootroot00000000000000=encoding utf8 =head1 NAME munin - Munin manpage hub =head1 DESCRIPTION Munin is a group of programs to gather data from hosts, graph them, create html-pages, and optionally warn contacts about any off-limit values. The Munin master contains the following programs: munin-update - to gather data from machines running munin-node. munin-graph - to create graphs from data contained in rrd-files. munin-limits - to check for any off-limit values. munin-html - to draw html-pages on an Munin installation munin-cron - to batch other munin programs =head1 SEE ALSO For more information, see the man pages of the individual programs L, L, L, L, L or the Munin homepage L. =head1 AUTHORS Jimmy Olsen, Audun Ytterdal, Brian de Wolf, Nicolai Langfeldt =head1 COPYRIGHT Copyright (C) 2002-2008 Audun Ytterdal, Jimmy Olsen, Nicolai Langfeldt, Linpro AS and others. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program is released under the GNU General Public License =cut munin-2.0.75/master/extras/000077500000000000000000000000001451614574100155445ustar00rootroot00000000000000munin-2.0.75/master/extras/munin-update_old.in000077500000000000000000001052721451614574100213520ustar00rootroot00000000000000#!@@PERL@@ -w # -*- cperl -*- # # Copyright (C) 2002-2009 Jimmy Olsen, Audun Ytterdal # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; version 2 dated June, # 1991. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Script to update the RRD-files with current information. # use strict; $|=1; use Carp; use English qw(-no_match_vars); use Getopt::Long; use IO::Socket; use Munin::Common::Defaults; use Munin::Common::Timeout; use Munin::Common::TLSClient; use Munin::Master::Logger; use Munin::Master::Utils; use POSIX ":sys_wait_h"; use RRDs; use Storable qw(fd_retrieve nstore_fd); use Time::HiRes; my $DEBUG=0; my $STATS; my $cli_do_fork; my $cli_timeout; my $conffile = "$Munin::Common::Defaults::MUNIN_CONFDIR/munin.conf"; my $config; my $do_fork = 1; my $serversocket = "munin-server-socket.$$"; my $timeout = 180; my $tls; my %children = (); my @limit_hosts = (); my @limit_services = (); my @queue = (); sub main { my $TIMEOUT = 240; my $oldconfig; parse_args(); exit_if_run_by_super_user(); $config = munin_readconfig_base($conffile); logger_open($config->{'logdir'}); $oldconfig = &munin_readconfig_part('datafile', 1); # CLI parameters override the configuration file. if (defined $cli_timeout) { $timeout = $cli_timeout; } elsif (exists $config->{'timeout'}) { $timeout = $config->{'timeout'}; } if (defined $cli_do_fork) { $do_fork = $cli_do_fork; } elsif (exists $config->{'fork'}) { $do_fork = ($config->{'fork'} =~ /yes/i ? 1 : 0); } if (! -d $config->{rundir}) { mkdir ($config->{rundir}, oct(700)); } munin_runlock("$config->{rundir}/munin-update.lock"); if (!open ($STATS,'>', "$config->{dbdir}/munin-update.stats.tmp")) { logger("[WARNING] Unable to open $config->{dbdir}/munin-update.stats"); # Use /dev/null instead - if the admin won't fix he won't care open($STATS, '>', "/dev/null") or die "Could not open STATS to /dev/null: $?"; } if ($do_fork) { set_up_ipc_socket(); } logger("Starting munin-update"); my @work = find_hosts_for_update(); @queue = build_work_queue($oldconfig, @work); do_some_work($TIMEOUT); unlink ("$config->{rundir}/$serversocket"); # XXX: WTF is that ? # { # my $overwrite = munin_readconfig($conffile); # $config = munin_overwrite($config, $overwrite); # } compare_configs ($oldconfig, $config); if (munin_getlock("$config->{rundir}/munin-datafile.lock")) { munin_writeconfig("$config->{dbdir}/datafile",$config); } else { die "Could not create lockfile '$config->{rundir}/munin-update.lock'"; } write_stats_and_log_update_time(); munin_removelock("$config->{rundir}/munin-datafile.lock"); munin_removelock("$config->{rundir}/munin-update.lock"); return 0; } sub parse_args { my $do_usage = 0; my $do_version = 0; # Get options print_usage_and_exit() unless GetOptions ( "host=s" => \@limit_hosts, "service=s" => \@limit_services, "config=s" => \$conffile, "debug!" => \$DEBUG, "version!" => \&print_version_and_exit, "fork!" => \$cli_do_fork, "timeout=i" => \$cli_timeout, "help" => \&print_usage_and_exit ); } sub print_usage_and_exit { print qq{Usage: $0 [options] Options: --version View version information. --help View this message. --service Limit graphed services to . Multiple --service options may be supplied. --host Limit graphed hosts to . Multiple --host options may be supplied. --config Use as configuration file. [$Munin::Common::Defaults::MUNIN_CONFDIR/munin.conf] --[no]debug View debug messages. [--nodebug] --[no]fork Don't fork one instance for each host. [--fork] --timeout= TCP timeout when talking to clients. [$timeout] }; exit 0; } sub print_version_and_exit { print qq{munin-update version $Munin::Common::Defaults::MUNIN_VERSION. Written by Audun Ytterdal, Jimmy Olsen, Tore Anderson / Linpro AS Copyright (C) 2002-2009 This is free software released under the GNU General Public License. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, please refer to the file COPYING that is included with this software or refer to http://www.fsf.org/licensing/licenses/gpl.txt }; exit 0; } sub exit_if_run_by_super_user { if ($EFFECTIVE_USER_ID == 0) { print qq{This program will easily break if you run it as root as you are trying now. Please run it as user '$Munin::Common::Defaults::MUNIN_USER'. The correct 'su' command on many systems is 'su - munin --shell=/bin/bash' Aborting.\n\n}; exit 1; } } sub set_up_ipc_socket { my $socket_file = "$config->{rundir}/$serversocket"; my $uaddr = sockaddr_un($socket_file); socket Server, PF_UNIX, SOCK_STREAM, 0 or die "socket: $!"; unlink $socket_file; bind Server, $uaddr; chmod oct(700), $socket_file; listen Server, SOMAXCONN; } sub find_hosts_for_update { my @hosts = (); if (@limit_hosts) { for my $nodename (@limit_hosts) { push @hosts, map { @{munin_find_field ($_->{$nodename}, "address")} } @{munin_find_field($config, $nodename)}; } } else { push @hosts, @{munin_find_field($config, "address")}; } return @hosts; } sub build_work_queue { my ($oldconfig, @work) = @_; # Go through scheduled work to weed out a few bits, and prepare some info for my $host (@work) { my $loc = munin_get_node_loc($host); my $name = munin_get_node_name($host); # Skip anything that has been disabled with the "update" setting if (!munin_get_bool ($host, "update", "true")) { logger ("Skipping \"$name\" (update disabled by config)"); next; } # We need to connect to this node; queue it logger ("Queuing \"$name\" for update."); push (@queue, [$loc, $host, munin_get_node ($oldconfig, $loc)]); } return @queue; } sub start_parallell_do_node { if (defined $config->{max_processes}) { while (keys %children < ($config->{max_processes} - 1)) { do_node(@{pop @queue}); } } else { do_node(@{pop @queue}) while @queue; # No limit on number of procs } } # FIX better name :) sub do_some_work { my ($TIMEOUT) = @_; my $bad_procs = 0; do_with_timeout($TIMEOUT, sub { if ($do_fork) { start_parallell_do_node(); # Loop as long as there are kids or queue... while ((scalar (keys %children) - $bad_procs > 0) or @queue) { logger ("Debug: Doing a pass to check children status.") if $DEBUG; my $timed_out = !do_with_timeout(10, sub { accept (Client, Server); }); # end eval if ($timed_out) { foreach my $key (keys %children) { if (waitpid ($key, WNOHANG) != 0) { my $loc = $children{$key}->[0]; my $newnode = $children{$key}->[1]; my $oldnode = $children{$key}->[2]; my $name = munin_get_node_name ($newnode); logger ("Reaping child: $name."); delete $children{$key}; munin_copy_node_toloc ($oldnode, $config, $loc); } } if (@queue and defined $config->{max_processes} and $config->{max_processes}) { logger ("Debug: Checking whether to spawn off more procs from queue."); while (keys %children < ($config->{max_processes}-1-$bad_procs)) { logger ("Debug: Popping queue item and spawning new proc."); do_node(@{pop @queue}); } } next; } close STDIN; ## no critic open (STDIN, "<&Client") || die "can't dup client to stdin"; ## critic my $pid; my $name; my $loc; my $tmpref; eval { $tmpref = fd_retrieve (\*STDIN); }; if ($@) { $bad_procs++; logger ("[WARNING] Error communicating with process: $@"); } else { ($pid, $loc, $name) = ($tmpref->[0], $tmpref->[1], $tmpref->[2]); logger ("connection from $name ($pid)"); eval { my $newnode = fd_retrieve (\*STDIN); munin_copy_node_toloc ($newnode, $config, $loc); }; if ($@) { logger ("[WARNING] Error during fd_retrieve of config: $@"); my $loc = $children{$pid}->[0]; my $newnode = $children{$pid}->[1]; my $oldnode = $children{$pid}->[2]; munin_copy_node_toloc ($oldnode, $config, $loc); } delete $children{$pid}; waitpid ($pid, 0); logger ("connection from $name ($pid) closed"); } if (@queue and defined $config->{max_processes} and $config->{max_processes} and scalar (keys %children) < (($config->{max_processes})-1-$bad_procs)) { do_node(@{pop @queue}); close (Client); } } } else { # No forking, just poll the nodes sequentially... while (@queue) { do_node(@{pop @queue}); } } }) or die "Timed out waiting for children.\n"; if ($bad_procs) # Use old configuration for killed children { foreach my $key (keys %children) { my $loc = $children{$key}->[0]; my $newnode = $children{$key}->[1]; my $oldnode = $children{$key}->[2]; my $name = munin_get_node_name ($newnode); munin_copy_node_toloc ($oldnode, $config, $loc); logger ("Attempting to use old configuration for $name."); } } } sub write_stats_and_log_update_time { my $update_time= Time::HiRes::time; $update_time = sprintf ("%.2f",(Time::HiRes::time - $update_time)); print $STATS "UT|$update_time\n"; close ($STATS); rename ("$config->{dbdir}/munin-update.stats.tmp", "$config->{dbdir}/munin-update.stats"); logger("Munin-update finished ($update_time sec)"); } # compare_configs is used to monitor for config changes which we # have to act upon. sub compare_configs { my $old = shift; my $new = shift; my $just_upgraded = 0; if (!defined $old->{version} or $old->{version} ne $Munin::Common::Defaults::MUNIN_VERSION) { $just_upgraded = 1; } foreach my $node (@{munin_find_field($new, "label")}) { my $oldnode = munin_get_node ($old, munin_get_node_loc ($node)); my $name = munin_get_node_name ($node); my ($oldval, $newval); $oldval = munin_get ($oldnode, "max", ""); $newval = munin_get ($node, "max", ""); if ($just_upgraded or $oldval ne $newval) { logger ("Notice: compare_configs: $name.max changed from ".(length $oldval?$oldval:"undefined")." to $newval."); change_max (munin_get_filename ($node), $newval); } $oldval = munin_get ($oldnode, "min", ""); $newval = munin_get ($node, "min", ""); if ($just_upgraded or $oldval ne $newval) { logger ("Notice: compare_configs: $name.min changed from ".(length $oldval?$oldval:"undefined")." to $newval."); change_min (munin_get_filename ($node), $newval); } $oldval = munin_get ($oldnode, "type", "GAUGE"); $newval = munin_get ($node, "type", "GAUGE"); if ($just_upgraded or $oldval ne $newval) { logger ("Notice: compare_configs: $name.type changed from ".(length $oldval?$oldval:"undefined")." to $newval."); change_type (munin_get_filename ($oldnode), munin_get_filename ($node), $newval); } } } sub change_type { my $ofile = shift; my $nfile = shift; my $val = shift; if (defined $ofile and -f $ofile) { logger ("[WARNING]: Changing name of $ofile to $nfile"); unless (rename ($ofile, $nfile)) { logger ("[ERROR]: Could not rename file: $!\n"); } } logger ("INFO: Changing type of $nfile to " . (defined $val?$val:"GAUGE")); RRDs::tune ($nfile, "-d", "42:".(defined $val?$val:"GAUGE")); } sub change_max { my $file = shift; my $val = shift; logger ("[WARNING]: Changing max of \"$file\" to \"$val\".\n"); RRDs::tune ($file, "-a", "42:".(defined $val?$val:"U")); } sub change_min { my $file = shift; my $val = shift; logger ("[WARNING]: Changing min of \"$file\" to \"$val\".\n"); RRDs::tune ($file, "-i", "42:".(defined $val?$val:"U")); } sub do_node { my ($loc, $newconf, $oldconf) = @_; return unless munin_get ($newconf, "update", "true"); # Skip unless we're updating it return unless munin_get ($newconf, "fetch_data", "true"); # Old name for "update" my $name = munin_get ($newconf, "host_name") || munin_get_node_name ($newconf); unless ($newconf->{"address"}) { logger("[ERROR] No address defined for node: $name"); return; } logger ("Debug: do_node: Starting on \"$name\".") if $DEBUG; # Then we fork... if ($do_fork) { my $pid = fork; if (!defined($pid)) { # Something went wrong logger ("Error: Unable to fork: $!"); return; } elsif ($pid) { # I'm the parent $children{$pid} = [$loc, $newconf, $oldconf]; return; } # else I'm the child -- go spawn } $0 .= " [$name]"; # First we get lock... unless (&munin_getlock(munin_get($newconf, "rundir")."/munin-".join('-',@{munin_get_node_loc($newconf)})."-".munin_get_node_name($newconf).".lock")) { logger ("[ERROR] Could not get lock for \"$name\". Skipping node."); if ($do_fork) { # Send the old config to the server before we die alarm (0); # Don't want to interrupt this. my @tmp = ($$, munin_get_node_loc($newconf), $name); if (ref $oldconf) { copy_node ($oldconf, $newconf); write_refs_to_parent($newconf, \@tmp, \%{munin_get_separated_node ($newconf)}); } else { # Well, we'll have to give _something_ to the server, or it'll time out. write_refs_to_parent($newconf, \@tmp, {}); } exit 1; } else { return 0; } } my $socket; if (munin_get ($newconf, "local_address")) { $socket = new IO::Socket::INET ('PeerAddr' => "$newconf->{address}:". munin_get ($newconf, "port", "4949"), 'LocalAddr' => munin_get ($newconf, "local_address", undef), 'Proto' => "tcp", "Timeout" => munin_get($newconf, "timeout", 60)); } else { $socket = new IO::Socket::INET ('PeerAddr' => "$newconf->{address}:". munin_get ($newconf, "port", "4949"), 'Proto' => "tcp", "Timeout" => munin_get($newconf, "timeout", 60)); } my $err = ($socket ? "" : $!); if ($do_fork) { $SIG{ALRM} = sub { close $socket; die "$!\n"}; alarm ($timeout); my @tmp = ($$, munin_get_node_loc ($newconf), $name); if (!$socket) { logger ("[ERROR] Could not connect to $name($newconf->{address}): $err - Attempting to use old configuration"); # If we can't reach the client. Using old Configuration. if (ref $oldconf) { copy_node ($oldconf, $newconf); alarm (0); # Don't want to interrupt this. write_refs_to_parent($newconf, \@tmp, \%{munin_get_separated_node ($newconf)}); } else { # Well, we'll have to give _something_ to the server, or it'll time out. write_refs_to_parent($newconf, \@tmp, {}); } } else { my $ctx; if (!config_and_fetch_node($newconf,$oldconf,$socket)) { copy_node ($oldconf, $newconf); write_refs_to_parent($newconf, \@tmp, \%{munin_get_separated_node ($newconf)}); exit 1; } close $socket; alarm (0); # Don't want to interrupt this. write_refs_to_parent($newconf, \@tmp, \%{munin_get_separated_node ($newconf)}); alarm ($timeout); } alarm (0); munin_removelock(munin_get($newconf, "rundir")."/munin-".join('-',@{munin_get_node_loc($newconf)})."-".munin_get_node_name($newconf).".lock"); exit; } else # No forking... { if (!$socket) { logger ("[ERROR] Could not connect to $name($newconf->{address}): $err\nAttempting to use old configuration"); # If we can't reach the client. Using old Configuration. if (ref $oldconf) { copy_node ($oldconf, $newconf); } } else { next unless (config_and_fetch_node($newconf,$oldconf,$socket)); close $socket; } } munin_removelock(munin_get($newconf, "rundir")."/munin-".join('-',@{munin_get_node_loc($newconf)})."-".munin_get_node_name($newconf).".lock"); } sub write_refs_to_parent { my ($conf, @refs) = @_; socket my $sock, PF_UNIX, SOCK_STREAM, 0 or die "socket: $!"; connect $sock, sockaddr_un(munin_get($conf, "rundir")."/$serversocket") or die "connect: $!"; for my $ref (@refs) { nstore_fd($ref, $sock) or die "Could not nstore_fd: $!"; } close $sock; } sub write_socket_single { my $socket = shift; my $text = shift; logger ("[DEBUG] Writing to socket: \"$text\".") if $DEBUG; my $timed_out = !do_with_timeout(5, sub { if ($tls && $tls->session_started()) { $tls->write($text) or exit 9; } else { print $socket $text; } }); if ($timed_out) { logger ("[WARNING] Socket write timed out\n"); return; } return 1; } sub read_socket_single { my $socket = shift; my $res; return unless defined $socket; my $timed_out = !do_with_timeout($timeout, sub { if ($tls && $tls->session_started()) { $res = $tls->read(); } else { $res = <$socket>; } chomp $res if defined $res; }); if ($timed_out) { logger ("[WARNING] Socket read timed out\n"); return; } logger ("[DEBUG] Reading from socket: \"$res\".") if $DEBUG; return $res; } sub read_socket { my $socket = shift; my @array; return unless defined $socket; local $_; my $timed_out = !do_with_timeout($timeout, sub { if ($tls && $tls->session_started()) { while (defined ($_ = $tls->read())) { chomp; last if (/^\.$/); push @array, $_; } } else { while (<$socket>) { chomp; last if (/^\.$/); push @array, $_; } } }); if ($timed_out) { logger ("[WARNING] Socket read timed out: $@\n"); return; } logger ("[DEBUG] Reading from socket: \"".(join ("|",@array))."\".") if $DEBUG; return (@array); } sub read_socket_discard_comments { my ($socket) = @_; my @chunks = read_socket($socket); return grep { $_ !~ /^#/} @chunks; } sub config_and_fetch_node { my ($newconf,$oldconf,$socket) = @_; my $fetchdomain; my $name = munin_get_node_name ($newconf); my $host_time = Time::HiRes::time; my $clientdomain = eval { get_node_domain($socket); }; if ($EVAL_ERROR) { logger("[WARNING] $EVAL_ERROR '$name' skipping"); return 0; } # Decide what to ask for if (munin_get_bool ($newconf, "use_node_name")) { $fetchdomain = $clientdomain; } else { $fetchdomain = $name; } run_starttls_if_required($socket, $name); negotiate_capabilities($socket); logger("[DEBUG] Configuring node: $name") if $DEBUG; my @services; my $timed_out = !do_with_timeout(5, sub { write_socket_single($socket, "list $name\n"); my $list = read_socket_single($socket); exit 1 unless defined $list; chomp $list; @services = split / /,$list; }); if ($timed_out) { logger ("Error: Could not get list from $newconf->{address}: $!\nAttempting to use old configuration"); if (ref $oldconf) { copy_node ($oldconf, $newconf); } @services = []; } for my $service (@services) { my $servname = $service; $servname =~ s/\W/_/g; config_node_service($oldconf, $newconf, $socket, $servname, $service); fetch_node_service ($newconf, $socket, $servname, $service); } $host_time = sprintf ("%.2f", (Time::HiRes::time - $host_time)); print $STATS "UD|$name|$host_time\n"; return 0 unless $socket; return 1; } sub get_node_domain { my ($socket) = @_; my $node_domain = read_socket_single ($socket); croak "Got no reply from node" unless $node_domain; chomp $node_domain; $node_domain =~ s/\#.*(?:lrrd|munin) (?:client|node) at // or croak "Got unknown reply from node"; return $node_domain; } sub run_starttls_if_required { my ($socket, $name) = @_; # TLS should only be attempted if explicitly enabled. The default # value is therefore "disabled" (and not "auto" as before). my $tls_requirement = &munin_get ($config, "tls", "disabled"); logger ("[DEBUG]: TLS set to \"$tls_requirement\".") if $DEBUG; if ($tls_requirement ne "disabled") { my $key; my $cert; my $depth; my $ca_cert; my $tls_verify; $key = $cert = munin_get ($config, "tls_pem"); $key = &munin_get ($config, "tls_private_key", "$Munin::Common::Defaults::MUNIN_CONFDIR/munin.pem") unless defined $key; $cert = &munin_get ($config, "tls_certificate", "$Munin::Common::Defaults::MUNIN_CONFDIR/munin.pem") unless defined $cert; $ca_cert = &munin_get ($config, "tls_ca_certificate", "$Munin::Common::Defaults::MUNIN_CONFDIR/cacert.pem") unless defined $ca_cert; $tls_verify=&munin_get ($config, "tls_verify_certificate", "no"); $depth=&munin_get ($config, "tls_verify_depth", 5); $tls = Munin::Common::TLSClient->new({ DEBUG => $DEBUG, logger => \&logger, read_fd => fileno($socket), read_func => sub { read_socket_single($socket) }, tls_ca_cert => $ca_cert, tls_cert => $cert, tls_paranoia => $tls_requirement, tls_priv => $key, tls_vdepth => $depth, tls_verify => $tls_verify, write_fd => fileno($socket), write_func => sub { write_socket_single($socket, @_) }, }); if (!$tls->start_tls()) { $tls = undef; if ($tls_requirement eq "paranoid" or $tls_requirement eq "enabled") { logger ("[ERROR]: Could not establish TLS connection to \"$name\". Skipping."); exit 13; } } } } sub negotiate_capabilities { my ($socket) = @_; my $capabilities = 'foo'; write_socket_single($socket, "cap $capabilities\n"); my ($cap) = read_socket_discard_comments($socket); logger("[DEBUG]: Session capabilities: $cap");# if $DEBUG; } sub config_node_service { my $oldconf = shift; my $newconf = shift; my $socket = shift; my $service = shift; my $servname = shift; my $fields = {}; munin_set_var_loc ($newconf, [$servname, "realservname"], $service); logger("[DEBUG] Inspecting possible service: $servname") if $DEBUG; return if (!munin_get_bool ($newconf->{$servname}, "update", "true")); return if (!munin_get_bool ($newconf->{$servname}, "fetch_data", "true")); return if (@limit_services and !grep (/^$servname$/, @limit_services)); my @graph_order = split (/\s+/, munin_get ($newconf->{$service}, "graph_order", "")); my $serviceconf_time = Time::HiRes::time; logger("[DEBUG] Configuring service: $servname") if $DEBUG; write_socket_single ($socket, "config $service\n"); my @lines = read_socket($socket); return unless $socket; return unless (@lines); for (@lines) { if (/\# timeout/) { logger("Client reported timeout in configuration of $servname"); if ($oldconf->{$servname}) { logger("Attempting to use old configuration"); copy_node ($newconf->{$servname}, $oldconf->{$servname}); } else { logger("Skipping configuration of $servname"); delete $newconf->{$servname}; } } elsif (/^(\w+)\.(\w+)\s+(.+)/) { my ($client,$type,$value) = ($1,$2,$3); $client = &sanitise_fieldname ($client, $fields); if (($type) and ($type eq "label")) { $value =~ s/\\/_/g; # Sanitise labels push (@graph_order,$client) unless grep (/^$client$/, @graph_order); } munin_set_var_loc ($newconf, [$servname, $client, $type], "$value"); logger ("config: $servname->$client.$type = $value") if $DEBUG; } elsif (/(^[^\s\#]+)\s+(.+)/) { my ($keyword) = $1; my ($value) = $2; munin_set_var_loc ($newconf, [$servname, $keyword], "$value"); logger ("Config: $servname->$keyword = $value") if $DEBUG; if ($keyword eq "graph_order") { @graph_order = split (/\s+/, $value); } } } for my $field (keys %{$newconf->{$servname}}) { # Skip anything that isn't a field next if $field =~ /^#%#/; next unless (ref ($newconf->{$servname}->{$field}) eq "HASH" and defined ($newconf->{$servname}->{$field}->{"label"})); my $fhash = $newconf->{$servname}->{$field}; # Check if file exists my $fname = munin_get_filename ($fhash); (my $dirname = $fname) =~ s/\/[^\/]+$//; if (! -f "$fname") { logger ("creating rrd-file for $servname->$field: \"$fname\""); munin_mkdir_p ($dirname, oct(777)); my @args = ("$fname", "DS:42:".munin_get($fhash, "type", "GAUGE").":600:". munin_get($fhash, "min", "U") . ":" . munin_get($fhash, "max", "U")); my $resolution = &munin_get ($fhash, "graph_data_size", "normal"); if ($resolution eq "normal") { push (@args, "RRA:AVERAGE:0.5:1:576", # resolution 5 minutes "RRA:MIN:0.5:1:576", "RRA:MAX:0.5:1:576", "RRA:AVERAGE:0.5:6:432", # 9 days, resolution 30 minutes "RRA:MIN:0.5:6:432", "RRA:MAX:0.5:6:432", "RRA:AVERAGE:0.5:24:540", # 45 days, resolution 2 hours "RRA:MIN:0.5:24:540", "RRA:MAX:0.5:24:540", "RRA:AVERAGE:0.5:288:450", # 450 days, resolution 1 day "RRA:MIN:0.5:288:450", "RRA:MAX:0.5:288:450"); } elsif ($resolution eq "huge") { push (@args, "RRA:AVERAGE:0.5:1:115200"); # resolution 5 minutes, for 400 days push (@args, "RRA:MIN:0.5:1:115200"); # Three times? ARGH! push (@args, "RRA:MAX:0.5:1:115200"); # Three times? ARGH! } RRDs::create @args; if (my $ERROR = RRDs::error) { logger ("[ERROR] Unable to create \"$fname\": $ERROR"); } } } munin_set_var_loc ($newconf, [$servname, "graph_order"], join(' ',@graph_order)); } sub fetch_node_service { my $newconf = shift; my $socket = shift; my $service = shift; my $realservname = shift; write_socket_single ($socket, "fetch $realservname\n"); my @lines = &read_socket($socket); return 0 unless $socket; my $fields = {}; for (@lines) { next unless defined $_; if (/\# timeout/) { logger("Client reported timeout in fetching of $service"); } elsif (/(\w+)\.value\s+([\S:]+)\s*(\#.*)?$/) { my $key = $1; my $value = $2; my $comment = $3; my $when = "N"; if ($value =~ /^(\d+):(.+)$/) { $when = $1; $value = $2; } if ($value =~ /\d[Ee]([+-]?\d+)$/) { # Looks like scientific format. RRDtool does not # like it so we convert it. my $magnitude = $1; if ($magnitude < 0) { # Preserve at least 4 significant digits $magnitude=abs($magnitude)+4; $value=sprintf("%.*f",$magnitude,$value); } else { $value=sprintf("%.4f",$value); } } $key = &sanitise_fieldname ($key, $fields); if (exists $newconf->{$service}->{$key}->{"label"}) { my $fname = munin_get_filename ($newconf->{$service}->{$key}); logger("[DEBUG] Updating $fname with $value") if $DEBUG; RRDs::update ("$fname", "$when:$value"); if (my $ERROR = RRDs::error) { logger ("[ERROR] In RRD: unable to update $fname: $ERROR"); } } else { logger ("[ERROR] Unable to update $service -> $key: No such field (no \"label\" field defined when running plugin with \"config\")."); } } elsif (/(\w+)\.extinfo\s+(.+)/) { # FIX never used? munin_set_var_loc ($newconf, [$service, $service, $1, "extinfo"], $2); } } return 1; } # FIX never called? sub fetch_node { my ($newconf,$oldconf,$socket) = @_; my $name = munin_get_node_name ($newconf); logger("[DEBUG] Fetching node: $name") if $DEBUG; for my $service (keys %{$newconf}) { next if ref ($newconf->{$service}) ne "HASH"; next if $service =~ /^#%#/; logger("[DEBUG] Fetching service: $service") if $DEBUG; next unless exists ($newconf->{$service}->{"graph_title"}); next unless (munin_get_bool ($newconf->{$service}, "update", "true")); next unless (munin_get_bool ($newconf->{$service}, "fetch_data", "true")); next if (@limit_services and !grep (/^$service$/, @limit_services)); # Read (and get rid of) realservname my $realservname = ( $newconf->{$service}->{"realservname"} || $service ); delete $newconf->{$service}->{"realservname"} if exists $newconf->{$service}->{"realservname"}; write_socket_single ($socket, "fetch $realservname\n"); my @lines = &read_socket($socket); return 0 unless $socket; my $fields = {}; for (@lines) { next unless defined $_; if (/\# timeout/) { logger("Client reported timeout in fetching of $service"); } elsif (/(\w+)\.value\s+([\S:]+)\s*(\#.*)?$/) { my $key = $1; my $value = $2; my $comment = $3; my $when = "N"; if ($value =~ /^(\d+):(.+)$/) { $when = $1; $value = $2; } if ($value =~ /\d[Ee]([+-]?\d+)$/) { # Looks like scientific format. RRDtool does not # like it so we convert it. my $magnitude = $1; if ($magnitude < 0) { # Preserve at least 4 significant digits $magnitude=abs($magnitude)+4; $value=sprintf("%.*f",$magnitude,$value); } else { $value=sprintf("%.4f",$value); } } $key = &sanitise_fieldname ($key, $fields); if (exists $newconf->{$service}->{$key}->{"label"}) { my $fname = munin_get_filename ($newconf->{$service}->{$key}); logger("[DEBUG] Updating $fname with $value") if $DEBUG; RRDs::update ("$fname", "$when:$value"); if (my $ERROR = RRDs::error) { logger ("[ERROR] In RRD: unable to update $fname: $ERROR"); } } else { logger ("[ERROR] Unable to update $name -> $service -> $key: No such field (no \"label\" field defined when running plugin with \"config\")."); } } elsif (/(\w+)\.extinfo\s+(.+)/) { munin_set_var_loc ($newconf, [$service, $service, $1, "extinfo"], $2); } } } return 1; } sub sanitise_fieldname { my $lname = shift; my $done = shift; my $old = shift || 0; $lname =~ s/[\W-]/_/g; return substr ($lname,-18) if $old; #$lname = Digest::MD5::md5_hex ($lname) if (defined $done->{$lname}); $done->{$lname} = 1; return $lname; } sub copy_node { my $from = shift; my $to = shift; if (ref ($from) eq "HASH") { foreach my $key (keys %$from) { next if $key =~ /^#%#/; $to->{$key} = $from->{$key}; } } else { $to = $from; } return $to; } exit main() unless caller; 1; __END__ =head1 NAME munin-update - A program to gather data from machines running munin-node =head1 SYNOPSIS munin-update [options] =head1 OPTIONS =over 5 =item B<< --service >> Limit fetched data to those of EserviceE. Multiple --service options may be supplied. [unset] =item B<< --host >> Limit fetched data to those from Ehost. Multiple --host options may be supplied. [unset] =item B<< --config >> Use EfileE as configuration file. [@@CONFDIR@@/munin.conf] =item B<< --help >> View help message. =item B<< --[no]debug >> If set, view debug messages. [--nodebug] =item B<< --[no]fork >> If set, will fork off one process for each host. [--fork] =item B<< --timeout >> Set the network timeout to . [180] =back =head1 DESCRIPTION Munin-update is a part of the package Munin, which is used in combination with Munin's node. Munin is a group of programs to gather data from Munin's nodes, graph them, create html-pages, and optionally warn Nagios about any off-limit values. Munin-update does the gathering. It is usually only used from within munin-cron. It contacts each host's munin-node in turn, gathers data from it, and stores them in .rrd-files. If necessary, it will create the rrd-files and the directories to store them in. =head1 FILES @@CONFDIR@@/munin.conf @@DBDIR@@/* @@LOGDIR@@/munin-update @@STATEDIR@@/* =head1 VERSION This is munin-update version @@VERSION@@ =head1 AUTHORS Audun Ytterdal, Jimmy Olsen, and Tore Anderson. =head1 BUGS munin-update does, as of now, not check the syntax of the configuration file. Please report other bugs in the bug tracker at L. =head1 COPYRIGHT Copyright © 2002-2009 Audun Ytterdal, Jimmy Olsen, and Tore Anderson / Linpro AS. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program is released under the GNU General Public License =head1 SEE ALSO For information on configuration options, please refer to the man page for F. =cut # vim:syntax=perl:ts=8 munin-2.0.75/master/extras/percentile.in000077500000000000000000000044571451614574100202430ustar00rootroot00000000000000#!@@PERL@@ -w =head1 NAME percentile - Munin support script to calculate the running n'th percentile of a data-set. =head1 USAGE To calculate the 95th percentile of the dataset "in" from a graph for all the different time periods: in95thday.calculate percentile %{in}f 1day 95 in95thweek.calculate percentile %{in}f 1week 95 in95thmonth.calculate percentile %{in}f 1month 95 in95thyear.calculate percentile %{in}f 1year 95 In the above the 95th perceintile for the different period lengths are stored in different time series and they will all be graphed on all the graphs. If you need only the monthly 95th percentile you can just calculate that instead. Remember to supply config for these data series in the usual way. =over 2 =item %{in}f is replaced with the name of the rrd file containing the in dataset. =item The second argument is the period to compute for, the values shown above are supported by rrd. =item 95 is the percentile to compute =back The returned value is stuffed in in95th(day|week|month|year).value and the value is graphed as usual. =head1 BUGS None! =head1 AUTHOR Unatributed, attached to tocket #443 by the anarcat in the Munin trac. =head1 LICENSE GPLv2 =cut use strict; use RRDs; ## no critic Prototypes my $me=$0; sub percentile($;$$) { my ($rrdfile,$duration,$n_th) = @_; # duration should be 1day, 1week, 1month or 1year. $duration ||= "1week"; $n_th ||= 95; # can't have 0th percentile my ($start,$step,$names,$data) = RRDs::fetch($rrdfile,"MAX","-s","-$duration"); my @sums = (); my $timestamp = $start; ROWLOOP: foreach my $row (@$data) { my $sum = 0; $timestamp += $step; foreach my $field (@$row) { next ROWLOOP if (! defined $field); # skip rows that have NaN $sum += $field; } push(@sums,$sum); } my $which = int(scalar(@sums)*($n_th)/100); @sums = sort{$a<=>$b}(@sums); return $sums[$which]; } # -- main() sub usage { if (defined($_[0])) { print STDERR "$me: $_[0]\n\n"; } print STDERR "$me <1day|1week|1month|1year> \n"; exit 1; } my $rrdfile = shift(@ARGV) or usage "No rrdfile name given."; my $period = shift(@ARGV) or usage "No period given."; my $nth = shift(@ARGV) or usage "No percentile given."; print ".value ",&percentile($rrdfile,$period,$nth) ,"\n"; munin-2.0.75/master/lib/000077500000000000000000000000001451614574100150045ustar00rootroot00000000000000munin-2.0.75/master/lib/Munin/000077500000000000000000000000001451614574100160725ustar00rootroot00000000000000munin-2.0.75/master/lib/Munin/Master/000077500000000000000000000000001451614574100173255ustar00rootroot00000000000000munin-2.0.75/master/lib/Munin/Master/Config.pm000066400000000000000000000421141451614574100210720ustar00rootroot00000000000000package Munin::Master::Config; use base qw(Munin::Common::Config); # Notes about config data structure: # # In Munin all configuration and gathered data is stored in the same # config tree of hashes. Since ~april 2009 we've made the tree object # oriented so the objects in there must be instantiated as the right # object type. And so we can use the object type to determine # behaviour when we itterate over the objects in the tree. # # The Class Munin::Common::Config is the base of Munin::Master::Config. # The master programs (munin-update, munin-graph, munin-html) instantiate # a Munin::Master::Config object. # # Please note that the munin-node configuration is also based on # Munin::Common::Config but is quite a lot simpler with regards to syntax # # The Class Munin::Master::GroupRepository is based on Munin::Master::Config # and contains a tree of Munin::Master::Group objects. # # The M::M::Group objects can be nested. Under a M::M::Group object there # can be a (flat) collection of M::M::Host objects. The M::M::Host class # is based in M::M::Group. # # A M::M::Host is a monitored host (not a node). Munin gathers data # about a host by connecting to a munin-node and asking about the host. # # Since multigraph plugins are hierarchical each host can contain # data for nested plugin names/dataseries labels. # # The configuration file formats are everywhere identical in structure # but the resulting configuration tree differs a bit. On the munin-master # the syntax is like this: # # Global setting: # # attribute value # # Simple group/host/service tree: # # Group;Host:service.attribute # Group;Host:service.label.attribute # # Groups can be nested: # # Group;Group;Group;Host:(...) # # (When multigraph is supported) services can be nested: # # (...);Host:service:service.(...) # (...);Host:service:service.service.(...) # # All attributes (attribute names) are known and appears in the @legal # array (and accompanying hash). # # Structure: # - A group name is always postfixed by a ; # - The host name is the first word with a : after it # - After that there are services and attributes # # For ease of configuration munin supports a [section] shorthand: # # [Group;] # [Group;Group;] # [Group;Host] # [Group;Host:service] # # The section is prefixed to the subsequent settings in the appropriate # manner with the correct infixes (";", ":" or "."). Usage can look like # this: # # [Group;] # Group;Host:service.attribute value # # is equivalent to # # [Group;Group;] # Host:service.attribute value # # is equivalent to # # [Group;Group;Host] # service.attribute value # # is equivalent to # # [Group;Group;Host:service] # attribute value # # As part of multigraph we're supporting nested services as well: # # [Group;Group;Host] # service.attribute value # service.service.attribute value # # [Group;Group;Host:service] # attribute value # Group;Group;Host:service.attribute # :service.attribute value # Group;Group;Host:service.service.attribute # # [Group;Group;Host:service.service] # attribute value # Group;Group;Host:service.service.attribute # service.attribute value # ...;Host:service:service.service.attribute # use warnings; use strict; use Carp; use English qw(-no_match_vars); use Munin::Common::Defaults; use Munin::Master::Group; use Munin::Master::Host; use Log::Log4perl qw( :easy ); my $MAXINT = 2 ** 53; my %booleans = map {$_ => 1} qw( debug fork tls_verify_certificate update use_node_name use_default_node ); { my $instance; sub instance { my ($class) = @_; $instance ||= bless { # To be able to identify if we're the root instance or a nested one. root_instance => 1, config => bless ( { config_file => "$Munin::Common::Defaults::MUNIN_CONFDIR/munin.conf", dbdir => $Munin::Common::Defaults::MUNIN_DBDIR, debug => 0, fork => 1, rrdcached_socket => "", # default is unused graph_data_size => 'normal', graph_strategy => 'cron', groups => {}, local_address => 0, logdir => $Munin::Common::Defaults::MUNIN_LOGDIR, max_processes => 16, rundir => $Munin::Common::Defaults::MUNIN_STATEDIR, timeout => 180, timeout_fetch_one_node => 180, timeout_fetch_all_nodes => 240, tls => 'disabled', tls_ca_certificate => "$Munin::Common::Defaults::MUNIN_CONFDIR/cacert.pem", tls_certificate => "$Munin::Common::Defaults::MUNIN_CONFDIR/munin.pem", tls_private_key => "$Munin::Common::Defaults::MUNIN_CONFDIR/munin.pem", tls_verify_certificate => 0, tls_verify_depth => 5, tmpldir => "$Munin::Common::Defaults::MUNIN_CONFDIR/templates", staticdir => "$Munin::Common::Defaults::MUNIN_CONFDIR/static", cgitmpdir => "$Munin::Common::Defaults::MUNIN_DBDIR/cgi-tmp", ssh_command => "ssh", ssh_options => "-o ChallengeResponseAuthentication=no -o StrictHostKeyChecking=no", }, $class ), oldconfig => bless ( { config_file => "$Munin::Common::Defaults::MUNIN_DBDIR/datafile", }, $class ), }, $class; return $instance; } } # Returns true if $char is the last character of $str. sub _final_char_is { # Not a object method. my ($char, $str) = @_; return rindex($str, $char) == ( length($str) - 1 ); } sub _create_and_set { my ($self,$groups,$host,$key,$value) = @_; # Nested creation of group and host class objects, and then set # attribute value. my $setref = $self; # Used as "iterator" as we traverse the hash. my @key = split(/\./, $key); my $last_word = pop @key; if ($booleans{$last_word}) { $value = $self->_parse_bool($value); } # If there is a host there is a group. So need only check group to see # how deep we go. if ($#{$groups} == -1) { $self->{$key} = $value; return; } foreach my $group (@{$groups}) { # Create nested group objects $setref->{groups}{$group} ||= Munin::Master::Group->new($group); if ($setref eq $self) { $setref->{groups}{$group}{group}=undef; } else { $setref->{groups}{$group}{group}=$setref; } $setref = $setref->{groups}{$group}; } if ($host) { if (! defined ( $setref->{hosts}{$host} ) ) { $setref->{hosts}{$host} = Munin::Master::Host->new($host,$setref,{ $key => $value }); } else { $setref->{hosts}{$host}->{$key} = $value; } } else { # Implant key/value into group $setref->{$key} = $value; } # } sub set_value { my ($self, $longkey, $value) = @_; my ($groups,$host,$key) = $self->_split_config_line($longkey); $self->_create_and_set($groups,$host,$key,$value); } sub _extract_group_name_from_definition { # Extract the group name from any munin.conf section name # # This a object method for the sake of finding it with the help of # a object of the right kind. # Cases: # * foo.example.com -> example.com # * bar;foo.example.com -> bar # * foo -> foo # * bar;foo -> bar # * bar; -> bar # # More cases: # * bar;foo.example.com:service my ($self, $definition) = @_; my $dot_loc = index($definition, '.'); my $sc_loc = index($definition, ';'); # Return bare hostname return $definition if $sc_loc == -1 and $dot_loc == -1; # Return explicit group name return substr($definition, 0, $sc_loc) if $sc_loc > -1 and ($dot_loc == -1 or $sc_loc < $dot_loc); # Return domain name as group name return substr($definition, $dot_loc + 1); } sub _concat_config_line { # Canonify and concatenate current prefix and and the config line # we're parsing now in a correct manner. # See also _split_config_line. # No sanity checking in this procedure. Use _concat_config_line_ok to # get sanity/syntax checking. my ($self, $prefix, $key, $value) = @_; my $longkey; # Allowed constructs: # [group;host] # port 4949 # # This is shorthand for [domain;host.domain]: # [host.domain] # port 4949 # # [group;] # [group;host] # [group;host:service] # [group;host:service.field] # [group1;group2;host:service.field] # keyword value # field.keyword value (only if no service in prefix) # group_order .... # # And more recently this for nested services (multigraph). # [group1;group2;host:service:service...] # :service.field.keyword value # # Rules: # - Last ';' terminates group part # - Last ':' terminates the host part # - The rest is a collection of services and time series data # - which we collect under the host name in the data-structure. # Note that keywords can come directly after group names in the # concatenated syntax: group;group_order ... if ($prefix eq '') { # If the prefix is empty then the key had better be well formed and # complete, because we'll use it without further checking. return $key; } if (index($prefix,';') == -1) { # Handle shorthand: Group name is given by host name my $group = $self->_extract_group_name_from_definition($prefix); $prefix = "$group;$prefix"; } if (_final_char_is(';',$prefix)) { # Prefix ended in the middle of a group. The rest can be # appended. $longkey = $prefix.$key; } elsif (index($prefix,':') != -1) { # Host name ends explicitly in the prefix. Use "." everywhere after : # Key is a nested service name $longkey = $prefix.'.'.$key; } else { # Prefix ends in host name but ":" is missing. $longkey = $prefix.':'.$key; } return $longkey; } sub _concat_config_line_ok { # Concatenate config line and do some extra syntaxy checks # # If the arrived at config line is not legal as far as we can tell # then croak here. my ($self, $prefix, $key, $value) = @_; if (!defined($key) or !$key) { ERROR "[ERROR] Somehow we're missing a keyword sometime after section [$prefix]"; die "[ERROR] Somehow we're missing a keyword sometime after section [$prefix]"; } my $longkey = $self->_concat_config_line($prefix,$key,$value); # _split_config_line_ok has the best starting point for checks on the # syntax/contents and so we call that to get the checks performed. # eval { $self->_split_config_line_ok($longkey); }; if ($EVAL_ERROR) { # _split_config_line_ok already logged the problem. my $err_msg = "[ERROR] config error under [$prefix] for '$key $value' : $EVAL_ERROR"; ERROR $err_msg; die $err_msg; } return $longkey; } sub _split_config_line { # After going to all that trouble with putting a "longkey" together # we now persue splitting the key in a nice and accurate manner. # # See also _concat_config_line my ($self,$line) = @_; my $groups; my $host; my $key; # Cases to keep in mind # htmldir # Group;address # Group;Group;address # Group;Host:address # Group;Host:if_eth0.in.value # Group;Host:snmp_foo_if_input.snmp_foo_if_input_0.value my $sc = index($line,';'); if ($sc == -1) { $groups=''; } else { # Note that .+ is greedy so $groups is the whole groups grouping $line =~ /(.+);(.*)/; ($groups, $line) = ($1, $2); } # Now left with (1:1 with cases above) # address # address # Host:address # Host:if_eth0.in.value # Host:snmp_foo_if_input.snmp_foo_if_input_0.value my $cc = index($line,':'); if ($cc == -1) { # No host delimiter: the rest is a setting $host = ''; $key = $line; } else { # Can see host delimiter. Copy it and the rest is a setting. $host = substr($line,0,$cc); substr($line,0,$cc+1) = ''; $key = $line; } return ([split(';',$groups)],$host,$key); } sub _split_config_line_ok { # Split config line and do some extra syntaxy checks # # If all is not well we'll corak here. my ($self,$longkey,$value) = @_; my ($groups,$host,$key) = $self->_split_config_line($longkey); my @words = split (/[.]/, $key); my $last_word = pop(@words); if (! $self->is_keyword($last_word)) { # We have seen some problems with $value in the following # error message. Se make sure it's defined so we can see the # message. $value = '' unless defined $value; croak "Parse error in ".$self->{config_file}." for $key:\n". " Unknown keyword at end of left hand side of line $NR ($key $value)\n"; } if ($host =~ /[^-A-Za-z0-9\.]/) { # Since we're not quite sure what context we're called in we'll report the error message more times rather than fewer. ERROR "[ERROR] Hostname '$host' contains illegal characters (http://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names). Please fix this by replacing illegal characters with '-'. Remember to do it on both in the master configuration and on the munin-node."; croak "[ERROR] Hostname '$host' contains illegal characters (http://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names). Please fix this by replacing illegal characters with '-'. Remember to do it on both in the master configuration and on the munin-node.\n"; } return ($groups,$host,$key); } sub _parse_config_line { # Parse and save contents of random user configuration. my ($self, $prefix, $key, $value, $skip_on_error) = @_; my $longkey; if ($skip_on_error) { eval { $longkey = $self->_concat_config_line_ok($prefix, $key, $value); }; # Skip single malformed lines (error messages were emitted before). # This is suitable for fault tolerant parsing of "datafile". return if $EVAL_ERROR; } else { # A problem with a malformed line is allowed to rise to the file level. # This is suitable for configuration files. $longkey = $self->_concat_config_line_ok($prefix, $key, $value); } $self->set_value($longkey,$value); } sub parse_config { my ($self, $io, $skip_line_on_error) = @_; my $section = undef; my $continuation = ''; my $prefix = ''; while (my $line = <$io>) { $self->_strip_comment($line); $self->_trim($line); # Handle continuation lines (ending in \) if ($line =~ s|\\$||) { $continuation .= $line; next; } elsif ($continuation) { $line = $continuation . $line; $continuation = ''; } # This must be handled after continuation hadling otherwise # empty lines will be ignored in continuation context. next if !length($line); # Group/host/service configuration is saved for later persual. # Everything else is saved at once. Note that _trim removes # leading whitespace so section changes can only happen if a new # [foo] comes along. if ($line =~ m{\A \[ ([^]]+) \] \s* \z}xms) { $prefix = $1; } else { my($key,$value) = split(/\s+/,$line,2); $self->_parse_config_line($prefix, $key, $value, $skip_line_on_error); } } } sub look_up { # The path through the hash works out to: # $self->{groups}{localdomain}[...]{hosts}{localhost} my ($self,$key) = @_; my (@groups) = split(';',$key); my $host = pop(@groups); my $value = $self; for my $group (@groups) { if (defined $value and defined $value->{groups} and defined $value->{groups}{$group}) { $value = $value->{groups}{$group}; } else { return undef; } } if (defined $value and defined $value->{hosts} and defined $value->{hosts}{$host}) { return $value->{hosts}{$host}; }; return undef; } sub get_groups_and_hosts { my ($self) = @_; return $self->{groups}; } sub get_all_hosts { # Note! This method is implemented in multiple classes to make the # recursion complete. my ($self) = @_; my @hosts = (); for my $group (values %{$self->{groups}}) { push @hosts, $group->get_all_hosts; } return @hosts; } sub set { my ($self, $config) = @_; # Note: config overrides self. %$self = (%$self, %$config); } 1; __END__ =head1 NAME Munin::Master::Config - Holds the master configuration. =head1 METHODS =over =item B my $config = Munin::Master::Config->instance; Returns the (possibly newly created) singleton configuration instance. =item B $config->set_value($longkey, $value); Set a value in the config, where $longkey is the full ;:. separated value. =item B $config->parse_config($io); Populates the fields of $config from the configuration file referred to by filehandle $io. =item B my $value = $config->look_up($key); Look up a group/host by a key such as "localdomain;localhost" etc. If the path does not exist create it with correct class and so on. Lookup ends at host name. If something is missing along the way undef is returned. =item B my $gah = $config->get_groups_and_hosts(); Returns all the groups and hosts defined in the configuration. =item B my $hosts = $config->get_all_hosts(); Returns a list of all the hosts defined in the configuration. =item B $config->set(\%attrs); Sets the keys and values in $config to those in %attrs. =back =cut # vim: ts=8 : sw=4 : et munin-2.0.75/master/lib/Munin/Master/GraphOld.pm000066400000000000000000001736071451614574100214010ustar00rootroot00000000000000package Munin::Master::GraphOld; # -*- cperl -*- =encoding utf-8 =begin comment This is Munin::Master::GraphOld, a package shell to make munin-graph modular (so it can loaded persistently in munin-cgi-graph for example) without making it object oriented yet. The non "old" module will feature propper object orientation like munin-update and will have to wait until later. Copyright (C) 2002-2010 Jimmy Olsen, Audun Ytterdal, Kjell Magne Øierud, Nicolai Langfeldt, Linpro AS, Redpill Linpro AS and others. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =end comment =cut use warnings; use strict; use Exporter; our (@ISA, @EXPORT); @ISA = qw(Exporter); @EXPORT = qw(graph_startup graph_check_cron graph_main graph_config); use IO::Socket; use IO::Handle; use RRDs; use POSIX qw(strftime); use Digest::MD5; use Getopt::Long; use Time::HiRes; use Text::ParseWords; # For UTF-8 handling (plugins are assumed to use Latin 1) if ($RRDs::VERSION >= 1.3) { use Encode; use Encode::Guess; Encode->import; Encode::Guess->import; } use Munin::Master::Logger; use Munin::Master::Utils; use Munin::Common::Defaults; use Log::Log4perl qw( :easy ); # RRDtool 1.2 requires \\: in comments my $RRDkludge = $RRDs::VERSION < 1.2 ? '' : '\\'; # And RRDtool 1.2.* draws lines with crayons so we hack # the LINE* options a bit. my $LINEkluge = 0; if ($RRDs::VERSION >= 1.2 and $RRDs::VERSION < 1.3) { # Only kluge the line widths in RRD 1.2* $LINEkluge = 1; } # RRD 1.3 has a "ADDNAN" operator which evaluates n + NaN = n instead of = NaN. my $AddNAN = '+'; if ($RRDs::VERSION >= 1.3) { $AddNAN = 'ADDNAN'; } # the ":dashes" syntax for LINEs is supported since rrdtool 1.5.3 my $RRDLineThresholdAttribute = ($RRDs::VERSION < 1.50003) ? '' : ':dashes'; # Force drawing of "graph no". my $force_graphing = 0; my $force_lazy = 1; my $do_usage = 0; my $do_version = 0; my $cron = 0; my $list_images = 0; my $output_file = undef; my $log_file = undef; my $skip_locking = 0; my $skip_stats = 0; my $stdout = 0; my $force_run_as_root = 0; my $conffile = $Munin::Common::Defaults::MUNIN_CONFDIR . "/munin.conf"; my $libdir = $Munin::Common::Defaults::MUNIN_LIBDIR; # Note: Nothing by default is more convenient and elliminates code while # for cgi graphing - but it breaks how munin-graph expected stuff to work. # I think. my %draw = ( 'day' => 0, 'week' => 0, 'month' => 0, 'year' => 0, 'sumyear' => 0, 'sumweek' => 0, 'pinpoint' => 0, ); my %init_draw = %draw; my $pinpoint = {}; my ($size_x, $size_y, $full_size_mode, $only_graph); my ($lower_limit, $upper_limit); my %PALETTE; # Hash of available palettes my @COLOUR; # Array of actuall colours to use { no warnings; $PALETTE{'old'} = [ # This is the old munin palette. It lacks contrast. qw(22ff22 0022ff ff0000 00aaaa ff00ff ffa500 cc0000 0000cc 0080C0 8080C0 FF0080 800080 688e23 408080 808000 000000 00FF00 0080FF FF8000 800000 FB31FB )]; $PALETTE{'default'} = [ # New default palette.Better contrast,more colours #Greens Blues Oranges Dk yel Dk blu Purple lime Reds Gray qw(00CC00 0066B3 FF8000 FFCC00 330099 990099 CCFF00 FF0000 808080 008F00 00487D B35A00 B38F00 6B006B 8FB300 B30000 BEBEBE 80FF80 80C9FF FFC080 FFE680 AA80FF EE00CC FF8080 666600 FFBFFF 00FFCC CC6699 999900 )]; # Line variations: Pure, earthy, dark pastel, misc colours } my $range_colour = "22ff22"; my $single_colour = "00aa00"; # Use 400 x RRA step, in order to have 1px per RRA sample. my %times = ( "day" => "-2000m", # (i.e. -33h20m) "week" => "-12000m", # (i.e. -8d13h20m) "month" => "-48000m", # (i.e. -33d8h) "year" => "-400d", "pinpoint" => "dummy", ); my %resolutions = ( "day" => "300", "week" => "1500", "month" => "7200", "year" => "86400" ); my %sumtimes = ( # time => [ label, seconds-in-period ] "week" => ["hour", 12], "year" => ["day", 288]); # Limit graphing to certain hosts and/or services my @limit_hosts = (); my @limit_services = (); my $only_fqn = ''; my $watermark = "Munin " . $Munin::Common::Defaults::MUNIN_VERSION; # RRD param for RRDCACHED_ADDRESS my @rrdcached_params; my $running = 0; my $max_running = 6; my $do_fork = 1; # "global" Configuration hash my $config = undef; # stats file handle my $STATS; my @init_limit_hosts = @limit_hosts; my @init_limit_services = @limit_services; sub process_pinpoint { my ($pinpoint, $arg_name, $arg_value) = @_; # XXX - Special hack^h^h^h^h treatment for --pinpoint if ($arg_value && $arg_value =~ m/^(\d+),(\d+)$/ ) { # "pinpoint" replaces all the other timing options $draw{'day'}=0; $draw{'week'}=0; $draw{'month'}=0; $draw{'year'}=0; $draw{'sumweek'}=0; $draw{'sumyear'}=0; $draw{'pinpoint'}=1; $$pinpoint->{'start'} = $1; # preparsed values $$pinpoint->{'end'} = $2; } } sub process_fqn { my ($fqn, $arg) = @_; # Reset what to draw whenever we specify a new fqn $draw{'day'} = $draw{'week'} = $draw{'month'} = $draw{'year'} = $draw{'sumweek'} = $draw{'sumyear'} = $draw{'pinpoint'} = 0; return $arg; } sub graph_startup { # Parse options and set up. Stuff that is usually only needed once. # # Do once pr. run, pr possebly once pr. graph in the case of # munin-cgi-graph # Localise the stuff, overwise it will be stacked up with CGI %draw = %init_draw; @limit_hosts = @init_limit_hosts; @limit_services = @init_limit_services; $pinpoint = undef; my $pinpointopt = undef; $force_graphing = 0; $force_lazy = 1; $do_usage = 0; $do_version = 0; $cron = 0; $list_images = 0; $output_file = undef; $log_file = undef; $skip_locking = 0; $skip_stats = 0; $stdout = 0; $size_x = undef; $size_y = undef; $full_size_mode = undef; $only_graph = undef; $lower_limit = undef; $upper_limit = undef; # Get options my ($args) = @_; local @ARGV = @{$args}; # NOTE! Some of these options are available in graph_main too # if you make changes here, make them there too. my $debug; &print_usage_and_exit unless GetOptions ( "force!" => \$force_graphing, "lazy!" => \$force_lazy, "host=s" => \@limit_hosts, "service=s" => \@limit_services, "only-fqn=s" => sub{ $only_fqn = process_fqn(@_); }, "config=s" => \$conffile, "stdout!" => \$stdout, "force-run-as-root!" => \$force_run_as_root, "day!" => \$draw{'day'}, "week!" => \$draw{'week'}, "month!" => \$draw{'month'}, "year!" => \$draw{'year'}, "pinpoint=s" => sub{ process_pinpoint(\$pinpoint,@_); }, "sumweek!" => \$draw{'sumweek'}, "sumyear!" => \$draw{'sumyear'}, "size_x=i" => \$size_x, "size_y=i" => \$size_y, "full_size_mode!"=> \$full_size_mode, "only_graph!"=> \$only_graph, "upper_limit=s" => \$upper_limit, "lower_limit=s" => \$lower_limit, "list-images!" => \$list_images, "o|output-file=s" => \$output_file, "l|log-file=s" => \$log_file, "skip-locking!" => \$skip_locking, "skip-stats!" => \$skip_stats, "version!" => \$do_version, "cron!" => \$cron, "fork!" => \$do_fork, "n=n" => \$max_running, "help" => \$do_usage, "debug!" => \$debug, ); if ($do_version) { print_version_and_exit(); } if ($do_usage) { print_usage_and_exit(); } exit_if_run_by_super_user() unless $force_run_as_root; # Only read $config once (thx Jani M.) # # FIXME - the loaded $config is stale within 5 minutes. # we either need to die or restart ourselves when this # happens. if (!defined($config)) { munin_readconfig_base($conffile); # XXX: check if it needs datafile at that point $config = munin_readconfig_part('datafile', 0); } $config->{debug} = $debug; my $palette = &munin_get($config, "palette", "default"); $max_running = &munin_get($config, "max_graph_jobs", $max_running); if ($config->{"rrdcached_socket"}) { if ($RRDs::VERSION >= 1.3){ # Using the RRDCACHED_ADDRESS environnement variable, as # it is way less intrusive than the command line args. $ENV{RRDCACHED_ADDRESS} = $config->{"rrdcached_socket"}; } else { ERROR "[ERROR] RRDCached feature ignored: RRD version must be at least 1.3. Version found: " . $RRDs::VERSION; } } if ($max_running == 0) { $do_fork = 0; } if (defined($PALETTE{$palette})) { @COLOUR = @{$PALETTE{$palette}}; } else { die "Unknown palette named by 'palette' keyword: $palette\n"; } return $config; } sub graph_check_cron { # Are we running from cron and do we have matching graph_strategy if (&munin_get($config, "graph_strategy", "cron") ne "cron" and $cron) { # Strategy mismatch: We're run from cron, but munin.conf says # we use dynamic graph generation return 0; } # Strategy match: return 1; } sub graph_main { my ($args) = @_; local @ARGV = @{$args}; # The loaded $config is stale within 5 minutes. # So, we need to reread it when this happens. $config = munin_readconfig_part('datafile'); # Reset an eventual custom size $size_x = undef; $size_y = undef; $full_size_mode = undef; $only_graph = undef; $lower_limit = undef; $upper_limit = undef; $pinpoint = undef; # XXX [DEBUG] my $debug = undef; GetOptions ( "host=s" => \@limit_hosts, "only-fqn=s" => sub { $only_fqn = process_fqn(@_); }, "day!" => \$draw{'day'}, "week!" => \$draw{'week'}, "month!" => \$draw{'month'}, "year!" => \$draw{'year'}, "pinpoint=s" => sub{ process_pinpoint(\$pinpoint,@_); }, "sumweek!" => \$draw{'sumweek'}, "sumyear!" => \$draw{'sumyear'}, "o|output-file=s" => \$output_file, # XXX [DEBUG] "debug!" => \$debug, "size_x=i" => \$size_x, "size_y=i" => \$size_y, "full_size_mode!"=> \$full_size_mode, "only_graph!" => \$only_graph, "upper_limit=s" => \$upper_limit, "lower_limit=s" => \$lower_limit, ); # XXX [DEBUG] logger_debug() if $debug; my $graph_time = Time::HiRes::time; munin_runlock("$config->{rundir}/munin-graph.lock") unless $skip_locking; unless ($skip_stats) { open($STATS, '>', "$config->{dbdir}/munin-graph.stats.tmp") or WARN "[WARNING] Unable to open $config->{dbdir}/munin-graph.stats.tmp"; autoflush $STATS 1; } process_work(@limit_hosts); $graph_time = sprintf("%.2f", (Time::HiRes::time - $graph_time)); rename( "$config->{dbdir}/munin-graph.stats.tmp", "$config->{dbdir}/munin-graph.stats" ); close $STATS unless $skip_stats; munin_removelock("$config->{rundir}/munin-graph.lock") unless $skip_locking; $running = wait_for_remaining_children($running); } # -------------------------------------------------------------------------- sub get_title { my $service = shift; my $scale = shift; my $scale_text; if ($pinpoint) { my $start_text = localtime($pinpoint->{"start"}); my $end_text = localtime($pinpoint->{"end"}); $scale_text = "from $start_text to $end_text"; } else { $scale_text = "by " . $scale; } my $title = munin_get($service, "graph_title", $service); # Substitute ${graph_period} in title my $period = munin_get($service, "graph_period", "second"); $title =~ s/\$\{graph_period\}/$period/g; return ("$title - $scale_text"); } sub get_custom_graph_args { my $service = shift; my $result = []; my $args = munin_get($service, "graph_args"); if (defined $args) { my $result = [ grep /\S/, "ewords('\s+', 0, $args) ]; return $result; } else { return; } } # insert these arguments after all others # needed for your own VDEF/CDEF/DEF combinations sub get_custom_graph_args_after { my $service = shift; my $result = []; my $args = munin_get($service, "graph_args_after"); if (defined $args) { my $result = ["ewords('\s+', 0, $args)]; return $result; } else { return; } } # set a graph end point in the future # needed for CDEF TREND and PREDICT sub get_end_offset { my $service = shift; # get number of seconds in future return munin_get($service, "graph_future", 0); } sub get_vlabel { my $service = shift; my $scale = munin_get($service, "graph_period", "second"); my $res = munin_get($service, "graph_vlabel", munin_get($service, "graph_vtitle")); if (defined $res) { $res =~ s/\$\{graph_period\}/$scale/g; } return $res; } sub should_scale { my $service = shift; my $ret; if (!defined($ret = munin_get_bool($service, "graph_scale"))) { $ret = !munin_get_bool($service, "graph_noscale", 0); } return $ret; } sub get_header { my $service = shift; my $scale = shift; my $sum = shift; my $result = []; my $tmp_field; # Picture filename push @$result, get_picture_filename($service, $scale, $sum || undef); # Title push @$result, ("--title", get_title($service, $scale)); # When to start the graph if ($pinpoint) { push @$result, "--start", $pinpoint->{start}; push @$result, "--end", $pinpoint->{end}; } else { push @$result, "--start", $times{$scale}; } # Custom graph args, vlabel and graph title if (defined($tmp_field = get_custom_graph_args($service))) { push(@$result, @{$tmp_field}); } if (defined($tmp_field = get_vlabel($service))) { push @$result, ("--vertical-label", $tmp_field); } push @$result, '--slope-mode' if $RRDs::VERSION >= 1.2; push @$result, "--height", ($size_y || munin_get($service, "graph_height", "175")); push @$result, "--width", ($size_x || munin_get($service, "graph_width", "400")); push @$result, "--full-size-mode" if ($full_size_mode); push @$result, "--only-graph" if ($only_graph); push @$result,"--rigid" if (defined $lower_limit || defined $upper_limit); push @$result, "--imgformat", "PNG"; push @$result, "--lazy" if ($force_lazy); push(@$result, "--units-exponent", "0") if (!should_scale($service)); return $result; } sub get_sum_command { my $field = shift; return munin_get($field, "sum"); } sub get_stack_command { my $field = shift; return munin_get($field, "stack"); } sub expand_specials { my $service = shift; my $order = shift; my $preproc = []; my $single ; # Test if already expanded { my $cached = $service->{"#%#expand_specials"}; if (defined $cached) { DEBUG "[DEBUG] expand_specials(): already processed " . munin_dumpconfig_as_str($cached); return $cached; } DEBUG "[DEBUG] expand_specials(): not processed, proceeding for " . munin_dumpconfig_as_str($service); } # we have to compute the result; my $result = []; my $fieldnum = 0; for my $field (@$order) { # Search for 'specials'... my $tmp_field; if ($field =~ /^-(.+)$/) { # Invisible field $field = $1; munin_set_var_loc($service, [$field, "graph"], "no"); } $fieldnum++; if ($field =~ /^([^=]+)=(.+)$/) { # Aliased in graph_order my $fname = $1; my $spath = $2; my $src = munin_get_node_partialpath($service, $spath); my $sname = munin_get_node_name($src); if(!defined $src) { ERROR "[ERROR] Failed to find $fname source at $spath, skipping field"; next; } DEBUG "[DEBUG] Copying settings from $sname to $fname."; foreach my $foption ("draw", "type", "rrdfile", "fieldname", "info") { if (!defined $service->{$fname}->{$foption}) { if (defined $src->{$foption}) { munin_set_var_loc($service, [$fname, $foption], $src->{$foption}); } } } if (!defined $service->{$fname}->{"label"}) { munin_set_var_loc($service, [$fname, "label"], $fname); } munin_set_var_loc( $service, [$fname, "filename"], munin_get_rrd_filename($src)); } elsif (defined($tmp_field = get_stack_command($service->{$field}))) { # Aliased with .stack DEBUG "[DEBUG] expand_specials ($tmp_field): Doing stack..."; my @spc_stack = (); foreach my $pre (split(/\s+/, $tmp_field)) { (my $name = $pre) =~ s/=.+//; # Auto selects the .draw my $draw = (!@spc_stack) ? munin_get($service->{$field}, "draw", "LINE1") : "STACK"; munin_set_var_loc($service, [$name, "draw"], $draw); # Don't process this field later munin_set_var_loc($service, [$field, "process"], "0"); push(@spc_stack, $name); push(@$preproc, $pre); push @$result, "$name.label"; push @$result, "$name.draw"; push @$result, "$name.cdef"; munin_set_var_loc($service, [$name, "label"], $name); munin_set_var_loc($service, [$name, "cdef"], "$name,UN,0,$name,IF"); if (munin_get($service->{$field}, "cdef") and !munin_get_bool($service->{$name}, "onlynullcdef", 0)) { DEBUG "[DEBUG] NotOnlynullcdef ($field)..."; $service->{$name}->{"cdef"} .= "," . $service->{$field}->{"cdef"}; $service->{$name}->{"cdef"} =~ s/\b$field\b/$name/g; } else { DEBUG "[DEBUG] Onlynullcdef ($field)..."; munin_set_var_loc($service, [$name, "onlynullcdef"], 1); push @$result, "$name.onlynullcdef"; } } } # if get_stack_command elsif (defined($tmp_field = get_sum_command($service->{$field}))) { my @spc_stack = (); my $last_name = ""; DEBUG "[DEBUG] expand_specials ($tmp_field): Doing sum..."; if (@$order == 1 or (@$order == 2 and munin_get($field, "negative", 0))) { $single = 1; } foreach my $pre (split(/\s+/, $tmp_field)) { (my $path = $pre) =~ s/.+=//; my $name = "z" . $fieldnum . "_" . scalar(@spc_stack); $last_name = $name; munin_set_var_loc($service, [$name, "cdef"], "$name,UN,0,$name,IF"); munin_set_var_loc($service, [$name, "graph"], "no"); munin_set_var_loc($service, [$name, "label"], $name); push @$result, "$name.cdef"; push @$result, "$name.graph"; push @$result, "$name.label"; push(@spc_stack, $name); push(@$preproc, "$name=$pre"); } $service->{$last_name}->{"cdef"} .= "," . join(",$AddNAN,", @spc_stack[0 .. @spc_stack - 2]) . ",$AddNAN"; if (my $tc = munin_get($service->{$field}, "cdef", 0)) { # Oh bugger... DEBUG "[DEBUG] Oh bugger...($field)...\n"; $tc =~ s/\b$field\b/$service->{$last_name}->{"cdef"}/; $service->{$last_name}->{"cdef"} = $tc; } munin_set_var_loc($service, [$field, "process"], "0"); munin_set_var_loc( $service, [$last_name, "draw"], munin_get($service->{$field}, "draw")); munin_set_var_loc( $service, [$last_name, "colour"], munin_get($service->{$field}, "colour")); munin_set_var_loc( $service, [$last_name, "label"], munin_get($service->{$field}, "label")); munin_set_var_loc( $service, [$last_name, "graph"], munin_get($service->{$field}, "graph", "yes")); if (my $tmp = munin_get($service->{$field}, "negative")) { munin_set_var_loc($service, [$last_name, "negative"], $tmp); } munin_set_var_loc($service, [$field, "realname"], $last_name); } elsif (my $nf = munin_get($service->{$field}, "negative", 0)) { if ( !munin_get_bool($service->{$nf}, "graph", 1) or munin_get_bool($service->{$nf}, "skipdraw", 0)) { munin_set_var_loc($service, [$nf, "graph"], "no"); } } } # for (@$order) # Return & save it for future use $service->{"#%#expand_specials"} = { "added" => $result, "preprocess" => $preproc, "single" => $single, }; return $service->{"#%#expand_specials"}; } sub single_value { my $service = shift; my $graphable = munin_get($service, "graphable", 0); if (!$graphable) { foreach my $field (@{munin_get_field_order($service)}) { DEBUG "[DEBUG] single_value: Checking field \"$field\"."; $graphable++ if munin_draw_field($service->{$field}); } munin_set_var_loc($service, ["graphable"], $graphable); } DEBUG "[DEBUG] service " . join(' :: ', @{munin_get_node_loc($service)}) . " has $graphable elements."; return ($graphable == 1); } sub get_field_name { my $name = shift; $name = substr(Digest::MD5::md5_hex($name), -15) if (length $name > 15); return $name; } sub process_work { my (@hosts) = @_; # Make array of what is probably needed to graph my $work_array = []; if ($only_fqn) { push @$work_array, munin_find_node_by_fqn($config,$only_fqn); } elsif (@hosts) { foreach my $nodename (@hosts) { push @$work_array, map {@{munin_find_field($_->{$nodename}, "graph_title")}} @{munin_find_field($config, $nodename)}; } } else { FATAL "[FATAL] In process_work, no fqn and no hosts!"; } # @$work_array contains copy of (or pointer to) each service to be graphed. for my $service (@$work_array) { # Want to avoid forking for that next if (skip_service($service)); # Fork (or not) and run the anonymous sub afterwards. fork_and_work(sub {process_service($service);}); } } sub process_field { my $field = shift; return munin_get_bool($field, "process", 1); } sub fork_and_work { my ($work) = @_; if (!$do_fork) { # We're not forking. Do work and return. DEBUG "[DEBUG] Doing work synchronously"; &$work; return; } # Make sure we don't fork too much while ($running >= $max_running) { DEBUG "[DEBUG] Too many forks ($running/$max_running), wait for something to get done"; look_for_child("block"); --$running; } my $pid = fork(); if (!defined $pid) { ERROR "[ERROR] fork failed: $!"; die "fork failed: $!"; } if ($pid == 0) { # This block does the real work. Since we're forking exit # afterwards. &$work; # See?! exit 0; } else { ++$running; DEBUG "[DEBUG] Forked: $pid. Now running $running/$max_running"; while ($running and look_for_child()) { --$running; } } } sub remove_dups { my @ret; my %keys; for my $order (@_) { (my $name = $order) =~ s/=.+//; push @ret, $order unless ($keys{$name} ++); } return @ret; } sub _sanitise_fieldname { # http://munin-monitoring.org/wiki/notes_on_datasource_names my ($name) = @_; $name =~ s/^[^A-Za-z_]/_/; $name =~ s/[^A-Za-z0-9_]/_/g; return $name; } sub process_service { my ($service) = @_; # See if we should skip the service return if (skip_service($service)); # Make my graphs my $sname = munin_get_node_name($service); my $skeypath = munin_get_keypath($service); my $service_time = Time::HiRes::time; my $lastupdate = 0; my $now = time; my $fnum = 0; my @rrd; DEBUG "[DEBUG] Node name: $sname\n"; my $field_count = 0; my $max_field_len = 0; my @field_order = (); my $rrdname; @field_order = @{munin_get_field_order($service)}; # Array to keep 'preprocess'ed fields. DEBUG "[DEBUG] Expanding specials for $sname: \"" . join("\",\"", @field_order) . "\"."; my $expanded_result = expand_specials($service, \@field_order); my $force_single_value = $expanded_result->{single}; my @added = @{ $expanded_result->{added} }; # put preprocessed fields in front unshift @field_order, @{ $expanded_result->{preprocess} }; # Remove duplicates, while retaining the order @field_order = remove_dups ( @field_order ); # Get max label length DEBUG "[DEBUG] Checking field lengths for $sname: \"" . join('","', @field_order) . '".'; $max_field_len = munin_get_max_label_length($service, \@field_order); # Global headers makes the value tables easier to read no matter how # wide the labels are. my $global_headers = 1; # Default format for printing under graph. my $avgformat; my $rrdformat = $avgformat = "%6.2lf"; if (munin_get($service, "graph_args", "") =~ /--base\s+1024/) { # If the base unit is 1024 then 1012.56 is a valid # number to show. That's 7 positions, not 6. $rrdformat = $avgformat = "%7.2lf"; } # Plugin specified complete printf format $rrdformat = munin_get($service, "graph_printf", $rrdformat); my $rrdscale = ''; if (munin_get_bool($service, "graph_scale", 1)) { $rrdscale = '%s'; } # Array to keep negative data until we're finished with positive. my @rrd_negatives = (); my $filename; my %total_pos; my %total_neg; my $autostacking = 0; DEBUG "[DEBUG] Treating fields \"" . join("\",\"", @field_order) . "\"."; for my $fname (@field_order) { my $path = undef; my $field = undef; if ($fname =~ s/=(.+)//) { $path = $1; } $field = munin_get_node($service, [$fname]); next if (!defined $field or !$field or !process_field($field)); DEBUG "[DEBUG] Processing field \"$fname\" [" . munin_get_node_name($field) . "]."; my $fielddraw = munin_get($field, "draw", "LINE1"); if ($field_count == 0 and $fielddraw eq 'STACK') { # Illegal -- first field is a STACK DEBUG "ERROR: First field (\"$fname\") of graph " . join(' :: ', munin_get_node_loc($service)) . " is STACK. STACK can only be drawn after a LINEx or AREA."; $fielddraw = "LINE1"; } if ($fielddraw eq 'AREASTACK') { if ($autostacking == 0) { $fielddraw = 'AREA'; $autostacking = 1; } else { $fielddraw = 'STACK'; } } if ($fielddraw =~ /LINESTACK(|\d+|\d+\.\d+)$/) { if ($autostacking == 0) { $fielddraw = "LINE$1"; $autostacking = 1; } else { $fielddraw = 'STACK'; } } # Getting name of rrd file $filename = munin_get_rrd_filename($field, $path); if (! $filename) { ERROR "[ERROR] filename is empty for " . munin_dumpconfig_as_str($field) . ", $path"; # Ignore this field next; } if(!defined $filename) { ERROR "[ERROR] Failed getting filename for $path, skipping field"; next; } # Here it is OK to flush the rrdcached, since we'll flush it anyway # with graph my $update = RRDs::last(@rrdcached_params, $filename); $update = 0 if !defined $update; if ($update > $lastupdate) { $lastupdate = $update; } # It does not look like $fieldname.rrdfield is possible to set my $rrdfield = munin_get($field, "rrdfield", "42"); my $single_value = $force_single_value || single_value($service); # XXX - single_value is wrong for some multigraph, disabling it for now $single_value = 0; my $has_negative = munin_get($field, "negative"); # Trim the fieldname to make room for other field names. $rrdname = &get_field_name($fname); reset_cdef($service, $rrdname); if ($rrdname ne $fname) { # A change was made munin_set($field, "cdef_name", $rrdname); } # Push will place the DEF too far down for some CDEFs to work unshift(@rrd, "DEF:g$rrdname=" . $filename . ":" . $rrdfield . ":AVERAGE"); unshift(@rrd, "DEF:i$rrdname=" . $filename . ":" . $rrdfield . ":MIN"); unshift(@rrd, "DEF:a$rrdname=" . $filename . ":" . $rrdfield . ":MAX"); if (munin_get_bool($field, "onlynullcdef", 0)) { push(@rrd, "CDEF:c$rrdname=g$rrdname" . (($now - $update) > 900 ? ",POP,UNKN" : "")); } if ( munin_get($field, "type", "GAUGE") ne "GAUGE" and graph_by_minute($service)) { push(@rrd, expand_cdef($service, \$rrdname, "$fname,60,*")); } if ( munin_get($field, "type", "GAUGE") ne "GAUGE" and graph_by_hour($service)) { push(@rrd, expand_cdef($service, \$rrdname, "$fname,3600,*")); } if (my $tmpcdef = munin_get($field, "cdef")) { push(@rrd, expand_cdef($service, \$rrdname, $tmpcdef)); push(@rrd, "CDEF:c$rrdname=g$rrdname"); DEBUG "[DEBUG] Field name after cdef set to $rrdname"; } elsif (!munin_get_bool($field, "onlynullcdef", 0)) { push(@rrd, "CDEF:c$rrdname=g$rrdname" . (($now - $update) > 900 ? ",POP,UNKN" : "")); } next if !munin_draw_field($field); DEBUG "[DEBUG] Drawing field \"$fname\"."; if ($single_value) { # Only one field. Do min/max range. push(@rrd, "CDEF:min_max_diff=a$rrdname,i$rrdname,-"); push(@rrd, "CDEF:re_zero=min_max_diff,min_max_diff,-") if !munin_get($field, "negative"); push(@rrd, "AREA:i$rrdname#ffffff"); push(@rrd, "STACK:min_max_diff#$range_colour"); push(@rrd, "LINE1:re_zero#000000") if !munin_get($field, "negative"); } # Push "global" headers or not if ($has_negative and !@rrd_negatives and $global_headers < 2) { # Always for -/+ graphs push(@rrd, "COMMENT:" . (" " x $max_field_len)); push(@rrd, "COMMENT:Cur (-/+)"); push(@rrd, "COMMENT:Min (-/+)"); push(@rrd, "COMMENT:Avg (-/+)"); push(@rrd, "COMMENT:Max (-/+) \\j"); $global_headers = 2; # Avoid further headers/labels } elsif ($global_headers == 1) { # Or when we want to. push(@rrd, "COMMENT:" . (" " x $max_field_len)); push(@rrd, "COMMENT: Cur$RRDkludge:"); push(@rrd, "COMMENT:Min$RRDkludge:"); push(@rrd, "COMMENT:Avg$RRDkludge:"); push(@rrd, "COMMENT:Max$RRDkludge: \\j"); $global_headers = 2; # Avoid further headers/labels } my $colour = munin_get($field, "colour"); if ($colour && $colour =~ /^COLOUR(\d+)$/) { $colour = $COLOUR[$1 % @COLOUR]; } # Select a default colour if no explict one $colour ||= ($single_value) ? $single_colour : $COLOUR[$field_count % @COLOUR]; my $warn_colour = $single_value ? "ff0000" : $colour; # colour needed for transparent predictions and trends munin_set($field, "colour", $colour); $field_count++; my $tmplabel = munin_get($field, "label", $fname); # Substitute ${graph_period} my $period = munin_get($service, "graph_period", "second"); $tmplabel =~ s/\$\{graph_period\}/$period/g; push(@rrd, $fielddraw . ":g$rrdname" . "#$colour" . ":" . escape($tmplabel) . (" " x ($max_field_len + 1 - length $tmplabel))); # Check for negative fields (typically network (or disk) traffic) if ($has_negative) { my $negfieldname = orig_to_cdef($service, munin_get($field, "negative")); my $negfield = $service->{_sanitise_fieldname(munin_get($field, "negative"))}; if (my $tmpneg = munin_get($negfield, "realname")) { $negfieldname = $tmpneg; $negfield = $service->{$negfieldname}; } if (!@rrd_negatives) { # zero-line, to redraw zero afterwards. push(@rrd_negatives, "CDEF:re_zero=g$negfieldname,UN,0,0,IF"); } push(@rrd_negatives, "CDEF:ng$negfieldname=g$negfieldname,-1,*"); if ($single_value) { # Only one field. Do min/max range. push(@rrd, "CDEF:neg_min_max_diff=i$negfieldname,a$negfieldname,-"); push(@rrd, "CDEF:ni$negfieldname=i$negfieldname,-1,*"); push(@rrd, "AREA:ni$negfieldname#ffffff"); push(@rrd, "STACK:neg_min_max_diff#$range_colour"); } push(@rrd_negatives, $fielddraw . ":ng$negfieldname#$colour"); # Draw HRULEs my $linedef = munin_get($negfield, "line"); if ($linedef) { my ($number, $ldcolour, $label) = split(/:/, $linedef, 3); unshift(@rrd_negatives, "HRULE:" . $number . ($ldcolour ? "#$ldcolour" : "#$colour")); } elsif (my $tmpwarn = munin_get($negfield, "warning")) { my ($warn_min, $warn_max) = split(':', $tmpwarn,2); if (defined($warn_min) and $warn_min ne '') { unshift(@rrd, "HRULE:${warn_min}#${warn_colour}${RRDLineThresholdAttribute}"); } if (defined($warn_max) and $warn_max ne '') { unshift(@rrd, "HRULE:${warn_max}#${warn_colour}${RRDLineThresholdAttribute}"); } } push(@rrd, "GPRINT:c$negfieldname:LAST:$rrdformat" . $rrdscale . "/\\g"); push(@rrd, "GPRINT:c$rrdname:LAST:$rrdformat" . $rrdscale . ""); push(@rrd, "GPRINT:i$negfieldname:MIN:$rrdformat" . $rrdscale . "/\\g"); push(@rrd, "GPRINT:i$rrdname:MIN:$rrdformat" . $rrdscale . ""); push(@rrd, "GPRINT:g$negfieldname:AVERAGE:$avgformat" . $rrdscale . "/\\g"); push(@rrd, "GPRINT:g$rrdname:AVERAGE:$avgformat" . $rrdscale . ""); push(@rrd, "GPRINT:a$negfieldname:MAX:$rrdformat" . $rrdscale . "/\\g"); push(@rrd, "GPRINT:a$rrdname:MAX:$rrdformat" . $rrdscale . "\\j"); push(@{$total_pos{'min'}}, "i$rrdname"); push(@{$total_pos{'avg'}}, "g$rrdname"); push(@{$total_pos{'max'}}, "a$rrdname"); push(@{$total_neg{'min'}}, "i$negfieldname"); push(@{$total_neg{'avg'}}, "g$negfieldname"); push(@{$total_neg{'max'}}, "a$negfieldname"); } else { push(@rrd, "COMMENT: Cur$RRDkludge:") unless $global_headers; push(@rrd, "GPRINT:c$rrdname:LAST:$rrdformat" . $rrdscale . ""); push(@rrd, "COMMENT: Min$RRDkludge:") unless $global_headers; push(@rrd, "GPRINT:i$rrdname:MIN:$rrdformat" . $rrdscale . ""); push(@rrd, "COMMENT: Avg$RRDkludge:") unless $global_headers; push(@rrd, "GPRINT:g$rrdname:AVERAGE:$avgformat" . $rrdscale . ""); push(@rrd, "COMMENT: Max$RRDkludge:") unless $global_headers; push(@rrd, "GPRINT:a$rrdname:MAX:$rrdformat" . $rrdscale . "\\j"); push(@{$total_pos{'min'}}, "i$rrdname"); push(@{$total_pos{'avg'}}, "g$rrdname"); push(@{$total_pos{'max'}}, "a$rrdname"); } # Draw HRULEs my $linedef = munin_get($field, "line"); if ($linedef) { my ($number, $ldcolour, $label) = split(/:/, $linedef, 3); $label =~ s/:/\\:/g if defined $label; unshift( @rrd, "HRULE:" . $number . ( $ldcolour ? "#$ldcolour" : ((defined $single_value and $single_value) ? "#ff0000" : "#$colour")) . ((defined $label and length($label)) ? ":$label" : ""), "COMMENT: \\j" ); } elsif (my $tmpwarn = munin_get($field, "warning")) { my ($warn_min, $warn_max) = split(':', $tmpwarn,2); if (defined($warn_min) and $warn_min ne '') { unshift(@rrd, "HRULE:${warn_min}#${warn_colour}${RRDLineThresholdAttribute}"); } if (defined($warn_max) and $warn_max ne '') { unshift(@rrd, "HRULE:${warn_max}#${warn_colour}${RRDLineThresholdAttribute}"); } } } my $graphtotal = munin_get($service, "graph_total"); if (@rrd_negatives) { push(@rrd, @rrd_negatives); push(@rrd, "LINE1:re_zero#000000"); # Redraw zero. if ( defined $graphtotal and exists $total_pos{'min'} and exists $total_neg{'min'} and @{$total_pos{'min'}} and @{$total_neg{'min'}}) { push(@rrd, "CDEF:ipostotal=" . join(",", @{$total_pos{'min'}}) . (",$AddNAN" x (@{$total_pos{'min'}} - 1))); push(@rrd, "CDEF:gpostotal=" . join(",", @{$total_pos{'avg'}}) . (",$AddNAN" x (@{$total_pos{'avg'}} - 1))); push(@rrd, "CDEF:apostotal=" . join(",", @{$total_pos{'max'}}) . (",$AddNAN" x (@{$total_pos{'max'}} - 1))); push(@rrd, "CDEF:inegtotal=" . join(",", @{$total_neg{'min'}}) . (",$AddNAN" x (@{$total_neg{'min'}} - 1))); push(@rrd, "CDEF:gnegtotal=" . join(",", @{$total_neg{'avg'}}) . (",$AddNAN" x (@{$total_neg{'avg'}} - 1))); push(@rrd, "CDEF:anegtotal=" . join(",", @{$total_neg{'max'}}) . (",$AddNAN" x (@{$total_neg{'max'}} - 1))); push(@rrd, "LINE1:gpostotal#000000:$graphtotal" . (" " x ($max_field_len - length($graphtotal) + 1))); push(@rrd, "GPRINT:gnegtotal:LAST:$rrdformat" . $rrdscale . "/\\g"); push(@rrd, "GPRINT:gpostotal:LAST:$rrdformat" . $rrdscale . ""); push(@rrd, "GPRINT:inegtotal:MIN:$rrdformat" . $rrdscale . "/\\g"); push(@rrd, "GPRINT:ipostotal:MIN:$rrdformat" . $rrdscale . ""); push(@rrd, "GPRINT:gnegtotal:AVERAGE:$avgformat" . $rrdscale . "/\\g"); push(@rrd, "GPRINT:gpostotal:AVERAGE:$avgformat" . $rrdscale . ""); push(@rrd, "GPRINT:anegtotal:MAX:$rrdformat" . $rrdscale . "/\\g"); push(@rrd, "GPRINT:apostotal:MAX:$rrdformat" . $rrdscale . "\\j"); } } elsif ( defined $graphtotal and exists $total_pos{'min'} and @{$total_pos{'min'}}) { push(@rrd, "CDEF:ipostotal=" . join(",", @{$total_pos{'min'}}) . (",$AddNAN" x (@{$total_pos{'min'}} - 1))); push(@rrd, "CDEF:gpostotal=" . join(",", @{$total_pos{'avg'}}) . (",$AddNAN" x (@{$total_pos{'avg'}} - 1))); push(@rrd, "CDEF:apostotal=" . join(",", @{$total_pos{'max'}}) . (",$AddNAN" x (@{$total_pos{'max'}} - 1))); push(@rrd, "LINE1:gpostotal#000000:$graphtotal" . (" " x ($max_field_len - length($graphtotal) + 1))); push(@rrd, "COMMENT: Cur$RRDkludge:") unless $global_headers; push(@rrd, "GPRINT:gpostotal:LAST:$rrdformat" . $rrdscale . ""); push(@rrd, "COMMENT: Min$RRDkludge:") unless $global_headers; push(@rrd, "GPRINT:ipostotal:MIN:$rrdformat" . $rrdscale . ""); push(@rrd, "COMMENT: Avg$RRDkludge:") unless $global_headers; push(@rrd, "GPRINT:gpostotal:AVERAGE:$avgformat" . $rrdscale . ""); push(@rrd, "COMMENT: Max$RRDkludge:") unless $global_headers; push(@rrd, "GPRINT:apostotal:MAX:$rrdformat" . $rrdscale . "\\j"); } # insert these graph args in the end if (defined(my $tmp_field = get_custom_graph_args_after($service))) { push(@rrd, @{$tmp_field}); } my $nb_graphs_drawn = 0; for my $time (keys %times) { next unless ($draw{$time}); my $picfilename = get_picture_filename($service, $time); DEBUG "[DEBUG] Looking into drawing $picfilename"; (my $picdirname = $picfilename) =~ s/\/[^\/]+$//; DEBUG "[DEBUG] Picture filename: $picfilename"; my @complete = get_fonts(); # Watermarks introduced in RRD 1.2.13. push(@complete, '-W', $watermark) if $RRDs::VERSION >= 1.2013; # Do the header (title, vtitle, size, etc...), but IN THE BEGINNING unshift @complete, @{get_header($service, $time)}; if ($LINEkluge) { @rrd = map { my $line = $_; $line =~ s/LINE3:/LINE2.2:/; $line =~ s/LINE2:/LINE1.6:/; # LINE1 is thin enough. $line; } @rrd; } push @complete, @rrd; # graph end in future push (@complete, handle_trends($time, $lastupdate, $pinpoint, $service, $RRDkludge, @added)); # Make sure directory exists munin_mkdir_p($picdirname, oct(777)); # Since version 1.3 rrdtool uses libpango which needs its input # as utf8 string. So we assume that every input is in latin1 # and decode it to perl's internal representation and then to utf8. if ($RRDs::VERSION >= 1.3) { @complete = map { my $str = $_; my $utf8 = guess_encoding($str, 'utf8'); ref $utf8 ? $str : encode("utf8", (decode("latin1", $_))); } @complete; } # Surcharging the graphing limits my ($upper_limit_overrided, $lower_limit_overrided); for (my $index = 0; $index <= $#complete; $index++) { if ($complete[$index] =~ /^(--upper-limit|-u)$/ && (defined $upper_limit)) { $upper_limit = get_scientific($upper_limit); $complete[$index + 1] = $upper_limit; $upper_limit_overrided = 1; } if ($complete[$index] =~ /^(--lower-limit|-l)$/ && (defined $lower_limit)) { $lower_limit = get_scientific($lower_limit); $complete[$index + 1] = $lower_limit; $lower_limit_overrided = 1; } } # Add the limit if not present if (defined $upper_limit && ! $upper_limit_overrided) { push @complete, "--upper-limit", $upper_limit; } if (defined $lower_limit && ! $lower_limit_overrided) { push @complete, "--lower-limit", $lower_limit; } DEBUG "\n\nrrdtool 'graph' '" . join("' \\\n\t'", @rrdcached_params, @complete) . "'\n"; $nb_graphs_drawn ++; RRDs::graph(@rrdcached_params, @complete); if (my $ERROR = RRDs::error) { ERROR "[RRD ERROR] Unable to graph $picfilename : $ERROR"; # ALWAYS dumps the cmd used when an error occurs. # Otherwise, it will be difficult to debug post-mortem ERROR "[RRD ERROR] rrdtool 'graph' '" . join("' \\\n\t'", @rrdcached_params, @complete) . "'\n"; } elsif (!-f $picfilename) { ERROR "[RRD ERROR] rrdtool graph did not generate the image (make sure there are data to graph).\n"; } else { # Set time of png file to the time of the last update of # the rrd file. This makes http's If-Modified-Since more # reliable, esp. in combination with munin-*cgi-graph. # Since this disrupts rrd's --lazy option we're disableing # it unless we (munin-graph) were specially asked --lazy. # This way --lazy continues to work as expected, and since # CGI uses --nolazy, http IMS are also working as expected. if (! $force_lazy) { DEBUG "[DEBUG] setting time on $picfilename"; utime $lastupdate, $lastupdate, $picfilename; } if ($list_images) { # Command-line option to list images created print $picfilename. "\n"; } } } if (munin_get_bool($service, "graph_sums", 0)) { foreach my $time (keys %sumtimes) { my $picfilename = get_picture_filename($service, $time, 1); DEBUG "Looking into drawing $picfilename"; (my $picdirname = $picfilename) =~ s/\/[^\/]+$//; next unless ($draw{"sum" . $time}); my @rrd_sum; push @rrd_sum, @{get_header($service, $time, 1)}; # graph end in future push (@rrd_sum, handle_trends($time, $lastupdate, $pinpoint, $service, $RRDkludge, @added)); push @rrd_sum, @rrd; my $labelled = 0; my @defined = (); for (my $index = 0; $index <= $#rrd_sum; $index++) { if ($rrd_sum[$index] =~ /^(--vertical-label|-v)$/) { (my $label = munin_get($service, "graph_vlabel")) =~ s/\$\{graph_period\}/$sumtimes{$time}[0]/g; splice(@rrd_sum, $index, 2, ("--vertical-label", $label)); $index++; $labelled++; } elsif ($rrd_sum[$index] =~ /^(LINE[123]|STACK|AREA|GPRINT):([^#:]+)([#:].+)$/) { my ($pre, $fname, $post) = ($1, $2, $3); next if $fname eq "re_zero"; if ($post =~ /^:AVERAGE/) { splice(@rrd_sum, $index, 1, $pre . ":x$fname" . $post); $index++; next; } next if grep /^x$fname$/, @defined; push @defined, "x$fname"; my @replace; if (munin_get($service->{$fname}, "type", "GAUGE") ne "GAUGE") { if ($time eq "week") { # Every plot is half an hour. Add two plots and multiply, to get per hour if (graph_by_minute($service)) { # Already multiplied by 60 push @replace, "CDEF:x$fname=PREV($fname),UN,0,PREV($fname),IF,$fname,+,5,*,6,*"; } elsif (graph_by_hour($service)) { # Already multiplied by 3600, have to *divide* by 12 push @replace, "CDEF:x$fname=PREV($fname),UN,0,PREV($fname),IF,$fname,+,12,/,6,*"; } else { push @replace, "CDEF:x$fname=PREV($fname),UN,0,PREV($fname),IF,$fname,+,300,*,6,*"; } } else { # Every plot is one day exactly. Just multiply. if (graph_by_minute($service)) { # Already multiplied by 60 push @replace, "CDEF:x$fname=$fname,5,*,288,*"; } elsif (graph_by_hour($service)) { # Already multiplied by 3600, have to *divide* by 12 push @replace, "CDEF:x$fname=$fname,12,/,288,*"; } else { push @replace, "CDEF:x$fname=$fname,300,*,288,*"; } } } push @replace, $pre . ":x$fname" . $post; splice(@rrd_sum, $index, 1, @replace); $index++; } elsif ( $rrd_sum[$index] =~ /^(--lower-limit|--upper-limit|-l|-u)$/) { $index++; $rrd_sum[$index] = $rrd_sum[$index] * 300 * $sumtimes{$time}->[1]; } } unless ($labelled) { my $label = munin_get($service, "graph_vlabel_sum_$time", $sumtimes{$time}->[0]); unshift @rrd_sum, "--vertical-label", $label; } DEBUG "[DEBUG] \n\nrrdtool graph '" . join("' \\\n\t'", @rrd_sum) . "'\n"; # Make sure directory exists munin_mkdir_p($picdirname, oct(777)); $nb_graphs_drawn ++; RRDs::graph(@rrdcached_params, @rrd_sum); if (my $ERROR = RRDs::error) { ERROR "[RRD ERROR(sum)] Unable to graph " . get_picture_filename($service, $time) . ": $ERROR"; } elsif ($list_images) { # Command-line option to list images created print get_picture_filename ($service, $time, 1), "\n"; } } # foreach (keys %sumtimes) } # if graph_sums $service_time = sprintf("%.2f", (Time::HiRes::time - $service_time)); DEBUG "[DEBUG] Graphed service $skeypath ($service_time sec for $nb_graphs_drawn graphs)"; print $STATS "GS|$service_time\n" unless $skip_stats; foreach (@added) { delete $service->{$_} if exists $service->{$_}; } @added = (); } # sets enddate --end and draws a vertical ruler if enddate is in the future # future trends are also added to the graph here sub handle_trends { my $time = shift; my $lastupdate = shift; my $pinpoint = shift; my $service = shift; my $RRDkludge = shift; my @added = @_; my @complete; # enddate possibly in future my $futuretime = $pinpoint ? 0 : $resolutions{$time} * get_end_offset($service); my $enddate = time + ($futuretime); DEBUG "[DEBUG] lastupdate: $lastupdate, enddate: $enddate\n"; # future begins at this horizontal ruler if ($enddate > $lastupdate) { push(@complete, "VRULE:$lastupdate#999999"); } # create trends/predictions foreach my $field (@{munin_find_field($service, "label")}) { my $fieldname = munin_get_node_name($field); my $colour = $single_colour; # Skip virtual fieldnames, otherwise beware of $hash->{foo}{bar}. # # On a sidenote, what's the output of the following code ? # perl -e '$a = {}; if ($a->{foo}{bar}) { print "Found Foo/Bar\n"; } \ # if ($a->{foo}) { print "Found Foo\n"; }' next if ! defined $service->{$fieldname}; if (defined $service->{$fieldname}{'colour'}) { $colour = "$service->{$fieldname}{'colour'}66"; } my $rrd_fieldname = get_field_name($fieldname); my $cdef = ""; if (defined $service->{$fieldname}{'cdef'}) { $cdef = "cdef"; } #trends if (defined $service->{$fieldname}{'trend'} and $service->{$fieldname}{'trend'} eq 'yes') { push (@complete, "CDEF:t$rrd_fieldname=c$cdef$rrd_fieldname,$futuretime,TRENDNAN"); push (@complete, "LINE1:t$rrd_fieldname#$colour:$service->{$fieldname}->{'label'} trend\\l"); DEBUG "[DEBUG] set trend for $fieldname\n"; } #predictions if (defined $service->{$fieldname}{'predict'}) { #arguments: pattern length (e.g. 1 day), smoothing (multiplied with timeresolution) my @predict = split(",", $service->{$fieldname}{'predict'}); my $predictiontime = int ($futuretime / $predict[0]) + 2; #2 needed for 1 day my $smooth = $predict[1]*$resolutions{$time}; push (@complete, "CDEF:p$rrd_fieldname=$predict[0],-$predictiontime,$smooth,c$cdef$rrd_fieldname,PREDICT"); push (@complete, "LINE1:p$rrd_fieldname#$colour:$service->{$fieldname}->{'label'} prediction\\l"); DEBUG "[DEBUG] set prediction for $fieldname\n"; } } push(@complete, "COMMENT:Last update$RRDkludge: " . RRDescape(scalar localtime($lastupdate)) . "\\r"); # If pinpointing, --end should *NOT* be changed if (! $pinpoint) { if (@added) { # stop one period earlier if it's a .sum or .stack push @complete, "--end", (int(($enddate-$resolutions{$time}) / $resolutions{$time})) * $resolutions{$time}; } else { push @complete, "--end", (int($enddate / $resolutions{$time})) * $resolutions{$time}; } } return @complete; } sub get_fonts { # Set up rrdtool graph font options according to RRD version. my @options; if ($RRDs::VERSION < 1.2) { # RRD before 1.2, no font options } elsif ($RRDs::VERSION < 1.3) { # RRD 1.2 # The RRD 1.2 documentation says you can identify font family # names but I never got that to work, but full font path worked @options = ( '--font', "LEGEND:7:$libdir/DejaVuSansMono.ttf", '--font', "UNIT:7:$libdir/DejaVuSans.ttf", '--font', "AXIS:7:$libdir/DejaVuSans.ttf", ); } else { # At least 1.3 @options = ( '--font', 'DEFAULT:0:DejaVuSans,DejaVu Sans,DejaVu LGC Sans,Bitstream Vera Sans', '--font', 'LEGEND:7:DejaVuSansMono,DejaVu Sans Mono,DejaVu LGC Sans Mono,Bitstream Vera Sans Mono,monospace', # Colors coordinated with CSS. '--color', 'BACK#F0F0F0', # Area around the graph '--color', 'FRAME#F0F0F0', # Line around legend spot '--color', 'CANVAS#FFFFFF', # Graph background, max contrast '--color', 'FONT#666666', # Some kind of gray '--color', 'AXIS#CFD6F8', # And axis like html boxes '--color', 'ARROW#CFD6F8', # And arrow, ditto. ); } if ($RRDs::VERSION >= 1.4) { # RRD 1.4 has border, adding it push @options, ( '--border', '0', ); } return @options; }; sub graph_by_minute { my $service = shift; return (munin_get($service, "graph_period", "second") eq "minute"); } sub graph_by_hour { my $service = shift; return (munin_get($service, "graph_period", "second") eq "hour"); } sub orig_to_cdef { my $service = shift; my $fieldname = shift; my $original_fieldname = shift || $fieldname; return unless ref($service) eq "HASH"; if (defined $service->{$fieldname} && defined $service->{$fieldname}->{"cdef_name"}) { return orig_to_cdef($service, $service->{$fieldname}->{"cdef_name"}, $original_fieldname); } # For unknown reasons the sanitizing of fieldnames in the context of RRD field names is not # applied consistently (maybe it should not be applied at all). # Thus we need to apply it here in the same way, as it seems to be applied at other places. if (_sanitise_fieldname($original_fieldname) ne $original_fieldname) { return get_field_name(_sanitise_fieldname($fieldname)); } else { return get_field_name($fieldname); } } sub reset_cdef { my $service = shift; my $fieldname = shift; return unless ref($service) eq "HASH"; if (defined $service->{$fieldname} && defined $service->{$fieldname}->{"cdef_name"}) { reset_cdef($service, $service->{$fieldname}->{"cdef_name"}); delete $service->{$fieldname}->{"cdef_name"}; } } sub ends_with { my ($src, $searched) = @_; my $is_ending = (substr($src, - length($searched)) eq $searched); return $is_ending; } sub skip_service { my $service = shift; my $fqn = munin_get_node_fqn($service); # Skip if we've limited services with the omnipotent cli option only-fqn return 1 if ($only_fqn and ! ends_with($fqn, $only_fqn)); DEBUG "[DEBUG] $fqn is in ($only_fqn)\n"; # Skip if we've limited services with cli options return 1 if (@limit_services and ! (grep { ends_with($fqn, $_) } @limit_services)); DEBUG "[DEBUG] $fqn is in (" . join(",", @limit_services) . ")\n"; # Always graph if --force is present return 0 if $force_graphing; # See if we should skip it because of conf-options return 1 if (munin_get($service, "graph", "yes") eq "on-demand" or !munin_get_bool($service, "graph", 1)); # Don't skip return 0; } sub expand_cdef { my $service = shift; my $cfield_ref = shift; my $cdef = shift; my $new_field = &get_field_name("cdef$$cfield_ref"); my ($max, $min, $avg) = ( "CDEF:a$new_field=$cdef", "CDEF:i$new_field=$cdef", "CDEF:g$new_field=$cdef" ); foreach my $field (@{munin_find_field($service, "label")}) { my $fieldname = munin_get_node_name($field); my $rrdname = &orig_to_cdef($service, $fieldname); if ($cdef =~ /\b$fieldname\b/) { $max =~ s/(?<=[,=(])$fieldname(?=[,=)]|$)/a$rrdname/g; $min =~ s/(?<=[,=(])$fieldname(?=[,=)]|$)/i$rrdname/g; $avg =~ s/(?<=[,=(])$fieldname(?=[,=)]|$)/g$rrdname/g; } } munin_set_var_loc($service, [$$cfield_ref, "cdef_name"], $new_field); $$cfield_ref = $new_field; return ($max, $min, $avg); } sub parse_path { my ($path, $domain, $node, $service, $field) = @_; my $filename = "unknown"; if ($path =~ /^\s*([^:]*):([^:]*):([^:]*):([^:]*)\s*$/) { $filename = munin_get_filename($config, $1, $2, $3, $4); } elsif ($path =~ /^\s*([^:]*):([^:]*):([^:]*)\s*$/) { $filename = munin_get_filename($config, $domain, $1, $2, $3); } elsif ($path =~ /^\s*([^:]*):([^:]*)\s*$/) { $filename = munin_get_filename($config, $domain, $node, $1, $2); } elsif ($path =~ /^\s*([^:]*)\s*$/) { $filename = munin_get_filename($config, $domain, $node, $service, $1); } return $filename; } # Wrapper for munin_get_picture_filename to handle pinpoint sub get_picture_filename { my $of; if (defined $output_file) { $of=$output_file; goto exit_label; } $of = munin_get_picture_filename(@_); exit_label: return $of; } sub escape { my $text = shift; return if not defined $text; $text =~ s/\\/\\\\/g; $text =~ s/:/\\:/g; return $text; } sub get_scientific { my $value = shift; $value =~ s/m/e-03/; $value =~ s/k/e+03/; $value =~ s/M/e+06/; $value =~ s/G/e+09/; return $value; } sub RRDescape { my $text = shift; return $RRDs::VERSION < 1.2 ? $text : escape($text); } sub print_usage_and_exit { print "Usage: $0 [options] Options: --[no]fork Do not fork. By default munin-graph forks sub processes for drawing graphs to utilize available cores and I/O bandwidth. [--fork] --n n Max number of concurrent processes [$max_running] --[no]force Force drawing of graphs that are not usually drawn due to options in the config file. [--noforce] --[no]lazy Only redraw graphs when needed. [--lazy] --help View this message. --version View version information. --debug View debug messages. --[no]cron Behave as expected when run from cron. (Used internally in Munin.) --host Limit graphed hosts to . Multiple --host options may be supplied. --only-fqn For internal use with CGI graphing. Graph only a single fully qualified named graph, e.g. --only-fqn root/Backend/dafnes.example.com/diskstats_iops Always use with the correct --host option. --config Use as configuration file. [$conffile] --[no]list-images List the filenames of the images created. [--nolist-images] --output-file -o Output graph file. (used for CGI graphing) --log-file -l Output log file. (used for CGI graphing) --[no]day Create day-graphs. [--day] --[no]week Create week-graphs. [--week] --[no]month Create month-graphs. [--month] --[no]year Create year-graphs. [--year] --[no]sumweek Create summarised week-graphs. [--summweek] --[no]sumyear Create summarised year-graphs. [--sumyear] --pinpoint Create custom-graphs. is the standard unix Epoch. [not active] --size_x Sets the X size of the graph in pixels [175] --size_y Sets the Y size of the graph in pixels [400] --lower_limit Sets the lower limit of the graph --upper_limit Sets the upper limit of the graph NOTE! --pinpoint and --only-fqn must not be combined with --[no] options. The result of doing that is undefined. "; exit 0; } 1; munin-2.0.75/master/lib/Munin/Master/Group.pm000066400000000000000000000047531451614574100207700ustar00rootroot00000000000000package Munin::Master::Group; use base qw(Munin::Master::GroupRepository); use warnings; use strict; use Carp; use Munin::Master::Host; sub new { my ($class, $group_name) = @_; my $self = { group_name => $group_name, hosts => {}, }; return bless $self, $class; } sub add_attributes { my ($self, $attributes) = @_; my %valid_attributes = map {$_ => 1} qw(node_order local_address contacts); croak "Invalid attributes: " . join(', ', keys %$attributes) if grep { !$valid_attributes{$_} } keys %$attributes; %$self = (%$self, %$attributes); } sub add_host { my ($self, $host) = @_; $self->{hosts}{$host->{host_name}} = $host; } sub give_attributes_to_hosts { my ($self) = @_; my %not_inheritable = map {$_ => 1} qw(group_name hosts node_order); my %attributes = grep { !$not_inheritable{$_} } %$self; map { $_->add_attributes_if_not_exists(\%attributes) } values %{$self->{hosts}}; return 1; } sub get_all_hosts { my ($self) = @_; my @hosts = (); for my $group (values %{$self->{groups}}) { push @hosts, $group->get_all_hosts; } push @hosts, values %{$self->{hosts}}; return @hosts; } 1; __END__ =head1 NAME Munin::Master::Group - Holds information on host groups. Groups can be nested. =head1 METHODS =over =item B my $group = Munin::Master::Group->new($name, $parent); Constructor. $name is the name of the group. =item B $group->add_attributes(\%attrs); Sets attributes %attrs for the group. Valid attributes are: =over 4 =item node_order Override the order of the hosts within the group. =item local_address The local address the update process should bind to when contacting the nodes in this group. =item contacts The contacts for this group. See L. =back An exception will be thrown if invalid attributes are provided. (Full details here: L.) =item B $group->add_host($host); Adds host $host to the group. =item B $group->give_attributes_to_hosts(); Propagates the attributes of $group to all hosts in the group. (This does B apply to hosts belonging to sub-groups.) =item B my @hosts = $group->get_all_hosts(); Returns the list of all hosts associated with this group, including those belonging to any sub-groups. =back =cut # vim: ts=4 : sw=4 : et munin-2.0.75/master/lib/Munin/Master/GroupRepository.pm000066400000000000000000000017131451614574100230610ustar00rootroot00000000000000package Munin::Master::GroupRepository; use base qw(Munin::Master::Config); use warnings; use strict; use Carp; use Log::Log4perl qw( :easy ); sub new { # This is now a container class used on some entries in the # Munin::Master::Config instance. It used to be a # self-contained, self-booting class instantiator. my ($class, $gah) = @_; my $self = bless {}, $class; # $gah is usually a pointer to # Munin::Master::Config->instance()->{config}{groups}; $self->{groups} = $gah; return $self; } 1; __END__ =head1 NAME Munin::Master::GroupRepository - FIX =head1 METHODS Inherits methods from Munin::Master::Config. =over =item B my $gr = Munin::Master::GroupRepository->new($groups_and_hosts); Constructor. $groups_and_hosts is the list of groups and hosts to associate with the instance. (This will usually be C<< Munin::Master::Config->instance()->{config}{groups}; >> =back =cut # vim: ts=4 : sw=4 : et munin-2.0.75/master/lib/Munin/Master/HTMLConfig.pm000066400000000000000000000623611451614574100215650ustar00rootroot00000000000000package Munin::Master::HTMLConfig; use warnings; use strict; use Exporter; our (@ISA, @EXPORT); @ISA = qw(Exporter); @EXPORT = qw(generate_config get_peer_nodes); use POSIX qw(strftime); use Getopt::Long; use Time::HiRes; use Scalar::Util qw( weaken ); use Munin::Master::Logger; use Munin::Master::Utils; use Data::Dumper; use Log::Log4perl qw( :easy ); my @times = ("day", "week", "month", "year"); my $DEBUG = 0; my $INDEX_FILENAME = "index.html"; my $config; my $limits; my $cache; my $categories; my $problems; sub generate_config { my $use_cache = shift; if ($use_cache) { $cache = undef; # undef, for RAM usage # if there is some cache, use it (for cgi) my $newcache = munin_readconfig_part('htmlconf', 1); if (defined $newcache) { $cache = $newcache; return $cache; } } $categories = {}; $problems = {"criticals" => [], "warnings" => [], "unknowns" => []}; my $rev = munin_configpart_revision(); $config = munin_readconfig_part('datafile', 0); initiate_cgiurl_graph(); # we don't set a default like for others if ($rev != munin_configpart_revision()) { # convert config for html generation: reorder nodes to their rightful group node_reorder($config); } $limits = munin_readconfig_part("limits"); # if only limits changed, still update our cache if ($rev != munin_configpart_revision()) { $cache = undef; # undef, for RAM usage $cache = get_group_tree($config); } return $cache; } sub node_reorder { my $totalconfig = shift; my $group = shift || $config; my $children = munin_get_sorted_children($group); # traverse group foreach my $child (@$children) { # if this is a node if(defined $child->{'address'}){ # if renaming is active if(defined $child->{"html_rename"}){ (my $groups, my $name) = munin_get_host_path_from_string($child->{"html_rename"}); # add the node at its new place my $currentgroup = $totalconfig; foreach my $local_group (@$groups){ if(!defined $currentgroup->{$local_group}){ $currentgroup->{$local_group} = {'#%#name' => $local_group, '#%#parent' => $currentgroup}; weaken($currentgroup->{$local_group}{'#%#parent'}); } $currentgroup = $currentgroup->{$local_group}; } if(defined $currentgroup->{$name}){ ERROR $child->{"html_rename"} . " already exists. Renaming not possible."; } else { # remove node from structure undef $group->{$child->{"#%#name"}}; # change name into new name $child->{"#%#origname"} = $child->{"#%#name"}; $child->{"#%#name"} = $name; # add to new group $child->{"#%#origparent"} = $group; $currentgroup->{$name} = $child; $child->{"#%#parent"} = $currentgroup; weaken($child->{"#%#parent"}); } } } else { # reorder group node_reorder($totalconfig, $child); } } } sub initiate_cgiurl_graph { if (!defined $config->{'cgiurl_graph'}) { if (defined $config->{'cgiurl'}) { $config->{'cgiurl_graph'} = $config->{'cgiurl'} . "/munin-cgi-graph"; } else { $config->{'cgiurl_graph'} = "/munin-cgi/munin-cgi-graph"; } DEBUG "[DEBUG] Determined that cgiurl_graph is ".$config->{'cgiurl_graph'}; } } sub add_graph_to_categories { my $srv = shift; my $category = $srv->{"category"}; my $srvname = $srv->{"label"}; if(!defined ($categories->{$category})){ $categories->{$category} = {}; } if(!defined ($categories->{$category}->{$srvname})){ $categories->{$category}->{$srvname} = []; } push @{$categories->{$category}->{$srvname}}, $srv; } sub get_group_tree { my $hash = shift; my $base = shift || ""; my $graphs = []; # Pushy array of graphs, [ { name => 'cpu'}, ...] my $groups = []; # Slices of the $config hash my $cattrav = {}; # Categories, array of strings my $cats = []; # Array of graph information ('categories') my $path = []; # (temporary) array of paths relevant to . (here) my $rpath = undef; my $visible = 0; my $css_name; my $children = munin_get_sorted_children($hash); foreach my $child (@$children) { next unless defined $child and ref($child) eq "HASH" and keys %$child; $child->{"#%#ParentsNameAsString"} = munin_get_node_name($hash); # TODO: is this value used anywhere? if (defined $child->{"graph_title"} and munin_get_bool($child, "graph", 1)) { #graph $child->{'#%#is_service'} = 1; # TODO: is this value used anywhere? my $childname = munin_get_node_name($child); my $childnode = generate_service_templates($child); push @$graphs, {"name" => $childname}; $childnode->{'name'} = $child->{"graph_title"}; # used in category view and comparison view for nested (multigraph) services $childnode->{'nodename'} = munin_get_parent_name($hash); add_graph_to_categories($childnode); # Make sure the link gets right even if the service has subservices if (munin_has_subservices ($child)) { $childnode->{'url'} = $base . $childname . "/$INDEX_FILENAME"; #TODO: html generation should generate urls } else { $childnode->{'url'} = $base . $childname . ".html"; #TODO: html generation should generate urls } $childnode->{'host_url'} = $base . $INDEX_FILENAME; #TODO: Think of a nicer way to generate relative urls (reference #1) for (my $shrinkpath = $childnode->{'url'}, my $counter = 0; $shrinkpath; $shrinkpath =~ s/^[^\/]+\/?//, $counter++) { die ("Munin::Master::HTMLConfig ran into an endless loop") if ($counter >= 100); $childnode->{'url' . $counter} = $shrinkpath; } push @{$cattrav->{lc munin_get($child, "graph_category", "other")}}, $childnode; # If this is a multigraph plugin there may be sub-graphs. push( @$groups, grep {defined $_} get_group_tree($child, $base.munin_get_node_name($child)."/")); $visible = 1; } elsif (ref($child) eq "HASH" and !defined $child->{"graph_title"}) { #group push( @$groups, grep {defined $_} get_group_tree($child, $base.munin_get_node_name($child) . "/")); if (scalar @$groups) { $visible = 1; } } } foreach my $group (@$groups) { $group->{'peers'} = get_peer_nodes($group->{"#%#hash"}, lc munin_get($group->{"#%#hash"}, "graph_category", "other")); } return unless $visible; $hash->{'#%#visible'} = 1; # TODO: is this value used anywhere? # We need the categories in another format. foreach my $cat (sort keys %$cattrav) { my $obj = {}; $obj->{'name'} = $cat; $obj->{'url'} = $base . "${INDEX_FILENAME}#" . $cat; $obj->{'services'} = [sort {lc($a->{'name'}) cmp lc($b->{'name'})} @{$cattrav->{$cat}}]; $obj->{'state_' . lc munin_category_status($hash, $limits, $cat, 1)} = 1; #TODO: shrinkpath reference #2 for ( my $shrinkpath = $obj->{'url'}, my $counter = 0; $shrinkpath =~ /\//; $shrinkpath =~ s/^[^\/]+\/*//, $counter++ ) { die ("Munin::Master::HTMLConfig ran into an endless loop") if ($counter >= 100); $obj->{'url' . $counter} = $shrinkpath; } push @$cats, $obj; } # ...and we need a couple of paths available. # TODO: think of a nicer way to generate urls @$path = reverse map { { "pathname" => $_, "path" => ( defined $rpath ? ($rpath .= "../") . $INDEX_FILENAME : ($rpath = ""))} } reverse(undef, split('\/', $base)); # TODO: think of a nicer way to generate urls my $root_path = get_root_path($path); # We need a bit more info for the comparison templates my $compare = munin_get_bool($hash, "compare", 1); my $comparecats = []; my $comparegroups = []; if($compare){ ($compare, $comparecats, $comparegroups) = generate_compare_groups($groups); } my %group_hash = (map {$_->{'name'} => $_} @{$groups}); my $ret = { "name" => munin_get_node_name($hash), "url" => $base . $INDEX_FILENAME, "path" => $path, "#%#hash" => $hash, "depth" => scalar(my @splitted_base = split("/", $base . $INDEX_FILENAME)) - 1, "filename" => munin_get_html_filename($hash), "css_name" => $css_name, "root_path" => $root_path, "groups" => $groups, "groups_hash" => \%group_hash, "graphs" => $graphs, "multigraph" => munin_has_subservices ($hash), "categories" => $cats, "ngroups" => scalar(@$groups), "ngraphs" => scalar(@$graphs), "ncategories" => scalar(@$cats), "compare" => $compare, "comparegroups" => $comparegroups, "ncomparegroups" => scalar(@$comparegroups), "comparecategories" => $comparecats, "ncomparecategories" => scalar(@$comparecats), }; if($ret->{'depth'} == 0){ #root node does not have peer nodes # add categories my $catarray = []; foreach (sort keys %{$categories}) { my $currentcat = $categories->{$_}; my $srvarray = []; foreach (sort keys %{$currentcat}) { my $srv_nodename = $_; $srv_nodename =~ s/ /_/g; my $srv = { "graphs" => $currentcat->{$_}, "name" => $_, "service" => $srv_nodename, }; push @$srvarray, $srv } my $filename = munin_get_html_filename($hash); $filename =~ s/index.html$/$_/; my $cat = { "name" => $_, "urlday" => "$_-day.html", "urlweek" => "$_-week.html", "urlmonth" => "$_-month.html", "urlyear" => "$_-year.html", "path" => $path, "graphs" => $srvarray, "filename-day" => $filename . "-day.html", "filename-week" => $filename . "-week.html", "filename-month" => $filename . "-month.html", "filename-year" => $filename . "-year.html", }; push @$catarray, $cat; } $ret->{"problems"} = $problems; $ret->{"globalcats"} = $catarray; $ret->{"nglobalcats"} = scalar(@$catarray); } #TODO: shrinkpath reference #3 if ($ret->{'url'} ne "/index.html") { for ( my $shrinkpath = $ret->{'url'}, my $counter = 0; $shrinkpath =~ /\//; $shrinkpath =~ s/^[^\/]+\/*//, $counter++ ) { die ("Munin::Master::HTMLConfig ran into an endless loop") if ($counter >= 100); $ret->{'url' . $counter} = $shrinkpath; } } return $ret; } sub generate_compare_groups { my $groups = shift; my $comparecats = []; my $comparecatshash = {}; my $comparegroups = []; foreach my $tmpgroup (@$groups) { # First we gather a bit of data into comparecatshash... if ($tmpgroup->{'ngraphs'} > 0 && !$tmpgroup->{"multigraph"}) { # no compare links for multigraphs push @$comparegroups, $tmpgroup; foreach my $tmpcat (@{$tmpgroup->{'categories'}}) { $comparecatshash->{$tmpcat->{'name'}}->{'groupname'} = $tmpcat->{'name'}; foreach my $tmpserv (@{$tmpcat->{'services'}}) { $comparecatshash->{$tmpcat->{'name'}}->{'services'}->{$tmpserv->{'name'}}->{'nodes'}->{$tmpgroup->{'name'}} = $tmpserv; $comparecatshash->{$tmpcat->{'name'}}->{'services'}->{$tmpserv->{'name'}}->{'nodes'}->{$tmpgroup->{'name'}}->{'nodename'} = $tmpgroup->{'name'}; $comparecatshash->{$tmpcat->{'name'}}->{'services'}->{$tmpserv->{'name'}}->{'nodes'}->{$tmpgroup->{'name'}}->{'nodeurl'} = $tmpgroup->{'url'}; } } } } if (scalar @$comparegroups <= 1) { return (0, [], []); # ($compare, $comparecats, $comparegroups) } # restructure it, comparecats need to end up looking like: ->[i]->{'services'}->[i]->{'nodes'}->[i]->{*} # not really restructuring; this just sorts all arrays, but doesn't take the node order into account. my $empty_node = { }; foreach my $tmpcat (sort keys %$comparecatshash) { foreach my $tmpserv (sort keys %{$comparecatshash->{$tmpcat}->{'services'}}) { my @nodelist = map {defined $comparecatshash->{$tmpcat}->{'services'}->{$tmpserv}->{'nodes'}->{$_->{'name'}} ? $comparecatshash->{$tmpcat}->{'services'}->{$tmpserv}->{'nodes'}->{$_->{'name'}} : { nodename => $_->{'name'}, } } (@$groups); delete $comparecatshash->{$tmpcat}->{'services'}->{$tmpserv}->{'nodes'}; $comparecatshash->{$tmpcat}->{'services'}->{$tmpserv}->{'nodes'} = \@nodelist; } my @servlist = map {$comparecatshash->{$tmpcat}->{'services'}->{$_}} sort keys %{$comparecatshash->{$tmpcat}->{'services'}}; delete $comparecatshash->{$tmpcat}->{'services'}; $comparecatshash->{$tmpcat}->{'services'} = \@servlist; } @$comparecats = map {$comparecatshash->{$_}} sort keys %$comparecatshash; return (1, $comparecats, $comparegroups); } # This is called both at group level, service level, and subservice level sub munin_get_sorted_children { my $hash = shift || return; my $children = munin_get_children($hash); my $group_order; my $ret = []; if (defined $hash->{'group_order'}) { $group_order = $hash->{'group_order'}; } elsif (defined $hash->{'domain_order'}) { $group_order = $hash->{'domain_order'}; } elsif (defined $hash->{'node_order'}) { $group_order = $hash->{'node_order'}; } else { $group_order = ""; } my %children = map {munin_get_node_name($_) => $_} @$children; foreach my $group (split /\s+/, $group_order) { if (defined $children{$group}) { push @$ret, $children{$group}; delete $children{$group}; } elsif ($group =~ /^(.+)=([^=]+)$/) { # "Borrow" the graph from another group my $groupname = $1; my $path = $2; my $borrowed = munin_get_node_partialpath($hash, $path); if (defined $borrowed) { munin_copy_node_toloc($borrowed, $hash, [$groupname]); $hash->{$groupname}->{'#%#origin'} = $borrowed; } push @$ret, $hash->{$groupname}; } } foreach my $group (sort {$a cmp $b} keys %children) { push @$ret, $children{$group}; } return $ret; } sub generate_service_templates { my $service = shift || return; return unless munin_get_bool($service, "graph", 1); my %srv; my $fieldnum = 0; my @graph_info; my @field_info; my @loc = @{munin_get_picture_loc($service)}; my $pathnodes = get_path_nodes($service); my $peers = get_peer_nodes($service, lc munin_get($service, "graph_category", "other")); my $parent = munin_get_parent_name($service); my $filename = munin_get_html_filename($service); my $root_path = get_root_path($pathnodes); my $bp = borrowed_path($service) || "."; $srv{'node'} = munin_get_node_name($service); DEBUG "[DEBUG] processing service: $srv{node}"; $srv{'service'} = $service; $srv{"multigraph"}= munin_has_subservices($service); $srv{'label'} = munin_get($service, "graph_title"); $srv{'category'} = lc(munin_get($service, "graph_category", "other")); $srv{'path'} = $pathnodes; $srv{'peers'} = $peers; $srv{'root_path'} = $root_path; $srv{'filename'} = $filename; $srv{'url'} = "$srv{node}.html"; my $path = join('/', @loc); my %imgs; $imgs{'imgday'} = "$path-day.png"; $imgs{'imgweek'} = "$path-week.png"; $imgs{'imgmonth'} = "$path-month.png"; $imgs{'imgyear'} = "$path-year.png"; $imgs{'cimgday'} = "$path-day.png"; $imgs{'cimgweek'} = "$path-week.png"; $imgs{'cimgmonth'} = "$path-month.png"; $imgs{'cimgyear'} = "$path-year.png"; if (munin_get_bool($service, "graph_sums", 0)) { $imgs{'imgweeksum'} = "$path-week-sum.png"; $imgs{'imgyearsum'} = "$path-year-sum.png"; } # dump all the png filename to a file my $fh = $config->{"#%#graphs_fh"}; if ($fh) { # values %imgs = the image file # get them uniq, so we don't write them twice my %paths = map { $_, 1 } (values %imgs); foreach my $img (keys %paths) { print $fh "/" . $img . "\n"; } } my $imgpath = $root_path; if ( munin_get($config, "graph_strategy", "cron") eq "cgi" ) { $imgpath = $config->{'cgiurl_graph'}; } map { $srv{$_} = $imgpath . "/" . $imgs{$_} } keys %imgs; # Compute the ZOOM urls { my $epoch_now = time; # The intervals are a bit larger, just like the munin-graph my $start_day = $epoch_now - (3600 * 30); my $start_week = $epoch_now - (3600 * 24 * 8); my $start_month = $epoch_now - (3600 * 24 * 33); my $start_year = $epoch_now - (3600 * 24 * 400); my $size_x = 800; my $size_y = 400; my $common_url = "$root_path/static/dynazoom.html?cgiurl_graph=$config->{'cgiurl_graph'}&plugin_name=$path&size_x=$size_x&size_y=$size_y"; $srv{zoomday} = "$common_url&start_epoch=$start_day&stop_epoch=$epoch_now"; $srv{zoomweek} = "$common_url&start_epoch=$start_week&stop_epoch=$epoch_now"; $srv{zoommonth} = "$common_url&start_epoch=$start_month&stop_epoch=$epoch_now"; $srv{zoomyear} = "$common_url&start_epoch=$start_year&stop_epoch=$epoch_now"; } for my $scale (@times) { my ($w, $h) = get_png_size(munin_get_picture_filename($service, $scale)); if ($w && $h) { $srv{"img" . $scale . "width"} = $w; $srv{"img" . $scale . "height"} = $h; } } if (munin_get_bool($service, "graph_sums", 0)) { $srv{imgweeksum} = "$srv{node}-week-sum.png"; $srv{imgyearsum} = "$srv{node}-year-sum.png"; for my $scale (["week", "year"]) { my ($w, $h) = get_png_size(munin_get_picture_filename($service, $scale, 1)); if ($w && $h) { $srv{"img" . $scale . "sumwidth"} = $w; $srv{"img" . $scale . "sumheight"} = $h; } } } # Do "help" section if (my $info = munin_get($service, "graph_info")) { my %graph_info; $graph_info{info} = $info; push @{$srv{graphinfo}}, \%graph_info; } #TODO move this ugly code to the templates $srv{fieldlist} .= "
"; foreach my $f (@{munin_get_field_order($service)}) { $f =~ s/=(.*)$//; my $path = $1; next if (!defined $service->{$f}); my $fieldobj = $service->{$f}; next if (ref($fieldobj) ne "HASH" or !defined $fieldobj->{'label'}); next if (!munin_draw_field($fieldobj)); #DEBUG "DEBUG: single_value: Checking field \"$f\" ($path)."; if (defined $path) { # This call is to make sure field settings are copied # for aliases, .stack, et al. Todo: put that part of # munin_get_rrd_filename into its own functino. munin_get_rrd_filename($f, $path); } my %field_info; $fieldnum++; $field_info{'hr'} = 1 unless ($fieldnum % 3); $field_info{'field'} = $f; $field_info{'label'} = munin_get($fieldobj, "label", $f); $field_info{'type'} = lc(munin_get($fieldobj, "type", "GAUGE")); $field_info{'warn'} = munin_get($fieldobj, "warning"); $field_info{'crit'} = munin_get($fieldobj, "critical"); $field_info{'info'} = munin_get($fieldobj, "info"); $field_info{'extinfo'} = munin_get($fieldobj, "extinfo"); my $state = munin_field_status($fieldobj, $limits, 1); if (defined $state) { $field_info{'state_warning'} = $state eq "warning" ? 1 : 0; $field_info{'state_critical'} = $state eq "critical" ? 1 : 0; $field_info{'state_unknown'} = $state eq "unknown" ? 1 : 0; } push @{$srv{'fieldinfo'}}, \%field_info; } my $state = munin_service_status($service, $limits, 1); if (defined $state) { $srv{'state_warning'} = $state eq "warning" ? 1 : 0; $srv{'state_critical'} = $state eq "critical" ? 1 : 0; $srv{'state_unknown'} = $state eq "unknown" ? 1 : 0; push @{$problems->{"warnings"}}, \%srv if $state eq "warning"; push @{$problems->{"criticals"}}, \%srv if $state eq "critical"; push @{$problems->{"unknowns"}}, \%srv if $state eq "unknown"; } return \%srv; } #TODO: move path specific information to html generation sub get_path_nodes { my $hash = shift || return; my $ret = []; my $link = $INDEX_FILENAME; unshift @$ret, {"pathname" => munin_get_node_name($hash), "path" => ""}; while ($hash = munin_get_parent($hash)) { unshift @$ret, {"pathname" => munin_get_node_name($hash), "path" => $link}; $link = "../" . $link; } $ret->[0]->{'pathname'} = undef; return $ret; } #TODO: This really needs some refactoring sub get_peer_nodes { my $hash = shift || return; my $category = shift; my $ret = []; my $parent = munin_get_parent($hash) || return; my $me = munin_get_node_name($hash); my $pchildren = munin_get_children($parent); my @peers = map { $_->[0] } sort { $a->[1] cmp $b->[1] } map { [ $_, munin_get_node_name($_) ] } @$pchildren; foreach my $peer (@peers) { next unless defined $peer and ref($peer) eq "HASH"; next if defined $category and lc(munin_get($peer, "graph_category", "other")) ne $category; next if (!defined $peer->{'graph_title'} and (!defined $peer->{'#%#visible'} or !$peer->{'#%#visible'})); next if (defined $peer->{'graph_title'} and !munin_get_bool($peer, "graph", 1)); my $peername = munin_get_node_name($peer); next if $peername eq "contact" and munin_get_node_name($parent) eq "root"; if ($peername eq $me) { unshift @$ret, {"name" => $peername, "link" => undef}; } else { # Handle different directory levels between subgraphs and regular graphs if (munin_has_subservices ($hash)) { if (munin_has_subservices ($peer)) { # I've got subgraphs, peer's got subgraphs unshift @$ret, {"name" => $peername, "link" => "../$peername/index.html"}; } else { # I've got subgraphs, peer's a regular graph unshift @$ret, {"name" => $peername, "link" => "../$peername.html"}; } } elsif (munin_has_subservices ($peer)) { # I'm a regular graph, peer's got subgraphs unshift @$ret, {"name" => $peername, "link" => "$peername/index.html"}; } else { if (defined $peer->{'graph_title'}) { # Both me and peer are regular graphs unshift @$ret, {"name" => $peername, "link" => "$peername.html"}; } else { # We're not on the graph level -- handle group peering unshift @$ret, {"name" => $peername, "link" => "../$peername/index.html"}; } } } } return $ret; } #TODO: move url logic to html generation sub get_root_path{ my ($path) = @_; if ($path) { (my $root = $path->[0]->{'path'}) =~ s/\/index.html$//; return $root; } return ""; } #TODO: move url logic to html generation sub borrowed_path { # I wish I knew what this function does. It appears to make # .. path elements to climb up the directory hierarchy. To # "borrow" something from a different directory level. my $hash = shift; my $prepath = shift || ""; my $postpath = shift || ""; return unless defined $hash and ref($hash) eq "HASH"; if (defined $hash->{'#%#origin'}) { return $prepath . "../" . munin_get_node_name($hash->{'#%#origin'}) . "/" . $postpath; } else { if (defined $hash->{'#%#parent'}) { if (defined $hash->{'graph_title'}) { return borrowed_path($hash->{'#%#parent'}, $prepath . "../", $postpath); } else { return borrowed_path( $hash->{'#%#parent'}, $prepath . "../", munin_get_node_name($hash) . "/" . $postpath ); } } else { return; } } } #TODO: This method is obsolete when cgi-graphing is the only strategy left sub get_png_size { my $filename = shift; my $width = undef; my $height = undef; return (undef, undef) if (munin_get($config, "graph_strategy", "cron") eq "cgi") ; if (open(my $PNG, '<', $filename)) { my $incoming; binmode($PNG); if (read($PNG, $incoming, 4)) { if ($incoming =~ /PNG$/) { if (read($PNG, $incoming, 12)) { if (read($PNG, $incoming, 4)) { $width = unpack("N", $incoming); read($PNG, $incoming, 4); $height = unpack("N", $incoming); } } } } close($PNG); } return ($width, $height); } 1; munin-2.0.75/master/lib/Munin/Master/HTMLOld.pm000066400000000000000000001036431451614574100210750ustar00rootroot00000000000000package Munin::Master::HTMLOld; =encoding utf-8 =begin comment -*- perl -*- This is Munin::Master::HTMLOld, a minimal package shell to make munin-html modular (so it can loaded persistently in munin-fastcgi-graph for example) without making it object oriented yet. The non-"old" module will feature propper object orientation like munin-update and will have to wait until later. Copyright (C) 2002-2009 Jimmy Olsen, Audun Ytterdal, Kjell Magne Øierud, Nicolai Langfeldt, Linpro AS, Redpill Linpro AS and others. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. This is the hierarchy of templates * munin-overview.tmpl - Overview with all groups and hosts shown (2 levels down) * munin-domainview.tmpl - all members of one domain, showing links down to each single service and/or sub-group * munin-nodeview.tmpl - two (=day, week) graphs from all plugins on the node * munin-serviceview.tmpl - deepest level of view, shows all 4 graphs from one timeseries * Zoom view - zoomable graph based on one of the other four graphs OR * munin-nodeview.tmpl - multigraph sub-level. When multigraph sublevels end ends the next is a munin-serviceview. * Comparison pages (x4) are at the service level. Not sure how to work multigraph into them so avoid it all-together. =end comment =cut use warnings; use strict; use Exporter; our (@ISA, @EXPORT); @ISA = qw(Exporter); @EXPORT = qw(html_startup html_main get_config emit_main_index emit_comparison_template emit_group_template emit_graph_template emit_service_template emit_category_template emit_problem_template update_timestamp); use HTML::Template; use POSIX qw(strftime); use Getopt::Long; use Time::HiRes; use File::Copy::Recursive qw(dircopy); use IO::File; use Munin::Master::Logger; use Munin::Master::Utils; use Munin::Master::HTMLConfig; use Log::Log4perl qw( :easy ); my @times = ("day", "week", "month", "year"); my $DEBUG = 0; my $conffile = "$Munin::Common::Defaults::MUNIN_CONFDIR/munin.conf"; my $do_usage = 0; my $do_version = 0; my $stdout = 0; my $force_run_as_root = 0; my $config; my $limits; my $htmltagline; my %comparisontemplates; my $tmpldir; my $htmldir; my $do_dump = 0; my $do_fork = 1; my $max_running=6; my $running=0; my $timestamp; my $htmlconfig; sub update_timestamp { # For timestamping graphs $timestamp = strftime("%Y-%m-%d %T%z (%Z)", localtime); if ($timestamp =~ /%z/) { # %z (numeric timezone offset) may not be available, but %Z # (timeszone name) seems to be universaly supported though the # timezone names are not really standardized. $timestamp = strftime("%Y-%m-%d %T%Z", localtime); } $htmltagline = "This page was generated by Munin version ".$Munin::Common::Defaults::MUNIN_VERSION." at $timestamp"; } sub html_startup { my ($args) = @_; local @ARGV = @{$args}; $do_usage = 1 unless GetOptions ( "host=s" => [], "service=s" => [], "config=s" => \$conffile, "debug!" => \$DEBUG, "stdout!" => \$stdout, "force-run-as-root!" => \$force_run_as_root, "help" => \$do_usage, "version!" => \$do_version, "dump!" => \$do_dump, "fork!" => \$do_fork, ); print_usage_and_exit() if $do_usage; print_version_and_exit() if $do_version; exit_if_run_by_super_user() unless $force_run_as_root; munin_readconfig_base($conffile); # XXX: should not need that part here, yet. $config = munin_readconfig_part('datafile', 0); logger_open($config->{'logdir'}); logger_debug() if $DEBUG; $tmpldir = $config->{tmpldir}; $htmldir = $config->{htmldir}; $max_running = &munin_get($config, "max_html_jobs", $max_running); update_timestamp(); %comparisontemplates = instanciate_comparison_templates($tmpldir); } sub get_config { my $use_cache = shift; # usecache should match being in a cgi ($ENV{SCRIPT_NAME}) $htmlconfig = undef; # avoid double ram usage if ($use_cache) { $htmlconfig = generate_config($use_cache); } else { my $graphs_filename = $config->{dbdir} . "/graphs"; my $graphs_filename_tmp = $graphs_filename . ".tmp." . $$; $config->{"#%#graphs_fh"} = new IO::File("> $graphs_filename_tmp"); $htmlconfig = generate_config($use_cache); # Closing the file $config->{"#%#graphs_fh"} = undef; # Atomic move rename($graphs_filename_tmp, $graphs_filename); # htmlconf cache # munin-html writes it # munin-cgi-html reads it # # full cron - written, never read # munin-html and munin-cgi-html - written, and read as cache # full munin-cgi-html - should not exist! # and here, we avoid leaving a never-updating cache file my $cachefile = "$config->{dbdir}/htmlconf.storable"; munin_writeconfig_storable($cachefile, $htmlconfig); } return $htmlconfig; } sub html_main { my $staticdir = $config->{staticdir}; copy_web_resources($staticdir, $htmldir); my $configtime = Time::HiRes::time; get_config(0); my $groups = $htmlconfig; $configtime = sprintf("%.2f", (Time::HiRes::time - $configtime)); INFO "[INFO] config generated ($configtime sec)"; if (munin_get($config,"html_strategy","cron") eq "cgi"){ INFO "[INFO] html_strategy is cgi. Skipping template generation"; return; } my $update_time = Time::HiRes::time; my $lockfile = "$config->{rundir}/munin-html.lock"; INFO "[INFO] Starting munin-html, getting lock $lockfile"; munin_runlock($lockfile); # Preparing the group tree... if (!defined($groups) or scalar(%{$groups} eq '0')) { LOGCROAK "[FATAL] There is nothing to do here, since there are no nodes with any plugins. Please refer to http://munin-monitoring.org/wiki/FAQ_no_graphs"; }; if (defined $groups->{"name"} and $groups->{"name"} eq "root") { $groups = $groups->{"groups"}; # root->groups } if ($do_dump) { print munin_dumpconfig_as_str($groups); exit 0; } generate_group_templates($groups); generate_category_templates($htmlconfig->{"globalcats"}); emit_main_index($groups,$timestamp,0); emit_problem_template(0); INFO "[INFO] Releasing lock file $lockfile"; munin_removelock("$lockfile"); $update_time = sprintf("%.2f", (Time::HiRes::time - $update_time)); INFO "[INFO] munin-html finished ($update_time sec)"; } sub find_complinks{ my($type) = @_; my @links = (); foreach my $current (qw(day week month year)) { my $data = {}; if ($type eq $current) { $data->{'LINK'} = undef; } else { $data->{'LINK'} = "comparison-$current.html"; } $data->{'NAME'} = $current; push(@links, $data); } return \@links; } sub emit_comparison_template { my ($key, $t, $emit_to_stdout) = @_; ( my $file = $key->{'filename'}) =~ s/index.html$//; $file .= "comparison-$t.html"; DEBUG "[DEBUG] Creating comparison page $file"; # Rewrite peer urls to point to comparison-$t my $comparepeers = []; for my $peer (@{$key->{'peers'}}){ my $comparelink = $peer->{"link"}; next unless $comparelink; # avoid dead links $comparelink =~ s/index.html$/comparison-$t.html/; push((@$comparepeers), {"name" => $peer->{"name"}, "link" => $comparelink}); } # when comparing categories, we're generating the page inside the # domain, but the images' urls will point inside the node itself, # or even worse within a category inside it. We strip out the # extra '../' that is used to generate a relative path which is no # longer valid. # Sadly this change is permanent and will influence successive # responses in a permanent execution environment like fcgid or # as a systemd socket service. Thus we need to revert it later. foreach my $cat(@{$key->{'comparecategories'}}) { foreach my $service(@{$cat->{'services'}}) { foreach my $node(@{$service->{'nodes'}}) { foreach my $imgsrc(qw(imgday imgweek imgmonth imgyear cimgday cimgweek cimgmonth cimgyear zoomday zoomweek zoommonth zoomyear)) { next unless defined($node->{$imgsrc}); # keep a copy of the original value (to be restored below) $node->{"orig_$imgsrc"} = $node->{$imgsrc}; $node->{$imgsrc} =~ s|^\.\./\.\./(?:\.\./)?|../|; } } } } $comparisontemplates{$t}->param( INFO_OPTION => 'Groups on this level', NAME => $key->{'name'}, GROUPS => $key->{'comparegroups'}, PATH => $key->{'path'}, CSS_NAME => get_css_name(), R_PATH => $key->{'root_path'}, COMPLINKS => find_complinks($t), LARGESET => decide_largeset($comparepeers), PEERS => $comparepeers, PARENT => $key->{'path'}->[-2]->{'name'}, CATEGORIES => $key->{'comparecategories'}, NCATEGORIES => $key->{'ncomparecategories'}, TAGLINE => $htmltagline, "COMPARISON-$t" => 1, ROOTGROUPS => $htmlconfig->{"groups"}, MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION, TIMESTAMP => $timestamp, NGLOBALCATS => $htmlconfig->{"nglobalcats"}, GLOBALCATS => $htmlconfig->{"globalcats"}, NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}), NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}), NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}), ); # store template output before reverting links my $template_output = $comparisontemplates{$t}->output; # restore the paths to their original value foreach my $cat(@{$key->{'comparecategories'}}) { foreach my $service(@{$cat->{'services'}}) { foreach my $node(@{$service->{'nodes'}}) { foreach my $imgsrc(qw(imgday imgweek imgmonth imgyear cimgday cimgweek cimgmonth cimgyear zoomday zoomweek zoommonth zoomyear)) { next unless defined($node->{$imgsrc}); $node->{$imgsrc} = $node->{"orig_$imgsrc"}; delete($node->{"orig_$imgsrc"}); } } } } if($emit_to_stdout){ print $template_output; } else { ensure_dir_exists($file); open(my $FILE, '>', $file) or die "Cannot open $file for writing: $!"; print $FILE $template_output; close $FILE; } } sub emit_graph_template { my ($key, $emit_to_stdout) = @_; my $graphtemplate = HTML::Template->new( filename => "$tmpldir/munin-nodeview.tmpl", die_on_bad_params => 0, global_vars => 1, loop_context_vars => 1, filter => sub { my $ref = shift; $$ref =~ s/URLX/URL$key->{'depth'}/g; }); DEBUG "[DEBUG] Creating graph(nodeview) page ".$key->{filename}; $graphtemplate->param( INFO_OPTION => 'Nodes on this level', GROUPS => $key->{'groups'}, PATH => $key->{'path'}, CSS_NAME => get_css_name(), R_PATH => $key->{'root_path'}, PEERS => $key->{'peers'}, LARGESET => decide_largeset($key->{'peers'}), PARENT => $key->{'path'}->[-2]->{'name'}, NAME => $key->{'name'}, CATEGORIES => $key->{'categories'}, NCATEGORIES => $key->{'ncategories'}, TAGLINE => $htmltagline, ROOTGROUPS => $htmlconfig->{"groups"}, MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION, TIMESTAMP => $timestamp, NGLOBALCATS => $htmlconfig->{"nglobalcats"}, GLOBALCATS => $htmlconfig->{"globalcats"}, NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}), NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}), NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}), ); if($emit_to_stdout){ print $graphtemplate->output; } else { my $filename = $key->{'filename'}; ensure_dir_exists($filename); open(my $FILE, '>', $filename) or die "Cannot open $filename for writing: $!"; print $FILE $graphtemplate->output; close $FILE; } } sub emit_category_template { my ($key, $time, $emit_to_stdout) = @_; my $graphtemplate = HTML::Template->new( filename => "$tmpldir/munin-categoryview.tmpl", die_on_bad_params => 0, global_vars => 1, loop_context_vars => 1, filter => sub { my $ref = shift; $$ref =~ s/URLX/URL/g; }, ); my $filename = $key->{'filename-' . $time}; DEBUG "[DEBUG] Creating global category page ".$filename; # Manipulate the relative paths for the requested root-level context. # Sadly this change is permanent and will influence successive # responses in a permanent execution environment like fcgid or # as a systemd socket service. Thus we need to revert it later. foreach my $graphs(@{$key->{'graphs'}}) { foreach my $graph(@{$graphs->{'graphs'}}) { foreach my $imgsrc(qw(imgday imgweek imgmonth imgyear cimgday cimgweek cimgmonth cimgyear zoomday zoomweek zoommonth zoomyear)) { # keep a copy of the original value (to be restored below) $graph->{"orig_$imgsrc"} = $graph->{$imgsrc}; $graph->{$imgsrc} =~ s|^(?:\.\./)+||; } } } $graphtemplate->param( PATH => $key->{'path'}, CSS_NAME => get_css_name(), HOST_URL => $key->{'host_url'}, R_PATH => ".", "TIME".$time => 1, NAME => $key->{'name'}, TAGLINE => $htmltagline, ROOTGROUPS => $htmlconfig->{"groups"}, MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION, TIMESTAMP => $timestamp, NGLOBALCATS => $htmlconfig->{"nglobalcats"}, GLOBALCATS => $htmlconfig->{"globalcats"}, CATEGORY => $key->{"name"}, SERVICES => $key->{"graphs"}, NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}), NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}), NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}), ); # store template output before reverting links my $template_output = $graphtemplate->output; # restore the paths to their original value foreach my $graphs(@{$key->{'graphs'}}) { foreach my $graph(@{$graphs->{'graphs'}}) { foreach my $imgsrc(qw(imgday imgweek imgmonth imgyear cimgday cimgweek cimgmonth cimgyear zoomday zoomweek zoommonth zoomyear)) { $graph->{$imgsrc} = $graph->{"orig_$imgsrc"}; delete($graph->{"orig_$imgsrc"}); } } } if($emit_to_stdout){ print $template_output; } else { ensure_dir_exists($filename); open(my $FILE, '>', $filename) or die "Cannot open $filename for writing: $!"; print $FILE $template_output; close $FILE; } } sub ensure_dir_exists { my $dirname = shift; $dirname =~ s/\/[^\/]*$//; munin_mkdir_p($dirname, oct(755)); } sub emit_problem_template { my ($emit_to_stdout) = @_; my $graphtemplate = HTML::Template->new( filename => "$tmpldir/munin-problemview.tmpl", die_on_bad_params => 0, global_vars => 1, loop_context_vars => 1, ); my $filename = munin_get_html_filename($config); $filename =~ s/index.html$/problems.html/g; INFO "[INFO] Creating problem page ".$filename; # Manipulate the relative paths for the requested root-level context. # Sadly this change is permanent and will influence successive # responses in a permanent execution environment like fcgid or # as a systemd socket service. Thus we need to revert it later. foreach my $problem_graphs (values %{$htmlconfig->{"problems"}}) { foreach my $graph(@$problem_graphs) { foreach my $imgsrc(qw(imgday imgweek imgmonth imgyear cimgday cimgweek cimgmonth cimgyear zoomday zoomweek zoommonth zoomyear)) { next unless defined($graph->{$imgsrc}); # keep a copy of the original value (to be restored below) $graph->{"orig_$imgsrc"} = $graph->{$imgsrc}; $graph->{$imgsrc} =~ s|^(?:\.\./)+||; } } } $graphtemplate->param( CSS_NAME => get_css_name(), R_PATH => ".", NAME => "Problem overview", TAGLINE => $htmltagline, ROOTGROUPS => $htmlconfig->{"groups"}, MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION, TIMESTAMP => $timestamp, NGLOBALCATS => $htmlconfig->{"nglobalcats"}, GLOBALCATS => $htmlconfig->{"globalcats"}, CRITICAL => $htmlconfig->{"problems"}->{"criticals"}, WARNING => $htmlconfig->{"problems"}->{"warnings"}, UNKNOWN => $htmlconfig->{"problems"}->{"unknowns"}, NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}), NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}), NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}), ); # store template output before reverting links my $template_output = $graphtemplate->output; # restore the paths to their original value foreach my $problem_graphs (values %{$htmlconfig->{"problems"}}) { foreach my $graph(@$problem_graphs) { foreach my $imgsrc(qw(imgday imgweek imgmonth imgyear cimgday cimgweek cimgmonth cimgyear zoomday zoomweek zoommonth zoomyear)) { next unless defined($graph->{$imgsrc}); $graph->{$imgsrc} = $graph->{"orig_$imgsrc"}; delete($graph->{"orig_$imgsrc"}); } } } if($emit_to_stdout){ print $template_output; } else { ensure_dir_exists($filename); open(my $FILE, '>', $filename) or die "Cannot open $filename for writing: $!"; print $FILE $template_output; close $FILE; } } sub emit_group_template { my ($key, $emit_to_stdout) = @_; my $grouptemplate = HTML::Template->new( filename => "$tmpldir/munin-domainview.tmpl", die_on_bad_params => 0, global_vars => 1, loop_context_vars => 1, filter => sub { my $ref = shift; $$ref =~ s/URLX/URL$key->{'depth'}/g; }); DEBUG "[DEBUG] Creating group page ".$key->{filename}; $grouptemplate->param( INFO_OPTION => 'Groups on this level', GROUPS => $key->{'groups'}, PATH => $key->{'path'}, R_PATH => $key->{'root_path'}, CSS_NAME => get_css_name(), PEERS => $key->{'peers'}, LARGESET => decide_largeset($key->{'peers'}), PARENT => $key->{'path'}->[-2]->{'name'} || "Overview", COMPARE => $key->{'compare'}, TAGLINE => $htmltagline, ROOTGROUPS => $htmlconfig->{"groups"}, MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION, TIMESTAMP => $timestamp, NGLOBALCATS => $htmlconfig->{"nglobalcats"}, GLOBALCATS => $htmlconfig->{"globalcats"}, NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}), NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}), NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}), ); if($emit_to_stdout){ print $grouptemplate->output; } else { my $filename = $key->{'filename'}; ensure_dir_exists($filename); open(my $FILE, '>', $filename) or die "Cannot open $filename for writing: $!"; print $FILE $grouptemplate->output; close $FILE or die "Cannot close $filename after writing: $!"; } } sub emit_zoom_template { my($srv, $emit_to_stdout) = @_; my $servicetemplate = HTML::Template->new( filename => "$tmpldir/munin-dynazoom.tmpl", die_on_bad_params => 0, global_vars => 1, loop_context_vars => 1 ); my $pathnodes = $srv->{'path'}; my $peers = $srv->{'peers'}; #remove underscores from peers and title (last path element) if ($peers){ $peers = [ map { $_->{'name'} =~ s/_/ /g; $_;} @$peers ]; } $pathnodes->[scalar(@$pathnodes) - 1]->{'pathname'} =~ s/_/ /g; $servicetemplate->param( INFO_OPTION => 'Graphs in same category', SERVICES => [$srv], PATH => $pathnodes, PEERS => $peers, LARGESET => decide_largeset($peers), R_PATH => $srv->{'root_path'}, CSS_NAME => get_css_name(), CATEGORY => ucfirst $srv->{'category'}, TAGLINE => $htmltagline, ROOTGROUPS => $htmlconfig->{"groups"}, MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION, TIMESTAMP => $timestamp, NGLOBALCATS => $htmlconfig->{"nglobalcats"}, GLOBALCATS => $htmlconfig->{"globalcats"}, NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}), NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}), NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}), SHOW_ZOOM_JS => 1, ); if($emit_to_stdout){ print $servicetemplate->output; } else { my $filename = $srv->{'filename'}; ensure_dir_exists($filename); DEBUG "[DEBUG] Creating service page $filename"; open(my $FILE, '>', $filename) or die "Cannot open '$filename' for writing: $!"; print $FILE $servicetemplate->output; close $FILE or die "Cannot close '$filename' after writing: $!"; } } sub emit_service_template { my ($srv, $emit_to_stdout) = @_; my $servicetemplate = HTML::Template->new( filename => "$tmpldir/munin-serviceview.tmpl", die_on_bad_params => 0, global_vars => 1, loop_context_vars => 1 ); my $pathnodes = $srv->{'path'}; my $peers = $srv->{'peers'}; #remove underscores from peers and title (last path element) if ($peers){ $peers = [ map { $_->{'name'} =~ s/_/ /g; $_;} @$peers ]; } $pathnodes->[scalar(@$pathnodes) - 1]->{'pathname'} =~ s/_/ /g; $servicetemplate->param( INFO_OPTION => 'Graphs in same category', SERVICES => [$srv], PATH => $pathnodes, NAME => $pathnodes->[-1]{'pathname'}, PEERS => $peers, LARGESET => decide_largeset($peers), R_PATH => $srv->{'root_path'}, CSS_NAME => get_css_name(), CATEGORY => ucfirst $srv->{'category'}, TAGLINE => $htmltagline, ROOTGROUPS => $htmlconfig->{"groups"}, MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION, TIMESTAMP => $timestamp, NGLOBALCATS => $htmlconfig->{"nglobalcats"}, GLOBALCATS => $htmlconfig->{"globalcats"}, NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}), NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}), NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}), ); # No stored filename for this kind of html node. if($emit_to_stdout){ print $servicetemplate->output; } else { my $filename = $srv->{'filename'}; ensure_dir_exists($filename); DEBUG "[DEBUG] Creating service page $filename"; open(my $FILE, '>', $filename) or die "Cannot open '$filename' for writing: $!"; print $FILE $servicetemplate->output; close $FILE or die "Cannot close '$filename' after writing: $!"; } } sub decide_largeset { my ($peers) = @_; return scalar(@$peers) > $config->{'dropdownlimit'} ? 1 : 0; } sub emit_main_index { # Draw main index my ($groups, $t, $emit_to_stdout) = @_; my $template = HTML::Template->new( filename => "$tmpldir/munin-overview.tmpl", die_on_bad_params => 0, loop_context_vars => 1, global_vars => 1, filter => sub { my $ref = shift; $$ref =~ s/URLX/URL0/g; }, ); # FIX: this sometimes bugs: # HTML::Template::param() : attempt to set parameter 'groups' with # a scalar - parameter is not a TMPL_VAR! at # /usr/local/share/perl/5.10.0/Munin/Master/HTMLOld.pm line 140 $template->param( TAGLINE => $htmltagline, GROUPS => $groups || [], CSS_NAME => get_css_name(), R_PATH => ".", ROOTGROUPS => $htmlconfig->{"groups"} || [], MUNIN_VERSION => $Munin::Common::Defaults::MUNIN_VERSION, TIMESTAMP => $timestamp, NGLOBALCATS => $htmlconfig->{"nglobalcats"} || [], GLOBALCATS => $htmlconfig->{"globalcats"} || [], NCRITICAL => scalar(@{$htmlconfig->{"problems"}->{"criticals"}}), NWARNING => scalar(@{$htmlconfig->{"problems"}->{"warnings"}}), NUNKNOWN => scalar(@{$htmlconfig->{"problems"}->{"unknowns"}}), ); if($emit_to_stdout){ print $template->output; } else { my $filename = munin_get_html_filename($config); ensure_dir_exists($filename); DEBUG "[DEBUG] Creating main index $filename"; open(my $FILE, '>', $filename) or die "Cannot open $filename for writing: $!"; print $FILE $template->output; close $FILE; } } sub copy_web_resources { my ($staticdir, $htmldir) = @_; unless(dircopy($staticdir, "$htmldir/static")){ ERROR "[ERROR] Could not copy contents from $staticdir to $htmldir"; die "[ERROR] Could not copy contents from $staticdir to $htmldir"; } } sub instanciate_comparison_templates { my ($tmpldir) = @_; return ( day => HTML::Template->new( filename => "$tmpldir/munin-comparison-day.tmpl", die_on_bad_params => 0, global_vars => 1, loop_context_vars => 1 ), week => HTML::Template->new( filename => "$tmpldir/munin-comparison-week.tmpl", die_on_bad_params => 0, global_vars => 1, loop_context_vars => 1 ), month => HTML::Template->new( filename => "$tmpldir/munin-comparison-month.tmpl", global_vars => 1, die_on_bad_params => 0, loop_context_vars => 1 ), year => HTML::Template->new( filename => "$tmpldir/munin-comparison-year.tmpl", global_vars => 1, die_on_bad_params => 0, loop_context_vars => 1 )); } sub get_css_name{ #NOTE: this will do more in future versions. knuthaug 2009-11-15 return "style.css"; } sub fork_and_work { my ($work) = @_; if (!$do_fork || !$max_running) { # We're not forking. Do work and return. DEBUG "[DEBUG] Doing work synchrnonously"; &$work; return; } # Make sure we don't fork too much while ($running >= $max_running) { DEBUG "[DEBUG] Too many forks ($running/$max_running), wait for something to get done"; look_for_child("block"); --$running; } my $pid = fork(); if (!defined $pid) { ERROR "[ERROR] fork failed: $!"; die "fork failed: $!"; } if ($pid == 0) { # This block does the real work. Since we're forking exit # afterwards. &$work; # See?! exit 0; } else { ++$running; DEBUG "[DEBUG] Forked: $pid. Now running $running/$max_running"; while ($running and look_for_child()) { --$running; } } } sub generate_category_templates { my $arr = shift || return; foreach my $key (@$arr) { foreach my $time (@times) { emit_category_template($key, $time, 0); } } } sub generate_group_templates { my $arr = shift || return; return unless ref($arr) eq "ARRAY"; foreach my $key (@$arr) { if (defined $key and ref($key) eq "HASH") { if (defined $key->{'ngroups'} and $key->{'ngroups'}) { fork_and_work(sub {generate_group_templates($key->{'groups'})}); emit_group_template($key,0); if ($key->{'compare'}) { # Create comparison templates as well foreach my $t (@times) { emit_comparison_template($key,$t,0); } } } if (defined $key->{'ngraphs'} and $key->{'ngraphs'}) { emit_graph_template($key, 0); foreach my $category (@{$key->{"categories"}}) { foreach my $serv (@{$category->{"services"}}) { unless($serv->{"multigraph"}){ emit_service_template($serv); #emit_zoom_template($serv); } } } } } } } sub print_usage_and_exit { print "Usage: $0 [options] Options: --help View this message. --debug View debug messages. --version View version information. --nofork Compatibility. No effect. --service Compatibility. No effect. --host Compatibility. No effect. --config Use as configuration file. [/etc/munin/munin.conf] "; exit 0; } 1; =head1 NAME munin-html - A program to draw html-pages on an Munin installation =head1 SYNOPSIS munin-html [options] =head1 OPTIONS =over 5 =item B<< --service >> Compatibility. No effect. =item B<< --host >> Compatibility. No effect. =item B<< --nofork >> Compatibility. No effect. =item B<< --config >> Use EfileE as configuration file. [/etc/munin/munin.conf] =item B<< --help >> View help message. =item B<< --[no]debug >> If set, view debug messages. [--nodebug] =back =head1 DESCRIPTION Munin-html is a part of the package Munin, which is used in combination with Munin's node. Munin is a group of programs to gather data from Munin's nodes, graph them, create html-pages, and optionally warn Nagios about any off-limit values. Munin-html creates the html pages. =head1 FILES @@CONFDIR@@/munin.conf @@DBDIR@@/datafile @@LOGDIR@@/munin-html @@HTMLDIR@@/* @@STATEDIR@@/* =head1 VERSION This is munin-html version @@VERSION@@ =head1 AUTHORS Knut Haugen, Audun Ytterdal and Jimmy Olsen. =head1 BUGS munin-html does, as of now, not check the syntax of the configuration file. Please report other bugs in the bug tracker at L. =head1 COPYRIGHT Copyright (C) 2002-2009 Knut Haugen, Audun Ytterdal, and Jimmy Olsen / Linpro AS. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program is released under the GNU General Public License =head1 SEE ALSO For information on configuration options, please refer to the man page for F. =cut # vim:syntax=perl:ts=8 munin-2.0.75/master/lib/Munin/Master/Host.pm000066400000000000000000000046671451614574100206150ustar00rootroot00000000000000package Munin::Master::Host; use base qw(Munin::Master::Group); use warnings; use strict; use Carp; sub new { my ($class, $host_name, $group, $attributes) = @_; $attributes ||= {}; my $self = { host_name => $host_name, group => $group, port => 4949, update => 1, use_node_name => 0, %$attributes, }; # "Address" is required but must be lazy about it. # die "Attribute 'address' is required for $host_name, config line $.\n" unless $self->{address}; return bless $self, $class; } sub get_full_path { # Find the full nested named path of the current host object # might one for M::M::Group too and make it recursive instead of # "just" iterative but not now. my ($self) = @_; my $group; my @groups = ( $self->{host_name} ); $group=$self->{group}; while (defined($group)) { unshift(@groups,$group->{group_name}); $group=$group->{group}; } return join(";",@groups); } sub add_attributes_if_not_exists { my ($self, $attributes) = @_; %$self = (%$attributes, %$self); } sub get_canned_ds_config { my ($self, $service, $data_source) = @_; # XXX: Could this be done in some sane way? my %ds_config; my $svc_ds_prefix = "$service.$data_source."; for my $svc_ds_prop (keys %$self) { if (index($svc_ds_prop, $svc_ds_prefix) == 0) { my $prop = substr($svc_ds_prop, length($svc_ds_prefix)); $ds_config{$prop} = $self->{$svc_ds_prop}; } } return \%ds_config; } 1; __END__ =head1 NAME Munin::Master::Host - Holds information on hosts we are interested in collecting data from. =head1 DESCRIPTION NOTE that a host and a node are not the same thing -- some hosts may report services for several nodes, for example if they have SNMP plugins installed. =head1 METHODS =over =item B my $host = Munin::Master::Host->new($hostname, $group, \%attrs); Constructor. C<$group> is the C object this host belongs to. Valid attributes include C, C, and c. =item B Returns the full nested named path of the host object (eg. "group1;group2;hostname"). =item B $host->add_attributes_if_not_exists(\%attrs); Merges the new attributes from %attrs into the host object, without overwriting any existing =item B FIX =back munin-2.0.75/master/lib/Munin/Master/LimitsOld.pm000066400000000000000000000774551451614574100216050ustar00rootroot00000000000000package Munin::Master::LimitsOld; # -*- perl -*- =head1 NAME Munin::Master::LimitsOld - Abstract base class for workers. =head1 SYNOPSIS This is Munin::Master::LimitsOld, a minimal package shell to make munin-limits modular (so it can be loaded persistently in a daemon for example) without making it object oriented yet. The non-'old' module will feature proper object orientation like munin-update and will have to wait until later. =begin comment Copyright (C) 2004-2009 Jimmy Olsen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. =end comment =cut use warnings; use strict; use Exporter; our (@ISA, @EXPORT); @ISA = qw ( Exporter ); @EXPORT = qw ( limits_startup limits_main ); use POSIX qw ( strftime ); use Getopt::Long; use Time::HiRes; use Text::Balanced qw ( extract_bracketed ); use Log::Log4perl qw ( :easy ); use Scalar::Util qw( looks_like_number ); use Munin::Master::Logger; use Munin::Master::Utils; use Munin::Common::Defaults; my $DEBUG = 0; my $conffile = "$Munin::Common::Defaults::MUNIN_CONFDIR/munin.conf"; my $do_usage = 0; my $do_version = 0; my @limit_hosts = (); my @limit_services = (); my @limit_contacts = (); my @always_send = (); my $stdout = 0; my $force = 0; my $force_run_as_root = 0; my %notes = (); my $config; my $oldnotes; my $modified = 0; my %default_text = ( "default" => '${var:group} :: ${var:host} :: ${var:graph_title}${if:cfields \n\tCRITICALs:${loop<,>:cfields ${var:label} is ${var:value} (outside range [${var:crange}])${if:extinfo : ${var:extinfo}}}.}${if:wfields \n\tWARNINGs:${loop<,>:wfields ${var:label} is ${var:value} (outside range [${var:wrange}])${if:extinfo : ${var:extinfo}}}.}${if:ufields \n\tUNKNOWNs:${loop<,>:ufields ${var:label} is ${var:value}${if:extinfo : ${var:extinfo}}}.}${if:fofields \n\tOKs:${loop<,>:fofields ${var:label} is ${var:value}${if:extinfo : ${var:extinfo}}}.}\n', "nagios" => '${var:host}\t${var:graph_title}\t${var:worstid}\t${strtrunc:350 ${if:cfields CRITICALs:${loop<,>:cfields ${var:label} is ${var:value} (outside range [${var:crange}])${if:extinfo : ${var:extinfo}}}.}${if:wfields WARNINGs:${loop<,>:wfields ${var:label} is ${var:value} (outside range [${var:wrange}])${if:extinfo : ${var:extinfo}}}.}${if:ufields UNKNOWNs:${loop<,>:ufields ${var:label} is ${var:value}${if:extinfo : ${var:extinfo}}}.}${if:fofields OKs:${loop<,>:fofields ${var:label} is ${var:value}${if:extinfo : ${var:extinfo}}}.}}', "old-nagios" => '${var:host}\t${var:plugin}\t${var:worstid}\t${strtrunc:350 ${var:graph_title}:${if:cfields CRITICALs:${loop<,>:cfields ${var:label} is ${var:value} (outside range [${var:crange}])${if:extinfo : ${var:extinfo}}}.}${if:wfields WARNINGs:${loop<,>:wfields ${var:label} is ${var:value} (outside range [${var:wrange}])${if:extinfo : ${var:extinfo}}}.}${if:ufields UNKNOWNs:${loop<,>:ufields ${var:label} is ${var:value}${if:extinfo : ${var:extinfo}}}.}${if:fofields OKs:${loop<,>:fofields ${var:label} is ${var:value}${if:extinfo : ${var:extinfo}}}.}}' ); sub limits_startup { # Get options my ($args) = @_; local @ARGV = @{$args}; $do_usage = 1 unless GetOptions( "host=s" => \@limit_hosts, "service=s" => \@limit_services, "contact=s" => \@limit_contacts, "config=s" => \$conffile, "debug!" => \$DEBUG, "stdout!" => \$stdout, "force!" => \$force, "always-send=s" => \@always_send, "force-run-as-root!" => \$force_run_as_root, "version!" => \$do_version, "help" => \$do_usage ); print_usage_and_exit() if $do_usage; print_version_and_exit() if $do_version; exit_if_run_by_super_user() unless $force_run_as_root; @always_send = qw{ok warning critical unknown} if $force; munin_readconfig_base($conffile); # XXX: check if it does actually need that part $config = munin_readconfig_part('datafile', 0); logger_open($config->{'logdir'}); logger_debug() if $DEBUG; } sub limits_main { # We're liable to receive SIGPIPEs if the given commands don't work $SIG{PIPE} = 'IGNORE'; my $update_time = Time::HiRes::time; my $lockfile = "$config->{rundir}/munin-limits.lock"; INFO "[INFO] Starting munin-limits, getting lock $lockfile"; munin_runlock("$config->{rundir}/munin-limits.lock"); $oldnotes = &munin_readconfig_part('limits', 1); initialize_for_nagios(); initialize_contacts(); process_limits(); close_pipes(); &munin_writeconfig("$config->{dbdir}/limits", \%notes); &munin_writeconfig_storable("$config->{dbdir}/limits.storable", \%notes); $update_time = sprintf("%.2f", (Time::HiRes::time - $update_time)); munin_removelock("$config->{rundir}/munin-limits.lock"); INFO "[INFO] munin-limits finished ($update_time sec)"; } sub close_pipes { foreach my $cont (@{munin_get_children($config->{"contact"})}) { if($cont->{pipe}) { my $c = munin_get_node_name($cont); DEBUG "[DEBUG] Closing pipe for contact $c"; close $cont->{pipe} or WARN "[WARNING] Failed to close pipe for contact $c: $!"; } } } sub process_limits { # Make array of what needs to be checked my %work_hash_tmp; my $work_array = []; foreach my $workfield ( @{munin_find_field_for_limits($config, qr/^(critical|warning)/)}) { my $parent; if (defined $workfield->{'graph_title'}) { # Limit is defined on a service and inherits to all fields, queue it $parent = $workfield; } else { # Limit is defined on a field, or a non-existent service # Assume field, grab parent service, and verify it is valid $parent = munin_get_parent($workfield); if (!defined $parent->{'graph_title'}) { DEBUG "[DEBUG] Ignoring work item for non-existent service: " . munin_get_node_name($parent) if ($DEBUG); next; } } if (!defined $work_hash_tmp{$parent}) { $work_hash_tmp{$parent} = 1; push @$work_array, $parent; } } # Process array containing services we need to check foreach my $workservice (@$work_array) { process_service($workservice); } } sub initialize_contacts { my $defaultcontacts = munin_get($config, "contacts", ""); if (!length $defaultcontacts) { my @tmpcontacts = (); foreach my $cont (@{munin_get_children($config->{"contact"})}) { if (munin_get($cont, "command")) { push @tmpcontacts, munin_get_node_name($cont); } } $defaultcontacts = join(' ', @tmpcontacts); } munin_set_var_loc($config, ["contacts"], $defaultcontacts); DEBUG "[DEBUG] Set default \"contacts\" to \"$defaultcontacts\""; } sub initialize_for_nagios { if ( !defined $config->{'contact'}->{'nagios'}->{'command'} and defined $config->{'nsca'}) { $config->{'contact'}->{'old-nagios'}->{'command'} = "$config->{nsca} $config->{nsca_server} -c $config->{nsca_config} -to 60"; $config->{'contact'}->{'old-nagios'}->{'always_send'} = "critical warning"; } if (!defined $config->{'contact'}->{'nagios'}->{'always_send'}) { $config->{'contact'}->{'nagios'}->{'always_send'} = "critical warning"; } } sub print_usage_and_exit { print "Usage: $0 [options] Options: --help View this message. --debug View debug messages. --stdout Log to stdout as well as the log file. --always-send Send messages to contacts even if state has not changed since the last run. The list is a space or comma separated list of severities. Choose from one or more of \"critical\", \"warning\", \"unknown\" and \"ok\". --force Alias for \"--always-send ok,warning,critical,unknown\". Overrides --always-send command line, as well as the always_send contact configuration options. --service Limit notified services to . Multiple --service options may be supplied. --host Limit notified hosts to . Multiple --host options may be supplied. --contact Limit notified contacts to . Multiple --contact options may be supplied. --config Use as configuration file. [/etc/munin/munin.conf] "; exit 0; } # Get the host of the service in question sub get_host_node { my $service = shift || return undef; my $parent = munin_get_parent($service) || return undef; if (munin_has_subservices($parent)) { return get_host_node($parent); } else { return $parent; } } sub get_notify_name { my $hash = shift || return; if (defined $hash->{'notify_alias'}) { return $hash->{'notify_alias'}; } elsif (defined $hash->{'graph_title'}) { return $hash->{'graph_title'}; } else { return munin_get_node_name($hash); } } # Joined "sub-path" under host level sub get_full_service_name { my $service = shift || return undef; my $parent = munin_get_parent($service); my $name = get_notify_name($service); if (defined $parent and munin_has_subservices($parent)) { return (get_full_service_name($parent) . " :: " . $name); } else { return $name; } } # Joined group path above host level sub get_full_group_path { my $group = shift || return undef; my $parent = munin_get_parent($group); my $name = get_notify_name($group); if (defined $parent and munin_get_node_name($parent) ne "root") { return (get_full_group_path($parent) . "-" . $name); } else { return $name; } } sub process_service { my $hash = shift || return; my $hobj = get_host_node($hash); my $host = munin_get_node_name($hobj); my $hostalias = get_notify_name($hobj); my $service = munin_get_node_name($hash); my $hparentobj = munin_get_parent($hobj); my $parent = munin_get_node_name($hobj); my $gparent = munin_get_node_name($hparentobj); my $field_order = munin_get_field_order($hash); if (!ref $hash) { LOGCROAK("I was passed a non-hash!"); } return if (@limit_hosts and !grep (/^$host$/, @limit_hosts)); return if (@limit_services and !grep (/^$service$/, @limit_services)); DEBUG "[DEBUG] processing service: $service"; # Some fields that are nice to have in the plugin output $hash->{'fields'} = join(' ', @$field_order); $hash->{'plugin'} = $service; $hash->{'graph_title'} = get_full_service_name($hash); $hash->{'host'} = $hostalias; $hash->{'group'} = get_full_group_path($hparentobj); $hash->{'worst'} = "OK"; $hash->{'worstid'} = 0; $hash->{'recovered'} = {}; my $state_file = sprintf ('%s/state-%s-%s.storable', $config->{dbdir}, $hash->{group}, $host); DEBUG "[DEBUG] state_file: $state_file"; my $state = munin_read_storable($state_file) || {}; my %seen = (); foreach my $fname (@$field_order) { # If field has an alias, strip it away and store it for use in munin_get_rrd_filename my $path = undef; $path = $1 if ($fname =~ s/=(.+)//); # Field order contains duplicates sometimes, skip if already seen next if (exists($seen{$fname})); $seen{$fname} = 1; my $field = munin_get_node($hash, [$fname]); next if (!defined $field or ref($field) ne "HASH"); my $fpath = munin_get_node_loc($field); my $onfield = munin_get_node($oldnotes, $fpath); my $oldstate = 'ok'; my ($warn, $crit, $unknown_limit) = get_limits($field); # Skip fields without warning/critical definitions next if (!defined $warn and !defined $crit); # get the old state if there is one, or leave it empty. if ( defined($onfield) and defined($onfield->{"state"}) ) { $oldstate = $onfield->{"state"}; } DEBUG "[DEBUG] processing field: " . join('::', @$fpath); DEBUG "[DEBUG] field: " . munin_dumpconfig_as_str($field); my $value; { my $rrd_filename = munin_get_rrd_filename($field, $path); my ($current_updated_timestamp, $current_updated_value) = @{ $state->{value}{"$rrd_filename:42"}{current} || [ ] }; my ($previous_updated_timestamp, $previous_updated_value) = @{ $state->{value}{"$rrd_filename:42"}{previous} || [ ] }; my $heartbeat = 600; # XXX - $heartbeat is a fixed 10 min (2 runs of 5 min). if (! defined $current_updated_value || $current_updated_value eq "U") { # No value yet. Report unknown. $value = "U"; } elsif (time > $current_updated_timestamp + $heartbeat) { # Current value is too old. Report unknown. $value = "U"; } elsif (! $field->{type} || $field->{type} eq "GAUGE") { # Non-compute up-to-date value. $value = $current_updated_value; } elsif (! defined $previous_updated_value || $previous_updated_value eq "U") { # No derive computing possible. Report unknown. $value = "U"; } elsif ($current_updated_timestamp == $previous_updated_timestamp || $current_updated_timestamp > $previous_updated_timestamp + $heartbeat) { # Old value does not exist or is too old. Report unknown. $value = "U"; } elsif ($field->{type} eq "ABSOLUTE") { # The previous value is unimportant, as if ABSOLUTE, the counter is reset every time the value is read $value = $current_updated_value / ($current_updated_timestamp - $previous_updated_timestamp); } elsif ($field->{type} eq "COUNTER" && $current_updated_value < $previous_updated_value) { # COUNTER never decrease. Report unknown. $value = "U"; } else { # Everything is ok for DERIVE/COUNTER # compute the value per timeunit $value = ($current_updated_value - $previous_updated_value) / ($current_updated_timestamp - $previous_updated_timestamp); } } # De-taint. if ($value eq "U") { $value = "unknown"; } else { my $formatted_value = sprintf "%.2f", $value; if (($formatted_value == 0) && !looks_like_number($value)) { warn "Failed to interpret expected numeric value of field '$fname' (host '$host'): '$value'"; } $value = $formatted_value; } # Some fields that are nice to have in the plugin output $field->{'value'} = $value; $field->{'crange'} = (defined $crit->[0] ? $crit->[0] : "") . ":" . (defined $crit->[1] ? $crit->[1] : ""); $field->{'wrange'} = (defined $warn->[0] ? $warn->[0] : "") . ":" . (defined $warn->[1] ? $warn->[1] : ""); DEBUG("[DEBUG] value: " . join('::', @$fpath) . ": $value (crit: " . $field->{'crange'} . ") (warn: " . $field->{'wrange'} . ")"); if ($value eq "unknown") { $crit->[0] ||= ""; $crit->[1] ||= ""; my $newstate = "unknown"; my $extinfo = defined $field->{"extinfo"} ? "unknown: " . $field->{"extinfo"} : "Value is unknown."; my $num_unknowns; # First we'll need to check whether the user wants to ignore # a few UNKNOWN values before actually changing the state to # UNKNOWN. if (($oldstate ne "unknown") and ($unknown_limit >= 1)) { if (!defined($onfield->{"num_unknowns"}) or ($onfield->{"num_unknowns"} < $unknown_limit)) { $newstate = $oldstate; $extinfo = $onfield->{$newstate}; if (defined($onfield->{"num_unknowns"})) { # Increment the number of UNKNOWN values seen. $num_unknowns = $onfield->{"num_unknowns"} + 1; } else { # Start counting the number of consecutive UNKNOWN values seen. $num_unknowns = 1; } } } # the state only changes if the above "unknown" counter is not used (i.e. the limit is not reached, yet) if (($oldstate ne "unknown") and !defined($num_unknowns)) { $hash->{'state_changed'} = 1; } if ($newstate eq "unknown") { $hash->{'worst'} = "UNKNOWN" if $hash->{"worst"} eq "OK"; $hash->{'worstid'} = 3 if $hash->{"worstid"} == 0; } elsif ($newstate eq "critical") { $hash->{'worst'} = "CRITICAL"; $hash->{'worstid'} = 2; } elsif ($newstate eq "warning") { $hash->{'worst'} = "WARNING" if $hash->{"worst"} ne "CRITICAL"; $hash->{'worstid'} = 1 if $hash->{"worstid"} != 2; } munin_set_var_loc(\%notes, [@$fpath, "state"], $newstate); munin_set_var_loc(\%notes, [@$fpath, $newstate], $extinfo); if (defined $num_unknowns) { munin_set_var_loc(\%notes, [@$fpath, "num_unknowns"], $num_unknowns); } } elsif ((defined($crit->[0]) and $value < $crit->[0]) or (defined($crit->[1]) and $value > $crit->[1])) { $crit->[0] ||= ""; $crit->[1] ||= ""; $hash->{'worst'} = "CRITICAL"; $hash->{'worstid'} = 2; munin_set_var_loc(\%notes, [@$fpath, "state"], "critical"); munin_set_var_loc( \%notes, [@$fpath, "critical"], ( defined $field->{"extinfo"} ? "$value (not in " . $field->{'crange'} . "): " . $field->{"extinfo"} : "Value is $value. Critical range (" . $field->{'crange'} . ") exceeded" )); if ( $oldstate ne "critical") { $hash->{'state_changed'} = 1; } } elsif ((defined($warn->[0]) and $value < $warn->[0]) or (defined($warn->[1]) and $value > $warn->[1])) { $warn->[0] ||= ""; $warn->[1] ||= ""; $hash->{'worst'} = "WARNING" if $hash->{"worst"} ne "CRITICAL"; $hash->{'worstid'} = 1 if $hash->{"worstid"} != 2; munin_set_var_loc(\%notes, [@$fpath, "state"], "warning"); munin_set_var_loc( \%notes, [@$fpath, "warning"], ( defined $field->{"extinfo"} ? "$value (not in " . $field->{'wrange'} . "): " . $field->{"extinfo"} : "Value is $value. Warning range (" . $field->{'wrange'} . ") exceeded" )); if ( $oldstate ne "warning") { $hash->{'state_changed'} = 1; } } else { munin_set_var_loc(\%notes, [@$fpath, "state"], "ok"); munin_set_var_loc(\%notes, [@$fpath, "ok"], "OK"); if ($oldstate ne 'ok') { $hash->{'state_changed'} = 1; $hash->{'recovered'}{$fname} = 1; } } } generate_service_message($hash); } sub get_limits { my $hash = shift || return; # This hash will have values that we can look up such as these: my $critical = undef; my $warning = undef; my $crit = munin_get($hash, "critical", undef); my $warn = munin_get($hash, "warning", undef); my $unknown_limit = munin_get($hash, "unknown_limit", 3); my $name = munin_get_node_name($hash); if (defined $crit and $crit =~ /^\s*([-+\d.]*):([-+\d.]*)\s*$/) { $critical = [undef, undef]; ${$critical}[0] = $1 if length $1; ${$critical}[1] = $2 if length $2; } elsif (defined $crit and $crit =~ /^\s*([-+\d.]+)\s*$/) { $critical = [undef, $1]; } elsif (defined $crit) { $critical = [0, 0]; } if(defined $crit) { DEBUG "[DEBUG] processing critical: $name -> " . (defined ${$critical}[0]? ${$critical}[0] : "") . " : " . (defined ${$critical}[1]? ${$critical}[1] : ""); } if (defined $warn and $warn =~ /^\s*([-+\d.]*):([-+\d.]*)\s*$/) { ${$warning}[0] = $1 if length $1; ${$warning}[1] = $2 if length $2; } elsif (defined $warn and $warn =~ /^\s*([-+\d.]+)\s*$/) { $warning = [undef, $1]; } elsif (defined $warn) { $warning = [0, 0]; } if(defined $warn) { DEBUG "[DEBUG] processing warning: $name -> " . (defined ${$warning}[0]? ${$warning}[0] : "") . " : " . (defined ${$warning}[1]? ${$warning}[1] : ""); } if ($unknown_limit =~ /^\s*(\d+)\s*$/) { $unknown_limit = $1; DEBUG "[DEBUG] processing unknown_limit: $name -> $unknown_limit"; } return ($warning, $critical, $unknown_limit); } sub generate_service_message { my $hash = shift || return; my $critical = undef; my $worst = $hash->{"worst"}; my %stats = ( 'critical' => [], 'warning' => [], 'unknown' => [], 'foks' => [], 'ok' => []); my $contacts = munin_get_children(munin_get_node($config, ["contact"])); DEBUG "[DEBUG] generating service message: " . join('::', @{munin_get_node_loc($hash)}); my $children = munin_get_children( munin_get_node(\%notes, munin_get_node_loc($hash))); if ( defined($children) ) { foreach my $field (@$children) { if (defined $field->{"state"}) { my $fname = munin_get_node_name($field); push @{$stats{$field->{'state'}}}, $fname; if ($field->{'state'} eq 'ok' and defined $hash->{'recovered'}{$fname}) { push @{$stats{'foks'}}, $fname; } } } } $hash->{'cfields'} = join " ", @{$stats{'critical'}}; $hash->{'wfields'} = join " ", @{$stats{'warning'}}; $hash->{'ufields'} = join " ", @{$stats{'unknown'}}; $hash->{'fofields'} = join " ", @{$stats{'foks'}}; $hash->{'ofields'} = join " ", @{$stats{'ok'}}; # The "fofields" (datasets that changed state from "failed" to "OK") can be empty under certain # legitimate circumstances (e.g. "munin-limits --force" sends messages also for unchanged "OK" # states). But we may never allow the fourth output field for NSCA to be empty - otherwise the # recipient (nagios/icinga) cannot determine, which fields are affected (and thus which test # succeeded). Thus we need to make sure, that "fofields" is always defined (since our # self-made trivial template language does not support expressions like "fofields || ofields"). # Here "ofields" is a reasonable fallback value: it contains all datasets with status "OK". if ($hash->{'fofields'} eq '') { $hash->{'fofields'} = $hash->{'ofields'}; } $hash->{'numcfields'} = scalar @{$stats{'critical'}}; $hash->{'numwfields'} = scalar @{$stats{'warning'}}; $hash->{'numufields'} = scalar @{$stats{'unknown'}}; $hash->{'numfofields'} = scalar @{$stats{'foks'}}; $hash->{'numofields'} = scalar @{$stats{'ok'}}; my $contactlist = munin_get($hash, "contacts", ""); DEBUG("[DEBUG] Contact list for " . join('::', @{munin_get_node_loc($hash)}) . ": $contactlist"); foreach my $c (split(/\s+/, $contactlist)) { next if $c eq "none"; my $contactobj = munin_get_node($config, ["contact", $c]); if(!defined $contactobj) { WARN("[WARNING] Missing configuration options for contact $c; skipping"); next; } if (@limit_contacts and !grep (/^$c$/, @limit_contacts)) { next; } my $obsess = 0; my $always_send; if (@always_send) { # List of severities from command line argument $always_send = \@always_send; } else { # List of severities from contact configuration my $always_send_config = munin_get( $contactobj, "always_send" ); my @always_send_config = ($always_send_config); $always_send = \@always_send_config; } $always_send = validate_severities($always_send); foreach my $cas ( @{$always_send} ) { if(defined($stats{$cas})) { $obsess += scalar @{$stats{$cas}}; } } if (!$hash->{'state_changed'} and !$obsess) { next; # No need to send notification } INFO("[INFO] state of $hash->{'group'}::$hash->{'host'}::$hash->{'plugin'} has changed to $hash->{'worst'}, notifying $c"); my $precmd = munin_get($contactobj, "command", undef); if(!defined $precmd) { WARN("[WARNING] Missing command option for contact $c; skipping"); next; } my $pretxt = munin_get( $contactobj, "text", munin_get( munin_get_node($config, ["contact", "default"]), "text", $default_text{$c} || $default_text{"default"})); my $txt = message_expand($hash, $pretxt, ""); my $cmd = message_expand($hash, $precmd, ""); $txt =~ s/\\n/\n/g; $txt =~ s/\\t/\t/g; if($cmd =~ /^\s*([|><]+)/) { WARN "[WARNING] Found \"$1\" at beginning of command. This should no longer be necessary and will be removed from the command before execution"; $cmd =~ s/^\s*[|><]+//; } my $maxmess = munin_get($contactobj, "max_messages", 0); my $curmess = munin_get($contactobj, "num_messages", 0); my $curcmd = munin_get($contactobj, "pipe_command", undef); my $pipe = munin_get($contactobj, "pipe", undef); if ($maxmess and $curmess >= $maxmess) { DEBUG "[DEBUG] Resetting pipe for $c because max messages was reached"; close($pipe) or WARN "[WARNING] Failed to close pipe for $c: $!"; $pipe = undef; munin_set($contactobj, "pipe", undef); } elsif ($curcmd and $curcmd ne $cmd) { DEBUG "[DEBUG] Resetting pipe for $c because the command has changed"; close($pipe) or WARN "[WARNING] Failed to close pipe for $c: $!"; $pipe = undef; munin_set($contactobj, "pipe", undef); } if (!defined $pipe) { DEBUG "[DEBUG] Opening pipe for $c"; pipe(my $r, my $w) or WARN "[WARNING] Failed to open pipe for $c: $!"; my $pid = fork(); defined($pid) or WARN "[WARNING] Failed fork for pipe for $c: $!"; if($pid) { # parent DEBUG "[DEBUG] Opened pipe for $c as pid $pid"; close $r; $pipe = $w; munin_set($contactobj, "pipe_command", $cmd); munin_set($contactobj, "pipe", $pipe); munin_set($contactobj, "num_messages", 0); $curmess = 0; } else { # child close $w; open(STDIN, "<&", $r); # We want to close stdout before calling the send command. This prevents the # notification program (e.g. "send_nsca") to write irrelevant status messages # to stdout and thus trigger notification emails (e.g. via cron). # See https://github.com/munin-monitoring/munin/issues/382 # and https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=291168. close(STDOUT); exec($cmd) or WARN "[WARNING] Failed to exec for contact $c in pid $$"; exit; } } DEBUG "[DEBUG] sending message to $c: \"$txt\""; if(defined $pipe and !print $pipe $txt, "\n") { WARN "[WARNING] Writing to pipe for $c failed: $!"; close($pipe) or WARN "[WARNING] Failed to close pipe for $c: $!"; $pipe = undef; munin_set($contactobj, "pipe", undef); } munin_set($contactobj, "num_messages", $curmess + 1); } } sub message_expand { my $hash = shift; my $text = shift; my @res = (); while (defined($text) && length($text)) { if ($text =~ /^([^\$]+|)(?:\$(\{.*)|)$/) { push @res, $1; $text = $2; } my @a = extract_bracketed($text, '{}'); if(!defined $a[0]) { $text = $a[1]; next; } if ($a[0] =~ /^\{var:(\S+)\}$/) { $a[0] = munin_get($hash, $1, ""); } elsif ($a[0] =~ /^\{loop<([^>]+)>:\s*(\S+)\s(.+)\}$/) { my $d = $1; my $f = $2; my $t = $3; my $fields = munin_get($hash, $f, ""); my @res = (); if ($fields) { foreach my $sub (split /\s+/, $fields) { if (defined $hash->{$sub}) { push @res, message_expand($hash->{$sub}, $t); } } } $a[0] = join($d, @res); } elsif ($a[0] =~ /^\{loop:\s*(\S+)\s(.+)\}$/) { my $f = $1; my $t = $2; my $fields = munin_get($hash, $f, ""); my $res = ""; if ($fields) { foreach my $sub (split /\s+/, $fields) { if (defined $hash->{$sub}) { push @res, message_expand($hash->{$sub}, $t); } } } $a[0] = $res; } elsif ($a[0] =~ /^\{strtrunc:\s*(\S+)\s(.+)\}$/) { my $f = "%." . $1 . "s"; my $t = $2; $a[0] = sprintf($f, message_expand($hash, $t)); } elsif ($a[0] =~ /^\{if:\s*(\!)?(\S+)\s(.+)\}$/) { my $n = $1; my $f = $2; my $t = $3; my $res = ""; my $field = munin_get($hash, $f, 0); my $check = (defined $field and $field ne "0" and length($field)); $check = (!$check) if $n; if ($check) { $res .= message_expand($hash, $t); } $a[0] = $res; } push @res, $a[0]; $text = $a[1]; } return join('', @res); } =pod Get a list of severities, and return a sorted, lower cased, unique and validated list of severities. Expects and returns an array reference. If none of the severities given are on the allowed_severities list, it will return a reference to an empty array. =cut sub validate_severities { my $severities_ref = shift; my @severities = grep { defined $_ } @{$severities_ref}; my @allowed_severities = qw{ok warning critical unknown}; # Flatten, split on comma and whitespace, and lowercase my @expanded_severities = split( /[[:space:],]+/, lc( join( ',', @severities ) ) ); # Remove duplicates my %seen; my @unique_severities = grep { !$seen{$_}++ } @expanded_severities; # Filter on allowed values my %count; my @validated_severities; foreach my $severity ( @unique_severities, @allowed_severities ) { $count{$severity}++; } foreach my $severity ( keys %count ) { if ( $count{$severity} == 2 ) { push @validated_severities, $severity; } } # Sort the final list my @sorted_severities = sort(@validated_severities); return \@sorted_severities; } 1; munin-2.0.75/master/lib/Munin/Master/Logger.pm000066400000000000000000000074421451614574100211110ustar00rootroot00000000000000package Munin::Master::Logger; =encoding utf-8 =head1 NAME Munin::Master::Logger - Munin master's old logging routines =head1 SYNOPSIS This module contains Munin master's old logging routines while we're switching to Log::Log4perl. It also sets up Log4perl according to our needs. Do not use "logger" when writing new code, use the Log::Log4perl :easy API. This module takes care of initializing Log::Log4perl at load time. =head1 SUBROUTINES =over =item B needs to be called once in the main program with one argument: The directory where the munin logs goes. The running programs name ($0) will be used as the log name (e.g. munin-graph.log). =item B Do not use. =item B Use to set the programs log level to debug, info, warn, error, or fatal. This corresponds to the log levels used in syslog, but syslog is not used for logging. =item B Set up DEBUG logging. Both STDOUT and the log file will receive DEBUG level output. =back =head1 AUTHOR Munin master logging ported to Log4perl by Nicolai Langfeldt. Split out into this module by Kjell Magne Øierud. =head1 LICENSE GPLv2 =cut use base qw(Exporter); use strict; use warnings; use Carp qw(confess); use English qw(-no_match_vars); use File::Basename qw(basename); use Log::Log4perl qw(:easy); our @EXPORT = qw(logger_open logger_open_stderr logger_debug logger_level logger); # Early open of the log. Warning and more urgent messages will go to # screen. Log::Log4perl->easy_init( $WARN ); my $logdir = undef; my $logopened = 0; my $me = $1 if basename($PROGRAM_NAME) =~ m/(.*)/; # Fast untaint $PROGRAM_NAME sub _warn_catcher { if ($logopened) { WARN "[PERL WARNING] ".join(" ",@_); } else { print STDERR join(" ",@_); } } sub logger_open_stderr { if (!$logopened) { # I'm a bit uncertain about the :utf8 bit. Log::Log4perl->easy_init( { level => $INFO, file => ":utf8>&STDERR" } ); $logopened = 1; } get_logger('')->debug("Opened log file"); # Get perl warnings into the log files $SIG{__WARN__} = \&_warn_catcher; } sub logger_open { # This is called when we have a directory and file name to log in. my $dirname = shift; $logdir=$dirname; if (!defined($dirname)) { confess("In logger_open, directory for log files undefined"); } my $log_filename = shift || "$dirname/$me.log"; if (!$logopened) { # I'm a bit uncertain about the :utf8 bit. Log::Log4perl->easy_init( { level => $INFO, file => ":utf8>>$log_filename" } ); # warn "Logging to $dirname/$me.log"; $logopened = 1; } get_logger('')->debug("Opened log file"); # Get perl warnings into the log files $SIG{__WARN__} = \&_warn_catcher; } sub logger_debug { # Adjust log level to DEBUG if user gave --debug option my $logger = get_logger(''); WARN "Setting log level to DEBUG\n"; if (defined($logdir)) { Log::Log4perl->easy_init( { level => $DEBUG, file => ":utf8>>$logdir/$me.log" }, { level => $DEBUG, file => "STDERR" } ); } else { # If we do not have a log file name to log to just send # everything to STDERR Log::Log4perl->easy_init( { level => $DEBUG, file => "STDERR" } ); } # And do not open the loggers again now. $logopened=1; } sub logger_level { my ($loglevel) = @_; my $logger = get_logger(''); $loglevel = lc $loglevel; my %level_map = ( debug => $DEBUG, info => $INFO, warn => $WARN, error => $ERROR, fatal => $FATAL, ); unless ($level_map{$loglevel}) { ERROR "Unknown log level: '$loglevel'\n"; return; } $logger->level($level_map{$loglevel}); INFO "Setting log level to $loglevel\n"; } sub logger { my ($comment) = @_; INFO @_; } 1; munin-2.0.75/master/lib/Munin/Master/Node.pm000066400000000000000000000546071451614574100205640ustar00rootroot00000000000000package Munin::Master::Node; # This module is used by UpdateWorker to keep in touch with a node and # parse some of the output. use warnings; use strict; use Carp; use Munin::Master::Config; use Munin::Common::Timeout; use Munin::Common::TLSClient; use Data::Dumper; use Log::Log4perl qw( :easy ); use Time::HiRes qw( gettimeofday tv_interval ); use IO::Socket::INET6; # Used as a timestamp value, this declares none was found use constant NO_TIMESTAMP => -1; my $config = Munin::Master::Config->instance()->{config}; # Quick version, to enable "DEBUG ... if $debug" constructs my $debug = $config->{debug}; # Note: This timeout governs both small commands and waiting for the total # output of a plugin. It is reset for each read. sub new { my ($class, $address, $port, $host, $configref) = @_; my $self = { address => $address, port => $port, host => $host, tls => undef, reader => undef, pid => undef, writer => undef, master_capabilities => "multigraph dirtyconfig", io_timeout => 120, configref => $configref, }; return bless $self, $class; } sub do_in_session { my ($self, $block) = @_; if ($self->_do_connect()) { $self->_run_starttls_if_required(); my $exit_value = $block->(); $self->_do_close(); return { exit_value => $exit_value }; # If we're still here } return 0; # _do_connect failed. } sub _do_connect { # Connect to a munin node. Return false if not, true otherwise. my ($self) = @_; LOGCROAK("[FATAL] No address! Did you forget to set 'update no' or to set 'address ' ?") if !defined($self->{address}); # Check if it's an URI or a plain host use URI; # Parameters are space-separated from the main address my ($url, $params) = split(/ +/, $self->{address}, 2); my $uri = new URI($url); # If address is only "ssh://host/" $params will not get set $params = "" unless defined $params; # If the scheme is not defined, it's a plain host. # Prefix it with munin:// to be able to parse it like others $uri = new URI("munin://" . $url) unless $uri->scheme; LOGCROAK("[FATAL] '$url' is not a valid address!") unless $uri->scheme; if ($uri->scheme eq "munin") { $self->{reader} = $self->{writer} = IO::Socket::INET6->new( PeerAddr => $uri->host, PeerPort => $self->{port} || 4949, LocalAddr => $self->_get_node_or_global_setting("local_address"), Proto => 'tcp', MultiHomed => 1, Timeout => $config->{timeout} ); unless ($self->{reader} && defined $self->{reader}->connected()) { ERROR "Failed to connect to node $self->{address}:$self->{port}/tcp : $!"; return 0; } } elsif ($uri->scheme eq "ssh") { my $ssh_command = sprintf("%s %s", $config->{ssh_command}, $config->{ssh_options}); my $user_part = ($uri->user) ? ($uri->user . "@") : ""; my $remote_cmd = ($uri->path ne '/') ? $uri->path : ""; # Add any parameter to the cmd my $remote_connection_cmd = $ssh_command . " -p " . $uri->port . " " . $user_part . $uri->host . " " . $remote_cmd . " " . $params; # Open a triple pipe use IPC::Open3; $self->{reader} = new IO::Handle(); $self->{writer} = new IO::Handle(); $self->{stderr} = new IO::Handle(); DEBUG "[DEBUG] open3($remote_connection_cmd)"; $self->{pid} = open3($self->{writer}, $self->{reader}, $self->{stderr}, $remote_connection_cmd); ERROR "Failed to connect to node $self->{address} : $!" unless $self->{pid}; } elsif ($uri->scheme eq "cmd") { # local commands should ignore the username, url and host my $local_cmd = $uri->path; my $local_pipe_cmd = "$local_cmd $params"; # Open a triple pipe use IPC::Open3; $self->{reader} = new IO::Handle(); $self->{writer} = new IO::Handle(); $self->{stderr} = new IO::Handle(); DEBUG "[DEBUG] open3($local_pipe_cmd)"; $self->{pid} = open3($self->{writer}, $self->{reader}, $self->{stderr}, $local_pipe_cmd); ERROR "Failed to execute local command: $!" unless $self->{pid}; } else { ERROR "Unknown scheme : " . $uri->scheme; return 0; } # check all the lines until we find one that matches the expected # greeting; ignore anything that doesn't look like it as long as # there is output. This allows to accept SSH connections where # lastlog or motd is used. until(defined($self->{node_name})) { my $greeting = $self->_node_read_single(); if (!$greeting) { die "[ERROR] Got unknown reply from node ".$self->{host}."\n"; } if ($greeting =~ /\#.*(?:lrrd|munin) (?:client|node) at (\S+)/i) { $self->{node_name} = $1; } }; INFO "[INFO] node $self->{host} advertised itself as $self->{node_name} instead." if $self->{node_name} && $self->{node_name} ne $self->{host}; return 1; } sub _get_node_or_global_setting { my ($self, $key) = @_; return exists $self->{configref}->{$key} ? $self->{configref}->{$key} : $config->{$key}; } sub _run_starttls_if_required { my ($self) = @_; # TLS should only be attempted if explicitly enabled. The default # value is therefore "disabled" (and not "auto" as before). my $tls_requirement = $self->_get_node_or_global_setting("tls"); DEBUG "TLS set to \"$tls_requirement\"."; return if $tls_requirement eq 'disabled'; my $logger = Log::Log4perl->get_logger("Munin::Master"); $self->{tls} = Munin::Common::TLSClient->new({ DEBUG => $config->{debug}, logger => sub { $logger->warn(@_) }, read_fd => fileno($self->{reader}), read_func => sub { _node_read_single($self) }, tls_ca_cert => $self->_get_node_or_global_setting("tls_ca_certificate"), tls_cert => $self->_get_node_or_global_setting("tls_certificate"), tls_paranoia => $tls_requirement, tls_priv => $self->_get_node_or_global_setting("tls_private_key"), tls_vdepth => $self->_get_node_or_global_setting("tls_verify_depth"), tls_verify => $self->_get_node_or_global_setting("tls_verify_certificate"), tls_match => $self->_get_node_or_global_setting("tls_match"), write_fd => fileno($self->{writer}), write_func => sub { _node_write_single($self, @_) }, }); if (!$self->{tls}->start_tls()) { $self->{tls} = undef; if ($tls_requirement eq "paranoid" or $tls_requirement eq "enabled") { die "[ERROR] Could not establish TLS connection to '$self->{address}'. Skipping.\n"; } } } sub _do_close { my ($self) = @_; close $self->{reader}; close $self->{writer}; $self->{reader} = undef; $self->{writer} = undef; # Close stderr if needed close $self->{stderr} if $self->{stderr}; $self->{stderr} = undef if $self->{stderr}; # Reap the underlying process waitpid($self->{pid}, 0) if (defined $self->{pid}); } sub negotiate_capabilities { my ($self) = @_; # Please note: Sone of the capabilities are asymetrical. Each # side simply announces which capabilities they have, and then the # other takes advantage of the capabilities it understands (or # dumbs itself down to the counterparts level of sophistication). DEBUG "[DEBUG] Negotiating capabilities\n"; $self->_node_write_single("cap $self->{master_capabilities}\n"); my $cap = $self->_node_read_single(); if (index($cap, 'cap ') == -1) { return ('NA'); } my @node_capabilities = split(/\s+/,$cap); shift @node_capabilities ; # Get rid of leading "cap". DEBUG "[DEBUG] Node says /$cap/\n"; return @node_capabilities; } sub list_plugins { my ($self) = @_; # Check for one on this node- if not, use the global one my $use_node_name = $self->_get_node_or_global_setting("use_node_name"); my $host = $use_node_name ? $self->{node_name} : $self->{host}; my $use_default_node = $self->_get_node_or_global_setting("use_default_node"); if (! $use_default_node && ! $host) { die "[ERROR] Couldn't find out which host to list on $host.\n"; } my $list_host = $use_default_node ? "" : $host; $self->_node_write_single("list $list_host\n"); my $list = $self->_node_read_single(); if (not $list) { WARN "[WARNING] Config node $self->{host} listed no services for $host, (advertised as $self->{node_name}). Please see http://munin-monitoring.org/wiki/FAQ_no_graphs for further information."; } return split / /, $list; } sub parse_service_config { my ($self, $service, $lines) = @_; my $errors; my $correct; my $plugin = $service; my $nodedesignation = $self->{host}."/".$self->{address}."/".$self->{port}; my $global_config = { multigraph => [], }; my $data_source_config = {}; my @graph_order = ( ); # Pascal style nested subroutine local *new_service = sub { push @{$global_config->{multigraph}}, $service; $global_config->{$service} = []; $data_source_config->{$service} = {}; }; local *push_graphorder = sub { my ($oldservice) = @_; # We always appends the field names in config order to any # graph_order given. # Note that this results in duplicates in the internal state # for @graph_order but munin_get_field_order() will eliminate # them before graphing. if (@graph_order) { foreach (@{$global_config->{$oldservice}}) { if ( $_->[0] eq 'graph_order' ) { # append to a given graph_order $_->[1] .= join(' ', '', @graph_order); @graph_order = ( ); return; } } push @{$global_config->{$oldservice}}, ['graph_order', join(' ', @graph_order)]; } @graph_order = ( ); }; DEBUG "[DEBUG] Now parsing config output from plugin $plugin on " .$self->{host}; new_service($service); for my $line (@$lines) { DEBUG "[CONFIG from $plugin] $line" if $debug; if ($line =~ /\# timeout/) { die "[ERROR] Timeout error on $nodedesignation during fetch of $plugin. \n"; } next unless $line; next if $line =~ /^\#/; if ($line =~ m{\A multigraph \s+ (.+) }xms) { push_graphorder($service); $service = $1; if ($service eq 'multigraph') { ERROR "[ERROR] SERVICE can't be named \"$service\" in plugin $plugin on ".$self->{host}."/".$self->{address}."/".$self->{port}; $errors++; last; } if ($service =~ /(^\.|\.$|\.\.)/) { ERROR "[ERROR] SERVICE \"$service\" contains dots in wrong places in plugin $plugin on ".$self->{host}."/".$self->{address}."/".$self->{port}; $errors++; last; } if ($service !~ m/^[-\w.:.]+$/) { ERROR "[ERROR] SERVICE \"$service\" contains weird characters in plugin $plugin on ".$self->{host}."/".$self->{address}."/".$self->{port}; $errors++; last; } new_service($service) unless $global_config->{$service}; DEBUG "[CONFIG multigraph $plugin] Service is now $service"; $correct++; } elsif ($line =~ m{\A ([^\s\.]+) \s+ (.+?) \s* $}xms) { $correct++; my $label = $self->_sanitise_fieldname($1); # add to config if not already here push @{$global_config->{$service}}, [$label, $2] unless grep { $_->[0] eq $label } @{$global_config->{$service}}; DEBUG "[CONFIG graph global $plugin] $service->$label = $2" if $debug; } elsif ($line =~ m{\A ([^\.]+)\.value \s+ (.+?) \s* $}xms) { $correct++; # Special case for dirtyconfig my ($ds_name, $value, $when) = ($1, $2, NO_TIMESTAMP); $ds_name = $self->_sanitise_fieldname($ds_name); if ($value =~ /^(\d+):(.+)$/) { $when = $1; $value = $2; } DEBUG "[CONFIG dirtyconfig $plugin] Storing $value from $when in $ds_name"; # Creating the datastructure if not created already $data_source_config->{$service}{$ds_name} ||= {}; $data_source_config->{$service}{$ds_name}{when} ||= []; $data_source_config->{$service}{$ds_name}{value} ||= []; # Saving the timed value in the datastructure push @{$data_source_config->{$service}{$ds_name}{when}}, $when; push @{$data_source_config->{$service}{$ds_name}{value}}, $value; } elsif ($line =~ m{\A ([^\.]+)\.([^\s]+) \s+ (.+?) \s* $}xms) { $correct++; my ($ds_name, $ds_var, $ds_val) = ($1, $2, $3); $ds_name = $self->_sanitise_fieldname($ds_name); $data_source_config->{$service}{$ds_name} ||= {}; $data_source_config->{$service}{$ds_name}{$ds_var} = $ds_val; DEBUG "[CONFIG dataseries $plugin] $service->$ds_name.$ds_var = $ds_val" if $debug; push ( @graph_order, $ds_name ) if $ds_var eq 'label'; } elsif ($line =~ m{\A ([^\.]+)\.([^\s]+) \s* $}xms) { # the field value is empty - ignore it # see https://github.com/munin-monitoring/contrib/issues/1156#issuecomment-746884950 # For example the meminfo "slab_size" graph may contain empty fields. These should not # end up as log noise, but can be safely ignored instead. $correct++; DEBUG "[DEBUG] Ignoring field without value ('$line') from $plugin on $nodedesignation.\n"; } else { $errors++; DEBUG "[DEBUG] Protocol exception: unrecognized line '$line' from $plugin on $nodedesignation.\n"; } } if ($errors) { WARN "[WARNING] $errors lines had errors while $correct lines were correct in data from 'config $plugin' on $nodedesignation"; } $self->_validate_data_sources($data_source_config); push_graphorder($service); return (global => $global_config, data_source => $data_source_config); } sub fetch_service_config { my ($self, $service) = @_; my $t0 = [gettimeofday]; DEBUG "[DEBUG] Fetching service configuration for '$service'"; $self->_node_write_single("config $service\n"); # The whole config in one fell swoop. my $lines = $self->_node_read(); my $elapsed = tv_interval($t0); my $nodedesignation = $self->{host}."/".$self->{address}."/".$self->{port}; DEBUG "[DEBUG] config: $elapsed sec for '$service' on $nodedesignation"; $service = $self->_sanitise_plugin_name($service); return $self->parse_service_config($service, $lines); } sub spoolfetch { my ($self, $timestamp) = @_; DEBUG "[DEBUG] Fetching spooled services since $timestamp (" . localtime($timestamp) . ")"; $self->_node_write_single("spoolfetch $timestamp\n"); # The whole stuff in one fell swoop. my $lines = $self->_node_read(); # using the multigraph parsing. # Using "__root__" as a special plugin name. return $self->parse_service_config("__root__", $lines); } sub _validate_data_sources { my ($self, $all_data_source_config) = @_; my $nodedesignation = $self->{host}."/".$self->{address}.":".$self->{port}; for my $service (keys %$all_data_source_config) { my $data_source_config = $all_data_source_config->{$service}; for my $ds (keys %$data_source_config) { if (!defined $data_source_config->{$ds}{label}) { ERROR "Missing required attribute 'label' for data source '$ds' in service $service on $nodedesignation"; $data_source_config->{$ds}{label} = 'No .label provided'; $data_source_config->{$ds}{extinfo} = "NOTE: The plugin did not provide any label for the data source $ds. It is in need of fixing."; } } } } sub parse_service_data { my ($self, $service, $lines) = @_; my $plugin = $service; my $errors = 0; my $correct = 0; my $nodedesignation = $self->{host}."/".$self->{address}.":".$self->{port}; my %values = ( $service => {}, ); DEBUG "[DEBUG] Now parsing fetch output from plugin $plugin on ". $nodedesignation; # every 'N' has the same value. Should not take parsing time into the equation my $now = time; for my $line (@$lines) { DEBUG "[FETCH from $plugin] $line"; if ($line =~ /\# timeout/) { die "[WARNING] Timeout in fetch from '$plugin' on ". $nodedesignation; } next unless $line; next if $line =~ /^\#/; if ($line =~ m{\A multigraph \s+ (.+) }xms) { $service = $1; if ($service =~ /(^\.|\.$|\.\.)/) { ERROR "[ERROR] SERVICE \"$service\" contains dots in wrong places in plugin $plugin on ".$self->{host}."/".$self->{address}."/".$self->{port}; $errors++; last; } if ($service !~ m/^[-\w.:.]+$/) { ERROR "[ERROR] SERVICE \"$service\" contains weird characters in plugin $plugin on ".$self->{host}."/".$self->{address}."/".$self->{port}; $errors++; last; } $values{$service} = {}; if ($service eq 'multigraph') { $errors++; ERROR "[ERROR] SERVICE can't be named \"$service\" in plugin $plugin on ". $nodedesignation; last; } $correct++; } elsif ($line =~ m{\A ([^\.]+)\.value \s+ ([\S:]+) }xms) { my ($data_source, $value, $when) = ($1, $2, $now); $correct++; $data_source = $self->_sanitise_fieldname($data_source); DEBUG "[FETCH from $plugin] Storing $value in $data_source"; if ($value =~ /^(\d+):(.+)$/) { $when = $1; $value = $2; } $values{$service}{$data_source} ||= { when => [], value => [], }; push @{$values{$service}{$data_source}{when}}, $when; push @{$values{$service}{$data_source}{value}}, $value; } elsif ($line =~ m{\A ([^\.]+)\.extinfo \s+ (.+?) \s* $}xms) { # Extinfo is used in munin-limits my ($data_source, $value) = ($1, $2); $correct++; $data_source = $self->_sanitise_fieldname($data_source); $values{$service}{$data_source} ||= {}; $values{$service}{$data_source}{extinfo} = $value; } else { $errors++; DEBUG "[DEBUG] Protocol exception while fetching '$service' from $plugin on $nodedesignation: unrecognized line '$line'"; next; } } if ($errors) { my $percent = ($errors / ($errors + $correct)) * 100; $percent = sprintf("%.2f", $percent); WARN "[WARNING] $errors lines had errors while $correct lines were correct ($percent%) in data from 'fetch $plugin' on $nodedesignation"; } return %values; } sub fetch_service_data { my ($self, $plugin) = @_; my $t0 = [gettimeofday]; $self->_node_write_single("fetch $plugin\n"); my $lines = $self->_node_read_fast(); my $elapsed = tv_interval($t0); my $nodedesignation = $self->{host}."/".$self->{address}."/".$self->{port}; DEBUG "[DEBUG] data: $elapsed sec for '$plugin' on $nodedesignation"; $plugin = $self->_sanitise_plugin_name($plugin); return $self->parse_service_data($plugin, $lines); } sub quit { my ($self) = @_; my $t0 = [gettimeofday]; $self->_node_write_single("quit \n"); my $elapsed = tv_interval($t0); my $nodedesignation = $self->{host}."/".$self->{address}."/".$self->{port}; DEBUG "[DEBUG] quit: $elapsed sec on $nodedesignation"; return 1; } sub _sanitise_plugin_name { my ($self, $name) = @_; $name =~ s/[^_A-Za-z0-9]/_/g; return $name; } sub _sanitise_fieldname { # http://munin-monitoring.org/wiki/notes_on_datasource_names my ($self, $name) = @_; $name =~ s/^[^A-Za-z_]/_/; $name =~ s/[^A-Za-z0-9_]/_/g; return $name; } sub _node_write_single { my ($self, $text) = @_; DEBUG "[DEBUG] Writing to socket: \"$text\"."; my $timed_out = !do_with_timeout($self->{io_timeout}, sub { if ($self->{tls} && $self->{tls}->session_started()) { $self->{tls}->write($text) or exit 9; } else { print { $self->{writer} } $text; } return 1; }); if ($timed_out) { LOGCROAK "[FATAL] Socket write timed out to ".$self->{host}. ". Terminating process."; } return 1; } sub _node_read_single { my ($self) = @_; my $res = undef; my $timed_out = !do_with_timeout($self->{io_timeout}, sub { if ($self->{tls} && $self->{tls}->session_started()) { $res = $self->{tls}->read(); } else { $res = readline $self->{reader}; } # Remove \r *and* \n. Normally only one, since we read line per line. $res =~ tr/\x{d}\x{a}//d if defined $res; return 1; }); if ($timed_out) { LOGCROAK "[FATAL] Socket read timed out to ".$self->{host}. ". Terminating process."; } if (!defined($res)) { # Probable socket not open. Why are we here again then? # aren't we supposed to be in "do in session"? LOGCROAK "[FATAL] Socket read from ".$self->{host}." failed. Terminating process."; } DEBUG "[DEBUG] Reading from socket to ".$self->{host}.": \"$res\"." if $debug; return $res; } sub _node_read_fast { my ($self) = @_; # We cannot bypass the IO if using TLS # so just reverting to normal mode. return _node_read(@_) if $self->{tls}; # Disable Buffering here, to be able to use sysread() local $| = 1; my $io_src = $self->{reader}; my $buf; my $offset = 0; while (my $read_len = sysread($io_src, $buf, 4096, $offset)) { $offset += $read_len; # Stop when we read a \n.\n # ... No need to have a full regex : simple index() my $start_offset = $offset - $read_len - 3; $start_offset = 0 if $start_offset < 0; last if index($buf, "\n.\n", $start_offset) >= 0; # if empty, the client only sends a plain ".\n" last if $buf eq ".\n"; } # Remove the last line that only contains ".\n" $buf =~ s/\.\n$//; return [ split(/\n/, $buf) ]; } sub _node_read { my ($self) = @_; my @array = (); my $line = $self->_node_read_single(); while($line ne ".") { push @array, $line; $line = $self->_node_read_single(); } DEBUG "[DEBUG] Reading from socket: \"".(join ("\\n",@array))."\"."; return \@array; } # Defines the URL::scheme for munin package URI::munin; # We are like a generic server require URI::_server; @URI::munin::ISA=qw(URI::_server); # munin://HOST[:PORT] sub default_port { return 4949; } 1; __END__ =head1 NAME Munin::Master::Node - Provides easy access to the munin node =head1 SYNOPSIS use Munin::Master::Node; my $node = Munin::Master::Node->new('localhost', '4949', 'foo'); $node->do_in_session(sub{ ... # Call misc. methods on $node }); =head1 METHODS =over =item B FIX =item B FIX =item B FIX =item B FIX =item B FIX =item B FIX =back munin-2.0.75/master/lib/Munin/Master/ProcessManager.pm000066400000000000000000000170351451614574100226020ustar00rootroot00000000000000package Munin::Master::ProcessManager; use warnings; use strict; use Carp; use English qw(-no_match_vars); use IO::Socket; use POSIX qw(:sys_wait_h); use Storable qw(nstore_fd fd_retrieve); use Time::HiRes; use Munin::Common::Timeout; use Munin::Master::Config; use Munin::Master::Logger; use Log::Log4perl qw( :easy ); my $E_DIED = 18; my $E_TIMED_OUT = 19; my $E_CONNECT = 20; my $config = Munin::Master::Config->instance()->{config}; sub new { my ($class, $result_callback, $error_callback) = @_; croak "Argument exception: result_callback" unless ref $result_callback eq 'CODE'; $error_callback ||= sub { warn "Worker failed: @_" }; my $self = { max_concurrent => $config->{max_processes}, socket_file => "$config->{rundir}/munin-master-processmanager-$$.sock", result_callback => $result_callback, error_callback => $error_callback, worker_timeout => $config->{timeout_fetch_one_node}, timeout => $config->{timeout_fetch_all_nodes}, accept_timeout => 10, active_workers => {}, result_queue => {}, pid2worker => {}, }; return bless $self, $class; } sub add_workers { my ($self, @workers) = @_; for my $worker (@workers) { croak "Argument exception: \@workers" unless $worker->isa('Munin::Master::Worker'); } $self->{workers} = \@workers; } sub start_work { my ($self) = @_; DEBUG "[DEBUG] Starting work"; my $sock = $self->_prepare_unix_socket(); $self->_start_waiting_workers(); do_with_timeout($self->{timeout}, sub { $self->_collect_results($sock); return 1; }) or croak "Work timed out before all workers finished"; $self->{workers} = []; DEBUG "[DEBUG] Work done"; $self->_free_socket($sock); } sub _prepare_unix_socket { my ($self) = @_; if ( -e $self->{socket_file} ) { unlink $self->{socket_file} or croak "unlink failed: $!"; } socket my $sock, PF_UNIX, SOCK_STREAM, 0 or croak "socket failed: $!"; bind $sock, sockaddr_un($self->{socket_file}) or croak "bind failed: $!"; chmod oct(700), $self->{socket_file} or croak "chmod failed: $!"; listen $sock, SOMAXCONN or croak "listen failed: $!"; return $sock; } sub _start_waiting_workers { my ($self) = @_; while (@{$self->{workers}}) { DEBUG "[DEBUG] Active workers: " . (scalar keys %{$self->{active_workers}}) . "/" . $self->{max_concurrent}; last if scalar keys %{$self->{active_workers}} == $self->{max_concurrent}; # Here we just start'em, check results in _collect_results $self->_start_next_worker(); } continue { # Sleep for a random time, to avoid too much process creation all at once sleep ( rand ($config->{worker_start_delay}) ) if $config->{worker_start_delay}; } } sub _start_next_worker { my ($self) = @_; my $worker = pop @{$self->{workers}}; my $pid = fork; if (!defined $pid) { LOGCROAK "Forking worker failed: $!"; } elsif ($pid) { $self->{active_workers}{$pid} = $worker; $self->{result_queue}{$worker->{ID}} = $worker; } else { $0 .= " [$worker]"; my $res = $self->_do_work($worker); DEBUG "[DEBUG] Exit status $res for worker $worker"; exit $res; } } sub _collect_results { my ($self, $sock) = @_; while (%{$self->{result_queue}} || @{$self->{workers}}) { do { $self->_vet_finished_workers(); $self->_start_waiting_workers(); } while (!%{$self->{result_queue}} && @{$self->{workers}}); my $worker_sock; DEBUG "[DEBUG] Active workers: " . (scalar keys %{$self->{active_workers}}) . "/" . $self->{max_concurrent}; if (not %{$self->{active_workers}}) { # Nothing left do do last; } my $timed_out = !do_with_timeout($self->{accept_timeout}, sub { accept $worker_sock, $sock; return 1; }); if ($timed_out) { if (defined($self->{host})) { my $nodedesignation = $self->{host}."/". $self->{host}{address}.":".$self->{host}{port}; WARN "[WARNING] Calling accept to collect results from " . $nodedesignation . " failed. This could be due to network problems/firewalled munin-node. Remaining workers: " . join(", ", keys %{$self->{result_queue}}); } else { INFO "[INFO] Remaining workers: " . join(", ", keys %{$self->{result_queue}}); } next; } # if timed_out next unless fileno $worker_sock; my $res = fd_retrieve($worker_sock); my ($worker_id, $real_res) = @$res; delete $self->{result_queue}{$worker_id}; $self->{result_callback}($res) if defined $real_res; } while (%{$self->{active_workers}}) { $self->_vet_finished_workers(); } } sub _free_socket { my ($self, $sock) = @_; unlink $self->{socket_file} or $! ne 'No such file or directory' && croak "unlink failed: $!"; close $sock or croak "socket close failed: $!"; } sub _vet_finished_workers { my ($self) = @_; while ((my $worker_pid = waitpid(-1, WNOHANG)) > 0) { if ($CHILD_ERROR) { DEBUG "[DEBUG] Got error from $worker_pid: $CHILD_ERROR. Handling error"; $self->_handle_worker_error($worker_pid); } my $child_exit = $CHILD_ERROR >> 8; my $child_signal = $CHILD_ERROR & 127; INFO "[INFO] Reaping $self->{active_workers}{$worker_pid}. Exit value/signal: $child_exit/$child_signal"; delete $self->{active_workers}{$worker_pid}; } } sub _handle_worker_error { my ($self, $worker_pid) = @_; my %code2msg = ( $E_TIMED_OUT => 'Timed out', $E_DIED => 'Died', $E_CONNECT => 'Connection failed', ); my $worker_id = $self->{active_workers}{$worker_pid}{ID}; my $exit_code = $CHILD_ERROR >> 8; $self->{error_callback}($self->{active_workers}{$worker_pid}, $code2msg{$exit_code} || $exit_code); } sub _do_work { my ($self, $worker) = @_; DEBUG "[DEBUG] Starting $worker"; my $retval = 0; my $res; eval { my $timed_out = !do_with_timeout($self->{worker_timeout}, sub { $res = $worker->do_work(); return 1; }); if ($timed_out) { ERROR "[ERROR] $worker timed out"; $res = undef; $retval = $E_TIMED_OUT; } elsif (!defined($res)) { ERROR "[ERROR] $worker failed to connect to node"; $retval = $E_CONNECT; } }; if ($EVAL_ERROR) { $EVAL_ERROR =~ s/\R//; ERROR "[ERROR] $worker died with '$EVAL_ERROR'"; $res = undef; $retval = $E_DIED; } # Stop trying if we already erred. return $retval if $retval; my $sock; unless (socket $sock, PF_UNIX, SOCK_STREAM, 0) { ERROR "[ERROR] Unable to create socket: $!"; return $E_DIED; } unless (connect $sock, sockaddr_un($self->{socket_file})) { ERROR "[ERROR] Unable to connect to socket: $!"; return $E_DIED; } nstore_fd([ $worker->{ID}, $res], $sock); close $sock; return $retval; } 1; __END__ =head1 NAME Munin::Master::ProcessManager - Manager for parallel execution of Workers. =head1 SYNOPSIS use Munin::Master::ProcessManager; my $pm = Munin::Master::ProcessManager->new(sub { my ($res) = @_; # Do something with $res ... }); $pm->add_workers(...); $pm->start_work(); =head1 DESCRIPTION FIX =head1 METHODS =over =item B FIX =item B FIX =item B FIX =back munin-2.0.75/master/lib/Munin/Master/Update.pm000066400000000000000000000263271451614574100211170ustar00rootroot00000000000000package Munin::Master::Update; use warnings; use strict; use English qw(-no_match_vars); use Carp; use Time::HiRes; use Log::Log4perl qw( :easy ); use Munin::Common::Defaults; use Munin::Master::Config; use Munin::Master::Logger; use Munin::Master::UpdateWorker; use Munin::Master::ProcessManager; use Munin::Master::Utils; my $config = Munin::Master::Config->instance()->{config}; sub new { my ($class) = @_; # This steals the groups from the master instance of the config. my $gah = $config->get_groups_and_hosts(); my $self = bless { STATS => undef, old_service_configs => {}, old_version => undef, service_configs => {}, workers => [], failed_workers => [], group_repository => Munin::Master::GroupRepository->new($gah), config_dump_file => "$config->{dbdir}/datafile", }, $class; } sub run { my ($self) = @_; $self->_create_rundir_if_missing(); $self->_do_with_lock_and_timing(sub { INFO "[INFO]: Starting munin-update"; $self->{old_service_configs} = $self->_read_old_service_configs(); $self->{workers} = $self->_create_workers(); $self->_run_workers(); # I wonder if the following should really be done with timing. - janl $self->_write_new_service_configs_locked(); }); } sub _read_old_service_configs { # Read the datafile containing old configurations. This should # not fail in case of problems with the file. In such a case the # file should simply be ingored and a new one written. Lets hope # it does not repeat itself then. my ($self) = @_; # Get old service configuration from the config instance since the # syntaxes are identical. my $oldconfig = Munin::Master::Config->instance()->{oldconfig}; my $datafile = $oldconfig->{config_file} = $config->{dbdir}.'/datafile'; my $file; if (-e $datafile ) { if (! open( $file, '<', $datafile)) { WARN "[Warning] Cannot open datafile $datafile"; return {}; } eval { # parse file but skip malformed lines $oldconfig->parse_config($file, 1); }; if ($EVAL_ERROR) { WARN "[Warning] Could not parse datafile $datafile: $EVAL_ERROR"; } } return $oldconfig; } sub _create_rundir_if_missing { my ($self) = @_; unless (-d $config->{rundir}) { mkdir $config->{rundir}, oct(700) or croak "Failed to create rundir (".$config->{rundir}."): $!"; } } sub _create_workers { my ($self) = @_; # FIX log skipped and queued workers: # logger("Skipping '$name' (update disabled by config)"); # logger("Queuing '$name' for update."); my @hosts = $self->{group_repository}->get_all_hosts(); if (%{$config->{limit_hosts}}) { @hosts = grep { $config->{limit_hosts}{$_->{host_name}} } @hosts } @hosts = grep { $_->{update} } @hosts; return [ map { Munin::Master::UpdateWorker->new($_) } @hosts ]; } sub _do_with_lock_and_timing { my ($self, $block) = @_; my $lock = "$config->{rundir}/munin-update.lock"; munin_runlock($lock); my $update_time = Time::HiRes::time; if (!open ($self->{STATS}, '>', "$config->{dbdir}/munin-update.stats.tmp")) { WARN "[WARNING] Could not open STATS to $config->{dbdir}/munin-update.stats.tmp: $!"; # Use /dev/null instead - if the admin won't fix he won't care open($self->{STATS}, '>', "/dev/null") or LOGCROAK "[FATAL] Could not open STATS to /dev/null (fallback for not being able to open $config->{dbdir}/munin-update.stats.tmp): $!"; } # Place global munin-update timeout here. my $retval = $block->(); $update_time = sprintf("%.2f", (Time::HiRes::time - $update_time)); print { $self->{STATS} } "UT|$update_time\n"; close ($self->{STATS}); $self->{STATS} = undef; rename ("$config->{dbdir}/munin-update.stats.tmp", "$config->{dbdir}/munin-update.stats"); INFO "[INFO]: Munin-update finished ($update_time sec)"; munin_removelock($lock); return $retval; } sub _run_workers { my ($self) = @_; if ($config->{fork}) { my $pm = Munin::Master::ProcessManager ->new($self->_create_self_aware_worker_result_handler(), $self->_create_self_aware_worker_exception_handler()); $pm->add_workers(@{$self->{workers}}); $pm->start_work(); } else { for my $worker (@{$self->{workers}}) { my $res ; eval { # do_work fails hard on a number of conditions $res = $worker->do_work(); }; $res=undef if $EVAL_ERROR; my $worker_id = $worker->{ID}; if (defined($res)) { $self->_handle_worker_result([$worker_id, $res]); } else { # Need to handle connection failure same as other # failures. do_connect fails softly. WARN "[WARNING] Failed worker ".$worker_id."\n"; push @{$self->{failed_workers}}, $worker_id; } } } } sub _create_self_aware_worker_result_handler { my ($self) = @_; return sub { $self->_handle_worker_result(@_); }; } sub _handle_worker_result { my ($self, $res) = @_; if (!defined($res)) { # no result? problem LOGCROAK("[FATAL] Handle_worker_result got handed a failed worker result"); } my ($worker_id, $time_used, $service_configs) = ($res->[0], $res->[1]{time_used}, $res->[1]{service_configs}); my $update_time = sprintf("%.2f", $time_used); INFO "[INFO]: Munin-update finished for node $worker_id ($update_time sec)"; if (! defined $self->{STATS} ) { # This is may only be the case when we get connection refused ERROR "[BUG!] Did not collect any stats for $worker_id. If this message appears in your logs a lot please email munin-users. Thanks."; } else { printf { $self->{STATS} } "UD|%s|%.2f\n", $worker_id, $time_used; } $self->{service_configs}{$worker_id} = $service_configs; } sub _create_self_aware_worker_exception_handler { my ($self) = @_; return sub { my ($worker, $reason) = @_; my $worker_id = $worker->{ID}; DEBUG "[DEBUG] In exception handler for failed worker $worker_id"; push @{$self->{failed_workers}}, $worker_id; }; } # FIX merge with UpdateWorker::_get_rrd_file_name # FIX seems like dead code? Or only used in ensure_? sub _get_rrd_file_name { my ($self, $host, $service, $ds_name, $ds_type) = @_; my $type_id = lc(substr(($ds_type), 0, 1)); my ($g, $h) = split /;/, $host; # // ... perl mode my $file = sprintf("%s-%s-%s-%s.rrd", $h, $service, $ds_name, $type_id); # Not really a danger (we're not doing this stuff via the shell), # so more to avoid confusion with silly filenames. ($g, $file) = map { my $p = $_; $p =~ tr/\//_/; $p =~ s/^\./_/g; $p; } ($g, $file); my $rrd_file = File::Spec->catfile($config->{dbdir}, $g, $file); croak "RRD file '$rrd_file' not found" unless -e $rrd_file; return $rrd_file; } sub _write_new_service_configs_locked { my ($self) = @_; my $lock_file = "$config->{rundir}/munin-datafile.lock"; munin_runlock($lock_file); my $config_dump_file = $self->{config_dump_file}; my $config_dump_file_tmp = "$config_dump_file.$$"; open my $dump, '>', $config_dump_file_tmp or croak "Fatal error: Could not open '$config_dump_file_tmp' for writing: $!"; $self->_write_new_service_configs($dump); close $dump or croak "Fatal error: Could not close '$config_dump_file_tmp': $!"; rename $config_dump_file_tmp, $config_dump_file or croak "Fatal error: Could not rename '$config_dump_file_tmp' to '$config_dump_file': $!"; munin_removelock($lock_file); } sub _write_new_service_configs { my ($self, $io) = @_; my $datafile_hash = {}; print $io "version $Munin::Common::Defaults::MUNIN_VERSION\n"; $datafile_hash->{version} = $Munin::Common::Defaults::MUNIN_VERSION; $self->_print_service_configs_for_not_updated_services($io, $datafile_hash); $self->_print_old_service_configs_for_failed_workers($io, $datafile_hash); for my $host (keys %{$self->{service_configs}}) { for my $service (keys %{$self->{service_configs}{$host}{data_source}}) { for my $attr (@{$self->{service_configs}{$host}{global}{$service}}) { print $io "$host:$service.$attr->[0] $attr->[1]\n"; munin_set_var_path($datafile_hash, "$host:$service.$attr->[0]", $attr->[1]); } for my $data_source (keys %{$self->{service_configs}{$host}{data_source}{$service}}) { for my $attr (keys %{$self->{service_configs}{$host}{data_source}{$service}{$data_source}}) { print $io "$host:$service.$data_source.$attr $self->{service_configs}{$host}{data_source}{$service}{$data_source}{$attr}\n"; munin_set_var_path($datafile_hash, "$host:$service.$data_source.$attr", $self->{service_configs}{$host}{data_source}{$service}{$data_source}{$attr}); } } } } # Also write the binary (Storable) version munin_writeconfig_storable($config->{dbdir}.'/datafile.storable', $datafile_hash); } sub _print_service_configs_for_not_updated_services { my ($self, $handle, $datafile_hash) = @_; my @hosts = $self->{group_repository}->get_all_hosts(); for my $workerdata (@hosts) { my $worker = $workerdata->get_full_path(); my @data = grep { /\.update$/ and !$workerdata->{$_} } keys %$workerdata; for my $match (@data) { my $prefix = substr $match, 0, -6; for my $datum (grep { /^\Q$prefix\E/ } keys %$workerdata) { printf $handle "%s:%s %s\n", $worker, $datum, $workerdata->{$datum}; munin_set_var_path($datafile_hash, $worker . ":". $datum, $workerdata->{$datum}); } } } } sub _print_old_service_configs_for_failed_workers { my ($self, $handle, $datafile_hash) = @_; for my $worker (@{$self->{failed_workers}}) { # The empty set contains "undef" it seems next if !defined($worker); my $workerdata = $self->{old_service_configs}->look_up($worker); # No data available on the failed worker if (!defined($workerdata)) { INFO "[INFO] No old data available for failed worker $worker. This node will disappear from the html web page hierarchy\n"; next; } for my $datum (keys %$workerdata) { # Skip some book-keeping next if ($datum eq 'group') or ($datum eq 'host_name'); if (defined $workerdata->{$datum}) { printf $handle "%s:%s %s\n", $worker, $datum, $workerdata->{$datum}; munin_set_var_path($datafile_hash, $worker . ":". $datum, $workerdata->{$datum}); } else { WARN "[Warning] no data $worker -> $datum"; } } } } 1; __END__ =head1 NAME Munin::Master::Update - Contacts Munin Nodes, gathers data from their service data sources, and stores this information in RRD files. =head1 SYNOPSIS my $update = Munin::Master::Update->new(); $update->run(); =head1 METHODS =over =item B my $update = Munin::Master::Update->new(); Constructor. =item B $update->run(); This is where all the work gets done. =back munin-2.0.75/master/lib/Munin/Master/UpdateWorker.pm000066400000000000000000000756311451614574100223130ustar00rootroot00000000000000package Munin::Master::UpdateWorker; use base qw(Munin::Master::Worker); use warnings; use strict; use Carp; use English qw(-no_match_vars); use Log::Log4perl qw( :easy ); use File::Basename; use File::Path; use File::Spec; use Munin::Master::Config; use Munin::Master::Node; use Munin::Master::Utils; use RRDs; use Time::HiRes; use Data::Dumper; use Scalar::Util qw(weaken); use List::Util qw(max); my $config = Munin::Master::Config->instance()->{config}; # Flags that have RRD autotuning enabled. my $rrd_tune_flags = { type => '--data-source-type', max => '--maximum', min => '--minimum', }; sub new { my ($class, $host, $worker) = @_; my $self = $class->SUPER::new($host->get_full_path); $self->{host} = $host; $self->{node} = Munin::Master::Node->new($host->{address}, $host->{port}, $host->{host_name}, $host); $self->{state} = {}; $self->{worker} = $worker; weaken($self->{worker}); return $self; } sub do_work { my ($self) = @_; my $update_time = Time::HiRes::time; my $host = $self->{host}{host_name}; my $path = $self->{host}->get_full_path; $path =~ s{[:;]}{-}g; # Parameters are space-separated from the main address my ($url, $params) = split(/ +/, $self->{host}{address}, 2); my $uri = new URI($url); # If the scheme is not defined, it's a plain host. # Prefix it with munin:// to be able to parse it like others $uri = new URI("munin://" . $url) unless $uri->scheme; my $nodedesignation; if ($uri->scheme eq "ssh" || $uri->scheme eq "cmd") { $nodedesignation = $host." (".$self->{host}{address}.")"; }else{ $nodedesignation = $host." (".$self->{host}{address}.":".$self->{host}{port}.")"; } my $lock_file = sprintf ('%s/munin-%s.lock', $config->{rundir}, $path); if (!munin_getlock($lock_file)) { WARN "Could not get lock $lock_file for $nodedesignation. Skipping node."; die "Could not get lock $lock_file for $nodedesignation. Skipping node.\n"; } # Reading the state file, no need to lock it, since it's per node and we # already have a lock on this. my $state_file = sprintf ('%s/state-%s.storable', $config->{dbdir}, $path); DEBUG "[DEBUG] Reading state for $path in $state_file"; $self->{state} = munin_read_storable($state_file) || {}; my %all_service_configs = ( data_source => {}, global => {}, ); INFO "[INFO] starting work in $$ for $nodedesignation.\n"; my $done = $self->{node}->do_in_session(sub { eval { # A I/O timeout results in a violent exit. Catch and handle. my @node_capabilities = $self->{node}->negotiate_capabilities(); # Handle spoolfetch, one call to retrieve everything my %whole_config; my @plugins; if (grep /^spool$/, @node_capabilities) { my $spoolfetch_last_timestamp = $self->get_spoolfetch_timestamp(); local $0 = "$0 -- spoolfetch($spoolfetch_last_timestamp)"; %whole_config = $self->uw_spoolfetch($spoolfetch_last_timestamp); # XXX - Commented out, should be protect by a "if logger.isDebugEnabled()" # since it is quite expensive #DEBUG "[DEBUG] whole_config:" . Dumper(\%whole_config); # spoolfetching reported no data, skipping it. if (! $whole_config{global}{multigraph}[1]) { INFO "[INFO] $nodedesignation didn't send any data for spoolfetch. Ignoring it."; # adding ourself to failed_workers, so we use push @{ $self->{worker}->{failed_workers} }, $self->{ID}; die "NO_SPOOLFETCH_DATA"; } # Gets the plugins from spoolfetch # Only keep the first one, the others will be multigraph-fetched @plugins = ( $whole_config{global}{multigraph}[0] ) ; } # Note: A multigraph plugin can present multiple services. @plugins = $self->{node}->list_plugins() unless @plugins; for my $plugin (@plugins) { if (%{$config->{limit_services}}) { next unless $config->{limit_services}{$plugin}; } DEBUG "[DEBUG] for my $plugin (@plugins)"; # Ask for config only if spoolfetch didn't already send it my %service_config = %whole_config; unless (%service_config) { local $0 = "$0 -- config($plugin)"; %service_config = $self->uw_fetch_service_config($plugin); } unless (%service_config) { WARN "[WARNING] Service $plugin on $nodedesignation ". "returned no config"; next; } # Check if this plugin has already sent its data via a dirtyconfig # Note that spoolfetch also uses dirtyconfig my %service_data = $self->handle_dirty_config(\%service_config); # default is 0 sec : always update when asked my $update_rate = get_global_service_value(\%service_config, $plugin, "update_rate", 0); my ($update_rate_in_seconds, $is_update_aligned) = parse_update_rate($update_rate); DEBUG "[DEBUG] update_rate $update_rate_in_seconds for $plugin on $nodedesignation"; if (! %service_data) { # Check if this plugin has to be updated if ($update_rate_in_seconds && $self->is_fresh_enough($nodedesignation, $plugin, $update_rate_in_seconds)) { # It's fresh enough, skip this $service DEBUG "[DEBUG] $plugin is fresh enough, not updating it"; next; } # __root__ is only a placeholder plugin for # an empty spoolfetch so we should ignore it # if asked to fetch it. # But we should still do everything after than. if ($plugin ne "__root__") { DEBUG "[DEBUG] No service data for $plugin, fetching it"; local $0 = "$0 -- fetch($plugin)"; %service_data = $self->{node}->fetch_service_data($plugin); } } # If update_rate is aligned, round the "when" for alignement if ($is_update_aligned) { foreach my $service (keys %service_data) { my $current_service_data = $service_data{$service}; foreach my $field (keys %$current_service_data) { my $whens = $current_service_data->{$field}->{when}; for (my $i = 0; $i < scalar @$whens; $i ++) { my $when = $whens->[$i]; my $rounded_when = round_to_granularity($when, $update_rate_in_seconds); $whens->[$i] = $rounded_when; } } } } # Since different plugins can populate multiple # positions in the service namespace we'll check for # collisions and warn of them. for my $service (keys %{$service_config{data_source}}) { if (defined($all_service_configs{data_source}{$service})) { WARN "[WARNING] Service collision: plugin $plugin on " ."$nodedesignation reports $service which already " ."exists on that host. Deleting new data."; delete($service_config{data_source}{$service}); delete($service_data{$service}) if defined $service_data{$service}; } } # .extinfo fields come from "fetch" but must be saved # like "config". for my $service (keys %service_data) { for my $ds (keys %{$service_data{$service}}) { my $extinfo = $service_data{$service}{$ds}{extinfo}; if (defined $extinfo) { $service_config{data_source}{$service}{$ds}{extinfo} = $extinfo; DEBUG "[DEBUG] Copied extinfo $extinfo into " ."service_config for $service / $ds on " .$nodedesignation; } } } $self->_compare_and_act_on_config_changes(\%service_config); %{$all_service_configs{data_source}} = ( %{$all_service_configs{data_source}}, %{$service_config{data_source}}); %{$all_service_configs{global}} = ( %{$all_service_configs{global}}, %{$service_config{global}}); my $last_updated_timestamp = $self->_update_rrd_files(\%service_config, \%service_data); if ($last_updated_timestamp) { $self->set_spoolfetch_timestamp($last_updated_timestamp); } } # for @plugins # Send "quit" to node $self->{node}->quit(); }; # eval # kill the remaining process if needed if ($self->{node}->{pid} && kill(0, $self->{node}->{pid})) { INFO "[INFO] Killing subprocess $self->{node}->{pid}"; kill 'TERM', $self->{node}->{pid}; } if ($EVAL_ERROR =~ m/^NO_SPOOLFETCH_DATA /) { INFO "[INFO] No spoolfetch data for $nodedesignation"; return; } elsif ($EVAL_ERROR) { ERROR "[ERROR] Error in node communication with $nodedesignation: " .$EVAL_ERROR; return; } # Everything went smoothly. DEBUG "[DEBUG] Everything went smoothly."; return 1; }); # do_in_session munin_removelock($lock_file); # Update the state file DEBUG "[DEBUG] Writing state for $path in $state_file"; munin_write_storable($state_file, $self->{state}); # This handles failure in do_in_session, return undef if ! $done || ! $done->{exit_value}; return { time_used => Time::HiRes::time - $update_time, service_configs => \%all_service_configs, } } sub get_global_service_value { my ($service_config, $service, $conf_field_name, $default) = @_; foreach my $array (@{$service_config->{global}{$service}}) { my ($field_name, $field_value) = @$array; if ($field_name eq $conf_field_name) { return $field_value; } } return $default; } sub is_fresh_enough { my ($self, $nodedesignation, $service, $update_rate_in_seconds) = @_; DEBUG "is_fresh_enough asked for $service with a rate of $update_rate_in_seconds"; my $last_updated = $self->{state}{last_updated}{$service} || "0 0"; DEBUG "last_updated{$service}: " . $last_updated; my @last = split(/ /, $last_updated); use Time::HiRes qw(gettimeofday tv_interval); my $now = [ gettimeofday ]; my $age = tv_interval(\@last, $now); DEBUG "last: [" . join(",", @last) . "], now: [" . join(", ", @$now) . "], age: $age"; my $is_fresh_enough = ($age < $update_rate_in_seconds) ? 1 : 0; DEBUG "is_fresh_enough $is_fresh_enough"; if (! $is_fresh_enough) { DEBUG "new value: " . join(" ", @$now); $self->{state}{last_updated}{$service} = join(" ", @$now); } return $is_fresh_enough; } sub get_spoolfetch_timestamp { my ($self) = @_; my $last_updated_value = $self->{state}{spoolfetch} || "0"; return $last_updated_value; } sub set_spoolfetch_timestamp { my ($self, $timestamp) = @_; DEBUG "[DEBUG] set_spoolfetch_timestamp($timestamp)"; # Using the last timestamp sended by the server : # -> It can be be different than "now" to be able to process the backlock slowly $self->{state}{spoolfetch} = $timestamp; } sub parse_update_rate { my ($update_rate_config) = @_; my ($is_update_aligned, $update_rate_in_sec); if ($update_rate_config =~ m/(\d+[a-z]?)( aligned)?/) { $update_rate_in_sec = to_sec($1); $is_update_aligned = $2; } else { return (0, 0); } return ($update_rate_in_sec, $is_update_aligned); } sub round_to_granularity { my ($when, $granularity_in_sec) = @_; $when = time if ($when eq "N"); # N means "now" my $rounded_when = $when - ($when % $granularity_in_sec); return $rounded_when; } sub handle_dirty_config { my ($self, $service_config) = @_; my %service_data; my $services = $service_config->{global}{multigraph}; foreach my $service (@$services) { my $service_data_source = $service_config->{data_source}->{$service}; foreach my $field (keys %$service_data_source) { my $field_value = $service_data_source->{$field}->{value}; my $field_when = $service_data_source->{$field}->{when}; # If value not present, this field is not dirty fetched next if (! defined $field_value); DEBUG "[DEBUG] handle_dirty_config:$service, $field, @$field_when"; # Moves the "value" to the service_data $service_data{$service}->{$field} ||= { when => [], value => [], }; push @{$service_data{$service}{$field}{value}}, @$field_value; push @{$service_data{$service}{$field}{when}}, @$field_when; delete($service_data_source->{$field}{value}); delete($service_data_source->{$field}{when}); } } return %service_data; } sub uw_spoolfetch { my ($self, $timestamp) = @_; my %whole_config = $self->{node}->spoolfetch($timestamp); # munin.conf might override stuff foreach my $plugin (keys %whole_config) { my $merged_config = $self->uw_override_with_conf($plugin, $whole_config{$plugin}); $whole_config{$plugin} = $merged_config; } return %whole_config; } sub uw_fetch_service_config { my ($self, $plugin) = @_; # Note, this can die for several reasons. Caller must eval us. my %service_config = $self->{node}->fetch_service_config($plugin); my $merged_config = $self->uw_override_with_conf($plugin, \%service_config); return %$merged_config; } sub uw_override_with_conf { my ($self, $plugin, $service_config) = @_; if ($self->{host}{service_config} && $self->{host}{service_config}{$plugin}) { my %merged_config = (%$service_config, %{$self->{host}{service_config}{$plugin}}); $service_config = \%merged_config; } return $service_config; } sub _compare_and_act_on_config_changes { my ($self, $nested_service_config) = @_; # Kjellm: Why do we need to tune RRD files after upgrade? # Shouldn't we create a upgrade script or something instead? # # janl: Upgrade script sucks. This way it's inline in munin and # no need to remember anything or anything. my $just_upgraded = 0; my $old_config = Munin::Master::Config->instance()->{oldconfig}; if (not defined $old_config->{version} or ($old_config->{version} ne $Munin::Common::Defaults::MUNIN_VERSION)) { $just_upgraded = 1; } for my $service (keys %{$nested_service_config->{data_source}}) { my $service_config = $nested_service_config->{data_source}{$service}; for my $data_source (keys %{$service_config}) { my $old_data_source = $data_source; my $ds_config = $service_config->{$data_source}; my $group = $self->{host}{group}{group_name}; my $host = $self->{host}{host_name}; my $old_host_config = $old_config->{groups}{$group}{hosts}{$host}; my $old_ds_config = undef; if ($old_host_config) { $old_ds_config = $old_host_config->get_canned_ds_config($service, $data_source); } if (defined($old_ds_config) and %$old_ds_config and defined($ds_config->{oldname}) and $ds_config->{oldname}) { $old_data_source = $ds_config->{oldname}; $old_ds_config = $old_host_config->get_canned_ds_config($service, $old_data_source); } if (defined($old_ds_config) and %$old_ds_config and not $self->_ds_config_eq($old_ds_config, $ds_config)) { $self->_ensure_filename($service, $old_data_source, $data_source, $old_ds_config, $ds_config) and $self->_ensure_tuning($service, $data_source, $ds_config); # _ensure_filename prints helpfull warnings in the log } elsif ($just_upgraded) { $self->_ensure_tuning($service, $data_source, $ds_config); } } } } sub _ds_config_eq { my ($self, $old_ds_config, $ds_config) = @_; $ds_config = $self->_get_rrd_data_source_with_defaults($ds_config); $old_ds_config = $self->_get_rrd_data_source_with_defaults($old_ds_config); # We only compare keys that are autotuned to avoid needless RRD tuning, # since RRD tuning is bad for perf (flush rrdcached) for my $key (keys %$rrd_tune_flags) { my $old_value = $old_ds_config->{$key}; my $value = $ds_config->{$key}; # if both keys undefined, look no further next unless (defined($old_value) || defined($value)); # so, at least one of the 2 is defined # False if the $old_value is not defined return 0 unless (defined($old_value)); # if something isn't the same, return false return 0 if (! defined $value || $old_value ne $value); } # Nothing different found, it has to be equal. return 1; } sub _ensure_filename { my ($self, $service, $old_data_source, $data_source, $old_ds_config, $ds_config) = @_; my $rrd_file = $self->_get_rrd_file_name($service, $data_source, $ds_config); my $old_rrd_file = $self->_get_rrd_file_name($service, $old_data_source, $old_ds_config); my $hostspec = $self->{node}{host}.'/'.$self->{node}{address}.':'. $self->{node}{port}; if ($rrd_file ne $old_rrd_file) { if (-f $old_rrd_file and -f $rrd_file) { my $host = $self->{host}{host_name}; WARN "[WARNING]: $hostspec $service $data_source config change " . "suggests moving '$old_rrd_file' to '$rrd_file' " . "but both exist; manually merge the data " . "or remove whichever file you care less about.\n"; return ''; } elsif (-f $old_rrd_file) { INFO "[INFO]: Config update, changing name of '$old_rrd_file'" . " to '$rrd_file' on $hostspec "; unless (rename ($old_rrd_file, $rrd_file)) { ERROR "[ERROR]: Could not rename '$old_rrd_file' to" . " '$rrd_file' for $hostspec: $!\n"; return ''; } } } return 1; } sub _ensure_tuning { my ($self, $service, $data_source, $ds_config) = @_; my $success = 1; my $rrd_file = $self->_get_rrd_file_name($service, $data_source, $ds_config); $ds_config = $self->_get_rrd_data_source_with_defaults($ds_config); for my $rrd_prop (keys %$rrd_tune_flags) { INFO "[INFO]: Config update, ensuring $rrd_prop of" . " '$rrd_file' is '$ds_config->{$rrd_prop}'.\n"; RRDs::tune($rrd_file, $rrd_tune_flags->{$rrd_prop}, "42:$ds_config->{$rrd_prop}"); if (my $tune_error = RRDs::error()) { ERROR "[ERROR] Tuning $rrd_prop of '$rrd_file' to" . " '$ds_config->{$rrd_prop}' failed.\n"; $success = ''; } } return $success; } sub _update_rrd_files { my ($self, $nested_service_config, $nested_service_data) = @_; my $nodedesignation = $self->{host}{host_name}."/". $self->{host}{address}.":".$self->{host}{port}; my $last_timestamp = max(0, map { my $svc = $_; map { my $ds = $_; @{$nested_service_data->{$svc}->{$ds}->{when} || []}; } keys %{$nested_service_config->{data_source}{$svc}}; } keys %{$nested_service_config->{data_source}} ); my $last_timestamp_or_now = ($last_timestamp > 0) ? $last_timestamp : time; for my $service (keys %{$nested_service_config->{data_source}}) { my $service_config = $nested_service_config->{data_source}{$service}; my $service_data = $nested_service_data->{$service}; for my $ds_name (keys %{$service_config}) { my $ds_config = $service_config->{$ds_name}; unless (defined($ds_config->{label})) { ERROR "[ERROR] Unable to update $service on $nodedesignation -> $ds_name: Missing data source configuration attribute: label"; next; } # Sets the DS resolution, searching in that order : # - per field # - per plugin # - globally my $configref = $self->{node}{configref}; $ds_config->{graph_data_size} ||= get_config_for_service($nested_service_config->{global}{$service}, "graph_data_size"); $ds_config->{graph_data_size} ||= $config->{graph_data_size}; $ds_config->{update_rate} ||= get_config_for_service($nested_service_config->{global}{$service}, "update_rate"); $ds_config->{update_rate} ||= $config->{update_rate}; $ds_config->{update_rate} ||= 300; # default is 5 min DEBUG "[DEBUG] asking for a rrd of size : " . $ds_config->{graph_data_size}; # Avoid autovivification (for multigraphs) my $first_epoch = (defined($service_data) and defined($service_data->{$ds_name}) and $service_data->{$ds_name}->{when}) ? ($service_data->{$ds_name}->{when}->[0]) : 0; my $rrd_file = $self->_create_rrd_file_if_needed($service, $ds_name, $ds_config, $first_epoch); if (defined($service_data) and defined($service_data->{$ds_name})) { $self->_update_rrd_file($rrd_file, $ds_name, $service_data->{$ds_name}, $last_timestamp_or_now); } else { WARN "[WARNING] Service $service on $nodedesignation returned no data for label $ds_name"; } } } return $last_timestamp; } sub get_config_for_service { my ($array, $key) = @_; for my $elem (@$array) { next unless $elem->[0] && $elem->[0] eq $key; return $elem->[1]; } # Not found return undef; } sub _get_rrd_data_source_with_defaults { my ($self, $data_source) = @_; # Copy it into a new hash, we don't want to alter the $data_source # and anything already defined should not be overridden by defaults my $ds_with_defaults = { type => 'GAUGE', min => 'U', max => 'U', update_rate => 300, graph_data_size => 'normal', }; for my $key (keys %$data_source) { $ds_with_defaults->{$key} = $data_source->{$key}; } return $ds_with_defaults; } sub _create_rrd_file_if_needed { my ($self, $service, $ds_name, $ds_config, $first_epoch) = @_; my $rrd_file = $self->_get_rrd_file_name($service, $ds_name, $ds_config); unless (-f $rrd_file) { $self->_create_rrd_file($rrd_file, $service, $ds_name, $ds_config, $first_epoch); } return $rrd_file; } sub _get_rrd_file_name { my ($self, $service, $ds_name, $ds_config) = @_; $ds_config = $self->_get_rrd_data_source_with_defaults($ds_config); my $type_id = lc(substr(($ds_config->{type}), 0, 1)); my $path = $self->{host}->get_full_path; $path =~ s{[;:]}{/}g; # Multigraph/nested services will have . in the service name in this function. $service =~ s{\.}{-}g; # The following is rigged to match the corresponding function in # munin-graph/munin-html where it's less clear what are groups and # what are hosts and what are services, and they simply pop # elements off the end and so on. my $file = sprintf("%s-%s-%s-%s.rrd", $path, $service, $ds_name, $type_id); $file = File::Spec->catfile($config->{dbdir}, $file); DEBUG "[DEBUG] rrd filename: $file\n"; return $file; } sub _create_rrd_file { my ($self, $rrd_file, $service, $ds_name, $ds_config, $first_epoch) = @_; INFO "[INFO] creating rrd-file for $service->$ds_name: '$rrd_file'"; munin_mkdir_p(dirname($rrd_file), oct(777)); my @args; $ds_config = $self->_get_rrd_data_source_with_defaults($ds_config); my $resolution = $ds_config->{graph_data_size}; my $update_rate = $ds_config->{update_rate}; if ($resolution eq 'normal') { $update_rate = 300; # 'normal' means hard coded RRD $update_rate push (@args, "RRA:AVERAGE:0.5:1:576", # resolution 5 minutes "RRA:MIN:0.5:1:576", "RRA:MAX:0.5:1:576", "RRA:AVERAGE:0.5:6:432", # 9 days, resolution 30 minutes "RRA:MIN:0.5:6:432", "RRA:MAX:0.5:6:432", "RRA:AVERAGE:0.5:24:540", # 45 days, resolution 2 hours "RRA:MIN:0.5:24:540", "RRA:MAX:0.5:24:540", "RRA:AVERAGE:0.5:288:450", # 450 days, resolution 1 day "RRA:MIN:0.5:288:450", "RRA:MAX:0.5:288:450"); } elsif ($resolution eq 'huge') { $update_rate = 300; # 'huge' means hard coded RRD $update_rate push (@args, "RRA:AVERAGE:0.5:1:115200", # resolution 5 minutes, for 400 days "RRA:MIN:0.5:1:115200", "RRA:MAX:0.5:1:115200"); } elsif ($resolution =~ /^custom (.+)/) { # Parsing resolution to achieve computer format as defined on the RFC : # FULL_NB, MULTIPLIER_1 MULTIPLIER_1_NB, ... MULTIPLIER_NMULTIPLIER_N_NB my @resolutions_computer = parse_custom_resolution($1, $update_rate); foreach my $resolution_computer(@resolutions_computer) { my ($multiplier, $multiplier_nb) = @{$resolution_computer}; # Always add 10% to the RRA size, as specified in # http://munin-monitoring.org/wiki/format-graph_data_size $multiplier_nb += int ($multiplier_nb / 10) || 1; push (@args, "RRA:AVERAGE:0.5:$multiplier:$multiplier_nb", "RRA:MIN:0.5:$multiplier:$multiplier_nb", "RRA:MAX:0.5:$multiplier:$multiplier_nb" ); } } # Add the RRD::create prefix (filename & RRD params) my $heartbeat = $update_rate * 2; unshift (@args, $rrd_file, "--start", ($first_epoch - $update_rate), "-s", $update_rate, sprintf('DS:42:%s:%s:%s:%s', $ds_config->{type}, $heartbeat, $ds_config->{min}, $ds_config->{max}), ); DEBUG "[DEBUG] RRDs::create @args"; RRDs::create @args; if (my $ERROR = RRDs::error) { ERROR "[ERROR] Unable to create '$rrd_file': $ERROR"; } } sub parse_custom_resolution { my @elems = split(',\s*', shift); my $update_rate = shift; DEBUG "[DEBUG] update_rate: $update_rate"; my @computer_format; # First element is always the full resolution my $full_res = shift @elems; if ($full_res =~ m/^\d+$/) { # Only numeric, computer format unshift @elems, "1 $full_res"; } else { # Human readable. Adding $update_rate in front of unshift @elems, "$update_rate for $full_res"; } foreach my $elem (@elems) { if ($elem =~ m/(\d+) (\d+)/) { # nothing to do, already in computer format push @computer_format, [$1, $2]; } elsif ($elem =~ m/(\w+) for (\w+)/) { my $nb_sec = to_sec($1); my $for_sec = to_sec($2); my $multiplier = int ($nb_sec / $update_rate); my $multiplier_nb = int ($for_sec / $nb_sec); DEBUG "[DEBUG] $elem" . " -> nb_sec:$nb_sec, for_sec:$for_sec" . " -> multiplier:$multiplier, multiplier_nb:$multiplier_nb" ; push @computer_format, [$multiplier, $multiplier_nb]; } } return @computer_format; } # return the number of seconds # for the human readable format # s : second, m : minute, h : hour # d : day, w : week, t : month, y : year sub to_sec { my $secs_table = { "s" => 1, "m" => 60, "h" => 60 * 60, "d" => 60 * 60 * 24, "w" => 60 * 60 * 24 * 7, "t" => 60 * 60 * 24 * 31, # a month always has 31 days "y" => 60 * 60 * 24 * 365, # a year always has 365 days }; my ($target) = @_; if ($target =~ m/(\d+)([smhdwty])/i) { return $1 * $secs_table->{$2}; } else { # no recognised unit, return the int value as seconds return int $target; } } sub to_mul { my ($base, $target) = @_; my $target_sec = to_sec($target); if ($target %% $base != 0) { return 0; } return round($target / $base); } sub to_mul_nb { my ($base, $target) = @_; my $target_sec = to_sec($target); if ($target %% $base != 0) { return 0; } } sub _update_rrd_file { my ($self, $rrd_file, $ds_name, $ds_values, $max_timestamp) = @_; my $values = $ds_values->{value}; # Some kind of mismatch between fetch and config can cause this. return if !defined($values); my ($previous_updated_timestamp, $previous_updated_value) = @{ $self->{state}{value}{"$rrd_file:42"}{current} || [ ] }; my @update_rrd_data; if ($config->{"rrdcached_socket"}) { if (! -e $config->{"rrdcached_socket"} || ! -w $config->{"rrdcached_socket"}) { WARN "[WARN] RRDCached feature ignored: rrdcached socket not writable"; } elsif($RRDs::VERSION < 1.3){ WARN "[WARN] RRDCached feature ignored: perl RRDs lib version must be at least 1.3. Version found: " . $RRDs::VERSION; } else { # Using the RRDCACHED_ADDRESS environnement variable, as # it is way less intrusive than the command line args. $ENV{RRDCACHED_ADDRESS} = $config->{"rrdcached_socket"}; } } my ($current_updated_timestamp, $current_updated_value) = ($previous_updated_timestamp, $previous_updated_value); for (my $i = 0; $i < scalar @$values; $i++) { my $value = $values->[$i]; my $when = $ds_values->{when}[$i]; if ($when == $self->{node}->NO_TIMESTAMP) { $when = $max_timestamp; } # Ignore values that is not in monotonic increasing timestamp for the RRD. # Otherwise it will reject the whole update next if ($current_updated_timestamp && $when <= $current_updated_timestamp); if ($value =~ /\d[Ee]([+-]?\d+)$/) { # Looks like scientific format. RRDtool does not # like it so we convert it. my $magnitude = $1; if ($magnitude < 0) { # Preserve at least 4 significant digits $magnitude = abs($magnitude) + 4; $value = sprintf("%.*f", $magnitude, $value); } else { $value = sprintf("%.4f", $value); } } # Schedule for addition push @update_rrd_data, "$when:$value"; $current_updated_timestamp = $when; $current_updated_value = $value; } DEBUG "[DEBUG] Updating $rrd_file with @update_rrd_data"; if ($ENV{RRDCACHED_ADDRESS} && (scalar @update_rrd_data > 32) ) { # RRDCACHED only takes about 4K worth of commands. If the commands is # too large, we have to break it in smaller calls. # # Note that 32 is just an arbitrary choosed number. It might be tweaked. # # For simplicity we only call it with 1 update each time, as RRDCACHED # will buffer for us as suggested on the rrd mailing-list. # https://lists.oetiker.ch/pipermail/rrd-users/2011-October/018196.html for my $update_rrd_data (@update_rrd_data) { RRDs::update($rrd_file, $update_rrd_data); # Break on error. last if RRDs::error; } } else { RRDs::update($rrd_file, @update_rrd_data); } if (my $ERROR = RRDs::error) { #confess Dumper @_; ERROR "[ERROR] In RRD: Error updating $rrd_file: $ERROR"; } # Stores the previous and the current value in the state db to avoid having to do an RRD lookup if needed $self->{state}{value}{"$rrd_file:42"}{current} = [ $current_updated_timestamp, $current_updated_value ]; $self->{state}{value}{"$rrd_file:42"}{previous} = [ $previous_updated_timestamp, $previous_updated_value ]; return scalar @update_rrd_data; } sub dump_to_file { my ($filename, $obj) = @_; open(DUMPFILE, ">> $filename"); use Data::Dumper; print DUMPFILE Dumper($obj); close(DUMPFILE); } 1; __END__ =head1 NAME Munin::Master::UpdateWorker - FIX =head1 SYNOPSIS FIX =head1 METHODS =over =item B FIX =item B FIX =back =head1 COPYING Copyright (C) 2002-2009 Jimmy Olsen, et al. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. munin-2.0.75/master/lib/Munin/Master/Utils.pm000066400000000000000000001420231451614574100207650ustar00rootroot00000000000000package Munin::Master::Utils; # -*- cperl -*- use strict; use warnings; use Carp; use Exporter; use English qw(-no_match_vars); use Fcntl qw(:DEFAULT :flock); use File::Path; use IO::Handle; use Munin::Common::Defaults; use Munin::Master::Logger; use Munin::Master::Config; use Munin::Common::Config; use Log::Log4perl qw(:easy); use POSIX qw(strftime); use POSIX qw(:sys_wait_h); use Symbol qw(gensym); use Data::Dumper; use Storable; use Scalar::Util qw(isweak weaken); our (@ISA, @EXPORT); @ISA = ('Exporter'); @EXPORT = ( 'munin_nscasend', 'munin_createlock', 'munin_removelock', 'munin_runlock', 'munin_getlock', 'munin_readconfig_raw', 'munin_writeconfig', 'munin_writeconfig_storable', 'munin_read_storable', 'munin_write_storable', 'munin_delete', 'munin_overwrite', 'munin_dumpconfig', 'munin_dumpconfig_as_str', 'munin_readconfig_base', 'munin_readconfig_part', 'munin_configpart_revision', 'munin_draw_field', 'munin_get_bool', 'munin_get_bool_val', 'munin_get', 'munin_field_status', 'munin_service_status', 'munin_node_status', 'munin_category_status', 'munin_get_picture_filename', 'munin_get_picture_loc', 'munin_get_html_filename', 'munin_get_filename', 'munin_get_keypath', 'munin_graph_column_headers', 'munin_get_max_label_length', 'munin_get_field_order', 'munin_get_rrd_filename', 'munin_get_node_name', 'munin_get_orig_node_name', 'munin_get_parent_name', 'munin_get_node_fqn', 'munin_find_node_by_fqn', 'munin_get_node_loc', 'munin_get_node', 'munin_set_var_loc', 'munin_set_var_path', 'munin_set', 'munin_copy_node_toloc', 'munin_get_separated_node', 'munin_mkdir_p', 'munin_find_field', 'munin_find_field_for_limits', 'munin_get_parent', 'munin_get_children', 'munin_get_node_partialpath', 'munin_has_subservices', 'munin_get_host_path_from_string', 'print_version_and_exit', 'exit_if_run_by_super_user', 'look_for_child', 'wait_for_remaining_children', ); my $VERSION = $Munin::Common::Defaults::MUNIN_VERSION; my $nsca = new IO::Handle; my $config = undef; my $config_parts = { # config parts that might be loaded and reloaded at times 'datafile' => { 'timestamp' => 0, 'config' => undef, 'revision' => 0, 'include_base' => 1, }, 'limits' => { 'timestamp' => 0, 'config' => undef, 'revision' => 0, 'include_base' => 1, }, 'htmlconf' => { 'timestamp' => 0, 'config' => undef, 'revision' => 0, 'include_base' => 0, }, }; my $configfile="$Munin::Common::Defaults::MUNIN_CONFDIR/munin.conf"; # Fields to copy when "aliasing" a field my @COPY_FIELDS = ("label", "draw", "type", "rrdfile", "fieldname", "info"); my @dircomponents = split('/',$0); my $me = pop(@dircomponents); sub munin_draw_field { my $hash = shift; return 0 if munin_get_bool ($hash, "skipdraw", 0); return 0 if !munin_get_bool ($hash, "graph", 1); return defined $hash->{"label"}; } sub munin_nscasend { my ($name,$service,$label,$level,$comment) = @_; if (!$nsca->opened) { open ($nsca ,'|-', "$config->{nsca} $config->{nsca_server} -c $config->{nsca_config} -to 60"); } if ($label) { print $nsca "$name\t$service: $label\t$level\t$comment\n"; DEBUG "To $nsca: $name;$service: $label;$level;$comment\n"; } else { print $nsca "$name\t$service\t$level\t$comment\n"; DEBUG "[DEBUG] To $nsca: $name;$service;$level;$comment\n"; } } sub munin_createlock { # Create lock file, fail and die if not possible. my ($lockname) = @_; if (sysopen (LOCK,$lockname,O_WRONLY | O_CREAT | O_EXCL)) { DEBUG "[DEBUG] Creating lock : $lockname succeeded\n"; print LOCK $$; # we want the pid inside for later use close LOCK; return 1; } else { LOGCROAK("Creating lock $lockname failed: $!\n"); } } sub munin_removelock { # Remove lock or die trying. my ($lockname) = @_; unlink $lockname or LOGCROAK("[FATAL ERROR] Error deleting lock $lockname: $!\n"); } sub munin_runlock { my ($lockname) = @_; unless (munin_getlock($lockname)) { LOGCROAK("[FATAL ERROR] Lock already exists: $lockname. Dying.\n"); } return 1; } sub munin_getlock { my ($lockname) = @_; if (-f $lockname) { DEBUG "[DEBUG] Lock $lockname already exists, checking process"; # Is the lockpid alive? # To check this is inteligent and so on. It also makes for a # nice locking racing-condition. BUT, since munin-* runs from # cron every 5 minutes this should not be a real threat. This # ream of code should complete in less than 5 minutes. open my $LOCK, '<', $lockname or LOGCROAK("Could not open $lockname for reading: $!\n"); my $pid = <$LOCK>; $pid = '' if !defined($pid); close($LOCK) or LOGCROAK("Could not close $lockname: $!\n"); DEBUG "[DEBUG] Lock contained pid '$pid'"; # Make sure it's a proper pid if (defined($pid) and $pid =~ /^(\d+)$/ and $1 != 1) { $pid = $1; if (kill(0, $pid)) { DEBUG "[DEBUG] kill -0 $pid worked - it is still alive. Locking failed."; return 0; } INFO "[INFO] Process $pid is dead, stealing lock, removing file"; } else { INFO "[INFO] PID in lock file is bogus. Removing lock file"; } munin_removelock($lockname); } DEBUG "[DEBUG] Creating new lock file $lockname"; munin_createlock($lockname); return 1; } sub munin_delete { my ($config,$data) = @_; for my $domain (keys %{$data->{domain}}) { unless ($config->{domain}->{$domain}) { DEBUG "[DEBUG] Removing domain: $domain"; delete ($data->{domain}->{$domain}); next; } for my $node (keys %{$data->{domain}->{$domain}->{node}}) { unless ($config->{domain}->{$domain}->{node}->{$node}) { DEBUG "[DEBUG] Removing node from $domain: $node"; delete ($data->{domain}->{$domain}->{node}->{$node}); } } } return ($data); } sub munin_overwrite { # copy from $overwrite OVER $config. my ($configfile,$overwrite) = @_; for my $key (keys %$overwrite) { next if substr($key,0,3) eq '#%#'; if (ref $overwrite->{$key}) { if (!defined $configfile->{$key}) { if (ref $overwrite->{$key} eq "HASH") { $configfile->{$key}->{'#%#parent'} = $configfile; weaken($configfile->{$key}->{'#%#parent'}); $configfile->{$key}->{'#%#name'} = $key; munin_overwrite($configfile->{$key},$overwrite->{$key}); } else { $configfile->{$key} = $overwrite->{$key}; } } else { munin_overwrite($configfile->{$key},$overwrite->{$key}); } } else { $configfile->{$key} = $overwrite->{$key}; } } return ($configfile); } sub munin_readconfig_raw { my ($conf, $missingok) = @_; my $part = undef; $conf ||= $configfile; # try first to read storable version $part = munin_read_storable("$conf.storable"); if (!defined $part) { if (! -r $conf and ! $missingok) { WARN "munin_readconfig: cannot open '$conf'"; return; } if (open (my $CFG, '<', $conf)) { my @contents = <$CFG>; close ($CFG); $part = munin_parse_config (\@contents); } } return $part; } sub munin_parse_config { my $lines = shift; my $hash = {}; my $prefix = ""; my $prevline = ""; foreach my $line (@{$lines}) { chomp $line; if ($line =~ /#/) { next if ($line =~ /^#/); $line =~ s/(^|[^\\])#.*/$1/g; $line =~ s/\\#/#/g; } next unless ($line =~ /\S/); # And empty lines... if (length $prevline) { $line = $prevline . $line; $prevline = ""; } if ($line =~ /\\\\$/) { $line =~ s/\\\\$/\\/; } elsif ($line =~ /\\$/) { ($prevline = $line) =~ s/\\$//; next; } $line =~ s/\s+$//g; # And trailing whitespace... $line =~ s/^\s+//g; # And heading whitespace... if ($line =~ /^\.(\S+)\s+(.+)\s*$/) { my ($var, $val) = ($1, $2); $hash = munin_set_var_path ($hash, $var, $val); } elsif ($line =~ /^\s*\[([^\]]*)]\s*$/) { $prefix = $1; if ($prefix =~ /^([^:]+);([^:;]+)$/) { $prefix .= ":"; } elsif ($prefix =~ /^([^:;]+);$/) { $prefix .= ""; } elsif ($prefix =~ /^([^:;]+);([^:;]+):(.*)$/) { $prefix .= "."; } elsif ($prefix =~ /^([^:;]+)$/) { (my $domain = $prefix) =~ s/^[^\.]+\.//; $prefix = "$domain;$prefix:"; } elsif ($prefix =~ /^([^:;]+):(.*)$/) { (my $domain = $prefix) =~ s/^[^\.]+\.//; $prefix = "$domain;$prefix."; } } elsif ($line =~ /^\s*(\S+)\s+(.+)\s*$/) { my ($var, $val) = ($1, $2); $hash = munin_set_var_path ($hash, "$prefix$var", $val); } else { warn "Malformed configuration line \"$line\"."; } } return $hash; } sub munin_get_var_path { my $hash = shift; my $var = shift; my $val = shift; DEBUG "DEBUG: Getting var \"$var\" = \"$val\"\n"; if ($var =~ /^\s*([^;:]+);([^;:]+):(\S+)\s*$/) { my ($dom, $host, $rest) = ($1, $2, $3); my @sp = split (/\./, $rest); if (@sp == 3) { return $hash->{domain}->{$dom}->{node}->{$host}->{client}->{$sp[0]}->{"$sp[1].$sp[2]"}; } elsif (@sp == 2) { return $hash->{domain}->{$dom}->{node}->{$host}->{client}->{$sp[0]}->{$sp[1]}; } elsif (@sp == 1) { return $hash->{domain}->{$dom}->{node}->{$host}->{$sp[0]}; } else { warn "munin_set_var_path: Malformatted variable path \"$var\"."; } } elsif ($var =~ /^\s*([^;:]+);([^;:]+)\s*$/) { my ($dom, $rest) = ($1, $2); my @sp = split (/\./, $rest); if (@sp == 1) { return $hash->{domain}->{$dom}->{$sp[0]}; } else { warn "munin_set_var_path: Malformatted variable path \"$var\"."; } } elsif ($var =~ /^\s*([^;:\.]+)\s*$/) { return $hash->{$1}; } else { warn "munin_set_var_path: Malformatted variable path \"$var\"."; } return; } sub munin_find_field { # Starting at the (presumably the root) $hash make recursive calls # until for example graph_title or value is found, and then # continue recursing and itterating until all are found. # # Then we return a array of pointers into the $hash # # This function will not use REs as they are inordinately # expensive. There is a munin_find_field_for_limits that will # match REs instead of whole strings. my ($hash, $field, $avoid) = @_; my $res = []; if (ref ($hash) eq "HASH") { foreach my $key (keys %{$hash}) { next if substr($key,0,3) eq '#%#'; last if defined $avoid and $key eq $avoid; # Always check $key eq $field first here, or we break if ($key eq $field) { push @$res, $hash; } elsif (ref ($hash->{$key}) eq "HASH") { push @$res, @{munin_find_field ($hash->{$key}, $field, $avoid)}; } } } return $res; } sub munin_find_field_for_limits { my ($hash, $field, $avoid) = @_; my $res = []; if (ref ($field) ne "Regexp") { $field = qr/^$field$/; } if (ref ($hash) eq "HASH") { foreach my $key (keys %{$hash}) { next if substr($key,0,3) eq '#%#'; last if defined $avoid and $key eq $avoid; if (ref ($hash->{$key}) eq "HASH") { push @$res, @{munin_find_field_for_limits ($hash->{$key}, $field, $avoid)}; } elsif ($key =~ $field) { push @$res, $hash; } } } return $res; } sub munin_get_children { my $hash = shift; my $res = []; return if (ref ($hash) ne "HASH"); foreach my $key (keys %{$hash}) { next if substr($key,0,3) eq '#%#'; if (defined $hash->{$key} and ref ($hash->{$key}) eq "HASH") { push @$res, $hash->{$key}; } } return $res; } sub munin_get_separated_node { my $hash = shift; my $ret = {}; if (ref ($hash) eq "HASH") { foreach my $key (keys %$hash) { next if substr($key,0,3) eq '#%#'; if (ref ($hash->{$key}) eq "HASH") { $ret->{$key} = munin_get_separated_node ($hash->{$key}); } else { $ret->{$key} = $hash->{$key}; } } } else { return; } return $ret; } sub munin_get_parent_name { my $hash = shift; if (ref ($hash) eq "HASH" and defined $hash->{'#%#parent'}) { return munin_get_node_name ($hash->{'#%#parent'}); } else { return "none"; } } sub munin_get_orig_node_name { my $hash = shift; if (ref ($hash) eq "HASH" and defined $hash->{'#%#name'}) { return (defined $hash->{'#%#origname'}) ? $hash->{'#%#origname'} : $hash->{'#%#name'}; } else { return; } } sub munin_get_node_name { my $hash = shift; if (ref ($hash) eq "HASH" and defined $hash->{'#%#name'}) { return $hash->{'#%#name'}; } else { return; } } sub munin_get_node_fqn { my $hash = shift; if (ref ($hash) eq "HASH") { my $fqn = ""; if (defined $hash->{'#%#name'}) { $fqn = $hash->{'#%#name'}; } if (defined $hash->{'#%#parent'}) { # Recursively prepend the parent, concatenation with / $fqn = munin_get_node_fqn ($hash->{'#%#parent'}) . "/" . $fqn; } return $fqn; } else { return; } } sub munin_find_node_by_fqn { # The FQN should be relative to the point in the hash we're handed. my($hash,$fqn) = @_; my $here = $hash; my @path = split('/',$fqn); foreach my $pc (@path) { next if $pc eq 'root'; if (exists $here->{$pc}) { $here = $here->{$pc}; } else { confess "Could not find FQN $fqn!"; } } return $here ; } sub munin_get_picture_loc { my $hash = shift; my $res = []; if (ref ($hash) ne "HASH") { # Not a hash node return; } if (defined $hash->{'#%#origin'}) { $res = munin_get_picture_loc ($hash->{'#%#origin'}); } elsif (defined $hash->{'#%#origparent'}){ $res = munin_get_picture_loc ($hash->{'#%#origparent'}); push @$res, munin_get_orig_node_name ($hash) if defined $res; } elsif (defined $hash->{'#%#parent'}) { $res = munin_get_picture_loc ($hash->{'#%#parent'}); push @$res, munin_get_orig_node_name ($hash) if defined $res; } return $res; } sub munin_get_node_loc { my $hash = shift; my $res = []; if (ref ($hash) ne "HASH") { # Not a has node return; } if (defined $hash->{'#%#parent'}) { if(defined $hash->{'#%#origparent'}){ $res = munin_get_node_loc ($hash->{'#%#origparent'}); } else { $res = munin_get_node_loc ($hash->{'#%#parent'}); } push @$res, munin_get_orig_node_name ($hash) if defined $res; } return $res; } sub munin_get_parent { my $hash = shift; if (ref ($hash) ne "HASH") { # Not a has node return; } if (defined $hash->{'#%#parent'}) { return $hash->{'#%#parent'}; } else { return; } } sub munin_get_node { # From the given point in the hash itterate deeper into the # has along the path given by the array in $loc. # # If any part of the path in $loc is undefined we bail. my $hash = shift; my $loc = shift; foreach my $tmpvar (@$loc) { if (! exists $hash->{$tmpvar} ) { # Only complain on a blank key if is no key like that. Usually it # shouln't, so it avoids a needless regexp in this highly used # function ERROR "[ERROR] munin_get_node: Cannot work on hash node '$tmpvar'" if ($tmpvar !~ /\S/); return undef; } $hash = $hash->{$tmpvar}; } return $hash; } sub munin_set { my $hash = shift; my $var = shift; my $val = shift; return munin_set_var_loc ($hash, [$var], $val); } sub munin_set_var_loc { my $hash = shift; my $loc = shift; my $val = shift; my $iloc = 0; # XXX - Dirty breaking recursive function # --> Using goto is BAD, but enough for now START: # Find the next normal value (that doesn't begin with #%#) my $tmpvar = $loc->[$iloc++]; $tmpvar = $loc->[$iloc++] while (defined $tmpvar and substr($tmpvar,0,3) eq '#%#'); if (index($tmpvar, " ") != -1) { ERROR "[ERROR] munin_set_var_loc: Cannot work on hash node \"$tmpvar\""; return; } if (scalar @$loc > $iloc) { if (!defined $hash->{$tmpvar} or !defined $hash->{$tmpvar}->{"#%#name"}) { # Init the new node $hash->{$tmpvar}->{"#%#parent"} = $hash; weaken($hash->{$tmpvar}->{"#%#parent"}); $hash->{$tmpvar}->{"#%#name"} = $tmpvar; } # Recurse $hash = $hash->{$tmpvar}; goto START; } else { WARN "[WARNING] munin_set_var_loc: Setting unknown option '$tmpvar' at " . munin_get_keypath($hash) unless Munin::Common::Config::cl_is_keyword($tmpvar); # FIX - or not. $hash->{$tmpvar} = $val; return $hash; } } sub munin_get_node_partialpath { my $hash = shift; my $var = shift; my $ret = undef; return if !defined $hash or ref ($hash) ne "HASH"; my $root = munin_get_root_node ($hash); my $hashloc = munin_get_node_loc ($hash); my $varloc = undef; if ($var =~ /^\s*([^:]+):(\S+)\s*$/) { my ($leftstring, $rightstring) = ($1, $2); my @leftarr = split (/;/, $leftstring); my @rightarr = split (/\./, $rightstring); push @$varloc, @leftarr, @rightarr } elsif ($var =~ /^\s*([^;:.]+)\s*$/) { push @$varloc, $var; } elsif ($var =~ /^\s*([^.]+)\.([^:;]+)$/) { my ($leftstring, $rightstring) = ($1, $2); my @leftarr = split (/;/, $leftstring); my @rightarr = split (/\./, $rightstring); push @$varloc, @leftarr, @rightarr; } elsif ($var =~ /^\s*(\S+)\s*$/) { my @leftarr = split (/;/, $1); push @$varloc, @leftarr; } else { ERROR "[ERROR] munin_get_node_partialpath: Malformed variable path \"$var\"."; } # We've got both parts of the loc (varloc and hashloc) -- let's figure out # where they meet up. do { $ret = munin_get_node ($root, [@$hashloc, @$varloc]); } while (!defined $ret and pop @$hashloc); return $ret; } sub munin_set_var_path { my $hash = shift; my $var = shift; my $val = shift; my $result = undef; DEBUG "[DEBUG] munin_set_var_path: Setting var \"$var\" = \"$val\""; if ($var =~ /^\s*([^:]+):(\S+)\s*$/) { my ($leftstring, $rightstring) = ($1, $2); my @leftarr = split (/;/, $leftstring); my @rightarr = split (/\./, $rightstring); $result = munin_set_var_loc ($hash, [@leftarr, @rightarr], $val); } elsif ($var =~ /^\s*([^;:\.]+)\s*$/) { $result = munin_set_var_loc ($hash, [$1], $val); } elsif ($var =~ /^\s*([^:;]+)$/) { my @leftarr = split (/\./, $1); $result = munin_set_var_loc ($hash, [@leftarr], $val); } elsif ($var =~ /^\s*(.+)\.([^\.:;]+)$/) { my ($leftstring, $rightstring) = ($1, $2); my @leftarr = split (/;/, $leftstring); my @rightarr = split (/\./, $rightstring); $result = munin_set_var_loc ($hash, [@leftarr, @rightarr], $val); } elsif ($var =~ /^\s*(\S+)\s*$/) { my @leftarr = split (/;/, $1); $result = munin_set_var_loc ($hash, [@leftarr], $val); } else { ERROR "Error: munin_set_var_path: Malformatted variable path \"$var\"."; } if (!defined $result) { ERROR "Error: munin_set_var_path: Failed setting \"$var\" = \"$val\"."; } return $hash; } sub munin_get_root_node { my $hash = shift; return if ref ($hash) ne "HASH"; while (defined $hash->{'#%#parent'}) { $hash = $hash->{'#%#parent'}; } return $hash; } sub munin_writeconfig_loop { my ($hash,$fh,$pre) = @_; foreach my $key (keys %$hash) { next if substr($key,0,3) eq '#%#'; my $path = (defined $pre ? join(';', ($pre, $key)) : $key); if (ref ($hash->{$key}) eq "HASH") { munin_writeconfig_loop ($hash->{$key}, $fh, $path); } else { next if !defined $pre and $key eq "version"; # Handled separately next if !defined $hash->{$key} or !length $hash->{$key}; (my $outstring = $hash->{$key}) =~ s/([^\\])#/$1\\#/g; # Too much. Can be seen in the file itself. # DEBUG "[DEBUG] Writing: $path $outstring\n"; if ($outstring =~ /\\$/) { # Backslash as last char has special meaning. Avoid it. print $fh "$path $outstring\\\n"; } else { print $fh "$path $outstring\n"; } } } } sub munin_read_storable { my ($storable_filename, $default) = @_; if (-e $storable_filename) { my $storable = eval { Storable::retrieve($storable_filename); }; return $storable unless $@; # Didn't managed to read storable. # Removing it as it is already torched anyway. unlink($storable_filename); } # return default if no better option return $default; } sub munin_write_storable { my ($storable_filename, $data) = @_; DEBUG "[DEBUG] about to write '$storable_filename'"; # We don't need to write anything if there is nothing to write. return unless defined $data; my $storable_filename_tmp = $storable_filename . ".tmp.$$"; # Write datafile.storable, in network order to be architecture indep Storable::nstore($data, $storable_filename_tmp); # Atomic commit rename ($storable_filename_tmp, $storable_filename); } sub munin_writeconfig_storable { my ($datafilename,$data) = @_; DEBUG "[DEBUG] Writing state to $datafilename"; munin_write_storable($datafilename, $data); } sub munin_writeconfig { my ($datafilename,$data,$fh) = @_; DEBUG "[DEBUG] Writing state to $datafilename"; my $is_fh_already_managed = defined $fh; if (! $is_fh_already_managed) { $fh = gensym(); unless (open ($fh, ">", $datafilename)) { LOGCROAK "Fatal error: Could not open \"$datafilename\" for writing: $!"; } } # Write version print $fh "version $VERSION\n"; # Write datafile munin_writeconfig_loop ($data, $fh); if (! $is_fh_already_managed) { DEBUG "[DEBUG] Closing filehandle \"$datafilename\"...\n"; close ($fh); } } sub munin_dumpconfig_as_str { my ($config) = @_; local $Data::Dumper::Sortkeys = sub { [ sort grep {!/^#%#parent$/} keys %{$_[0]} ]; }; local $Data::Dumper::Indent = 1; return Dumper $config; } sub munin_dumpconfig { my ($config) = @_; my $indent = $Data::Dumper::Indent; my $sorter = $Data::Dumper::Sortkeys; $Data::Dumper::Sortkeys = sub { [ sort grep {!/^#%#parent$/} keys %{$_[0]} ]; }; $Data::Dumper::Indent = 1; print "##\n"; print "## NOTE: #%#parent is hidden to make the print readable!\n"; print "##\n"; print Dumper $config; $Data::Dumper::Sortkeys = $sorter; $Data::Dumper::Indent = $indent; } sub munin_configpart_revision { my $what = shift; if (defined $what and defined $config_parts->{$what}) { return $config_parts->{$what}{revision}; } my $rev = 0; foreach $what (keys %$config_parts) { $rev += $config_parts->{$what}{revision}; } return $rev; } sub munin_readconfig_part { my $what = shift; my $missingok = shift; if (! defined $config_parts->{$what}) { ERROR "[ERROR] munin_readconfig_part with unknown part name ($what)."; return undef; } # for now, we only really care about storable. # No reason to bother reading non-storable elements anyway. my $filename = "$config->{dbdir}/$what.storable"; my $part = {}; my $doupdate = 0; if (! -f $filename) { unless (defined $missingok and $missingok) { ERROR "[FATAL] munin_readconfig_part($what) - missing file"; exit(1); } # missing ok, return last value if we have one, copy config if not unless (defined $config_parts->{$what}{config}) { # well, not if we shouldn't include the config if ($config_parts->{$what}{include_base}) { $doupdate = 1; } } } else { my @stat = stat($filename); if ($config_parts->{$what}{timestamp} < $stat[9]) { # could use _raw if we wanted to read non-storable fallback $config_parts->{$what}{config} = undef; # Unalloc RAM for old config, since it will be overriden anyway. $part = munin_read_storable($filename); $config_parts->{$what}{timestamp} = $stat[9]; $doupdate = 1; } } if ($doupdate) { $part->{'#%#name'} = 'root'; $part->{'#%#parent'} = undef; $part = munin_overwrite($part, $config) if ($config_parts->{$what}{include_base}); $config_parts->{$what}{config} = $part; ++$config_parts->{$what}{revision}; } return $config_parts->{$what}{config}; } sub munin_readconfig_base { my $conffile = shift; $conffile ||= $configfile; $config = munin_readconfig_raw($conffile); if (defined $config->{'includedir'}) { my $dirname = $config->{'includedir'}; DEBUG "Includedir statement to include files in $dirname"; my $DIR; opendir($DIR, $dirname) or WARN "[Warning] Could not open includedir directory $dirname: $OS_ERROR\n"; my @files = grep { ! /^\.|~$/ } readdir($DIR); closedir($DIR); @files = map { $_ = $dirname.'/'.$_; } (sort @files); foreach my $f (@files) { INFO "Reading additional config from $f"; my $extra = munin_readconfig_raw ($f); # let the new values overwrite what we already have $config = munin_overwrite($config, $extra); } } # Some important defaults before we return... $config->{'dropdownlimit'} ||= $Munin::Common::Defaults::DROPDOWNLIMIT; $config->{'rundir'} ||= $Munin::Common::Defaults::MUNIN_STATEDIR; $config->{'dbdir'} ||= $Munin::Common::Defaults::MUNIN_DBDIR; $config->{'logdir'} ||= $Munin::Common::Defaults::MUNIN_LOGDIR; $config->{'tmpldir'} ||= "$Munin::Common::Defaults::MUNIN_CONFDIR/templates/"; $config->{'staticdir'} ||= "$Munin::Common::Defaults::MUNIN_CONFDIR/static/"; $config->{'htmldir'} ||= $Munin::Common::Defaults::MUNIN_HTMLDIR; $config->{'spooldir'} ||= $Munin::Common::Defaults::MUNIN_SPOOLDIR; $config->{'#%#parent'} = undef; $config->{'#%#name'} = "root"; return $config; } sub munin_get_html_filename { # Actually only used in M::M::HTMLOld my $hash = shift; my $loc = munin_get_node_loc ($hash); my $ret = munin_get ($hash, 'htmldir'); my $plugin = "index"; # Sanitise $ret =~ s/[^\w_\/"'\[\]\(\)+=-]\./_/g; $hash =~ s/[^\w_\/"'\[\]\(\)+=-]/_/g; @$loc = map { my $l = $_; $l =~ s/\//_/g; $l =~ s/^\./_/g; $l; } @$loc; if (defined $hash->{'graph_title'} and !munin_has_subservices ($hash)) { $plugin = pop @$loc or return; } if (@$loc) { # The rest is used as directory names... $ret .= "/" . join ('/', @$loc); } return "$ret/$plugin.html"; } sub munin_get_picture_filename { my $hash = shift; my $scale = shift; my $sum = shift; my $loc = munin_get_picture_loc ($hash); my $ret = munin_get ($hash, 'htmldir'); # Sanitise $ret =~ s/[^\w_\/"'\[\]\(\)+=-]\./_/g; $hash =~ s/[^\w_\/"'\[\]\(\)+=-]/_/g; $scale =~ s/[^\w_\/"'\[\]\(\)+=-]/_/g; @$loc = map { my $l = $_; $l =~ s/\//_/g; $l =~ s/^\./_/g; $l; } @$loc; my $plugin = pop @$loc or return; my $node = pop @$loc or return; if (@$loc) { # The rest is used as directory names... $ret .= "/" . join ('/', @$loc); } if (defined $sum and $sum) { return "$ret/$node/$plugin-$scale-sum.png"; } elsif (defined $scale) { return "$ret/$node/$plugin-$scale.png"; } else { return "$ret/$node/$plugin.png"; } } sub munin_path_to_loc { my $path = shift; my $result = undef; if ($path =~ /^\s*([^:]+):(\S+)\s*$/) { my ($leftstring, $rightstring) = ($1, $2); my @leftarr = split (/;/, $leftstring); my @rightarr = split (/\./, $rightstring); $result = [@leftarr, @rightarr]; } elsif ($path =~ /^\s*([^;:\.]+)\s*$/) { $result = [$1]; } elsif ($path =~ /^\s*(.+)\.([^\.:;]+)$/) { my ($leftstring, $rightstring) = ($1, $2); my @leftarr = split (/;/, $leftstring); my @rightarr = split (/\./, $rightstring); $result = [@leftarr, @rightarr]; } elsif ($path =~ /^\s*(\S+)\s*$/) { my @leftarr = split (/;/, $1); $result = [@leftarr]; } else { ERROR "[ERROR] munin_path_to_loc: Malformatted variable path \"$path\"."; } if (!defined $result) { ERROR "[ERROR] munin_path_to_loc: Failed converting \"$path\"."; } return $result; } sub munin_get_keypath { my $hash = shift; my $asfile = shift || ''; my @group = (); my $host = 0; my @service = (); my $i = $hash; while (ref ($i) eq "HASH") { # Not sure when a #%#name node can go missing my $name = $i->{'#%#name'} || '*BUG*'; goto gotoparent if $name eq '*BUG*'; last if $name eq 'root'; if ($host) { # Into group land now unshift(@group,$name); } else { # In service land, working towards host. # If i or my parent has a graph_title we're still working with services if (defined $i->{'#%#parent'}{graph_title} or defined $i->{graph_title}) { $name =~ s/-/_/g; # can't handle dashes in service or below unshift(@service,$name); } else { $host = 1; unshift(@group,$name); } } gotoparent: $i=$i->{'#%#parent'}; } if ($asfile) { return (shift @group).'/'.join('/',@group).'-'.join('-',@service); } else { return join(';',@group).':'.join('.',@service); } } sub munin_get_filename { my $hash = shift; my $loc = munin_get_keypath ($hash,1); my $ret = munin_get ($hash, "dbdir"); if (!defined $loc or !defined $ret) { return; } return ($ret . "/$loc-" . lc substr (munin_get($hash, "type", "GAUGE"), 0,1). ".rrd"); } sub munin_get_bool { my $hash = shift; my $field = shift; my $default = shift; my $ans = munin_get ($hash, $field, $default); return if not defined $ans; if ($ans =~ /^yes$/i or $ans =~ /^true$/i or $ans =~ /^on$/i or $ans =~ /^enable$/i or $ans =~ /^enabled$/i ) { return 1; } elsif ($ans =~ /^no$/i or $ans =~ /^false$/i or $ans =~ /^off$/i or $ans =~ /^disable$/i or $ans =~ /^disabled$/i ) { return 0; } elsif ($ans !~ /\D/) { return $ans; } else { return $default; } } sub munin_get_bool_val { my $field = shift; my $default = shift; if (!defined $field) { if (!defined $default) { return 0; } else { return $default; } } if ($field =~ /^yes$/i or $field =~ /^true$/i or $field =~ /^on$/i or $field =~ /^enable$/i or $field =~ /^enabled$/i ) { return 1; } elsif ($field =~ /^no$/i or $field =~ /^false$/i or $field =~ /^off$/i or $field =~ /^disable$/i or $field =~ /^disabled$/i ) { return 0; } elsif ($field !~ /\D/) { return $field; } else { return; } } sub munin_get { my ($hash, $field, $default) = @_; # Iterate to top if needed do { return $default if (ref ($hash) ne "HASH"); my $hash_field = $hash->{$field}; return $hash_field if (defined $hash_field && ref($hash_field) ne "HASH"); # Go up $hash = $hash->{'#%#parent'}; } while (defined $hash); return $default; } sub munin_copy_node_toloc { my $from = shift; my $to = shift; my $loc = shift; return unless defined $from and defined $to and defined $loc; if (ref ($from) eq "HASH") { foreach my $key (keys %$from) { next if substr($key,0,3) eq '#%#'; if (ref ($from->{$key}) eq "HASH") { munin_copy_node_toloc ($from->{$key}, $to, [@$loc, $key]); } else { munin_set_var_loc ($to, [@$loc, $key], $from->{$key}); } } } else { $to = $from; } return $to; } sub munin_copy_node { my $from = shift; my $to = shift; if (ref ($from) eq "HASH") { foreach my $key (keys %$from) { if (ref ($from->{$key}) eq "HASH") { # Easier to do with the other copy function munin_copy_node_toloc ($from->{$key}, $to, [$key]); } else { munin_set_var_loc ($to, [$key], $from->{$key}); } } } else { $to = $from; } return $to; } sub munin_node_status { my ($config, $limits, $domain, $node, $check_draw) = @_; my $state = "ok"; return unless defined $config->{domain}->{$domain}->{node}->{$node}; my $snode = $config->{domain}->{$domain}->{node}->{$node}; foreach my $service (keys %{$snode}) { my $fres = munin_service_status ($config, $limits, $domain, $node, $service, $check_draw); if (defined $fres) { if ($fres eq "critical") { $state = $fres; last; } elsif ($fres eq "warning") { $state = $fres; } } } return $state; } sub munin_category_status { my $hash = shift || return; my $limits = shift || return; my $category = shift || return; my $check_draw = 1; my $state = "ok"; return unless (defined $hash and ref ($hash) eq "HASH"); foreach my $service (@{munin_get_children ($hash)}) { next if (!defined $service or ref ($service) ne "HASH"); next if (!defined $service->{'graph_title'}); next if ($category ne munin_get ($service, "graph_category", "other")); next if ($check_draw and not munin_get_bool ($service, "graph", 1)); my $fres = munin_service_status ($service, $limits, $check_draw); if (defined $fres) { if ($fres eq "critical") { $state = $fres; last; } elsif ($fres eq "warning") { $state = $fres; } elsif ($fres eq "unknown" and $state eq "ok") { $state = $fres; } } } return $state; } sub munin_service_status { my ($config, $limits, $check_draw) = @_; my $state = "ok"; return unless defined $config; for my $fieldnode (@{munin_find_field ($config, "label")}) { my $field = munin_get_node_name ($fieldnode); my $fres = munin_field_status ($fieldnode, $limits, $check_draw); if (defined $fres) { if ($fres eq "critical") { $state = $fres; last; } elsif ($fres eq "warning") { $state = $fres; } } } return $state; } sub munin_field_status { my ($hash, $limits, $check_draw) = @_; my $state = "ok"; # Return undef if nagios is turned off, or the field doesn't have any limits if ((!defined munin_get ($hash, "warning", undef)) and (!defined munin_get ($hash, "critical"))) { return; } if (!defined $check_draw or !$check_draw or munin_draw_field ($hash)) { my $loc = munin_get_node_loc ($hash); my $node = munin_get_node ($limits, $loc); return $node->{"state"} || "ok"; } return $state; } sub munin_graph_column_headers { my ($config, $domain, $node, $serv) = @_; my $ret = 0; my @fields = (); foreach my $field (keys %{$config->{domain}->{$domain}->{node}->{$node}->{client}->{$serv}}) { if ($field =~ /^([^\.]+)\.negative$/ and munin_draw_field ($config->{domain}->{$domain}->{node}->{$node}, $serv, $1)) { return 1; } elsif ($field =~ /^([^\.]+)\.label$/ and munin_draw_field ($config->{domain}->{$domain}->{node}->{$node}, $serv, $1)) { push @fields, $1; } } return 1 if (munin_get_max_label_length ($config->{'domain'}->{$domain}->{'node'}->{$node}, $config, $domain, $node, $serv, \@fields) > 20); return $ret; } sub munin_get_max_label_length { my $service = shift; my $order = shift; my $result = 0; my $tot = munin_get ($service, "graph_total"); for my $field (@$order) { my $path = undef; (my $f = $field) =~ s/=.+//; next if (!munin_get_bool ($service->{$f}, "process", 1)); next if (munin_get_bool ($service->{$f}, "skipdraw", 0)); next if (!munin_get_bool ($service->{$f}, "graph", 1)); my $len = length (munin_get ($service->{$f}, "label") || $f); if ($result < $len) { $result = $len; } } if (defined $tot and length $tot > $result) { $result = length $tot; } return $result; } sub munin_get_field_order { my $hash = shift; my $result = []; return if !defined $hash or ref($hash) ne "HASH"; my $order = munin_get ($hash, "graph_order"); if (defined $hash->{graph_sources}) { foreach my $gs (split /\s+/, $hash->{'graph_sources'}) { push (@$result, "-".$gs); } } if (defined $order) { push (@$result, split /\s+/, $order); } for my $fieldnode (@{munin_find_field ($hash, "label")}) { my $fieldname = munin_get_node_name ($fieldnode); push @$result,$fieldname if !grep m[^\Q$fieldname\E(?:=|$)], @$result; } for my $fieldnode (@{munin_find_field ($hash, "stack")}) { my $fieldname = munin_get_node_name ($fieldnode); push @$result,$fieldname if !grep m[^\Q$fieldname\E(?:=|$)], @$result;; } # We have seen some occurances of redundance in the graph_order # due to plugin bugs and so on. This make process_service # generate rrd commands with multiple definitions of the same # data. SO: Make sure there is no redundance in the order. my %seen = (); my $nresult = []; for my $field (@$result) { next if exists($seen{$field}); push @$nresult, $field; $seen{$field}=1; } return $nresult; } sub munin_get_rrd_filename { my $field = shift; my $path = shift; my $result = undef; # Bail out on bad input data return if !defined $field or ref ($field) ne "HASH"; # If the field has a .filename setting, use it if ($result = munin_get ($field, "filename")) { return $result; } # Handle custom paths (used in .sum, .stack, graph_order, et al) if (defined $path and length $path) { my $sourcenode = munin_get_node_partialpath ($field, $path); $result = munin_get_filename ($sourcenode); for my $f (@COPY_FIELDS) { if (not exists $field->{$f} and exists $sourcenode->{$f}) { DEBUG "DEBUG: Copying $f...\n"; munin_set_var_loc ($field, [$f], $sourcenode->{$f}); } } } else { $result = munin_get_filename ($field); } return $result; } sub munin_mkdir_p { my ($dirname, $umask) = @_; eval { mkpath($dirname, 0, $umask); }; return if $EVAL_ERROR; return 1; } sub exit_if_run_by_super_user { if ($EFFECTIVE_USER_ID == 0) { print qq{This program will easily break if you run it as root as you are trying now. Please run it as user '$Munin::Common::Defaults::MUNIN_USER'. The correct 'su' command on many systems is 'su - munin --shell=/bin/bash' Aborting. }; exit 1; } } sub print_version_and_exit { print qq{munin version $Munin::Common::Defaults::MUNIN_VERSION. Copyright (C) 2002-2009 Jimmy Olsen, Audun Ytterdal, Tore Andersson, Kjell-Magne Øierud, Nicolai Langfeldt, Linpro AS, Redpill Linpro AS and others. This is free software released under the GNU General Public License. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, please refer to the file COPYING that is included with this software or refer to http://www.fsf.org/licensing/licenses/gpl.txt }; exit 0; } sub look_for_child { # wait for child process in blocking or non-blocking mode. my ($block) = @_; my $pid; if ($block) { $pid = waitpid(-1, 0); } else { $pid = waitpid(-1, WNOHANG); if ($pid == 0) { return 0; } } if ($pid < 0) { ERROR "[ERROR] Unexpectedly ran out of children: $!"; croak "[ERROR] Ran out of children: $!\n"; } if ($? != 0) { WARN "[WARNING] Child $pid failed: " . ($? << 8) . "(signal " . ($? & 0xff) . ")"; } return 1; } sub wait_for_remaining_children { my ($running) = @_; while ($running > 0) { look_for_child("block"); --$running; } return $running; } sub munin_has_subservices { my ($hash) = @_; return 0 unless defined $hash; # Only services (which again require graph_title) can have subservices return 0 unless defined $hash->{'graph_title'}; if (!defined $hash->{'#%#has_subservices'}) { $hash->{'#%#has_subservices'} = scalar (grep $_, map { ref($hash->{$_}) eq "HASH" and $_ ne '#%#parent' and defined $hash->{$_}->{'graph_title'} ? 1 : undef } keys %$hash); } return $hash->{'#%#has_subservices'}; } sub munin_get_host_path_from_string { # splits a host definition, as used in the config, into a group array and a host name my $key = shift; my (@groups) = split(';', $key); my $host = pop(@groups); if(scalar @groups > 0){ } else { my @groups = split('.', $key); if(scalar @groups > 1){ @groups = ($groups[0]); } } return (\@groups, $host); } 1; __END__ =head1 NAME Munin::Master::Utils - Exports a lot of utility functions. =head1 SYNOPSIS use Munin::Master::Utils; =head1 SUBROUTINES =over =item B Gets current status of a category. Parameters: - $hash: A ref to the hash node whose children to check - $limits: A ref to the root node of the limits tree - $category: The category to review - $check_draw: [optional] Ignore undrawn fields Returns: - Success: The status of the field - Failure: undef =item B Read configuration file, include dir files, and initialize important default values that are optional. Parameters: - $file: munin.conf filename. If omitted, default filename is used. Returns: - Success: The $config hash (also cached in module) =item B Copy hash node. Parameters: - $from: Hash node to copy - $to: Where to copy it to Returns: - Success: $to - Failure: undef =item B Copy hash node at. Parameters: - $from: Hash node to copy - $to: Where to copy it to - $loc: Path to node under $to Returns: - Success: $to - Failure: undef =item B =item B =item B Check whether a field will be visible in the graph or not. Parameters: - $hash: A ref to the hash node for the field Returns: - Success: Boolean; true if field will be graphed, false if not - Failure: undef =item B Gets current status of a field. Parameters: - $hash: A ref to the field hash node - $limits: A ref to the root node of the limits tree - $check_draw: [optional] Ignore undrawn fields Returns: - Success: The status of the field - Failure: undef =item B Search a hash to find hash nodes with $field defined. Parameters: - $hash: A hash ref to search - $field: The name of the field to search for, or a regex - $avoid: [optional] Stop traversing further down if this field is found Returns: - Success: A ref to an array of the hash nodes containing $field. - Failure: undef =item B Get variable. Parameters: - $hash: Ref to hash node - $field: Name of field to get - $default: [optional] Value to return if $field isn't set Returns: - Success: field contents - Failure: $default if defined, else undef =item B Get boolean variable. Parameters: - $hash: Ref to hash node - $field: Name of field to get - $default: [optional] Value to return if $field isn't set Returns: - Success: 1 or 0 (true or false) - Failure: $default if defined, else undef =item B =item B Get all child hash nodes. Parameters: - $hash: A hash ref to the parent node Returns: - Success: A ref to an array of the child nodes - Failure: undef =item B Get the field order in a graph. Parameters: - $hash: A hash ref to the service Returns: - Success: A ref to an array of the field names - Failure: undef =item B Get rrd filename for a field, without any bells or whistles. Used by munin-update to figure out which file to update. Parameters: - $hash: Ref to hash field Returns: - Success: Full path to rrd file - Failure: undef =item B Get the full path-name of an html file. Parameters: - $hash: A ref to the service hash node Returns: - Success: The file name with full path - Failure: undef =item B Get the length of the longest label in a graph. Parameters: - $hash: the graph in question - $order: A ref to an array of fields (graph_order) Returns: - Success: The length of the longest label in the graph - Failure: undef =item B Gets a node by loc. Parameters: - $hash: A ref to the hash to set the variable in - $loc: A ref to an array with the full path of the node Returns: - Success: The node ref found by $loc - Failure: undef =item B Get location array for hash node. Parameters: - $hash: A ref to the node Returns: - Success: Ref to an array with the full path of the variable - Failure: undef =item B Return the name of the hash node supplied. Parameters: - $hash: A ref to the hash node Returns: - Success: The name of the node =item B Gets a node from a partial path. Parameters: - $hash: A ref to the "current" location in the hash tree - $var: A path string with relative location (from the $hash). Returns: - Success: The node - Failure: undef =item B Get parent node of a hash. Parameters: - $hash: A ref to the node Returns: - Success: Ref to an parent - Failure: undef =item B Return the name of the parent of the hash node supplied Parameters: - $hash: A ref to the hash node Returns: - Success: The name of the parent node - Failure: If no parent node exists, "none" is returned. =item B Get the full path+name of a picture file. Parameters: - $hash: A ref to the service hash node - $scale: [optional] The scale (day, week, year, month) - $sum: [optional] Boolean value, whether it's a sum graph or not. Returns: - Success: The file name with full path - Failure: undef =item B Get location array for hash node for picture purposes. Differs from munin_get_node_loc in that it honors #%#origin metadata Parameters: - $hash: A ref to the node Returns: - Success: Ref to an array with the full path of the variable - Failure: undef =item B Get the root node of the hash tree. Parameters: - $hash: A hash node to traverse up from Returns: - Success: A ref to the root hash node - Failure: undef =item B Get the name of the rrd file corresponding to a field. Checks for lots of bells and whistles. This function is the correct one to use when figuring out where to fetch data from. Parameters: - $field: The hash object of the field - $path: [optional] The path to the field (as given in graph_order/sum/stack/et al) Returns: - Success: A string with the filename of the rrd file - Failure: undef =item B Copy a node to a separate node without "specials". Parameters: - $hash: The node to copy Returns: - Success: A ref to a new node without "#%#"-fields - Failure: undef =item B =item B =item B =item B munin_has_subservices($hash); Checks whether the service represented by $hash has subservices (multigraph), and returns the result. Parameters: - $hash: Hash reference pointing to a service Returns: - true: if the hash is indeed a service, and said service has got subservices - false: otherwise =item B munin_mkdir_p('/a/path/', oct('777')); Make a directory and recursively any nonexistent directory in the path to it. =item B =item B =item B Take contents of one config-namespace and replace/insert the instances needed. =item B =item B Returns a loc array from a path string. Parameters: - $path: A path string Returns: - Success: A ref to an array with the loc - Failure: undef =item B Read a partial configuration Parameters: - $what: name of the part that should be loaded (datafile or limits) Returns: - Success: a $config with the $specified part, but overwritten by $config =item B =item B =item B Gets current status of a service. Parameters: - $hash: A ref to the field hash node - $limits: A ref to the root node of the limits tree - $check_draw: [optional] Ignore undrawn fields Returns: - Success: The status of the field - Failure: undef =item B Sets a variable in a hash. Parameters: - $hash: A ref to the hash to set the variable in - $var: The name of the variable - $val: The value to set the variable to Returns: - Success: The $hash we were handed - Failure: undef =item B Sets a variable in a hash. Parameters: - $hash: A ref to the hash to set the variable in - $loc: A ref to an array with the full path of the variable - $val: The value to set the variable to Returns: - Success: The $hash we were handed - Failure: undef =item B Sets a variable in a hash. Parameters: - $hash: A ref to the hash to set the variable in - $var: A string with the full path of the variable - $val: The value to set the variable to Returns: - Success: The $hash we were handed - Failure: The $hash we were handed =item B =item B =back =head1 COPYING Copyright (C) 2003-2007 Jimmy Olsen, Audun Ytterdal This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. =cut # vim: syntax=perl ts=8 munin-2.0.75/master/lib/Munin/Master/Worker.pm000066400000000000000000000017321451614574100211370ustar00rootroot00000000000000package Munin::Master::Worker; use warnings; use strict; use Scalar::Util qw(refaddr); use overload q{""} => 'to_string'; sub new { my ($class, $identity) = @_; my $self = bless {}, $class; $identity = refaddr($self) unless defined $identity; $self->{ID} = $identity; return $self; } sub to_string { my ($self) = @_; return sprintf("%s<%s>", ref $self, $self->{ID}); } 1; __END__ =head1 NAME Munin::Master::Worker - Abstract base class for workers. =head1 SYNOPSIS See L. =head1 METHODS =over =item B Munin::Master::Worker->new($identity); Constructor. This is an abstract class, so this shouldn't be called directly. The optional $identity argument should be a unique identifier for the process. =item B print $worker->string; # Stringifies too print "Worker $worker just died"; Returns a unique string representation of the worker. =back =cut # vim: ts=4 : sw=4 : et munin-2.0.75/master/munin.conf.in000066400000000000000000000131071451614574100166420ustar00rootroot00000000000000# Example configuration file for Munin, generated by 'make build' # The next three variables specifies where the location of the RRD # databases, the HTML output, logs and the lock/pid files. They all # must be writable by the user running munin-cron. They are all # defaulted to the values you see here. # #dbdir @@DBDIR@@ #htmldir @@HTMLDIR@@ #logdir @@LOGDIR@@ #rundir @@STATEDIR@@ # Where to look for the HTML templates # #tmpldir @@CONFDIR@@/templates # Where to look for the static www files # #staticdir @@CONFDIR@@/static # temporary cgi files are here. note that it has to be writable by # the cgi user (usually nobody or httpd). # # cgitmpdir @@DBDIR@@/cgi-tmp # (Exactly one) directory to include all files from. includedir @@CONFDIR@@/munin-conf.d # You can choose the time reference for "DERIVE" like graphs, and show # "per minute", "per hour" values instead of the default "per second" # #graph_period second # Graphics files are generated either via cron or by a CGI process. # See http://munin-monitoring.org/wiki/CgiHowto2 for more # documentation. # Since 2.0, munin-graph has been rewritten to use the cgi code. # It is single threaded *by design* now. # #graph_strategy cron # munin-cgi-graph is invoked by the web server up to very many times at the # same time. This is not optimal since it results in high CPU and memory # consumption to the degree that the system can thrash. Again the default is # 6. Most likely the optimal number for max_cgi_graph_jobs is the same as # max_graph_jobs. # #munin_cgi_graph_jobs 6 # If the automatic CGI url is wrong for your system override it here: # #cgiurl_graph /munin-cgi/munin-cgi-graph # max_size_x and max_size_y are the max size of images in pixel. # Default is 4000. Do not make it too large otherwise RRD might use all # RAM to generate the images. # #max_size_x 4000 #max_size_y 4000 # HTML files are normally generated by munin-html, no matter if the # files are used or not. You can change this to on-demand generation # by following the instructions in http://munin-monitoring.org/wiki/CgiHowto2 # # Notes: # - moving to CGI for HTML means you cannot have graph generated by cron. # - cgi html has some bugs, mostly you still have to launch munin-html by hand # #html_strategy cron # munin-update runs in parallel. # # The default max number of processes is 16, and is probably ok for you. # # If set too high, it might hit some process/ram/filedesc limits. # If set too low, munin-update might take more than 5 min. # # If you want munin-update to not be parallel set it to 0. # #max_processes 16 # RRD updates are per default, performed directly on the rrd files. # To reduce IO and enable the use of the rrdcached, uncomment it and set it to # the location of the socket that rrdcached uses. # #rrdcached_socket /var/run/rrdcached.sock # Drop somejuser@fnord.comm and anotheruser@blibb.comm an email everytime # something changes (OK -> WARNING, CRITICAL -> OK, etc) #contact.someuser.command mail -s "Munin ${var:worst}: ${var:group}::${var:host}::${var:plugin}" somejuser@fnord.comm #contact.anotheruser.command mail -s "Munin ${var:worst}: ${var:group}::${var:host}::${var:plugin}" anotheruser@blibb.comm # # For those with Nagios, the following might come in handy. In addition, # the services must be defined in the Nagios server as well. #contact.nagios.command /usr/bin/send_nsca nagios.host.comm -c /etc/nsca.conf # The maximum time the munin-update may take to get updates from all nodes, # this might be interesting when using munin-async in case of large transactions and/or backlog. # When using the munin protocol to connect to a node, then this value shouldn't be set higher than 240. # In case it's higher, gaps might be seen in the graphs. timeout_fetch_all_nodes 240 # The maximum amount of time in seconds we may work on 1 node. # The value will be limited with timeout_fetch_all_nodes. timeout_fetch_one_node 180 # a simple host tree [@@HOSTNAME@@] address 127.0.0.1 use_node_name yes # # A more complex example of a host tree # ## First our "normal" host. # [fii.foo.com] # address foo # ## Then our other host... # [fay.foo.com] # address fay # ## IPv6 host. note that the ip adress has to be in brackets # [ip6.foo.com] # address [2001::1234:1] # ## Then we want totals... # [foo.com;Totals] #Force it into the "foo.com"-domain... # update no # Turn off data-fetching for this "host". # # # The graph "load1". We want to see the loads of both machines... # # "fii=fii.foo.com:load.load" means "label=machine:graph.field" # load1.graph_title Loads side by side # load1.graph_order fii=fii.foo.com:load.load fay=fay.foo.com:load.load # # # The graph "load2". Now we want them stacked on top of each other. # load2.graph_title Loads on top of each other # load2.dummy_field.stack fii=fii.foo.com:load.load fay=fay.foo.com:load.load # load2.dummy_field.draw AREA # We want area instead the default LINE2. # load2.dummy_field.label dummy # This is needed. Silly, really. # # # The graph "load3". Now we want them summarised into one field # load3.graph_title Loads summarised # load3.combined_loads.sum fii.foo.com:load.load fay.foo.com:load.load # load3.combined_loads.label Combined loads # Must be set, as this is # # not a dummy field! # ## ...and on a side note, I want them listen in another order (default is ## alphabetically) # # # Since [foo.com] would be interpreted as a host in the domain "com", we # # specify that this is a domain by adding a semicolon. # [foo.com;] # node_order Totals fii.foo.com fay.foo.com # munin-2.0.75/master/static/000077500000000000000000000000001451614574100155255ustar00rootroot00000000000000munin-2.0.75/master/static/definitions.html000066400000000000000000000047621451614574100207370ustar00rootroot00000000000000 Munin :: definitions
FieldTypeWarnCrit

Definition of terms used in Munin

Data types:

– Gauge A data source of type gauge shows the state of the data source at the exact moment that Munin is run (every 5 minutes). Any peaks in-between data gatherings, will not be in the graph.
– Counter A data source of type counter shows the state of the data source as an average between two plots (i.e. 5 minutes). Short peaks will therefore be hard to spot, but long peaks will be spottable, even though it occurs between plots.
– Derive For the purposes of viewing data, the derive type works the same way as a counter
– Absolute Absolute works much as a counter, with the exception that it is assumed that the counter value is set to 0 upon each read of it. It's not a good idea to run these plugins by hand in-between Munin runs, since Munin won't receive all the data it expects.

Munin
munin-2.0.75/master/static/dynazoom.html000066400000000000000000000164561451614574100202670ustar00rootroot00000000000000 Munin: dynamic graph zoom
Dynamically zoomable graph
Zooming is very easy, it's done in 3 clicks (regular clicks, no drag&drop):
  1. First click to define the start of zoom.
  2. Second click to define the ending of zoom.
  3. Third click inside the defined zone to zoom, outside to cancel the zone.
Plugin Name (domain/hostname/plugin_name) :
Start/Stop of the graph
(format:2005-08-15T15:52:01+0000)
(epoch) :
/
( / )
Limit low/high : /
Graph size (w/o legend) (pixels): /

munin-2.0.75/master/static/favicon.ico000066400000000000000000000037761451614574100176630ustar00rootroot00000000000000)<()xUUU @!! !!@!! !@B@! ! !  @!! !B@$! $@"! @! @B! @A@!  @! @@! @! !$!!!!!BA33113313333313333333333333331331313333333333133"313313111 !"""!B@!@"DDB""@   D D@Aomunin-2.0.75/master/static/formatdate.js000066400000000000000000000306501451614574100202150ustar00rootroot00000000000000// formatDate : // a PHP date like function, for formatting date strings // authored by Svend Tofte // the code is in the public domain // // see http://www.svendtofte.com/javascript/javascript-date-string-formatting/ // and http://www.php.net/date // // thanks to // - Daniel Berlin , // major overhaul and improvements // - Matt Bannon, // correcting some stupid bugs in my days-in-the-months list! // - levon ghazaryan. pointing out an error in z switch. // - Andy Pemberton. pointing out error in c switch // // input : format string // time : epoch time (seconds, and optional) // // if time is not passed, formatting is based on // the current "this" date object's set time. // // supported switches are // a, A, B, c, d, D, F, g, G, h, H, i, I (uppercase i), j, l (lowecase L), // L, m, M, n, N, O, P, r, s, S, t, U, w, W, y, Y, z, Z // // unsupported (as compared to date in PHP 5.1.3) // T, e, o Date.prototype.formatDate = function (input,time) { var daysLong = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; var daysShort = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; var monthsShort = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; var monthsLong = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; var switches = { // switches object a : function () { // Lowercase Ante meridiem and Post meridiem return date.getHours() > 11? "pm" : "am"; }, A : function () { // Uppercase Ante meridiem and Post meridiem return (this.a().toUpperCase ()); }, B : function (){ // Swatch internet time. code simply grabbed from ppk, // since I was feeling lazy: // http://www.xs4all.nl/~ppk/js/beat.html var off = (date.getTimezoneOffset() + 60)*60; var theSeconds = (date.getHours() * 3600) + (date.getMinutes() * 60) + date.getSeconds() + off; var beat = Math.floor(theSeconds/86.4); if (beat > 1000) beat -= 1000; if (beat < 0) beat += 1000; if ((String(beat)).length == 1) beat = "00"+beat; if ((String(beat)).length == 2) beat = "0"+beat; return beat; }, c : function () { // ISO 8601 date (e.g.: "2004-02-12T15:19:21+00:00"), as per // http://www.cl.cam.ac.uk/~mgk25/iso-time.html return (this.Y() + "-" + this.m() + "-" + this.d() + "T" + this.H() + ":" + this.i() + ":" + this.s() + this.P()); }, d : function () { // Day of the month, 2 digits with leading zeros var j = String(this.j()); return (j.length == 1 ? "0"+j : j); }, D : function () { // A textual representation of a day, three letters return daysShort[date.getDay()]; }, F : function () { // A full textual representation of a month return monthsLong[date.getMonth()]; }, g : function () { // 12-hour format of an hour without leading zeros, 1 through 12! if (date.getHours() == 0) { return 12; } else { return date.getHours()>12 ? date.getHours()-12 : date.getHours(); } }, G : function () { // 24-hour format of an hour without leading zeros return date.getHours(); }, h : function () { // 12-hour format of an hour with leading zeros var g = String(this.g()); return (g.length == 1 ? "0"+g : g); }, H : function () { // 24-hour format of an hour with leading zeros var G = String(this.G()); return (G.length == 1 ? "0"+G : G); }, i : function () { // Minutes with leading zeros var min = String (date.getMinutes ()); return (min.length == 1 ? "0" + min : min); }, I : function () { // Whether or not the date is in daylight saving time (DST) // note that this has no bearing in actual DST mechanics, // and is just a pure guess. buyer beware. var noDST = new Date ("January 1 " + this.Y() + " 00:00:00"); return (noDST.getTimezoneOffset () == date.getTimezoneOffset () ? 0 : 1); }, j : function () { // Day of the month without leading zeros return date.getDate(); }, l : function () { // A full textual representation of the day of the week return daysLong[date.getDay()]; }, L : function () { // leap year or not. 1 if leap year, 0 if not. // the logic should match iso's 8601 standard. // http://www.uic.edu/depts/accc/software/isodates/leapyear.html var Y = this.Y(); if ( (Y % 4 == 0 && Y % 100 != 0) || (Y % 4 == 0 && Y % 100 == 0 && Y % 400 == 0) ) { return 1; } else { return 0; } }, m : function () { // Numeric representation of a month, with leading zeros var n = String(this.n()); return (n.length == 1 ? "0"+n : n); }, M : function () { // A short textual representation of a month, three letters return monthsShort[date.getMonth()]; }, n : function () { // Numeric representation of a month, without leading zeros return date.getMonth()+1; }, N : function () { // ISO-8601 numeric representation of the day of the week var w = this.w(); return (w == 0 ? 7 : w); }, O : function () { // Difference to Greenwich time (GMT) in hours var os = Math.abs(date.getTimezoneOffset()); var h = String(Math.floor(os/60)); var m = String(os%60); h.length == 1? h = "0"+h:1; m.length == 1? m = "0"+m:1; return date.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m; }, P : function () { // Difference to GMT, with colon between hours and minutes var O = this.O(); return (O.substr(0, 3) + ":" + O.substr(3, 2)); }, r : function () { // RFC 822 formatted date var r; // result // Thu , 21 Dec 2000 r = this.D() + ", " + this.d() + " " + this.M() + " " + this.Y() + // 16 : 01 : 07 0200 " " + this.H() + ":" + this.i() + ":" + this.s() + " " + this.O(); return r; }, s : function () { // Seconds, with leading zeros var sec = String (date.getSeconds ()); return (sec.length == 1 ? "0" + sec : sec); }, S : function () { // English ordinal suffix for the day of the month, 2 characters switch (date.getDate ()) { case 1: return ("st"); case 2: return ("nd"); case 3: return ("rd"); case 21: return ("st"); case 22: return ("nd"); case 23: return ("rd"); case 31: return ("st"); default: return ("th"); } }, t : function () { // thanks to Matt Bannon for some much needed code-fixes here! var daysinmonths = [null,31,28,31,30,31,30,31,31,30,31,30,31]; if (this.L()==1 && this.n()==2) return 29; // ~leap day return daysinmonths[this.n()]; }, U : function () { // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) return Math.round(date.getTime()/1000); }, w : function () { // Numeric representation of the day of the week return date.getDay(); }, W : function () { // Weeknumber, as per ISO specification: // http://www.cl.cam.ac.uk/~mgk25/iso-time.html var DoW = this.N (); var DoY = this.z (); // If the day is 3 days before New Year's Eve and is Thursday or earlier, // it's week 1 of next year. var daysToNY = 364 + this.L () - DoY; if (daysToNY <= 2 && DoW <= (3 - daysToNY)) { return 1; } // If the day is within 3 days after New Year's Eve and is Friday or later, // it belongs to the old year. if (DoY <= 2 && DoW >= 5) { return new Date (this.Y () - 1, 11, 31).formatDate ("W"); } var nyDoW = new Date (this.Y (), 0, 1).getDay (); nyDoW = nyDoW != 0 ? nyDoW - 1 : 6; if (nyDoW <= 3) { // First day of the year is a Thursday or earlier return (1 + Math.floor ((DoY + nyDoW) / 7)); } else { // First day of the year is a Friday or later return (1 + Math.floor ((DoY - (7 - nyDoW)) / 7)); } }, y : function () { // A two-digit representation of a year var y = String(this.Y()); return y.substring(y.length-2,y.length); }, Y : function () { // A full numeric representation of a year, 4 digits // we first check, if getFullYear is supported. if it // is, we just use that. ppks code is nice, but wont // work with dates outside 1900-2038, or something like that if (date.getFullYear) { var newDate = new Date("January 1 2001 00:00:00 +0000"); var x = newDate .getFullYear(); if (x == 2001) { // i trust the method now return date.getFullYear(); } } // else, do this: // codes thanks to ppk: // http://www.xs4all.nl/~ppk/js/introdate.html var x = date.getYear(); var y = x % 100; y += (y < 38) ? 2000 : 1900; return y; }, z : function () { // The day of the year, zero indexed! 0 through 366 var s = "January 1 " + this.Y() + " 00:00:00 GMT" + this.O(); var t = new Date(s); var diff = date.getTime() - t.getTime(); return Math.floor(diff/1000/60/60/24); }, Z : function () { // Timezone offset in seconds return (date.getTimezoneOffset () * -60); } } function getSwitch(str) { if (switches[str] != undefined) { return switches[str](); } else { return str; } } var date; if (time) { var date = new Date (time); } else { var date = this; } var formatString = input.split(""); var i = 0; while (i < formatString.length) { if (formatString[i] == "%") { // this is our way of allowing users to escape stuff formatString.splice(i,1); } else { formatString[i] = getSwitch(formatString[i]); } i++; } return formatString.join(""); } // Some (not all) predefined format strings from PHP 5.1.1, which // offer standard date representations. // See: http://www.php.net/manual/en/ref.datetime.php#datetime.constants // // Atom "2005-08-15T15:52:01+00:00" Date.DATE_ATOM = "Y-m-d%TH:i:sP"; // ISO-8601 "2005-08-15T15:52:01+0000" Date.DATE_ISO8601 = "Y-m-d%TH:i:sO"; // RFC 2822 "Mon, 15 Aug 2005 15:52:01 +0000" Date.DATE_RFC2822 = "D, d M Y H:i:s O"; // W3C "2005-08-15 15:52:01+00:00" Date.DATE_W3C = "Y-m-d%TH:i:sP"; munin-2.0.75/master/static/logo-h.png000066400000000000000000000034021451614574100174170ustar00rootroot00000000000000PNG  IHDRs)!bgAMA abKGD pHYs B(xtIME  J&IDATh͚ylTE?H Š(AT)T!`2` *hЈƢb""$(hB "@b0Ev?oa:}{&M:߼|h@5h}=@؟o$ǣmʀ_9KV2uFd`˚޵5(`] ^6\}ȳ&TH8%wEdИAfp'p p"Pv([YrҺ'xNCInf,wj2ZCM.0[ H002A"n+>@6"6` 0V"`PtjFMJ07p |g̾&`thg*$0zIfDr;kNHM&w(`y~8P,S>q㣁,Kf>05d^Vf(h!S9Bf[!4t 7(30k9*2/<1[jI4( P*|( s{ H`,'y:QFX,o[`abˍY$jZ?ZrɐY#v u+pڪzbPkldvZ X9s\4)b"g:0MKZHK{O߹hs\9L9!ÊPiGV=}u~<{VL9O5?\Ʌk&ѿs̍Iΰ$yׄLq(r85ƒ04ƀM,ȱAN<V/yy'6xjqwvb'jO 'yUqsR *X %/$fSY'2"^*!)h(ܤWŭ $sMZ ;𵗼 F1 *@;dt,DQaڇŀ3^!3)Eװf҄v"߸) Iiv?[+ o B֡}ޑbFPC^2 "m  є+Ec (~`));gF̱oMCB#g)՚ZCi)M縆(E;#1˓ J9׮cO/ ɠJrIiN}40[rrYϬJCN=](vYs=2TBQb( /"iZ3IkkMH^9+c.czTB޵6X|ګ35V B(;59FF'zKQ_))0#yN3 l)ǹ%U-V(_'Oڹ7ehhs2 back to // See: http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4.1 qs = qs.replace(/\+/g, ' '); var args = qs.split('&'); // parse out name/value pairs separated via & // split out each name=value pair for (var i = 0; i < args.length; i++) { var pair = args[i].split('='); var name = decodeURIComponent(pair[0]); var value = (pair.length==2) ? decodeURIComponent(pair[1]) : name; this.params[name] = value; } } Querystring.prototype.get = function(key, default_) { var value = this.params[key]; return (value != null) ? value : default_; } Querystring.prototype.contains = function(key) { var value = this.params[key]; return (value != null); } munin-2.0.75/master/static/style-1.2.css000066400000000000000000000070431451614574100177010ustar00rootroot00000000000000body, h1, h2, h3, p, span, div { font-family: verdana, helvetica, arial, sans-serif; font-size: small; } body { background-color: #ffffff; color: #000000; } h3.nobottom { margin-top: 20px; margin-bottom: 0px; } div.logo { background-image: url(logo.png); background-repeat: no-repeat; width: 41px; height: 59px; } div.lighttext { background-color: #ffffff; color: #777777; font-style: italic; } .domain { font-size: medium; font-weight: bold; } .host { font-weight: bold; } .service { font-size: x-small; font-weight: bold; } .center { text-align: center; } .small { font-size: smaller; } .noborder { border-width: 0px; border-collapse: collapse; } table.largeinvisiblebox { border-width: thin; border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; border-color: #999; border-style: solid; padding: 0px 0px 0px 0px; margin: 0px 0px 0px 0px; white-space: nowrap; max-width: 90%; width: 80%; } table.invisiblebox { border-width: thin; border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; border-color: #999; border-style: solid; padding: 0px 0px 0px 0px; margin: 0px 0px 0px 0px; white-space: nowrap; width: 1%; } td.graphbox { border-width: thin; border-top: 1px; border-bottom: 1px; border-left: 1px; border-right: 1px; border-color: #999; border-style: solid; padding: 4px 4px 4px 4px; margin: 0px 0px 0px 0px; white-space: nowrap; width: 100%; } .box { border-width: thin; border-top: 1px; border-bottom: 1px; border-left: 1px; border-right: 1px; border-color: #999; border-style: solid; padding: 4px 4px 4px 4px; margin: 2px 2px 2px 2px; text-align: left; width: 80%; max-width: 90% } td.legendbox { border-width: thin; border-top: 1px; border-bottom: 1px; border-left: 1px; border-right: 1px; border-color: #999; border-style: solid; padding: 8px 4px 2px 2px; margin: 2px 2px 2px 2px; text-align: left; width: 100%; } td.wrap { white-space: normal; max-width: 1%; } td.linkbox { border-width: thin; border-top: 1px; border-bottom: 1px; border-left: 1px; border-right: 1px; border-color: #999; border-style: solid; padding: 4px 4px 4px 4px; margin: 2px 2px 2px 2px; width: 100%; white-space: normal; } .warntext { font-weight: bold; background-color: #cccc00; } .crittext { font-weight: bold; background-color: #ff6f22; } .ruler { background-color: #999999; border: 0px; height: 1px; } .ruler2 { background-color: #999999; border: 0px; height: 2px; } a.meek:link { color: #486aaf; font-size: small; } a.meek:visited { color: #af1a00; font-size: small; } a.meek:active { color: #486aaf; font-size: small; } a.meek:hover { color: #999999; font-size: small; } a.warn:link { color: #cc9900; } a.warn:visited { color: #cc9900; } a.warn:active { color: #cc9900; } a.warn:hover { color: #cc9900; } a.crit:link { color: #cc0000; } a.crit:visited { color: #cc0000; } a.crit:active { color: #cc0000; } a.crit:hover { color: #cc0000; } a:link { color: #486aaf; } a:visited { color: #486aaf; } a:active { color: #486aaf; } a:hover { color: #486aaf; } munin-2.0.75/master/static/style-new.css000066400000000000000000000077761451614574100202070ustar00rootroot00000000000000/* custom settings */ @import "custom.css"; /* Overall layout */ html, body { margin: 0; padding: 0; height: 100%; background: #fdfdfd; font-size: 90%; } select { border: 1px solid #d1d1d1; } body, h1, h2, h3, p, span, div { font-family: "vera sans","dejavu sans",helvetica,verdana,arial,sans-serif; color: #666666; } h3 { margin-top: 1em; margin-bottom: 0px; font-size: 110%; letter-spacing: 0.05em; margin-left: 0px; padding-left: 10px; padding-bottom: 0px; padding-top: 0px; text-decoration: underline; } a:link, a:visited, a:link:active, a:link:hover { color: #486aaf; text-decoration: none; } a:link:hover { text-decoration: underline;} .warntext { font-weight: bold; background-color: #cccc00; padding: 0px; margin: -1px; border: 1px solid #b9b900; } .crittext { font-weight: bold; background-color: #ff6f22; padding: 0px; margin: -1px; border: 1px solid #f16722; } a.unkn:link { color: #ffaa00; } a.unkn:visited { color: #ffaa00; } a.unkn:active { color: #ffaa00; } a.unkn:hover { color: #ffaa00; } a.warn:link { color: #ffd300; } a.warn:visited { color: #ffd300; } a.warn:active { color: #ffd300; } a.warn:hover { color: #ffd300; } a.crit:link { color: #ff0000; } a.crit:visited { color: #ff0000; } a.crit:active { color: #ff0000; } a.crit:hover { color: #ff0000; } img { border: 0px; } img.i { border: 1px solid #a0a0a0; } img.iwarn { border: 1px solid #ffd300; } img.icrit { border: 1px solid #ff0000; } img.iunkn { border: 1px solid #ffaa00; } /* Header */ #header { width: 90%; margin: 0 auto; background-color: #fbfbfb; border: 1px solid #cfd6f8; border-bottom-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-top: 0; padding: 3px; height: 40px; } #header p { margin: 0; } h1, h2 { margin: 0; padding: 0; font-size: 110%; font-weight: bold; margin-left: auto; margin-right: auto; } #nav h2 { margin-top: 0.4em; } h1 .logo { display: block; float: left; background-image: url(logo-h.png); background-repeat: no-repeat; width: 115px; height: 30px; padding-right: 10px; margin-top: 7px; padding-bottom: 6px; } /* Main */ #main { width: 90%; margin: 0 auto; } .contentpusher { clear: both; } /* Navigation */ #nav { float: left; width: 200px; } #nav ul li { list-style-type: none; margin: 0 0 0 3px; padding: 2px; } #nav ul { margin: 0.2em 0 0.8em 0; padding: 0; } .navigationjump { display: none; } /* Content */ #content { border-left: 1px solid #cfd6f8; margin-left: 220px; padding: 1em; } #content li { margin-left: -20px; } #content > ul:first-of-type { margin-left: 2em; } .comparison #content { margin-left: 0; } #content h2 { display: none; } ul.groupview li.last { margin-bottom: 1em; } /* Content: comparison */ td { vertical-align: top; } .node { width: 500px; } /* Content: service view */ #legend { margin: 2px; border-collapse: collapse; } #legend tr, #legend td{ padding: 3px 2px 3px 2px; margin: 1px; } #legend th{ border-bottom: 1px solid #999999; } #legend .oddrow{ background-color: #f8f8f8; } #legend .oddrow td{ border-bottom: 1px solid #d8d8d8; } #legend .evenrow{ background-color: #fdfdfd; } #legend .evenrow td{ border-bottom: 1px solid #d8d8d8; } #legend .lastrow td{ border-bottom: 0px solid transparent !important; } #legend .field, #legend .internal { width: 20em; text-align: left; } #legend .type { width: 7em; text-align: center; } #legend .warn, #legend .crit { width: 5em; text-align: center; } /* Footer */ #footer { width: 90%; margin: 0 auto; background-color: #fbfbfb; border: 1px solid #cfd6f8; padding: 5px; clear: both; } #footer p.navigation { margin: 0; } #footer p.tagline { margin: 0; font-size: 80%; text-align: left; } .categoryview .node { display: inline-block; } munin-2.0.75/master/static/style.css000066400000000000000000000123471451614574100174060ustar00rootroot00000000000000html { } body, h1, h2, h3, p, span, div { font-family: "vera sans", "dejavu sans", helvetica, verdana, arial, sans-serif; color: #333333; } h1{ margin: 0.5em; } body { background-color: #fdfdfd; width: 99%; margin: 0em auto 1em auto; padding: 0.0em 0.1em 1em 0.1em; font-size: 90%; } select { border: 1px solid #d1d1d1; } h2 { margin: 0em; padding: 0px; font-size: 110%; font-weight: bold; margin-left: auto; margin-right: auto; } h3.nobottom { margin-top: 0px; margin-bottom: 0px; font-size: 120%; letter-spacing: 0.05em; margin-left: 0px; padding-left: 10px; padding-bottom: 0px; padding-top: 0px; } div.logo { background-image: url(logo-h.png); background-repeat: no-repeat; width: 115px; height: 30px; padding-right: 10px; margin-top: 7px; padding-bottom: 6px; } div.lighttext { background-color: #ffffff; color: #777777; font-style: italic; } .domain { font-size: medium; font-weight: bold; } .host { font-weight: bold; } .service { font-size: small; font-weight: bold; } .center { text-align: center; } .small { font-size: smaller; } .noborder { border-width: 0px; border-collapse: collapse; } table.largeinvisiblebox { border-width: thin; border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; border-color: #999; border-style: solid; padding: 0px 0px 0px 0px; margin: 0px auto 0px auto; white-space: nowrap; max-width: 97%; width: 90%; min-width: 50%; } #header-table{ width: 90%; margin: 0 auto 0em auto; background-color: #fbfbfb; border-bottom: 1px solid #cfd6f8; border-left: 1px solid #cfd6f8; border-right: 1px solid #cfd6f8; overflow: auto; } #header-table td{ padding-bottom: 0px; margin: 0em auto 0em auto; } #header-table tr{ margin: 0em auto 0em auto; padding: 0px; } #header-table td.overview{ margin: 0em auto 0em auto; text-align: left; } #header-table td.overview h1{ margin-top: 0em; margin-bottom: 0em; margin-left: 0.1em; padding-top: 1px; } #header-table td.logo{ width: 115px; height: 48px; margin: 0em auto 0em auto; } table.invisiblebox { border: 0px solid #999999; width: 90%; padding: 0px 0px 0px 0px; margin-top: 10px; margin-left: auto; margin-bottom: 0px; margin-right: auto; white-space: nowrap; } table.invisiblebox tr{ padding-bottom: 15px; } td.graphbox { padding: 5px; white-space: nowrap; } td.graphbox > table { border-left: 1px dashed #aaaaaa; padding-left: 10px; margin-bottom: 1em; white-space: nowrap; } .box { padding: 4px; margin: 10px auto 0px auto; text-align: left; } #legend { margin: 2px; } #legend tr, #legend td{ padding: 3px 2px 3px 2px; margin: 1px; } #legend th{ border-bottom: 1px solid #999999; } #legend .oddrow{ background-color: #f8f8f8; } #legend .oddrow td{ border-bottom: 1px solid #d8d8d8; } #legend .evenrow{ background-color: #fdfdfd; } #legend .evenrow td{ border-bottom: 1px solid #d8d8d8; } #legend .lastrow td{ border-bottom: 0px solid transparent !important; } td.legendbox { border: 1px solid #999999; padding: 8px 4px 2px 2px; margin: 8px 2px 2px 2px; text-align: left; width: 90%; } td.wrap { white-space: normal; width: 20%; } td.linkbox { padding: 4px 4px 4px 4px; margin: 10px auto 0px auto; max-width: 97%; width: 90%; min-width: 50%; white-space: normal; } #generated-table { width: 90%; margin: 1em auto -0.5em auto; padding-right: 0.5em; background-color: #fbfbfb; border: 1px solid #bbc3ea; } #generated-table tbody td { text-align: right; margin: 0px; padding: 0px; } .warntext { font-weight: bold; background-color: #cccc00; padding: 0px; margin: -1px; border: 1px solid #b9b900; } .crittext { font-weight: bold; background-color: #ff6f22; padding: 0px; margin: -1px; border: 1px solid #f16722; } .ruler { background-color: #999999; border: 0px; height: 1px; } .ruler2 { background-color: #999999; border: 0px; height: 2px; } a:link,a:visited,a:active { text-decoration: none; } a:hover { text-decoration: underline; } a.meek:link { color: #486aaf; font-size: small; } a.meek:visited { color: #af1a00; font-size: small; } a.meek:active { color: #486aaf; font-size: small; } a.meek:hover { color: #999999; font-size: small; } li li { margin-left: -1.2em; } ul li { margin-left: -1.2em; } a { border-width: 0px; } .heading { font-weight: bold; } a.unkn:link { color: #ffaa00; } a.unkn:visited { color: #ffaa00; } a.unkn:active { color: #ffaa00; } a.unkn:hover { color: #ffaa00; } a.warn:link { color: #ffd300; } a.warn:visited { color: #ffd300; } a.warn:active { color: #ffd300; } a.warn:hover { color: #ffd300; } a.crit:link { color: #ff0000; } a.crit:visited { color: #ff0000; } a.crit:active { color: #ff0000; } a.crit:hover { color: #ff0000; } a:link { color: #486aaf; } a:visited { color: #486aaf; } a:active { color: #486aaf; } a:hover { color: #486aaf; } img { border-width: 2px; border-color: transparent; } img.warn { border-width: 2px; border-color: #ffd300; } img.crit { border-width: 2px; border-color: #ff0000; } img.unkn { border-width: 2px; border-color: #ffaa00; } munin-2.0.75/master/static/zoom.js000066400000000000000000000112301451614574100170440ustar00rootroot00000000000000// Insert values in the form var qs = new Querystring(); var form = document.getElementById("myNewForm"); var image = document.getElementById("image"); var divOverlay = document.getElementById("overlayDiv"); form.plugin_name.value = qs.get("plugin_name", "localdomain/localhost.localdomain/if_eth0"); form.start_epoch.value = qs.get("start_epoch", "1236561663"); form.stop_epoch.value = qs.get("stop_epoch", "1237561663"); form.lower_limit.value = qs.get("lower_limit", ""); form.upper_limit.value = qs.get("upper_limit", ""); form.size_x.value = qs.get("size_x", ""); form.size_y.value = qs.get("size_y", ""); form.btnMaj.onclick = majDates; form.btnZoomOut.onclick = zoomOut; // Refresh the image with the selected params var scale = refreshImg(); function refreshImg() { image.src = qs.get("cgiurl_graph", "/munin-cgi/munin-cgi-graph"); + form.plugin_name.value + "-pinpoint=" + parseInt(form.start_epoch.value) + "," + parseInt(form.stop_epoch.value) + ".png" + "?" + "&lower_limit=" + form.lower_limit.value + "&upper_limit=" + form.upper_limit.value + "&size_x=" + form.size_x.value + "&size_y=" + form.size_y.value ; return ((+form.stop_epoch.value) - (+form.start_epoch.value)) / (+form.size_x.value); } var start_epoch = (+form.start_epoch.value); var stop_epoch = (+form.stop_epoch.value); var initial_left; var initial_top; updateStartStop(); function updateStartStop() { form.start_iso8601.value = new Date(form.start_epoch.value * 1000).formatDate(Date.DATE_ISO8601); form.stop_iso8601.value = new Date(form.stop_epoch.value * 1000).formatDate(Date.DATE_ISO8601); } function divMouseMove(mouseMouveEvent) { var delta_x; var size_x; // Handling the borders (X1>X2 ou X1 'set TEST_POD to enable this test' unless $ENV{TEST_POD}; eval 'use Test::Perl::Critic'; plan ( skip_all => "Test::Perl::Critic required for testing coding standard" ) if $@; plan ( skip_all => "Only tested in dev environment (\$ENV{MUNIN_ENVIRONMENT} eq 'dev')" ) if $ENV{MUNIN_ENVIRONMENT} && $ENV{MUNIN_ENVIRONMENT} ne 'dev'; all_critic_ok(); munin-2.0.75/master/t/munin_master_config.t000066400000000000000000000145351451614574100207240ustar00rootroot00000000000000# -*- cperl -*- # vim: ts=4 : sw=4 : et use warnings; use strict; use Test::More tests => 5; use_ok('Munin::Master::Config'); use Munin::Master::Config; use Munin::Common::Defaults; my $config = Munin::Master::Config->instance(); my $userconfig = $config->{config}; $userconfig->parse_config(\*DATA); # Build the correct answer by hand. my $fasit = { 'root_instance' => 1, oldconfig => { config_file => "$Munin::Common::Defaults::MUNIN_DBDIR/datafile" }, config => { config_file => "$Munin::Common::Defaults::MUNIN_CONFDIR/munin.conf", dbdir => '/opt/munin/sandbox/var/opt/munin', debug => 0, fork => 1, graph_data_size => 'normal', graph_strategy => 'cron', groups => { marvin => { hosts => { marvin => { use_node_name => 1, address => '127.0.0.1', port => '4948', 'load1.graph_title' => 'Loads side by side', 'load1.graph_order' => 'fii=fii.foo.com:load.load fay=fay.foo.com:load.load', host_name => 'marvin', update => 1, }, }, group => undef, group_name => 'marvin', }, }, htmldir => '/opt/munin/sandbox/www', local_address => 0, logdir => '/opt/munin/sandbox/var/log/munin', max_processes => 16, rundir => '/opt/munin/sandbox/var/run/munin', ssh_command => "ssh", ssh_options => "-o ChallengeResponseAuthentication=no -o StrictHostKeyChecking=no", timeout => 180, timeout_fetch_all_nodes => 240, timeout_fetch_one_node => 180, tls => 'disabled', tls_ca_certificate => '/opt/munin/common/t/tls/CA/ca_cert.pem', tls_certificate => '/opt/munin/common/t/tls/master_cert.pem', tls_private_key => '/opt/munin/common/t/tls/master_key.pem', tls_verify_certificate => 1, tls_verify_depth => '5', tmpldir => '/opt/munin/sandbox/etc/opt/munin/templates', staticdir => '/opt/munin/sandbox/etc/opt/munin/static', rrdcached_socket => '/var/run/rrdcached.sock', cgitmpdir => '/opt/munin/sandbox/var/cache/munin-cgi', }, }; $fasit->{config}{groups}{marvin}{hosts}{marvin}{group} = $fasit->{config}{groups}{marvin}; is_deeply($config, $fasit, 'Config hash contents'); ### _final_char_is ok( Munin::Master::Config::_final_char_is('h', 'blah'), 'it was the last character'); ok(! Munin::Master::Config::_final_char_is('a', 'blah'), 'it was not the last character'); ok(! Munin::Master::Config::_final_char_is('z', 'blah'), 'it not in the string at all'); __DATA__ # Example configuration file for Munin, generated by 'make build' # The next three variables specifies where the location of the RRD # databases, the HTML output, and the logs, severally. They all # must be writable by the user running munin-cron. dbdir /opt/munin/sandbox/var/opt/munin htmldir /opt/munin/sandbox/www logdir /opt/munin/sandbox/var/log/munin rundir /opt/munin/sandbox/var/run/munin # Where to look for the HTML templates tmpldir /opt/munin/sandbox/etc/opt/munin/templates staticdir /opt/munin/sandbox/etc/opt/munin/static # Make graphs show values per minute instead of per second #graph_period minute # Graphics files are normally generated on-demand by a CGI process. # See http://munin-monitoring.org/wiki/CgiHowto2 for more # documentation. # #graph_strategy cgi cgitmpdir /opt/munin/sandbox/var/cache/munin-cgi # Drop somejuser@fnord.comm and anotheruser@blibb.comm an email everytime # something changes (OK -> WARNING, CRITICAL -> OK, etc) #contact.someuser.command mail -s "Munin notification" somejuser@fnord.comm #contact.anotheruser.command mail -s "Munin notification" anotheruser@blibb.comm # # For those with Nagios, the following might come in handy. In addition, # the services must be defined in the Nagios server as well. #contact.nagios.command /usr/bin/send_nsca nagios.host.comm -c /etc/nsca.conf tls disabled tls_private_key /opt/munin/common/t/tls/master_key.pem tls_certificate /opt/munin/common/t/tls/master_cert.pem tls_ca_certificate /opt/munin/common/t/tls/CA/ca_cert.pem tls_verify_certificate yes tls_verify_depth 5 rrdcached_socket /var/run/rrdcached.sock # a simple host tree [marvin] address 127.0.0.1 port 4948 use_node_name yes load1.graph_title Loads side by side load1.graph_order fii=fii.foo.com:load.load fay=fay.foo.com:load.load # # A more complex example of a host tree # ## First our "normal" host. # [fii.foo.com] # address foo # ## Then our other host... # [fay.foo.com] # address fay # ## Then we want totals... # [foo.com;Totals] #Force it into the "foo.com"-domain... # update no # Turn off data-fetching for this "host". # # # The graph "load1". We want to see the loads of both machines... # # "fii=fii.foo.com:load.load" means "label=machine:graph.field" # load1.graph_title Loads side by side # load1.graph_order fii=fii.foo.com:load.load fay=fay.foo.com:load.load # # # The graph "load2". Now we want them stacked on top of each other. # load2.graph_title Loads on top of each other # load2.dummy_field.stack fii=fii.foo.com:load.load fay=fay.foo.com:load.load # load2.dummy_field.draw AREA # We want area instead the default LINE2. # load2.dummy_field.label dummy # This is needed. Silly, really. # # # The graph "load3". Now we want them summarised into one field # load3.graph_title Loads summarised # load3.combined_loads.sum fii.foo.com:load.load fay.foo.com:load.load # load3.combined_loads.label Combined loads # Must be set, as this is # # not a dummy field! # ## ...and on a side note, I want them listen in another order (default is ## alphabetically) # # # Since [foo.com] would be interpreted as a host in the domain "com", we # # specify that this is a domain by adding a semicolon. # [foo.com;] # node_order Totals fii.foo.com fay.foo.com # munin-2.0.75/master/t/munin_master_config_concat.t000077500000000000000000000153121451614574100222500ustar00rootroot00000000000000# -*- cperl -*- use warnings; use strict; use Test::More tests => 45; # use Test::More qw(no_plan); use_ok('Munin::Master::Config'); # Get a empty class variable so we can test some class functions alone my $c = bless { }, 'Munin::Master::Config'; # Test class my $tc; $tc = "Simple basics "; is ( $c->_concat_config_line('','htmldir','/foo/bar'), 'htmldir', $tc.'1'); is ( $c->_concat_config_line('grouphere;lookfar.langfeldt.net:if_eth0','graph_args','--l 0 -base'), 'grouphere;lookfar.langfeldt.net:if_eth0.graph_args', $tc.'2'); is ( $c->_concat_config_line('grouphere;lookfar.langfeldt.net:if_eth0','up.label','Spazz'), 'grouphere;lookfar.langfeldt.net:if_eth0.up.label', $tc.'3'); $tc = "Settings at all levels "; is ( $c->_concat_config_line('','address','127.0.0.1'), 'address', $tc.'1'); is ( $c->_concat_config_line('Group;','address','127.0.0.1'), 'Group;address', $tc.'2'); is ( $c->_concat_config_line('Group;Group2;','address','127.0.0.1'), 'Group;Group2;address', $tc.'3'); is ( $c->_concat_config_line('Group;','Group2;address','127.0.0.1'), 'Group;Group2;address', $tc.'4'); is ( $c->_concat_config_line('Group;Host.example.com','address','127.0.0.1'), 'Group;Host.example.com:address', $tc.'5'); is ( $c->_concat_config_line('Group;','Host.example.com:address','127.0.0.1'), 'Group;Host.example.com:address', $tc.'6'); is ( $c->_concat_config_line('Group;Group2;','Host.example.com:address','127.0.0.1'), 'Group;Group2;Host.example.com:address', $tc.'7'); is ( $c->_concat_config_line('Group;','Group2;Host.example.com:address','127.0.0.1'), 'Group;Group2;Host.example.com:address', $tc.'8'); is ( $c->_concat_config_line('Group;Group2;','graph_title','Test9'), 'Group;Group2;graph_title', $tc.'9'); is ( $c->_concat_config_line('Group;Host.example.com','service.graph_title','Foo!'), 'Group;Host.example.com:service.graph_title', $tc.'10'); is ( $c->_concat_config_line('Group;Host.example.com:service','graph_title','Foo!'), 'Group;Host.example.com:service.graph_title', $tc.'11'); is ( $c->_concat_config_line('Group;Host.example.com','service.service2.graph_title','Foo!'), 'Group;Host.example.com:service.service2.graph_title', $tc.'12'); is ( $c->_concat_config_line('Group;Host.example.com:service','service2.graph_title','Foo!'), 'Group;Host.example.com:service.service2.graph_title', $tc.'13'); is ( $c->_concat_config_line('Group;Host.example.com:service.service2','graph_title','Foo!'), 'Group;Host.example.com:service.service2.graph_title', $tc.'14'); $tc = "Implicit group names "; is ( $c->_concat_config_line('foo.example.com','port','4949'), 'example.com;foo.example.com:port', $tc.'1'); is ( $c->_concat_config_line('foo.example.com','address','4949'), 'example.com;foo.example.com:address', $tc.'2'); is ( $c->_concat_config_line('foo.example.com','if_eth0.up.label','4949'), 'example.com;foo.example.com:if_eth0.up.label', $tc.'3'); is ( $c->_concat_config_line('localhost','port','4949'), 'localhost;localhost:port', $tc.'4'); $tc = "Prefix/keyword combos "; is ( $c->_concat_config_line('Group;','port','4949'), 'Group;port', $tc.'1'); is ( $c->_concat_config_line('Group;','Host.example.com:port','4949'), 'Group;Host.example.com:port', $tc.'2'); is ( $c->_concat_config_line('Group;Host.example.com','port','4949'), 'Group;Host.example.com:port', $tc.'3' ); is ( $c->_concat_config_line('Group;Host.example.com','service.port','4949'), 'Group;Host.example.com:service.port', $tc.'4' ); is ( $c->_concat_config_line('Group;Host.example.com:service','port','4949'), 'Group;Host.example.com:service.port', $tc.'5' ); $tc = "Prefix/keyword combos, nested groups "; is ( $c->_concat_config_line('Group;','Group2;Host.example.com:port','4949'), 'Group;Group2;Host.example.com:port', $tc.'1'); is ( $c->_concat_config_line('Group;Group2;','Host.example.com:port','4949'), 'Group;Group2;Host.example.com:port', $tc.'2'); is ( $c->_concat_config_line('Group;Group2;Host.example.com','port','4949'), 'Group;Group2;Host.example.com:port', $tc.'3' ); is ( $c->_concat_config_line('Group;Group2;Host.example.com','service.port','4949'), 'Group;Group2;Host.example.com:service.port', $tc.'4' ); is ( $c->_concat_config_line('Group;Group2;Host.example.com:service','port','4949'), 'Group;Group2;Host.example.com:service.port', $tc.'5' ); $tc = "Prefix/keyword combos, nested services "; is ( $c->_concat_config_line('Group;','Host.example.com:service.service2.port','4949'), 'Group;Host.example.com:service.service2.port', $tc.'1' ); is ( $c->_concat_config_line('Group;Host.example.com','service.service2.port','4949'), 'Group;Host.example.com:service.service2.port', $tc.'2' ); is ( $c->_concat_config_line('Group;Host.example.com:service','service2.port','4949'), 'Group;Host.example.com:service.service2.port', $tc.'3' ); is ( $c->_concat_config_line('Group;Host.example.com:service.service2','port','4949'), 'Group;Host.example.com:service.service2.port', $tc.'4' ); $tc = "Prefix/keyword combos, nested groups and services "; is($c->_concat_config_line('Group;','Group2;Host.example.com:service.service2.port','4949'), 'Group;Group2;Host.example.com:service.service2.port', $tc.'1' ); is($c->_concat_config_line('Group;Group2;','Host.example.com:service.service2.port','4949'), 'Group;Group2;Host.example.com:service.service2.port', $tc.'2' ); is($c->_concat_config_line('Group;Group2;Host.example.com','service.service2.port','4949'), 'Group;Group2;Host.example.com:service.service2.port', $tc.'3' ); is($c->_concat_config_line('Group;Group2;Host.example.com:service','service2.port','4949'), 'Group;Group2;Host.example.com:service.service2.port', $tc.'4' ); is( $c->_concat_config_line('Group;Group2;Host.example.com:service.service2','port','4949'), 'Group;Group2;Host.example.com:service.service2.port', $tc.'5' ); $tc = "Prefix/keyword combos, various service.field.keyword combinations "; is ( $c->_concat_config_line('Group;','Host.example.com:service.field.max','4949'), 'Group;Host.example.com:service.field.max', $tc.'1' ); is ( $c->_concat_config_line('Group;Host.example.com','service.field.max','4949'), 'Group;Host.example.com:service.field.max', $tc.'2' ); is ( $c->_concat_config_line('Group;Host.example.com:service','field.max','4949'), 'Group;Host.example.com:service.field.max', $tc.'3' ); is ( $c->_concat_config_line('Group;Host.example.com:service.field','max','4949'), 'Group;Host.example.com:service.field.max', $tc.'4' ); # Alright, can anyone think of any more tests? # Probably even more combinations of nesting in both ends to see if that # somehow trips up the code. munin-2.0.75/master/t/munin_master_config_split.t000077500000000000000000000103251451614574100221330ustar00rootroot00000000000000# -*- cperl -*- use warnings; use strict; # This test-set does the reverse of munin_master_config_concat, but this is simpler # because all variations of prefix/key combined in the concat function works out to # one canonical format, which breaks down in a predictable manner. use Test::More tests => 24; # use Test::More qw(no_plan); use_ok('Munin::Master::Config'); # Get a empty class variable so we can test some class functions alone my $c = bless { }, 'Munin::Master::Config'; # Test class my $tc; $tc = "Simple basics "; use Data::Dumper; # print Dumper $c->_split_config_line('htmldir'); is_deeply ( [$c->_split_config_line('htmldir')], [[],'','htmldir'],$tc.'1'); is_deeply ( [ $c->_split_config_line('grouphere;lookfar.langfeldt.net:if_eth0.graph_args') ], [['grouphere'],'lookfar.langfeldt.net','if_eth0.graph_args'], $tc.'2'); is_deeply ( [ $c->_split_config_line('grouphere;lookfar.langfeldt.net:if_eth0.up.label') ], [['grouphere'],'lookfar.langfeldt.net','if_eth0.up.label'],$tc.'3'); $tc = "Settings at all levels "; is_deeply ( [ $c->_split_config_line('address') ], [[],'','address'], $tc.'1'); is_deeply ( [ $c->_split_config_line('Group;address') ], [['Group'],'','address'], $tc.'2'); is_deeply ( [ $c->_split_config_line('Group;Group2;address') ], [ ['Group','Group2'],'','address'], $tc.'3'); is_deeply ( [ $c->_split_config_line('Group;Host.example.com:address') ], [['Group'],'Host.example.com','address'], $tc.'5'); is_deeply ( [ $c->_split_config_line('Group;Group2;Host.example.com:address') ], [['Group','Group2'],'Host.example.com','address'], $tc.'7'); is_deeply ( [ $c->_split_config_line('Group;Group2;graph_title') ], [['Group','Group2'],'','graph_title'], $tc.'9'); is_deeply ( [ $c->_split_config_line('Group;Host.example.com:service.graph_title') ], [['Group'],'Host.example.com','service.graph_title'], $tc.'10'); is_deeply ( [ $c->_split_config_line('Group;Host.example.com:service.service2.graph_title') ], [['Group'],'Host.example.com','service.service2.graph_title'], $tc.'12'); $tc = "Implicit group names "; is_deeply ( [ $c->_split_config_line('example.com;foo.example.com:port') ], [['example.com'],'foo.example.com','port'], $tc.'1'); is_deeply ( [ $c->_split_config_line('example.com;foo.example.com:address') ], [['example.com'],'foo.example.com','address'], $tc.'2'); is_deeply ( [ $c->_split_config_line('example.com;foo.example.com:if_eth0.up.label') ], [['example.com'],'foo.example.com','if_eth0.up.label'], $tc.'3'); is_deeply ( [ $c->_split_config_line('localhost;localhost:port') ], [['localhost'],'localhost','port'], $tc.'4'); $tc = "Prefix/keyword combos "; is_deeply ( [ $c->_split_config_line('Group;port') ] , [['Group'],'','port'], $tc.'1'); is_deeply ( [ $c->_split_config_line('Group;Host.example.com:port')], [['Group'],'Host.example.com','port'], $tc.'2'); is_deeply ( [ $c->_split_config_line('Group;Host.example.com:service.port') ], [['Group'],'Host.example.com','service.port'], $tc.'4' ); $tc = "Prefix/keyword combos, nested groups "; is_deeply ( [ $c->_split_config_line('Group;Group2;Host.example.com:port') ], [['Group','Group2'], 'Host.example.com', 'port'], $tc.'1'); is_deeply ( [ $c->_split_config_line('Group;Group2;Host.example.com:service.port') ], [['Group','Group2'], 'Host.example.com', 'service.port'], $tc.'4' ); $tc = "Prefix/keyword combos, nested services "; is_deeply ( [ $c->_split_config_line('Group;Host.example.com:service.service2.port') ], [['Group'],'Host.example.com','service.service2.port'], $tc.'1' ); $tc = "Prefix/keyword combos, nested groups and services "; is_deeply ( [ $c->_split_config_line('Group;Group2;Host.example.com:service.service2.port') ], [['Group','Group2'],'Host.example.com','service.service2.port'], $tc.'1' ); $tc = "Prefix/keyword combos, various service.field.keyword combinations "; is_deeply ( [ $c->_split_config_line('Group;Host.example.com:service.field.max') ], [['Group'],'Host.example.com','service.field.max'], $tc.'1' ); # Alright, can anyone think of any more tests? # Probably even more combinations of nesting in both ends to see if that # somehow trips up the code. munin-2.0.75/master/t/munin_master_grouprepository.t000066400000000000000000000001411451614574100227370ustar00rootroot00000000000000use warnings; use strict; use Test::More tests => 1; use_ok('Munin::Master::GroupRepository'); munin-2.0.75/master/t/munin_master_host.t000066400000000000000000000001261451614574100204230ustar00rootroot00000000000000use warnings; use strict; use Test::More tests => 1; use_ok('Munin::Master::Host'); munin-2.0.75/master/t/munin_master_htmlconfig.t000066400000000000000000000007041451614574100216020ustar00rootroot00000000000000use warnings; use strict; use English '-no_match_vars'; use Test::More tests => 4; use_ok('Munin::Master::HTMLConfig'); my $test_path = [ {'path' => "../../index.html" } ]; #munin_get_root { is(Munin::Master::HTMLConfig::get_root_path($test_path), "../.."); $test_path = [ {'path' => "../index.html" } ]; is(Munin::Master::HTMLConfig::get_root_path($test_path), ".."); is(Munin::Master::HTMLConfig::get_root_path(), ""); } munin-2.0.75/master/t/munin_master_limitsold.t000066400000000000000000000023631451614574100214530ustar00rootroot00000000000000# -*- perl -*- use warnings; use strict; use Test::More tests => 7; use_ok('Munin::Master::LimitsOld'); # Test values my $severities_csv = ["ok,warning,critical,unknown"]; my $severities_mix = [ "ok,Warning, CRITICAL", "unknown" ]; my $severities_bad = [ "ok", "warning", "critical", "unknown", "fluffy" ]; my $severities_dup = [ "ok", "warning", "ok", "critical", "unknown" ]; # Result value (must be sorted) my $severities = [ "critical", "ok", "unknown", "warning" ]; is_deeply( Munin::Master::LimitsOld::validate_severities($severities), $severities, "validate always-send" ); is_deeply( Munin::Master::LimitsOld::validate_severities($severities_csv), $severities, "validate always-send (comma separated)" ); is_deeply( Munin::Master::LimitsOld::validate_severities($severities_mix), $severities, "validate always-send (mixed separation and case)" ); is_deeply( Munin::Master::LimitsOld::validate_severities($severities_bad), $severities, "validate always-send (with bad value)" ); is_deeply( Munin::Master::LimitsOld::validate_severities($severities_dup), $severities, "validate always-send (with duplicate value)" ); is_deeply( Munin::Master::LimitsOld::validate_severities([]), [], "validate always-send (with empty list)" ); munin-2.0.75/master/t/munin_master_logger.t000066400000000000000000000001301451614574100207200ustar00rootroot00000000000000use warnings; use strict; use Test::More tests => 1; use_ok('Munin::Master::Logger'); munin-2.0.75/master/t/munin_master_node.t000066400000000000000000000163151451614574100204020ustar00rootroot00000000000000# -*- cperl -*- use warnings; use strict; use Munin::Master::Config; use Test::More tests => 15; use Test::MockModule; use Test::MockObject::Extends; use Test::Exception; use Test::Differences; use Data::Dumper; use_ok('Munin::Master::Node'); my $config = Munin::Master::Config->instance(); $config->{node}{use_node_name} = 1; sub setup { my $node = Munin::Master::Node->new('127.0.0.1', 4949, 'node'); my $node_mock = Test::MockObject::Extends->new($node); $node_mock->mock('_node_write_single', sub {}); $node_mock->mock('_node_read_fast', sub { my ($self) = @_; return $self->_node_read(); }); return $node_mock; } ### new { my $node = Munin::Master::Node->new(); isa_ok($node, 'Munin::Master::Node','Create a node object'); } ### _do_connect { my $node = setup(); $node->mock('_node_read_single', sub { return '# munin node at foo.example.com' }); my $connected_socket = Test::MockObject->new(); $connected_socket->set_true('connected'); my $inet = Test::MockModule->new('IO::Socket::INET6'); $inet->mock(new => sub { return $connected_socket }); $node->_do_connect(); is($node->{node_name}, 'foo.example.com','Node name is detected'); } ### _extract_name_from_greeting { sub _extract_name_from_greeting { my ($greeting) = @_; if ($greeting && ($greeting =~ /\#.*(?:lrrd|munin) (?:client|node) at (\S+)/i)) { return $1; } else { return ""; } } my $node = Munin::Master::Node->new(); is(_extract_name_from_greeting('# munin node at foo.example.com'), 'foo.example.com', 'Node name from new greeting'); is(_extract_name_from_greeting('# lrrd client at foo.example.com'), 'foo.example.com', 'Node name from old greeting'); } ### negotiate_capabilities { my $node = setup(); $node->mock('_node_read_single', sub { return 'cap multigraph'; }); my @res = $node->negotiate_capabilities(); is_deeply(\@res, ['multigraph'], 'Capabilities - single'); } { my $node = setup(); $node->mock('_node_read_single', sub { return '# Unknown command. Try list, nodes, config, fetch, version or quit'; }); my @res = $node->negotiate_capabilities(); is_deeply(\@res, ['NA'], 'Capabilities - single'); } { my $node = setup(); $node->mock('_node_read_single', sub { my @array = ('cap bar baz foo'); return \@array; }); my @res = $node->negotiate_capabilities(); is_deeply(\@res, ['NA'], 'Capabilities - none'); } ### list_plugins { my $node = setup(); $node->mock('_node_read_single', sub { return 'foo bar baz'; }); $node->{node_name} = 'node'; my @res = $node->list_plugins(); is_deeply(\@res, [qw(foo bar baz)], 'List plugins'); } ### fetch_service_config { my $node = setup(); $node->mock('_node_read', sub { my @array = ('# timeout: bla bla bla'); return \@array; }); throws_ok { $node->fetch_service_config('foo') } qr/Timeout error on node/, 'Fetch service config - Timeout throws exception'; } { my $node = setup(); $node->mock('_node_read', sub { my @array = ( '', '# bla bla bla', 'foo bar', 'zap gabonk', 'baz.bar foo', ); return \@array; }); #die Dumper { $node->fetch_service_config('fun') }; my %res = $node->fetch_service_config('fun'); eq_or_diff(\%res, { global => { multigraph => [ 'fun' ], fun => [ [ 'foo', 'bar' ], [ 'zap', 'gabonk' ], ], }, data_source => { fun => { baz => { bar => 'foo', extinfo => 'NOTE: The plugin did not provide any label for the data source baz. It is in need of fixing.', label => 'No .label provided', }, }, }, }, 'Fetch service config - missing label', ); } { my $node = setup(); $node->mock('_node_read', sub { my @array = ( '', '# bla bla bla', 'foo bar', 'zap gabonk', 'baz.label foo', 'zip.label bar', ); return \@array; }); my %res = $node->fetch_service_config('fun'); is_deeply(\%res, { global => { multigraph => ['fun'], fun => [ [qw(foo bar)], [qw(zap gabonk)], ['graph_order', 'baz zip'], ], }, data_source => { fun => { baz => {label => 'foo'}, zip => {label => 'bar'}, }, }, }, 'Fetch service config - implicit graph order' ); } { my $node = setup(); $node->mock('_node_read', sub { my @array = ( '', '# bla bla bla', 'foo bar', 'zap gabonk', 'baz.label foo', 'zip.label bar', 'graph_order zip baz', ); return \@array; }); my %res = $node->fetch_service_config('fun'); is_deeply(\%res, { global => { multigraph => [qw( fun )], fun => [ [qw( foo bar )], [qw( zap gabonk )], # The internal "graph_order" implementation changed in 53f22440a and now # includes the list of data field appearances after the explicitly configured # graph_order. [ 'graph_order', 'zip baz baz zip' ], ], }, data_source => { fun => { baz => { label => 'foo', }, zip => { label => 'bar', }, } }, }, 'Fetch service config - explicit graph_order' ); } ### fetch_service_data { my $node = setup(); $node->mock('_node_read', sub { my @array = ('# timeout: bla bla bla'); return \@array; }); throws_ok { $node->fetch_service_data('foo') } qr/Timeout in fetch from 'foo'/, 'Fetch service data - Timeout throws exception'; } { my $node = setup(); $node->mock('_node_read', sub { my @array = ( '', '# bla bla bla', 'fun.value bar', 'zap.value gabonk', 'baz.value foo', ); return \@array; }); my $time = time; # this will work, except when the clock ticks at the wrong time my %res = $node->fetch_service_data('foo'); is_deeply(\%res, { foo => { fun => { value => ['bar'], when => [$time], }, zap => { value => ['gabonk'], when => [$time], }, baz => { value => ['foo'], when => [$time], }, }, }, 'Fetch service data' ); } # vim: sw=8 : ts=4 : et munin-2.0.75/master/t/munin_master_plugin_config.t000066400000000000000000000160771451614574100223050ustar00rootroot00000000000000# -*- cperl -*- use warnings; use strict; use English qw(-no_match_vars); use Data::Dumper; use Test::More qw(no_plan); # use Test::More tests => 15; use_ok('Munin::Master::Node'); my $node = bless { address => "127.0.0.1", port => "4949", host => "localhost" }, "Munin::Master::Node"; $INPUT_RECORD_SEPARATOR = ''; my @input = split("\n",); # print "Input: ",@input,"\n"; my %answer = $node->parse_service_config("Test", \@input); =comment # This is the old datastructure returned by parse_service_config. # Kept here as a reference until multigraph is fully working. -janl 2009-10-23 my $fasit = { 'data_source' => { 'system' => { 'info' => 'CPU time spent by the kernel in system activities', 'draw' => 'AREA', 'min' => '0', 'max' => '200', 'critical' => '100', 'warning' => '60', 'label' => 'system', 'type' => 'DERIVE' }, 'user' => { 'info' => 'CPU time spent by normal programs and daemons', 'draw' => 'STACK', 'min' => '0', 'warning' => '160', 'max' => '200', 'type' => 'DERIVE', 'label' => 'user' } }, 'global' => [ [ 'graph_title', 'CPU usage' ], [ 'graph_order', 'system user nice idle iowait irq softirq' ], [ 'graph_args', '--base 1000 -r --lower-limit 0 --upper-limit 200' ], [ 'graph_vlabel', '%' ], [ 'graph_scale', 'no' ], [ 'graph_info', 'This graph shows how CPU time is spent.' ], [ 'graph_category', 'system' ], [ 'graph_period', 'second' ] ] }; =cut my $fasit = { 'data_source' => { 'Test' => { 'system' => { 'info' => 'CPU time spent by the kernel in system activities', 'draw' => 'AREA', 'min' => '0', 'max' => '200', 'critical' => '100', 'warning' => '60', 'label' => 'system', 'type' => 'DERIVE' }, 'user' => { 'info' => 'CPU time spent by normal programs and daemons', 'draw' => 'STACK', 'min' => '0', 'warning' => '160', 'max' => '200', 'type' => 'DERIVE', 'label' => 'user' } } }, 'global' => { 'multigraph' => [ 'Test' ], 'Test' => [ [ 'graph_title', 'CPU usage' ], [ 'graph_order', # Since 53f22440a the internal "graph_order" implementation # includes the list of data field appearances after the # explicitly configured graph_order. 'system user nice idle iowait irq softirq system user' ], [ 'graph_args', '--base 1000 -r --lower-limit 0 --upper-limit 200' ], [ 'graph_vlabel', '%' ], [ 'graph_scale', 'no' ], [ 'graph_info', 'This graph shows how CPU time is spent.' ], [ 'graph_category', 'system' ], [ 'graph_period', 'second' ] ] } }; is_deeply(\%answer,$fasit,"Plugin config output"); __DATA__ graph_title CPU usage graph_order system user nice idle iowait irq softirq graph_args --base 1000 -r --lower-limit 0 --upper-limit 200 graph_vlabel % graph_scale no graph_info This graph shows how CPU time is spent. graph_category system graph_period second system.label system system.draw AREA system.max 200 system.min 0 system.type DERIVE system.warning 60 system.critical 100 system.info CPU time spent by the kernel in system activities user.label user user.draw STACK user.min 0 user.max 200 user.warning 160 user.type DERIVE user.info CPU time spent by normal programs and daemons munin-2.0.75/master/t/munin_master_plugin_fetch.t000066400000000000000000000046071451614574100221250ustar00rootroot00000000000000# -*- cperl -*- use warnings; use strict; use English qw(-no_match_vars); use Data::Dumper; # use Test::More qw(no_plan); use Test::More tests => 2; use_ok('Munin::Master::Node'); my $node = bless { address => "127.0.0.1", port => "4949", host => "localhost" }, "Munin::Master::Node"; $INPUT_RECORD_SEPARATOR = ''; my @input = split("\n",); # make time() return a known-good value. BEGIN { *CORE::GLOBAL::time = sub { 1234567890 }; } my $time = time; my %answer = $node->parse_service_data("cpu", \@input); =comment Keep old correct answer for reference. my $fasit = { 'irq' => { 'when' => '1256305015', 'value' => '2770' }, 'system' => { 'when' => 'N', 'value' => '66594' }, 'softirq' => { 'when' => '1256305015', 'value' => '127' }, 'user' => { 'when' => 'N', 'value' => '145923' }, 'idle' => { 'when' => 'N', 'value' => '2245122' }, 'iowait' => { 'when' => 'N', 'value' => '14375' }, 'nice' => { 'when' => 'N', 'value' => '268' } }; =cut my $fasit = { cpu => { irq => { when => [ 1256305015 ], value => [ 2770 ], }, system => { when => [ $time ], value => [ 66594 ], }, softirq => { when => [ 1256305015 ], value => [ 127 ], }, user => { when => [ $time ], value => [ 145923 ], }, iowait => { when => [ $time ], value => [ 14375 ], }, idle => { when => [ $time ], value => [ 2245122 ], }, nice => { when => [ $time ], value => [ 268 ], }, }, }; is_deeply(\%answer,$fasit,"Plugin fetch output"); __DATA__ user.value 145923 nice.value 268 system.value 66594 idle.value 2245122 iowait.value 14375 irq.value 1256305015:2770 softirq.value 1256305015:127 munin-2.0.75/master/t/munin_master_plugin_multigraph_config.t000066400000000000000000000206471451614574100245370ustar00rootroot00000000000000# -*- cperl -*- use warnings; use strict; use English qw(-no_match_vars); use Data::Dumper; use Test::More qw(no_plan); # use Test::More tests => 15; use_ok('Munin::Master::Node'); # use_ok('Munin::Master::Logger'); # logger_debug(); # Mock object enough to be able to call (some) object methods. my $node = bless { address => "127.0.0.1", port => "4949", host => "localhost" }, "Munin::Master::Node"; $INPUT_RECORD_SEPARATOR = ''; my @input = split("\n",); # print "Input: ",@input,"\n"; my %answer = $node->parse_service_config("multigraph_tester", \@input); # Output captured from Data::Dumper my $fasit = { 'data_source' => { 'multigraph_outofscope' => { 'i' => { 'label' => 'number i' } }, 'multigraph_tester.en' => { 'one' => { 'label' => 'number 1' } }, 'multigraph_tester' => { 'three' => { 'label' => 'number 3' }, 'one' => { 'label' => 'number 1' }, 'two' => { 'label' => 'number 2' } }, 'multigraph_tester.to' => { 'two' => { 'label' => 'number 2' } }, 'multigraph_tester.tre' => { 'three' => { 'label' => 'number 3' } } }, 'global' => { 'multigraph_outofscope' => [ [ 'graph_title', 'The out of namespace graph' ], [ 'graph_info', 'The "multigraph protocol keyword allows the plugin to place data anywhere in the host/node namespace. The intended use is to be able to produce multiple root graphs and sub-graph spaces, but this is not enforced.' ], [ 'graph_order', 'i' ] ], 'multigraph' => [ 'multigraph_tester', 'multigraph_tester.en', 'multigraph_tester.to', 'multigraph_tester.tre', 'multigraph_outofscope' ], 'multigraph_tester.en' => [ [ 'graph_title', 'The number 1 sub-graph' ], [ 'graph_info', 'This and the other . (dot) separated nested graphs are presented in a page linked to from the root graph' ], [ 'graph_order', 'one' ] ], 'multigraph_tester' => [ [ 'graph_title', 'Root graph of multigraph test' ], [ 'graph_info', 'The root graph is used to click into sub-graph page. Eventually the root graph should be able to borrow data from the sub graphs in a fairly easy manner. But not right now.' ], [ 'graph_order', 'one two three' ] ], 'multigraph_tester.to' => [ [ 'graph_title', 'The number 2 sub-graph' ], [ 'graph_order', 'two' ] ], 'multigraph_tester.tre' => [ [ 'graph_title', 'The number 3 sub-graph' ], [ 'graph_order', 'three' ] ] } }; # print Dumper %answer; is_deeply(\%answer,$fasit,"Multigraph plugin config output"); __DATA__ graph_title Root graph of multigraph test graph_info The root graph is used to click into sub-graph page. Eventually the root graph should be able to borrow data from the sub graphs in a fairly easy manner. But not right now. one.label number 1 two.label number 2 three.label number 3 # multigraph multigraph_tester.en graph_title The number 1 sub-graph graph_info This and the other . (dot) separated nested graphs are presented in a page linked to from the root graph one.label number 1 # multigraph multigraph_tester.to graph_title The number 2 sub-graph two.label number 2 # multigraph multigraph_tester.tre graph_title The number 3 sub-graph three.label number 3 # multigraph multigraph_outofscope graph_title The out of namespace graph graph_info The "multigraph protocol keyword allows the plugin to place data anywhere in the host/node namespace. The intended use is to be able to produce multiple root graphs and sub-graph spaces, but this is not enforced. i.label number i munin-2.0.75/master/t/munin_master_plugin_multigraph_fetch.t000066400000000000000000000035631451614574100243610ustar00rootroot00000000000000# -*- cperl -*- # vim: ts=4 : sw=4 : et use warnings; use strict; use English qw(-no_match_vars); use Data::Dumper; use Test::More tests => 2; use_ok('Munin::Master::Node'); # use_ok('Munin::Master::Logger'); # logger_debug(); # Mock object enough to be able to call (some) object methods. my $node = bless { address => "127.0.0.1", port => "4949", host => "localhost" }, "Munin::Master::Node"; $INPUT_RECORD_SEPARATOR = ''; my @input = split("\n",); # make time() return a known value. BEGIN { *CORE::GLOBAL::time = sub { 1234567890 }; } my $time = time; my %answer = $node->parse_service_data("multigraph_tester", \@input); # Output captured from Data::Dumper my $fasit = { 'multigraph_outofscope' => { i => { when => [ $time ], value => [ 42 ], } }, 'multigraph_tester' => { three => { when => [ $time ], value => [ 3 ], }, one => { when => [ $time ], value => [ 1 ], }, two => { when => [ 1256305817 ], value => [ 2 ], } }, 'multigraph_tester.en' => { one => { when => [ 1256305817 ], value => [ 1 ], } }, 'multigraph_tester.to' => { two => { when => [ $time ], value => [ 2 ], } }, 'multigraph_tester.tre' => { three => { when => [ $time ], value => [ 3 ], } } }; # print Dumper \%answer; is_deeply(\%answer, $fasit, 'Multigraph plugin fetch output'); __DATA__ one.value 1 two.value 1256305817:2 three.value 3 # multigraph multigraph_tester.en one.value 1256305817:1 # multigraph multigraph_tester.to two.value 2 # multigraph multigraph_tester.tre three.value 3 # multigraph multigraph_outofscope i.value 42 munin-2.0.75/master/t/munin_master_update.t000066400000000000000000000036011451614574100207310ustar00rootroot00000000000000use warnings; use strict; use English qw(-no_match_vars); use Test::MockModule; use Test::More tests => 2; use File::Temp qw( tempdir ); use_ok('Munin::Master::Update'); my $config = Munin::Master::Config->instance()->{config}; $config->{dbdir} = tempdir(CLEANUP => 1); # Make 'keys' return the keys in sorted order. package Munin::Master::Update; use subs 'keys'; package main; *Munin::Master::Update::keys = sub { my %hash = @_; sort(CORE::keys(%hash)); }; # sub remove_indentation { my ($str) = @_; $str =~ s{\n\ *}{\n}xmsg; $str =~ s{\A \n }{}xms; return $str; } # my $mockconfig = Test::MockModule->new('Munin::Master::Config'); $mockconfig->mock(get_groups_and_hosts => sub { return () }); # { my $update = Munin::Master::Update->new(); $update->{service_configs} = { 'g1;host1' => { global => { service1 => [['graph_title', 'service1']], }, data_source => { service1 => { data_source1 => {max => 'U', min => 'U'}, data_source2 => {max => 'U', min => 'U'}, }, }, }, 'g1;host2' => { global => { service1 => [['graph_title', 'service1']], }, data_source => { service1 => { data_source1 => {max => 'U', min => 'U'}, }, }, }, }; my $result = ""; open my $fh, '>', \$result or die $OS_ERROR; $update->_write_new_service_configs($fh); no warnings 'once'; my $expected = "version $Munin::Common::Defaults::MUNIN_VERSION\n" . remove_indentation(q{ g1;host1:service1.graph_title service1 g1;host1:service1.data_source1.max U g1;host1:service1.data_source1.min U g1;host1:service1.data_source2.max U g1;host1:service1.data_source2.min U g1;host2:service1.graph_title service1 g1;host2:service1.data_source1.max U g1;host2:service1.data_source1.min U }); is($result, $expected, 'Write new service config'); } munin-2.0.75/master/t/munin_master_updateworker.t000066400000000000000000000001371451614574100221640ustar00rootroot00000000000000use warnings; use strict; use Test::More tests => 1; use_ok('Munin::Master::UpdateWorker'); munin-2.0.75/master/t/munin_master_utils.t000066400000000000000000000037541451614574100206200ustar00rootroot00000000000000use warnings; use strict; use English '-no_match_vars'; use Scalar::Util "weaken"; use Test::More tests => 11; use_ok('Munin::Master::Utils'); # munin_mkdir_p { ok(munin_mkdir_p("./mkdirt", oct(444)), "Creating valid dir"); SKIP: { skip "Directory permission cannot be tested by root", 1 if $REAL_USER_ID == 0; ok(!munin_mkdir_p("./mkdirt/bad", oct(444)), "Creating invalid dir"); } eval { rmdir("./mkdirt") }; ok(!$EVAL_ERROR, "Removing dir (please do manual cleanup on failure)"); } # munin_get { my $sentinel = 12345; is(munin_get(undef, undef, $sentinel), $sentinel, "munin_get from undef gives back default"); my $th1 = {"tfield" => 5}; is(munin_get($th1, "tfield", $sentinel), 5, "munin_get recovers numeric field"); my $th2 = {"tfield" => {"innerfield" => 5}}; is(munin_get($th2, "tfield", $sentinel), $sentinel, "munin_get returns default on hash field"); my $h = { "a" => { "upper1" => "1-a", "upper2" => "2-a", "aa" => { "field1" => "1-aa", "field2" => "2-aa", }, "ab" => { "field1" => "1-aa", "field2" => "2-ab", "field3" => "3-ab", }, "ac" => { "field1" => "1-ac", "field2" => "2-ac", }, }, "b" => { "ba" => { "field1" => "1-ba", }, }, }; $h->{a}{aa}{'#%#parent'} = $h->{a}; weaken($h->{a}{aa}{'#%#parent'}); $h->{a}{ab}{'#%#parent'} = $h->{a}; weaken($h->{a}{ab}{'#%#parent'}); $h->{a}{ac}{'#%#parent'} = $h->{a}; weaken($h->{a}{ac}{'#%#parent'}); $h->{b}{ba}{'#%#parent'} = $h->{b}; weaken($h->{b}{ba}{'#%#parent'}); is(munin_get($h, "a", $sentinel), $sentinel, "munin_get returns default on hash field"); is(munin_get($h->{a}->{aa}, "field1", $sentinel), $h->{a}->{aa}->{field1}, "munin_get recovers field value"); is(munin_get($h->{a}->{aa}, "upper2", $sentinel), $h->{a}->{upper2}, "munin_get recovers field value, from above"); is(munin_get($h->{a}->{ba}, "upper2", $sentinel), $sentinel, "munin_get recovers default, no value from above"); } munin-2.0.75/master/t/no-fixes.t000066400000000000000000000025211451614574100164160ustar00rootroot00000000000000# vim: ts=4 : sw=4 : et use warnings; use strict; # Check for comments and POD flagged with FIX. # Are there any modules that already does this? # Perl::Critic::Policy::Bangs::ProhibitFlagComments doesn't consider # POD. Make a Perl::Critic::Policy out of this? use English qw(-no_match_vars); use File::Find; use FindBin; use Test::More; use Pod::Simple::TextContent; if ($ENV{TEST_POD}) { plan tests => 1; } else { plan skip_all => 'set TEST_POD to enable this test' } my $count = 0; find(\&fixes, "$FindBin::Bin/../lib"); is($count, 0, "Should not find any FIX comments"); sub fixes { my $file = $File::Find::name; # skip SVN stuff if ( -d and m{\.svn}) { $File::Find::prune = 1; return; } return unless -f $file; # # Check comments # open my $F, '<', $file or warn "Couldn't open $file: $!" && return; while (<$F>) { if (m{#\s*FIX}) { printf "Found a FIX comment at %s: %d\n", $file, $.; $count++; } } close $F; # # Check POD # my $pod_parser = Pod::Simple::TextContent->new; my $pod = ""; $pod_parser->output_string(\$pod); $pod_parser->parse_file($file); my $pod_count = scalar(grep {/FIX/} split /\n/, $pod); printf "Found %d FIX(es) in POD in %s\n", $pod_count,$file if $pod_count; $count += $pod_count; } munin-2.0.75/master/t/pod-coverage.t000066400000000000000000000007251451614574100172450ustar00rootroot00000000000000use strict; use warnings; use Test::More; use FindBin; use lib "$FindBin::Bin/../lib"; plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD}; eval 'use Test::Pod::Coverage'; plan ( skip_all => "Test::Pod::Coverage required for testing POD coverage" ) if $@; plan ( skip_all => "Only tested in dev environment (\$ENV{MUNIN_ENVIRONMENT} eq 'dev')" ) if $ENV{MUNIN_ENVIRONMENT} && $ENV{MUNIN_ENVIRONMENT} ne 'dev'; all_pod_coverage_ok(); munin-2.0.75/master/t/pod.t000066400000000000000000000004401451614574100154460ustar00rootroot00000000000000use strict; use warnings; use Test::More; use FindBin; use lib "$FindBin::Bin/../lib"; plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD}; eval 'use Test::Pod'; plan ( skip_all => "Test::Pod required for testing for POD errors" ) if $@; all_pod_files_ok(); munin-2.0.75/master/www/000077500000000000000000000000001451614574100150625ustar00rootroot00000000000000munin-2.0.75/master/www/munin-categoryview.tmpl000066400000000000000000000054011451614574100216140ustar00rootroot00000000000000 munin-2.0.75/master/www/munin-comparison-day.tmpl000066400000000000000000000027031451614574100220330ustar00rootroot00000000000000 munin-2.0.75/master/www/munin-comparison-month.tmpl000066400000000000000000000027221451614574100224040ustar00rootroot00000000000000 munin-2.0.75/master/www/munin-comparison-week.tmpl000066400000000000000000000027611451614574100222150ustar00rootroot00000000000000 munin-2.0.75/master/www/munin-comparison-year.tmpl000066400000000000000000000027131451614574100222170ustar00rootroot00000000000000 munin-2.0.75/master/www/munin-domainview.tmpl000066400000000000000000000061611451614574100212520ustar00rootroot00000000000000 munin-2.0.75/master/www/munin-dynazoom.tmpl000066400000000000000000000035531451614574100207520ustar00rootroot00000000000000

Graph zoom

Zooming is easy, it's done in 3 clicks (regular clicks, no drag&drop):
  1. First click to define the start of zoom.
  2. Second click to define the ending of zoom.
  3. Third click inside the defined zone to zoom, outside to cancel the zone.
Plugin Name (domain/hostname/plugin_name) :
Start/Stop of the graph
(format:2005-08-15T15:52:01+0000)
(epoch) :
/
( / )
Limit low/high : /
Graph size (w/o legend) (pixels): /

munin-2.0.75/master/www/munin-htaccess.in000066400000000000000000000015211451614574100203320ustar00rootroot00000000000000# This file can be used as a .htaccess file, or a part of your apache # config file. # # For the .htaccess file option to work the munin www directory # (@@HTMLDIR@@) must have "AllowOverride all" or something close # to that set. # # As a config file enclose it in like so: # # AuthUserFile @@CONFDIR@@/munin-htpasswd AuthName "Munin" AuthType Basic require valid-user # This next part requires mod_expires to be enabled. # # We could use around here, but I want it to be # as evident as possible that you either have to load mod_expires _or_ # you coment out/remove these lines. # Set the default expiery time for files 5 minutes 10 seconds from # their creation (modification) time. There are probably new files by # that time. ExpiresActive On ExpiresDefault M310 # munin-2.0.75/master/www/munin-nodeview.tmpl000066400000000000000000000025141451614574100207260ustar00rootroot00000000000000 munin-2.0.75/master/www/munin-overview.tmpl000066400000000000000000000211741451614574100207570ustar00rootroot00000000000000 munin-2.0.75/master/www/munin-problemview.tmpl000066400000000000000000000111251451614574100214370ustar00rootroot00000000000000 munin-2.0.75/master/www/munin-serviceview.tmpl000066400000000000000000000116321451614574100214420ustar00rootroot00000000000000

Service graphs

"> " alt="daily graph" class="iwarncrit" width="" height=""/> "> " alt="weekly graph" class="iwarncrit" width="" height=""/>
"> " alt="monthly graph" class="iwarncrit" width="" height=""/> "> " alt="yearly graph" class="iwarncrit" width="" height=""/>
" alt="summed weekly graph" width="" height=""/> " alt="summed yearly graph" width="" height=""/>

Graph Information

Note: This service is in WARNING state because one of the values reported is outside the allowed range. Please see further down for information about the ranges and the graph for the values.

Note: This service is in CRITICAL state because one of the values reported is outside the allowed range. Please see further down for information about the ranges and the graph for the values.

Note: This service is in UNKNOWN state, because something bad happened. Please check your network, the munin-node, and the plugin.

oddrowevenrow lastrow"> oddrowevenrow">
Field Internal name /static/definitions.html#data_types">Type Warn Crit Info
 
 
 
This field has the following extra information:
munin-2.0.75/master/www/partial/000077500000000000000000000000001451614574100165165ustar00rootroot00000000000000munin-2.0.75/master/www/partial/bottom_navigation.tmpl000066400000000000000000000023071451614574100231410ustar00rootroot00000000000000 :: "> "> Overview :: :: :: "> munin-2.0.75/master/www/partial/footer.tmpl000066400000000000000000000004601451614574100207120ustar00rootroot00000000000000 munin-2.0.75/master/www/partial/generated_by.tmpl000066400000000000000000000001711451614574100220430ustar00rootroot00000000000000
munin-2.0.75/master/www/partial/head.tmpl000066400000000000000000000025441451614574100203220ustar00rootroot00000000000000 /static/style-new.css" type="text/css" />