123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490 |
- Program nutmon;
- {
- $Id$
- Simple nut ups monitor for netware, see http://www.networkupstools.org
- This program can be used to shut down a netware server on power
- failure. It requires nut to be installed on a *nix server (the serial
- or usb ups control is not connected to the netware server, this will
- be handled by the upsd on a *nix server)
- FreePascal >= 1.9.5 (http://www.freepascal.org) is needed to compile this.
- This source 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; either version 2 of the License, or
- (at your option) any later version.
- This code 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.
- A copy of the GNU General Public License is available on the World
- Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also
- obtain it by writing to the Free Software Foundation,
- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- First Version 2004/12/16 Armin Diehl <[email protected]>
- **********************************************************************}
- {$mode objfpc}
- {$M 65535,0,0}
- {$if defined(netware)}
- {$if defined(netware_clib)}
- {$description nut ups monitor - clib}
- {$else}
- {$description nut ups monitor - libc}
- {$endif}
- {$copyright Copyright 2004 Armin Diehl <[email protected]>}
- {$screenname DEFAULT} // dont use none because writeln will not work with none
- {$version 1.0.0}
- {$endif netware}
- uses
- sysutils, nutconnection, inifiles
- {$if defined(netware_libc)}
- ,libc
- {$elseif defined(netware_clib)}
- ,nwserv,nwnit
- {$endif}
- ;
- const
- CMD_NONE = 0;
- CMD_STATUS = 1;
- CMD_TESTSHUTDOWN = 2;
- var
- nut : TNutConnection;
- nutUser : string;
- nutPassword : string;
- nutPollfreq : integer;
- nutPollfreqAlert : integer;
- nutReconnectFreq : integer;
- nutUpsName : string;
- terminated : boolean = false;
- upsStatus,lastupsStatus : TUpsStatus;
- waitSemaphore: longint;
- commandAfterDown,powerOffFileName : ansistring;
- downIfCapaityBelow:integer = 0;
- {$if defined(netware)}
- CmdParserStruct : TcommandParserStructure;
- CurrentCommand : byte;
- oldNetwareUnloadProc : pointer;
- MainLoopTerminated : boolean = false;
- {$endif}
- const mainSection = 'nutmon';
- procedure readConfig;
- var fn : string;
- t : tiniFile;
- begin
- fn := ChangeFileExt(paramstr(0),'.ini');
- t := TIniFile.Create (fn);
- try
- nut.host := t.readString (mainSection,'host','');
- if nut.host = '' then
- begin
- writeln (stderr,paramstr(0)+': host= not specified in '+fn+' exiting');
- halt;
- end;
- nut.port := word (t.readInteger (mainSection,'port',NutDefaultPort));
- nutUser := t.readString (mainSection,'user','');
- if nutUser = '' then
- begin
- writeln (stderr,paramstr(0)+': user= not specified in '+fn+' exiting');
- halt;
- end;
- nutPassword := t.readString (mainSection,'password','');
- if nutPassword = '' then
- begin
- writeln (stderr,paramstr(0)+': password= not specified in '+fn+' exiting');
- halt;
- end;
- nutUpsName := t.readString (mainSection,'upsname','');
- if nutUpsname = '' then
- begin
- writeln (stderr,paramstr(0)+': upsname= not specified in '+fn+' exiting');
- halt;
- end;
- nutPollfreq := t.readInteger (mainSection,'pollfreq',10);
- nutPollfreqAlert := t.readInteger (mainSection,'pollfrqalert',5);
- nut.Debug := (t.readInteger (mainSection,'debug',0) > 0);
- commandAfterDown := t.readString (mainSection,'commandAfterDown','');
- nutReconnectFreq := t.readInteger (mainSection,'reconnectFreq',30);
- powerOffFileName := t.readString (mainSection,'createPoweroffFile','');
- downIfCapaityBelow := t.readInteger (mainSection,'downIfCapacityBelow',0);
- finally
- t.free;
- end;
- end;
- {$if defined(netware)}
- procedure onNetwareUnload;
- var i : integer;
- begin
- terminated := true;
- SignalLocalSemaphore (waitSemaphore); // this ends doDelay
- // here we wait for the main thread to terminate
- // we have to wait because system.pp will deinit winsock
- // to allow unload in case a blocking winsock call is
- // active. In case we wont wait here, our tcp socket
- // will be destroyed before we have the chance to send
- // a logout command to upsd
- i := 500;
- System.NetwareUnloadProc := oldNetwareUnloadProc;
- while (i > 0) and (not MainLoopTerminated) do
- begin
- dec(i);
- delay(500);
- end;
- end;
- {$endif}
- procedure doDelay (seconds : integer);
- {$if defined(netware)}
- begin
- TimedWaitOnLocalSemaphore (waitSemaphore,seconds*1000);
- end;
- {$else}
- var i : integer;
- begin
- i := seconds * 2;
- while (not terminated) and (i > 0) do
- begin
- sysutils.sleep(500);
- dec(i);
- end;
- end;
- {$endif}
- var lastAlert : TUpsStatus = [UPS_Online];
- procedure doAlert (status : TUpsStatus);
- {$if defined(netware)}
- var nwAlert : TNetWareAlertStructure;
- s : AnsiString;
- begin
- FillChar(nwAlert, sizeof(nwAlert),0);
- nwAlert.nwAlertID := ALERT_UPS;
- nwAlert.nwTargetNotificationBits := NOTIFY_ERROR_LOG_BIT+NOTIFY_CONSOLE_BIT;
- nwAlert.nwAlertLocus := LOCUS_UPS;
- nwAlert.nwAlertClass := CLASS_GENERAL_INFORMATION;
- nwAlert.nwAlertSeverity := SEVERITY_CRITICAL;
- if UPS_lowBatt in Status then
- s := 'UPS low Battery, shutting down' else
- if UPS_FSD in Status then
- s := 'UPS Forced Shuttdown' else
- if UPS_online in Status then
- s := 'Power/communication Restored, UPS is online' else
- if UPS_onBatt in Status then
- s := 'Power Failure, UPS is on battery' else
- if UPS_Stale in Status then
- s := 'Lost communication to UPS' else
- if UPS_Disconnected in Status then
- s := 'Lost communication to upsd';
- if lastAlert <> status then
- if (UPS_onBatt in Status) or
- (UPS_lowBatt in Status) or
- (UPS_FSD in Status) or
- (UPS_Online in Status) then
- nwAlert.nwTargetNotificationBits := nwAlert.nwTargetNotificationBits + NOTIFY_EVERYONE_BIT;
- lastAlert := status;
- nwAlert.nwControlString := pchar(s);
- NetWareAlert(GetNlmHandle, @nwAlert, 0, []);
- end;
- {$else}
- begin
- end;
- {$endif}
- procedure doStatusChange (newStatus,oldStatus : TUpsStatus);
- begin
- writeln (#13'nutmon: ups status change from '+UpsStatus2Txt (oldStatus)+' to '+UpsStatus2Txt (newStatus));
- doAlert (newStatus);
- end;
- procedure doShutdown (Reason : AnsiString = 'Server shutting down because of power failure');
- var err:integer;
- begin
- if poweroffFileName <> '' then
- begin
- err := FileCreate (powerOffFileName);
- if err <> -1 then
- FileClose (err)
- else
- writeln (#13,'nutmon: warning, can not create power off flag file ('+powerOffFileName+')');
- end;
- {$if defined(netware_clib)}
- SendConsoleBroadcast(pchar(Reason),0,nil);
- err := DownFileServer (1);
- try
- nut.login := false; // notify upds that we are shutting down
- writeln (#13'numon: informed upsd that we have done shutdown');
- except
- on e:Exception do
- begin
- writeln (#13'nutmon: got exception while trying to logout (',e.Message,')');
- try
- nut.connected := false;
- except
- end;
- end;
- end;
- if err = 0 then
- writeln (#13'nutmon: Server is down')
- else
- writeln (#13'nutmon: DownFileServer returned error ',Err);
- if commandAfterDown <> '' then
- nwserv._system (pchar(commandAfterDown));
- repeat
- sysutils.sleep(30);
- until false;
- {$elseif defined(netware_libc)}
- ShutdownServer(nil,false,nil,0);
- repeat
- sysutils.sleep(30);
- until false;
- {$else}
- writeln (stderr,'no shutdown call available, terminating');
- halt;
- {$endif}
- end;
- procedure mainLoop;
- var s : string;
- begin
- while not terminated do
- begin
- if not nut.connected then
- begin
- try
- nut.connected := true;
- try
- nut.upsName := nutUpsName;
- except
- if nut.LastResult <> NutDataStale then
- begin
- writeln(stderr,#13'invalid ups name, terminating');
- nut.free;
- halt;
- end else
- begin // special case: on start UPS is in stale status, disconnect and try later
- upsStatus := [UPS_Stale];
- if (upsStatus <> lastUpsStatus) then doStatusChange (upsStatus, lastUpsStatus);
- lastUpsStatus := upsStatus;
- nut.connected := false;
- end;
- end;
- try
- nut.UpsStatus;
- except
- on e:exception do
- begin
- writeln(stderr,#13'unable get ups status ('+e.Message+'), terminating');
- nut.free;
- halt;
- end;
- end;
- try
- nut.Username := nutUser;
- nut.Password := nutPassword;
- nut.Login := true;
- except
- on e:exception do
- begin
- writeln(stderr,#13'unable to login ('+e.Message+'), terminating');
- nut.free;
- halt;
- end;
- end;
- lastUpsStatus := [UPS_disconnected];
- WriteLn(#13'nutmon: connected to '+nutUpsName+'@'+nut.Host);
- except
- on e:exception do
- begin
- writeln (stderr,#13'nutmon: connect error, will retry in ',nutReconnectFreq,' seconds ('+e.message+')');
- doDelay (nutReconnectFreq);
- end;
- end;
- end else
- begin // we are connected, poll status
- try
- upsStatus := nut.upsStatus;
- if (upsStatus <> lastUpsStatus) then doStatusChange (upsStatus, lastUpsStatus);
- lastUpsStatus := upsStatus;
- if (UPS_lowBatt in upsStatus) or
- (UPS_FSD in upsStatus) then doShutdown;
- if downIfCapaityBelow > 0 then
- if (UPS_onBatt in upsStatus) then
- if nut.UpsChargeInt < downIfCapaityBelow then
- //writeln ('battery below ',downIfCapaityBelow);
- doShutdown ('Server shutting down,power failure and battery < '+IntToStr(downIfCapaityBelow)+'%');
- if UPS_online in upsStatus then
- doDelay (nutPollfreq)
- else
- doDelay (nutPollfreqAlert);
- except
- end;
- end;
- {$if defined(netware)}
- if CurrentCommand <> CMD_NONE then
- begin
- case CurrentCommand of
- CMD_STATUS: begin
- if nut.connected then
- begin
- writeln (#13'UPS Status:');
- writeln (' connected to: ',nut.UpsName+'@',nut.host,':',nut.Port);
- writeln (' UPS is: ',UpsStatus2Txt(nut.UpsStatus));
- try
- s := nut.upsMfr;
- writeln (' manufacturer: ',s);
- except
- end;
- try
- s := nut.upsModel;
- writeln (' model: ',s);
- except
- end;
- try
- s := nut.UpsLoad;
- writeln (' Percent load: ',s);
- except
- end;
- try
- s := nut.upsTemperature;
- writeln (' temp: ',s);
- except
- end;
- try
- s := nut.upsInputVoltage;
- writeln (' input Voltage: ',s);
- except
- end;
- try
- s := nut.upsOutputVoltage;
- writeln (' output Voltage: ',s);
- except
- end;
- try
- s := nut.upsInputFrequency;
- writeln ('input Frequency: ',s);
- except
- end;
- try
- s := nut.upsRuntime;
- writeln ('Battery Runtime: ',s);
- except
- end;
- try
- s := nut.upsCharge;
- writeln (' Battery Charge: ',s);
- except
- end;
- try
- s := nut.numLogins;
- writeln (' num Logins: ',s);
- except
- end;
- Writeln (nut.Version);
- end else
- writeln (#13'UPS Status: not connected to upsd');
- end;
- CMD_TESTSHUTDOWN:
- begin
- upsStatus := [UPS_FSD];
- doStatusChange (upsStatus, lastUpsStatus);
- doShutdown;
- end;
- end;
- CurrentCommand := CMD_NONE;
- end;
- {$endif}
- end;
- end;
- {$if defined(netware)}
- // handle the command "UPS STATUS"
- // only set the requested command and let the main thread handle it
- function UpsCommandlineParser (ScreenId : scr_t; commandLine : pchar) : longint; cdecl;
- begin
- if strlicomp(commandLine,'ups status',10) = 0 then
- begin
- result := HANDLEDCOMMAND;
- CurrentCommand := CMD_STATUS;
- SignalLocalSemaphore (waitSemaphore);
- end else
- if strlicomp(commandLine,'ups testshutdown',16) = 0 then
- begin
- result := HANDLEDCOMMAND;
- CurrentCommand := CMD_TESTSHUTDOWN;
- SignalLocalSemaphore (waitSemaphore);
- end else
- result := NOTMYCOMMAND;
- end;
- {$endif}
- begin
- try
- {$if defined(netware)}
- waitSemaphore := OpenLocalSemaphore (0);
- CmdParserStruct.Link := nil;
- CmdParserStruct.parseRoutine := @UpsCommandLineParser;
- CmdParserStruct.RTag := AllocateResourceTag (GetNlmHandle,'nutmon command line parser',ConsoleCommandSignature);
- if RegisterConsoleCommand(CmdParserStruct) <> 0 then
- writeln (stderr,#13'nutmon: RegisterConsoleCommand failed (ups status console command will not work)')
- else begin
- writeln (#13'nutmon console commands available:');
- writeln (#13'ups status - show ups status');
- writeln (#13'ups testshutdown - shutdown as if a low power condition is reached');
- writeln;
- end;
- CurrentCommand := CMD_NONE;
- oldNetwareUnloadProc := System.NetwareUnloadProc;
- System.NetwareUnloadProc := @onNetwareUnload;
- {$endif}
- nut := TNutConnection.create;
- readConfig;
- if poweroffFileName <> '' then
- if FileExists (powerOffFileName) then
- if not DeleteFile (powerOffFileName) then
- writeln (#13,'nutmon: warning, can not delete power off flag file ('+powerOffFileName+')');
- if downIfCapaityBelow > 0 then
- writeln (#13'nutmon: will shutdown if battery < ',downIfCapaityBelow,'%');
- mainLoop;
- nut.login := false;
- nut.connected := false;
- nut.free;
- finally
- {$if defined(netware)}
- CloseLocalSemaphore (waitSemaphore);
- UnRegisterConsoleCommand (CmdParserStruct);
- MainLoopTerminated := true;
- {$endif}
- end;
- end.
- {
- $Log$
- Revision 1.1 2004-12-29 21:39:53 armin
- * changed makefile version to 1.9.6, added samples for Netware
- }
|