#! /usr/bin/perl use strict ; use warnings ; use Dmon ; OBB -> import ; Util -> import ; my @COMMAND_LINE = ( $0, @ARGV ) ; my @COMMAND_ARGS = qw(stop start reload state) ; my $PAT_ARGS = join '|', @COMMAND_ARGS ; my $prog = substr $0, rindex ( $0, '/' ) + 1 ; my $Usage = < ; default [@{[join ',', Conf::FILES]}] USAGE sub Usage { die "$_[0]$Usage" ; } sub Error { die "[error] $prog: $_[0]\n" ; } sub Warn { warn "[warn] $prog: $_[0]\n" ; } # usage: &GetOptions(ARG,ARG,..) defines $opt_ID as 1 or user spec'ed value # usage: &GetOptions(\%opt,ARG,ARG,..) defines $opt{ID} as 1 or user value # ARG = 'ID' | 'ID=SPC' | 'ID:SPC' for no-arg, required-arg or optional-arg # ID = perl identifier # SPC = i|f|s for integer, fixedpoint real or string argument use Getopt::Long ; Getopt::Long::config ( 'no_ignore_case' ) ; my %opt = () ; Usage '' unless GetOptions ( \%opt, qw(s q v d T h t i c=s version test) ) ; Usage("Arg count\n") unless @ARGV <= 1 ; Usage("Bad argument ($ARGV[0]) \n") if @ARGV and $ARGV[0] !~ /^$PAT_ARGS$/ ; $opt{d} ||= $opt{T} ; $opt{v} ||= $opt{d} ; OBB -> Verbosity ( 'Silent' ) if $opt{s} ; OBB -> Verbosity ( 'Quiet' ) if $opt{q} ; OBB -> Verbosity ( 'Verbose' ) if $opt{v} ; OBB -> Verbosity ( 'Debug' ) if $opt{d} ; OBB -> Verbosity ( 'Trace' ) if $opt{T} ; if ( $opt{h} ) { print $Usage ; exit ; } if ( $opt{version} ) { print Dmon -> Version, "\n" ; exit ; } my $ARG = shift || '' ; # printf "%s\n", Util::ddate time ; exit ; my $conf = Conf -> Make ( file => $opt{c} ) ; $conf -> PORT ( 23007 ) if $opt{test} ; $conf -> Dmp ( 'config' ) if $opt{v} ; Util -> INITx ( conf => $conf ) ; my $HUP = 0 ; my $USR1 = 0 ; $SIG { INT } = sub { OBB::logx ( 'SIG INT' ) ; exit ; } ; $SIG { HUP } = sub { OBB::logx ( 'SIG HUP' ) ; $HUP = 1 ; } ; $SIG { USR1 } = sub { OBB::logx ( 'SIG USR1' ) ; $USR1 = time ; } ; my $daemon = Dmon::Daemon -> Make ( conf => $conf ) -> sss_exit ( $ARG, $opt{i} ) unless $opt{t} ; my $threads = Threads -> Make unless $opt{t} ; my $hnam = $Util::canonical_hostname ; my $SERVER = Server -> Make ( conf => $conf, threads => $threads ) if $conf -> server eq $hnam ; my $CLIENT = Client -> Make ( conf => $conf, threads => $threads ) ; my $itms = $CLIENT -> work -> hosts -> { $hnam } ; my $server = $CLIENT -> server ; my $stamp = $CLIENT -> work -> stamp ; my $srv_hnm = $server -> hnam ; my $snd_rps = 1 ; if ( $SERVER ) { $SERVER -> CLIENT ( $CLIENT ) ; $CLIENT -> SERVER ( $SERVER ) ; } sub time_x ($$) { my $incr = shift ; my $rand = shift ; int ( time + $incr - $rand / 2 + rand $rand ) ; } sub reload { logq ( 'reload' ) ; my $newc ; my $eval = eval { $newc = Conf -> Make ( file => $conf -> file ) ; 1 ; } ; my $res = 1 ; unless ( defined $eval ) { logq ( "reload bad [$@]" ) ; $res = 0 ; } else { $conf = $newc ; $conf -> PORT ( 23007 ) if $opt{test} ; OBB -> Verbosity ( $conf -> loglvl ) ; $threads -> stop ; $SERVER -> Init ( conf => $conf, threads => $threads ) if $SERVER ; $CLIENT -> Init ( conf => $conf, threads => $threads ) ; $snd_rps = 1 ; logq ( 'reload ok' ) ; } $res ; } sub mk_next_stat { map { ( $_ => time ) ; } $CLIENT -> state -> hnams ; } if ( $opt{v} ) { printf "hostname [%s]\n", $hnam ; printf "logdir [%s]\n", $conf -> logdir ; printf "rundir [%s]\n", $conf -> rundir ; printf "rotate [%s]\n", scalar localtime time + $conf -> rot_ivl ; printf "server [%s]\n", $server ? $srv_hnm : 'no server' ; printf "distrib [%s]\n", $Util::DISTRIB ; printf "stamp [%s]\n", scalar localtime $stamp ; printf "levels [%s]\n", $CLIENT -> show_levels ; $CLIENT -> work -> Dmp ( 'client work' ) ; if ( $SERVER ) { printf "ServHist [%s]\n", $SERVER -> CLIENT -> hist ; printf "pmaker [%s]\n", $SERVER -> work -> pmaker ; $SERVER -> work -> Dmp ( 'server work' ) ; } } printf "my items :\n [ %s\n ]\n", join "\n , ", sort keys %$itms if $opt{v} ; exit if $opt{t} ; $| = 1 ; my %next_stat = mk_next_stat ; my $next_work = time + $conf -> ival_check_work ; my $next_rotl = time + $conf -> rot_ivl ; my $next_zaps = time + 30 ; my $from_server = [] ; my $work_lm = undef ; $CLIENT -> send_server_reboot ( $hnam, $from_server ) ; while ( 1 ) { for my $hnam ( $CLIENT -> state -> hnams ) { if ( time >= $next_stat { $hnam } ) { my ( $upds, $events ) = $CLIENT -> mk_update ( $hnam ) ; if ( $upds ) { my $hash = $CLIENT -> state -> hist_hash ( $hnam ) ; logt ( 'save to history' ) ; OBB::Dmp ( $hash, 'hist hash' ) if OBB -> Terse ; $CLIENT -> save_hist ( $hnam, $hash ) ; OBB::TT 'event %s', $_ for @$events ; } if ( @$events ) { logt ( "send events (%s) to $srv_hnm", scalar @$events ) ; my $text = Util::as_text ( OBB -> Unbless ( $events ) ) ; my $sender = $CLIENT -> send_server ( "EVENTS $text", $from_server ) ; logq ( "can't connect to server $srv_hnm" ) unless $sender ; } $next_stat { $hnam } = time_x $conf -> ival_make_state, 0 ; } } if ( time > $next_zaps ) { my @hnams = $CLIENT -> state -> hnams ; logt ( 'do ZAP [%s]', join ',', @hnams ) ; $CLIENT -> zap_hist ( $_ ) for @hnams ; $CLIENT -> zap_old ( Dmon::T_EVNTS, $conf -> ival_keep_events ) if $SERVER ; $next_zaps = time_x $conf -> zap_ivl, 10 ; } if ( time >= $CLIENT -> next_send ) { OBB::TT 'make to server' ; logt ( "send REPORT to $srv_hnm" ) ; my $text = 'REPORT ' . $CLIENT -> state -> as_text ; my $send_server = $CLIENT -> send_server ( $text, $from_server ) ; OBB::TT 'make to server done' ; my $next = time_x $conf -> ival_send_report, 10 ; unless ( $send_server ) { logq ( "can't connect to server $srv_hnm" ) ; $next = time_x 60, 10 ; } $CLIENT -> next_send ( $next ) ; $work_lm = undef ; } $threads -> do_a_loop ; while ( @$from_server ) { my $recv = shift @$from_server ; logq ( "warn [%s]\n", $recv -> resp ) if $recv -> err ; logt ( $recv -> err ? $recv -> resp : "received message type [%s]\n", $recv -> type ) ; logv ( "resp [%s]\n", $recv -> resp ) ; logd ( "received:\n%send", $recv -> body ) ; if ( ! $recv -> err and $recv -> type eq 'COMMAND REPORT' ) { $work_lm = $recv -> work_lm ; } } if ( time > $next_work ) { my $HUP1 = $CLIENT -> new_work ( $work_lm ) ; my $HUP2 = ( $SERVER and $SERVER -> new_work ) ; $HUP ||= ( $HUP1 || $HUP2 ) ; $next_work = time_x $conf -> ival_check_work, 8 ; } if ( time > $next_rotl ) { Util::rotate ( $conf ) ; $next_rotl = time + $conf -> rot_ivl ; } if ( $SERVER and $snd_rps and ! $opt{test} ) { logt ( "send ALLSEND to $srv_hnm" ) ; my $send_server = $CLIENT -> send_server ( 'ALLSEND', $from_server ) ; logq ( "can't connect to server $srv_hnm" ) unless $send_server ; $snd_rps = 0 ; } if ( $HUP ) { $HUP = 0 ; %next_stat = mk_next_stat if reload ; } my $rexec = $CLIENT -> rexec || $USR1 ; if ( $rexec and time > $rexec ) { $Util::EXEC = 1 ; $Util::STOP = 'rexec' ; Dmon::Daemon -> ulock ; exit ; } } END { if ( Util::MODE ) { syslog ( $Util::STOP eq 'dirty' ? 'daemon exits dirty' : "daemon stopped ($Util::STOP)" ) ; $conf -> rm_sys_lock ; } exec @COMMAND_LINE if $Util::EXEC ; } __END__ =pod =head1 NAME dmon - a distributed monitor =head1 SYNOPSIS Usage: dmon [OPTIONS] [COMMANDS] OPTIONS : [-s] [-q] [-v] [-d] [-h] [-i] [-t] [-c file] COMMANDS : stop|start|reload|state option s : be silent option q : be quiet ; only errors option v : be verbose ; show all actions option d : show debug info ; internals option T : trace ; show all debug info option h : show help ; exit option t : test config and work ; exit option i : interactive ; don't fork the daemon option c : use config ; default [dmon.conf,/etc/dmon/conf] =head1 DESCRIPTION Program B provides I on a collection of hosts. Each host runs C as a I. =over 4 =item * On each host, C is a I ; on one host C is also a I ; and one host is also the (web) I. =item * On each host, C monitors I ; things like I, I, the I of a couple of I etc. =item * Every minute, C computes a fresh value for each item, and stores these values in a local (sqlite) database. =item * Values of items have a I ; typically : fine soso sick critical dead The I of an item is determined by a configurable (per [host-]item) I. =item * Every 5 minutes C sends a I to the server ; this reports contains current information about the I. =item * If/when the fitness of an host-item changes, C sends an I to the server. On receiving events, the server may send mails to selected users, depending on the event (host, item, current level, previous level). =back In a few web-pages, B shows you : =over 4 =item * all B : host-items that aren't I =item * recent B ; host-item fitness changes =item * the B of each host-item ; in a graph ; per day, hour, day, month or year =item * various overviews ; items by host, hosts by item ; for various intervals =back I the B-system does, is determined by a I file. It contains (among other things) : =over 4 =item * a list of hosts and host-groups =item * for each host and/or host-group, the items it must monitor =item * the designated I =item * fitness-levels and (per host and/or item) fitness-functions =item * a list of users and user-groups =item * for all fitness-changes, the users that must receive an alert =back Program L reads the work-config file and generates a I for each client, the server and the pagemaker. The system is I in the sense that : =over 4 =item * each client-dmon runs on its own ; there is no central (work)flow-control =item * each client-dmon keeps its own history in a local (sqlite) database =item * each client-dmon can talk to the server ; the server can talk to each client ; all connections are short-lived. =back =head1 OPTIONS =over 4 =item -s be silent =item -q be quiet ; only errors =item -v be verbose ; show all actions =item -d show debug info ; internals =item -T trace ; show all debug info =item -h show help ; exit =item -t test config and work ; exit =item -i interactive ; don't fork the daemon =item -c use config I ; default : C, C =back =head1 CONFIG FILE =head2 location The default locations of the config file are : =over 4 =item * B<./dmon.conf> =item * B =back =head2 syntax A config file looks like this : +-------------------------------------------------- |# lines that start with '#' are comment |# blank lines are ignored too |# tabs are replaced by a space | |# the config entries are 'key' and 'value' pairs |# a 'key' begins in column 1 |# the 'value' is the rest of the line |somekey part1 part2 part3 ... |otherkey part1 part2 part3 ... | |# indented lines are glued |# the next three lines mean 'somekey part1 part2 part3' |somekey part1 | part2 | part3 +-------------------------------------------------- =head2 config file : required entries =over 4 =item server I Specify the (fully qualified) hostname of the server. =back =head2 config file : optional entries =over 4 =item hostname I =item domain I Specify a fully qualified hostname for the client. The default is : hostname `hostname` If C<`hostname`> is not fully qualified (does not contain a dot), B attempts to append a a domain-string. If defined, B uses config-option I ; otherwise file C is searched for a I list and the first name found used. If I<$hostname> resolves to a I, the pointed-to name is used. Using C tells you which (canonical) I<$hostname> B uses. =item PORT I Specify a number ; the default is : PORT 22007 The server listens for connections on port I<$PORT>. The client listens for connections on port I<$PORT+1>. =item ival_make_state I Specify the interval after which the client will compute the next I. The default is (one minute) : ival_make_state 1m An B can be given in seconds (as in B<22> or B<22s>), minutes [B], hours [B], days [B] and/or weeks [B]. The I can be combined in any order : dw # a day and a week 7d+24h # same thing w-0.5h # a week minus half an hour hm6 # 3666 seconds =item ival_send_report I Specify the interval after which the client will send the next I to the server. The default is : ival_send_report 5m =item ival_check_work I Specify the interval after which the client will request a fresh I from the server. The default is : ival_check_work 10m =item ival_keep_events I Specify how long the server must store events. The default is four weeks : ival_keep_events 4w The server cleans up the event-history hourly. =item loglvl Silent | Quiet | Terse | Verbose | Debug | Trace Specify a I ; the default is loglvl Terse =item plot_url I Script L generates references to plotter L as I<$url> ; the default is : plot_url /plotter.php A leading slash is interpreted as the DOCUMENTROOT of the vhost running L. =item rotate I I Specify parameters for I ; the default is rotate 8 1d On start-up, and after I seconds, B will rotate its logfile (C), saving I files. =item bindir I Specify the directory where the programs live. The default is : bindir /usr/sbin Option I<$bindir> is used by the C-facility. The server sends (and the clients installs in) I<$bindir>C. Option I<$bindir> is ignored by C. =item logdir I Specify the directory where the logfiles live. The default is : logdir /var/log/dmon =item vardir I Specify a B keeps it files. The default is : vardir /var/dmon =item rundir I Specify a I. The default is : rundir /var/run/dmon This is where the daemon keeps its files. =item lckdir I Specify a I. The default is : rundir /var/lock/subsys =item httpdgid group-name | number There is no default. Specify the group (name or number) under which the httpd is running on the pagemaker. Gid I<$httpdgid> is used by program L to make the user-database writable for the http-daemon. =item page_sec I Specify a (https) I ; there is no default. I<$page_sec> is used on the I by cgi-script L to refer to the secure dmon-web-site, where users can I. =back =head1 Client =head2 Client commands By default, the clients listens on port 22008 for connections. The client accepts connections from I and the server. It expects a I from the list below. To send a command to the client, use I (C) : echo | nc localhost 22007 echo | nc 22007 =over 4 =item * PING The server responds : COMMAND PING PONG from Client dmon-0.05-p179 COMMAND DONE =item * STATE The client responds with something like : COMMAND STATE -- version dmon-0.05-p184 -- logfile /var/log/dmon/dmon.log -- loglevel Trace -- listening on port 22008 as a Client -- Client is processing a command-session << 127.0.0.1 port 22008 -- hostname science-bs32.science.uu.nl -- server down.science.uu.nl -- work Thu Jan 28 14:21:27 2016 -- Client state : { ... } COMMAND DONE =item * STOP I Command C stops the client. On startup, the daemon generates a I and stores it (by default) in file C, mode C<0600>. This secret must be supplied in the C command. Since the secret is only available to the owner of the daemon, only the owner of the daemon can use the C command. =item * HIST [ ival [ pnts [ host [ name ... ] ] ] ] where =over 4 =item * I represents an interval : Bour, Bay, Beek, Bonth or Bear ; only the first letter is used ; default B =item * I is ignored ; default 100 =item * I is a hostname ; default : the client's canonical hostname =item * I is an item-name ; default : all item-names for I =back The client queries its history-database, grouping rows and averaging over groups. +----------+------------+------+ | Interval | Group by | rows | |----------+------------+------| | Hour | 1 minute | 60 | | Day | 10 minutes | 145 | | Week | 1 hour | 168 | | Month | 4 hours | 186 | | Year | 2 days | 184 | +----------+------------+------+ Note ; instead of these constants, we should be able to specify the approximate number of rows we want (I<$pnts>). There is a relation with C. The client responds with a json-encoded summary of the host-item history ; something like COMMAND HIST { "resp" : "ok rows 145" , "data" : { "cols" : [ "TIME", item1, ... ] , "rows" : [ [ time, value, ... ], ... ] } } COMMAND DONE where I is a list of (requested) item-names (ordered alphbetically), and I is a list of [time,values] tuples. The C command is used by the plotter to retrieve historical data from a client, so it can generate a plot. =item * SEND Command C instructs the client to send a report to the server. The client re-schedules sending its next report to a randomised time in the near future ; this avoids congestion on the server if/when all clients receive a C command as a result of a server's C command. The client responds with : COMMAND SEND next_send down.science.uu.nl Sat Feb 13 18:45:39 2016 COMMAND DONE =item * UPGRADE { "text" : program text } The client expects a json-coded fresh program-text. It saves and tests the new program and, if ok, installs the program, sends a reponse, and re-execs itself. The client responds with something like : COMMAND UPGRADE upgrade science-vs14.science.uu.nl ... ok dmon-0.05-p183 → dmon-0.05-p184 ... COMMAND DONE Note: by default (or when running as C), the server sends (and the client installs in) C. Note: a client accepts an C command only from the server. =item * SERVER I The client connects to the server and issues command I. The clients responds with the server's response. This facility is used for testing. =item * REPORT The client responds with a json-dump of its current state. This facility is used for testing. =item * ZAP The C command instructs the client to I its history database : the client reduces the number of rows in its history by replacing groups of rows by a single row containing the average values of the group. This facility is used for testing ; clients zaps their history just after startup, and subsequently every hour. =back =head1 Server =head2 Server commands By default, the server listens on port 22007 for connections. The server accepts connections from I and the clients. It expects a I from the list below. To send a command to the server, use I (C) : echo | nc localhost 22007 echo | nc 22007 =over 4 =item * PING The server responds : COMMAND PING PONG from Server COMMAND DONE =item * STATE The server responds with something like : COMMAND STATE -- version dmon-0.05-p179 -- logfile /var/log/dmon/dmon.log -- loglevel Trace -- listening on port 22008 as a Client -- listening on port 22007 as a Server -- Server is processing a command-session << 127.0.0.1 port 22007 -- Server state : keeping state for 74 clients COMMAND DONE =item * WORK I I where I == C | C | C The server responds with something like : COMMAND WORK { "resp" : "ok work" , "data" : contents of the work-file as a string , "lm" : work-file's last-modified timestamp } COMMAND DONE where (by default) the work-file is : /var/dmon/works//.txt On the server, C retrieves work-files from the file-system. The clients and the pagemaker retrieve a work-file from the server ; except when its (config) server is equal to its (canonical) hostname, in which case the work-file is retrieved from the file-system. =item * WORK_LM I I Same as command C except that I is always C. After startup, periodicly (default every 10 minutes), a client issues a I command to the server, which responds with the current last-modified timestamp of the client's work-file. If the timestamp has changed, the client reloads itself. =item * REPORT I The server expects a I : a one-line json-hash describing the current I of the hosts the client monitors (a client can monitor other (dumb) hosts like upses, using SNMP). The json-hash contains the I of one or more hosts : { host1 : { item1 : ... , ... } , host2 : ... } where per item it has a I, I, I etc. The server just timestamps and stores the hash ; it responds with something like : COMMAND REPORT { "resp": "ok report from [] for " , "work": } COMMAND DONE The I-element is the timestamp of the workfile of the requesting host. The client caches this info, to avoid a C request. The I retrieves the combined state of all the hosts with server-command B ; see below. =item * EVENTS I The server expects a list of I. Each event describes a change of the fitness of an item on a host ; attributes : I, I, I, I, I, I. The server stores the events in a database. The I may retrieve events from the server using the server command B ; see below. =item * CLIENTS [HOST] [ITEM] [interval-spec ∥ I] The server responds with something like : COMMAND CLIENTS { "resp" : "ok clients" , "cdmp" : { host : state of host, ... } , "events" : { recent events ; optionally selected by time or count } } COMMAND DONE If I is specified, I only contains the state of I. If I and/or I is specified, I only contains event pertaining to I and/or I. The default for the last argument is I<0> (meaning all events) ; if the argument looks like a number, only the last I events are sent ; otherwise only the events in the specified interval are sent. The C command is issued by the pagemaker when it generates a html report-page. =item * CLIENT I I The server connects with client I and issues command I. The server responds with the client's response. This facility enables a client-host to talk with another client-host. It is used by the pagemaker to retrieve history information from clients, unless the pagemaker runs on the same host as the server (in which case it can talk directly to any client). =item * ALLPING The server issues (in parallel) a C command to each client. The server's response is the concatenation of the client-responses. =item * ALLSEND The server issues (in parallel) a C command to each client. The server's response is the concatenation of the client-responses ; something like : COMMAND SEND next_send down.science.uu.nl Sat Feb 13 18:09:11 2016 ... COMMAND DONE On the server-host, the client sends an C to its server-part, on start-up and after a I ; so the server is quickly up-to-date. =item * UPGRADE =item * UPGRADE hostname The server issues a C command to each client (or only client I). Sent with the command is a B program text. The client saves and tests the program text and, if the text is ok, installs it and then re-execs itself. The server's response is the concatenation of the client-responses ; something like : COMMAND UPGRADE upgrade science-vs14.science.uu.nl ... ok dmon-0.05-p183 → dmon-0.05-p184 ... COMMAND DONE Note: by default (or when running as C), the server sends (and the client installs in) C. Note: the server accepts an C command only from I ; clients accept an C command only from the server. =back =head1 INSTALL =head2 Requirements =over 4 =item * Daemon C must run as C because many system diagnostic tools can only be used as C. =item * The I must be able to connect to all the clients on port B<22008>. The server listens on port B<22007> ; It allows connections from I and the clients, rejecting others. =item * A I must be able to connect to the server on port B<22007>. A client listens on port B<22008> ; it allows connections from I and the server, rejecting others. =item * Program C requires a bunch of perl modules, most of which are I (come with C) ; the others are widely available from platform-repo's. =item * A C system works best if the I runs on I hardware that is not dependent on other hosts you want to monitor. This gives you the best chance that monitoring is available when some calamity occurs. Whatever your initial setup may be, it is easy to (later) move the C-server to another host ; idem for the required web-service. =item * To use C, one of the monitored hosts must be a I ; it is efficient (but not necessary) to have the I and the I on the same host. =back =head2 Installation =head3 Installation - server =over 4 =item * Fetch the software Create some directory ; for instance C ; then get C : % rsync -avz dmon.science.uu.nl::dmon-dev/ /local/dmon/ =item * Install perl modules : % cd /local/dmon/ % perl dmon -v -t Perl will probably complain with something like : Can't locate xxx.pm ... ... where I is a missing modules. Install the module with your favorite tool (yum, apt-get), or use program C. Installing modules with C is usually horrible ; use C instead ; see the C file for hints ; also CPAN's L. If C fails, view the C-build-log ; some modules require C. =item * Install perl modules (repeat) Install missing perl modules until C complains about a missing I. =item * create a dmon-config file in C Supply the server's hostname or (preferably) a CNAME for your server-host, so you can switch later. server ... loglvl Verbose =item * create a dmon-work-config file in C # define a host host hostname-of-your-server myhost host hostname-of-your-webserver www pmaker www # set fitness levels fit_level fine fit_level soso fit_level sick fit_level crit critical fit_level dead # on myhost, monitor some items get myhost cpu_load root_avail root_usage uptime ... where I is a fully-quallified hostname, and I is just a unique tag, used to refer to the host. =item * create dmon work-files in C Run program L : % mk-dmon-work -v -f =item * try dmon Start the daemon ; for now, don't fork (use C<-i>) : % dmon start -i # stop it with ^C If all is well, you will see C start up, first as server, then as client. After a while, =over 4 =item * every minute : the daemon (as client) computes fresh item values ; =item * every five minutes : =over 4 =item * the daemon (as client) sends a report to the server ; =item * the daemon (as server) acceps the report, responds to the client ; =item * the client receives the response from the server ; closes the connection. =back =back =item * install C as a I =over 4 =item * copy C : % cp init.d /etc/init.d/dmon =item * Make sure C is started after a reboot ; use C : % chkconfig --add dmon or (on Ubuntu) : % update-rc.d dmon defaults 50 50 =back =back =head3 Installation - client =over 4 =item * Fetch the software Same as server-install. =item * Install missing perl modules Same as server-install. =item * create a dmon-config file in C Supply the server's hostname or (preferably) a CNAME for your server-host, so you can switch later. server ... Configure C if necessary ; see config option C. =item * install C as a I Same as server-install. =back =head3 Installation - pagemaker The I must be a dmon-host and a web-server. =over 4 =item * install L on the webserver =over 4 =item * Make sure the webserver runs L as a cgi-script. =item * Script L generates a login-reference ; preferably to a secure site ; configure something like : page_sec https://dmon.your.org/cgi-bin/gen-dmon-page Just use I instead of I if you don't have a secure site (yet). =back =item * install L on the webserver =over 4 =item * Make sure the webserver runs L as a php-script. =item * Script L generates references to the plotter as C. Configure option C to change that. =item * The plotter uses package L ; install the package and make sure the plotter can do : require_once ( 'jpgraph/jpgraph.php' ) ; require_once ( 'jpgraph/jpgraph_canvas.php' ) ; require_once ( 'jpgraph/jpgraph_line.php' ) ; require_once ( 'jpgraph/jpgraph_date.php' ) ; That is, if your plotter lives in : /path/to/plotter.php install C in : /path/to/jpgraph-/ and make a symlink : % ln -s /path/to/jpgraph-/src/ /path/to/jpgraph/ =back =back =head1 PRODUCTION Here are some tips for using L : =over 4 =item * In general, monitoring works best if the server runs on stand-alone hardware, that doesn't use any other resources. B is very light-weight, so some old box will probably work. =item * Dress up the server as a I and run the pagemaker (L and L) on the server ; this is a little more efficient. =item * To make installing B on clients easier, dress up the server as a I ; then : =over 4 =item * Create a module C<[dmon]> containing the downloaded B distro. =item * In module C<[dmon]> copy the C that comes with B, to file C : cp Makefile makefile Tweak the C to meet your local needs. Note : for C, the C takes precedence over C. =back On a client, rsync B from the rsync server : % mkdir -p /local/dmon/ % rsync -avz dmon.your.org::dmon/ /local/dmon/ Then, on the client you can run a C : % cd /local/dmon/ # repeat until all perl-modules are installed : % ./dmon -t -v % make install # start dmon % /etc/init.d/dmon start # fix iptables ; allow tcp connections from dmon.your.org:22008 =back =head1 SIGNALS HUP : dmon reloads ; dmon re-reads the config ; re-inits client and server USR1 : dmon re-execs ; dmon stops with END { exec $PROGRAM_NAME, @ARGV } =head1 FILES =over 4 =item * all hosts /etc/dmon/conf dmon-configuration /etc/init.d/dmon dmon start/stop script /var/dmon/data.lite database for item-history data /var/dmon/probes dmon probes ; installed by dmon on startup /var/lock/subsys/dmon touched on dmon-startup ; removed on stop /var/log/dmon logfiles /var/run/dmon/dmon.lck lock-file /var/run/dmon/dmon.pid pid-file /var/run/dmon/dmon.stp dmon stop-secret ; for client STOP-command =item * server only /etc/dmon/work work-configuration /var/dmon/works per/client work-files /var/dmon/works.tmp staging directory for /var/dmon/works =item * pagemaker only /var/dmon/cgi-data database for gen-dmon-page ; login/out-log /var/dmon/cgi-secret secret for cookie-verification ; should be 0600 =back =head1 SEE ALSO L, L, L =head1 LICENSE You may distribute under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl 5.10.0 README file. =head1 AUTHOR =for html dmon © 2015-2016 Henk P. Penning - All rights reserved ; dmon-0.05-p214 - Thu Oct 13 17:38:37 2016 =for man Henk P. Penning, ; dmon-0.05-p214 - Thu Oct 13 17:38:37 2016 =cut