2022-07-10 10:23:45 +03:00
/* upsd.c - watches ups state files and answers queries
2010-03-26 01:20:59 +02:00
Copyright ( C )
2012-06-01 16:55:19 +03:00
1999 Russell Kroll < rkroll @ exploits . org >
2008 Arjen de Korte < adkorte - guest @ alioth . debian . org >
2011 - 2012 Arnaud Quette < arnaud . quette . free . fr >
2022-07-10 10:23:45 +03:00
2019 Eaton ( author : Arnaud Quette < ArnaudQuette @ eaton . com > )
2010-03-26 01:20:59 +02:00
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 ; either version 2 of the License , or
( at your option ) any later version .
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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2022-07-10 10:23:45 +03:00
# include "config.h" /* must be the first header */
2010-03-26 01:20:59 +02:00
# include "upsd.h"
# include "upstype.h"
# include "conf.h"
# include "netcmds.h"
# include "upsconf.h"
# include <sys/un.h>
# include <sys/socket.h>
# include <netdb.h>
2022-07-10 10:23:45 +03:00
# ifdef HAVE_SYS_SIGNAL_H
# include <sys/signal.h>
# endif
# ifdef HAVE_SIGNAL_H
# include <signal.h>
# endif
2010-03-26 01:20:59 +02:00
# include "user.h"
2012-01-24 12:22:33 +02:00
# include "nut_ctype.h"
2022-07-10 10:23:45 +03:00
# include "nut_stdint.h"
2010-03-26 01:20:59 +02:00
# include "stype.h"
2012-08-13 00:39:31 +03:00
# include "netssl.h"
2010-03-26 01:20:59 +02:00
# include "sstate.h"
# include "desc.h"
# include "neterr.h"
# ifdef HAVE_WRAP
# include <tcpd.h>
int allow_severity = LOG_INFO ;
int deny_severity = LOG_WARNING ;
# endif /* HAVE_WRAP */
2022-07-10 10:23:45 +03:00
/* externally-visible settings and pointers */
upstype_t * firstups = NULL ;
/* default 15 seconds before data is marked stale */
int maxage = 15 ;
2010-03-26 01:20:59 +02:00
2022-07-10 10:23:45 +03:00
/* default to 1h before cleaning up status tracking entries */
int tracking_delay = 3600 ;
2010-03-26 01:20:59 +02:00
2022-07-10 10:23:45 +03:00
/*
* Preloaded to ALLOW_NO_DEVICE from upsd . conf or environment variable
* ( with higher prio for envvar ) ; defaults to disabled for legacy compat .
*/
int allow_no_device = 0 ;
2010-03-26 01:20:59 +02:00
2022-07-10 10:23:45 +03:00
/* preloaded to {OPEN_MAX} in main, can be overridden via upsd.conf */
nfds_t maxconn = 0 ;
2010-03-26 01:20:59 +02:00
2022-07-10 10:23:45 +03:00
/* preloaded to STATEPATH in main, can be overridden via upsd.conf */
char * statepath = NULL ;
2010-03-26 01:20:59 +02:00
2022-07-10 10:23:45 +03:00
/* preloaded to DATADIR in main, can be overridden via upsd.conf */
char * datapath = NULL ;
2010-03-26 01:20:59 +02:00
2022-07-10 10:23:45 +03:00
/* everything else */
static const char * progname ;
2010-03-26 01:20:59 +02:00
2012-06-01 16:55:19 +03:00
nut_ctype_t * firstclient = NULL ;
2012-01-24 12:22:33 +02:00
/* static nut_ctype_t *lastclient = NULL; */
2010-03-26 01:20:59 +02:00
2022-07-10 10:23:45 +03:00
/* default is to listen on all local interfaces */
2010-03-26 01:20:59 +02:00
static stype_t * firstaddr = NULL ;
static int opt_af = AF_UNSPEC ;
typedef enum {
DRIVER = 1 ,
CLIENT ,
SERVER
} handler_type_t ;
typedef struct {
handler_type_t type ;
void * data ;
} handler_t ;
2022-07-10 10:23:45 +03:00
/* Commands and settings status tracking */
/* general enable/disable status info for commands and settings
* ( disabled by default )
* Note that only client that requested it will have it enabled
* ( see nut_ctype . h ) */
static int tracking_enabled = 0 ;
/* Commands and settings status tracking structure */
typedef struct tracking_s {
char * id ;
int status ;
time_t request_time ; /* for cleanup */
/* doubly linked list */
struct tracking_s * prev ;
struct tracking_s * next ;
} tracking_t ;
static tracking_t * tracking_list = NULL ;
2010-03-26 01:20:59 +02:00
/* pollfd */
static struct pollfd * fds = NULL ;
static handler_t * handler = NULL ;
/* pid file */
static char pidfn [ SMALLBUF ] ;
/* set by signal handlers */
static int reload_flag = 0 , exit_flag = 0 ;
2022-07-10 10:23:45 +03:00
/* Minimalistic support for UUID v4 */
/* Ref: RFC 4122 https://tools.ietf.org/html/rfc4122#section-4.1.2 */
# define UUID4_BYTESIZE 16
2010-03-26 01:20:59 +02:00
static const char * inet_ntopW ( struct sockaddr_storage * s )
{
static char str [ 40 ] ;
switch ( s - > ss_family )
{
case AF_INET :
return inet_ntop ( AF_INET , & ( ( ( struct sockaddr_in * ) s ) - > sin_addr ) , str , 16 ) ;
case AF_INET6 :
return inet_ntop ( AF_INET6 , & ( ( ( struct sockaddr_in6 * ) s ) - > sin6_addr ) , str , 40 ) ;
default :
errno = EAFNOSUPPORT ;
return NULL ;
}
}
/* return a pointer to the named ups if possible */
upstype_t * get_ups_ptr ( const char * name )
{
upstype_t * tmp ;
if ( ! name ) {
return NULL ;
}
for ( tmp = firstups ; tmp ; tmp = tmp - > next ) {
if ( ! strcasecmp ( tmp - > name , name ) ) {
return tmp ;
}
}
return NULL ;
}
/* mark the data stale if this is new, otherwise cleanup any remaining junk */
static void ups_data_stale ( upstype_t * ups )
{
/* don't complain again if it's already known to be stale */
if ( ups - > stale = = 1 ) {
return ;
}
ups - > stale = 1 ;
upslogx ( LOG_NOTICE , " Data for UPS [%s] is stale - check driver " , ups - > name ) ;
}
/* mark the data ok if this is new, otherwise do nothing */
static void ups_data_ok ( upstype_t * ups )
{
if ( ups - > stale = = 0 ) {
return ;
}
ups - > stale = 0 ;
upslogx ( LOG_NOTICE , " UPS [%s] data is no longer stale " , ups - > name ) ;
}
/* add another listening address */
void listen_add ( const char * addr , const char * port )
{
stype_t * server ;
/* don't change listening addresses on reload */
if ( reload_flag ) {
return ;
}
/* grab some memory and add the info */
server = xcalloc ( 1 , sizeof ( * server ) ) ;
server - > addr = xstrdup ( addr ) ;
server - > port = xstrdup ( port ) ;
server - > sock_fd = - 1 ;
server - > next = firstaddr ;
firstaddr = server ;
upsdebugx ( 3 , " listen_add: added %s:%s " , server - > addr , server - > port ) ;
}
/* create a listening socket for tcp connections */
static void setuptcp ( stype_t * server )
{
struct addrinfo hints , * res , * ai ;
int v = 0 , one = 1 ;
upsdebugx ( 3 , " setuptcp: try to bind to %s port %s " , server - > addr , server - > port ) ;
memset ( & hints , 0 , sizeof ( hints ) ) ;
hints . ai_flags = AI_PASSIVE ;
hints . ai_family = opt_af ;
hints . ai_socktype = SOCK_STREAM ;
hints . ai_protocol = IPPROTO_TCP ;
if ( ( v = getaddrinfo ( server - > addr , server - > port , & hints , & res ) ) ! = 0 ) {
if ( v = = EAI_SYSTEM ) {
fatal_with_errno ( EXIT_FAILURE , " getaddrinfo " ) ;
}
fatalx ( EXIT_FAILURE , " getaddrinfo: %s " , gai_strerror ( v ) ) ;
}
for ( ai = res ; ai ; ai = ai - > ai_next ) {
int sock_fd ;
if ( ( sock_fd = socket ( ai - > ai_family , ai - > ai_socktype , ai - > ai_protocol ) ) < 0 ) {
upsdebug_with_errno ( 3 , " setuptcp: socket " ) ;
continue ;
}
2022-07-10 10:23:45 +03:00
2010-03-26 01:20:59 +02:00
if ( setsockopt ( sock_fd , SOL_SOCKET , SO_REUSEADDR , ( void * ) & one , sizeof ( one ) ) ! = 0 ) {
fatal_with_errno ( EXIT_FAILURE , " setuptcp: setsockopt " ) ;
}
if ( bind ( sock_fd , ai - > ai_addr , ai - > ai_addrlen ) < 0 ) {
upsdebug_with_errno ( 3 , " setuptcp: bind " ) ;
close ( sock_fd ) ;
continue ;
}
if ( ( v = fcntl ( sock_fd , F_GETFL , 0 ) ) = = - 1 ) {
fatal_with_errno ( EXIT_FAILURE , " setuptcp: fcntl(get) " ) ;
}
if ( fcntl ( sock_fd , F_SETFL , v | O_NDELAY ) = = - 1 ) {
fatal_with_errno ( EXIT_FAILURE , " setuptcp: fcntl(set) " ) ;
}
if ( listen ( sock_fd , 16 ) < 0 ) {
upsdebug_with_errno ( 3 , " setuptcp: listen " ) ;
close ( sock_fd ) ;
continue ;
}
server - > sock_fd = sock_fd ;
break ;
}
freeaddrinfo ( res ) ;
2012-01-24 12:22:33 +02:00
/* leave up to the caller, server_load(), to fail silently if there is
* no other valid LISTEN interface */
2010-03-26 01:20:59 +02:00
if ( server - > sock_fd < 0 ) {
2012-01-24 12:22:33 +02:00
upslogx ( LOG_ERR , " not listening on %s port %s " , server - > addr , server - > port ) ;
2010-03-26 01:20:59 +02:00
} else {
upslogx ( LOG_INFO , " listening on %s port %s " , server - > addr , server - > port ) ;
}
return ;
}
/* decrement the login counter for this ups */
static void declogins ( const char * upsname )
{
upstype_t * ups ;
ups = get_ups_ptr ( upsname ) ;
if ( ! ups ) {
upslogx ( LOG_INFO , " Tried to decrement invalid ups name (%s) " , upsname ) ;
return ;
}
ups - > numlogins - - ;
if ( ups - > numlogins < 0 ) {
upslogx ( LOG_ERR , " Programming error: UPS [%s] has numlogins=%d " , ups - > name , ups - > numlogins ) ;
}
}
/* disconnect a client connection and free all related memory */
2012-01-24 12:22:33 +02:00
static void client_disconnect ( nut_ctype_t * client )
2010-03-26 01:20:59 +02:00
{
if ( ! client ) {
return ;
}
upsdebugx ( 2 , " Disconnect from %s " , client - > addr ) ;
shutdown ( client - > sock_fd , 2 ) ;
close ( client - > sock_fd ) ;
if ( client - > loginups ) {
declogins ( client - > loginups ) ;
}
ssl_finish ( client ) ;
pconf_finish ( & client - > ctx ) ;
if ( client - > prev ) {
client - > prev - > next = client - > next ;
} else {
/* deleting first entry */
firstclient = client - > next ;
}
if ( client - > next ) {
client - > next - > prev = client - > prev ;
} else {
/* deleting last entry */
/* lastclient = client->prev; */
}
free ( client - > addr ) ;
free ( client - > loginups ) ;
free ( client - > password ) ;
free ( client - > username ) ;
free ( client ) ;
return ;
}
2022-07-10 10:23:45 +03:00
/* send the buffer <sendbuf> of length <sendlen> to host <dest>
* returns effectively a boolean : 0 = failed , 1 = sent ok
*/
2012-01-24 12:22:33 +02:00
int sendback ( nut_ctype_t * client , const char * fmt , . . . )
2010-03-26 01:20:59 +02:00
{
2022-07-10 10:23:45 +03:00
ssize_t res ;
size_t len ;
char ans [ NUT_NET_ANSWER_MAX + 1 ] ;
va_list ap ;
2010-03-26 01:20:59 +02:00
if ( ! client ) {
return 0 ;
}
va_start ( ap , fmt ) ;
vsnprintf ( ans , sizeof ( ans ) , fmt , ap ) ;
va_end ( ap ) ;
len = strlen ( ans ) ;
2022-07-10 10:23:45 +03:00
/* System write() and our ssl_write() have a loophole that they write a
* size_t amount of bytes and upon success return that in ssize_t value
*/
assert ( len < SSIZE_MAX ) ;
2013-11-24 17:00:12 +02:00
# ifdef WITH_SSL
2010-03-26 01:20:59 +02:00
if ( client - > ssl ) {
res = ssl_write ( client , ans , len ) ;
2022-07-10 10:23:45 +03:00
} else
2013-11-24 17:00:12 +02:00
# endif /* WITH_SSL */
{
2010-03-26 01:20:59 +02:00
res = write ( client - > sock_fd , ans , len ) ;
}
2022-07-10 10:23:45 +03:00
upsdebugx ( 2 , " write: [destfd=%d] [len=%zu] [%s] " , client - > sock_fd , len , str_rtrim ( ans , ' \n ' ) ) ;
2010-03-26 01:20:59 +02:00
2022-07-10 10:23:45 +03:00
if ( res < 0 | | len ! = ( size_t ) res ) {
2010-03-26 01:20:59 +02:00
upslog_with_errno ( LOG_NOTICE , " write() failed for %s " , client - > addr ) ;
client - > last_heard = 0 ;
return 0 ; /* failed */
}
return 1 ; /* OK */
}
/* just a simple wrapper for now */
2012-01-24 12:22:33 +02:00
int send_err ( nut_ctype_t * client , const char * errtype )
2010-03-26 01:20:59 +02:00
{
if ( ! client ) {
return - 1 ;
}
upsdebugx ( 4 , " Sending error [%s] to client %s " , errtype , client - > addr ) ;
return sendback ( client , " ERR %s \n " , errtype ) ;
}
/* disconnect anyone logged into this UPS */
void kick_login_clients ( const char * upsname )
{
2012-01-24 12:22:33 +02:00
nut_ctype_t * client , * cnext ;
2010-03-26 01:20:59 +02:00
for ( client = firstclient ; client ; client = cnext ) {
cnext = client - > next ;
/* if it's not logged in, don't check it */
if ( ! client - > loginups ) {
continue ;
}
if ( ! strcmp ( client - > loginups , upsname ) ) {
upslogx ( LOG_INFO , " Kicking client %s (was on UPS [%s]) \n " , client - > addr , upsname ) ;
client_disconnect ( client ) ;
}
}
}
/* make sure a UPS is sane - connected, with fresh data */
2012-01-24 12:22:33 +02:00
int ups_available ( const upstype_t * ups , nut_ctype_t * client )
2010-03-26 01:20:59 +02:00
{
if ( ups - > sock_fd < 0 ) {
send_err ( client , NUT_ERR_DRIVER_NOT_CONNECTED ) ;
return 0 ;
}
if ( ups - > stale ) {
send_err ( client , NUT_ERR_DATA_STALE ) ;
return 0 ;
}
/* must be OK */
return 1 ;
}
/* check flags and access for an incoming command from the network */
2022-07-10 10:23:45 +03:00
static void check_command ( int cmdnum , nut_ctype_t * client , size_t numarg ,
2010-03-26 01:20:59 +02:00
const char * * arg )
{
2022-07-10 10:23:45 +03:00
upsdebugx ( 6 , " Entering %s: %s " , __func__ , numarg > 0 ? arg [ 0 ] : " <> " ) ;
2010-03-26 01:20:59 +02:00
if ( netcmds [ cmdnum ] . flags & FLAG_USER ) {
2022-07-10 10:23:45 +03:00
/* command requires previous authentication */
2010-03-26 01:20:59 +02:00
# ifdef HAVE_WRAP
struct request_info req ;
# endif /* HAVE_WRAP */
if ( ! client - > username ) {
2022-07-10 10:23:45 +03:00
upsdebugx ( 1 , " %s: client not logged in yet " , __func__ ) ;
2010-03-26 01:20:59 +02:00
send_err ( client , NUT_ERR_USERNAME_REQUIRED ) ;
return ;
}
if ( ! client - > password ) {
2022-07-10 10:23:45 +03:00
upsdebugx ( 1 , " %s: client not logged in yet " , __func__ ) ;
2010-03-26 01:20:59 +02:00
send_err ( client , NUT_ERR_PASSWORD_REQUIRED ) ;
return ;
}
# ifdef HAVE_WRAP
2011-01-26 11:35:08 +02:00
request_init ( & req , RQ_DAEMON , progname , RQ_FILE , client - > sock_fd , RQ_USER , client - > username , 0 ) ;
fromhost ( & req ) ;
2010-03-26 01:20:59 +02:00
if ( ! hosts_access ( & req ) ) {
2022-07-10 10:23:45 +03:00
upsdebugx ( 1 ,
" %s: while authenticating %s found that "
" tcp-wrappers says access should be denied " ,
__func__ , client - > username ) ;
2010-03-26 01:20:59 +02:00
send_err ( client , NUT_ERR_ACCESS_DENIED ) ;
return ;
}
# endif /* HAVE_WRAP */
}
2022-07-10 10:23:45 +03:00
upsdebugx ( 6 , " %s: Calling command handler for %s " , __func__ , numarg > 0 ? arg [ 0 ] : " <> " ) ;
2010-03-26 01:20:59 +02:00
/* looks good - call the command */
2022-07-10 10:23:45 +03:00
netcmds [ cmdnum ] . func ( client , ( numarg < 2 ) ? 0 : ( numarg - 1 ) , ( numarg > 1 ) ? & arg [ 1 ] : NULL ) ;
2010-03-26 01:20:59 +02:00
}
/* parse requests from the network */
2012-01-24 12:22:33 +02:00
static void parse_net ( nut_ctype_t * client )
2010-03-26 01:20:59 +02:00
{
int i ;
/* shouldn't happen */
if ( client - > ctx . numargs < 1 ) {
send_err ( client , NUT_ERR_UNKNOWN_COMMAND ) ;
return ;
}
for ( i = 0 ; netcmds [ i ] . name ; i + + ) {
if ( ! strcasecmp ( netcmds [ i ] . name , client - > ctx . arglist [ 0 ] ) ) {
check_command ( i , client , client - > ctx . numargs , ( const char * * ) client - > ctx . arglist ) ;
return ;
}
}
/* fallthrough = not matched by any entry in netcmds */
send_err ( client , NUT_ERR_UNKNOWN_COMMAND ) ;
}
/* answer incoming tcp connections */
static void client_connect ( stype_t * server )
{
struct sockaddr_storage csock ;
2022-07-10 10:23:45 +03:00
# if defined(__hpux) && !defined(_XOPEN_SOURCE_EXTENDED)
2011-06-01 23:31:49 +03:00
int clen ;
# else
2010-03-26 01:20:59 +02:00
socklen_t clen ;
2011-06-01 23:31:49 +03:00
# endif
2010-03-26 01:20:59 +02:00
int fd ;
2012-01-24 12:22:33 +02:00
nut_ctype_t * client ;
2010-03-26 01:20:59 +02:00
clen = sizeof ( csock ) ;
fd = accept ( server - > sock_fd , ( struct sockaddr * ) & csock , & clen ) ;
if ( fd < 0 ) {
return ;
}
client = xcalloc ( 1 , sizeof ( * client ) ) ;
client - > sock_fd = fd ;
time ( & client - > last_heard ) ;
client - > addr = xstrdup ( inet_ntopW ( & csock ) ) ;
2022-07-10 10:23:45 +03:00
client - > tracking = 0 ;
2010-03-26 01:20:59 +02:00
pconf_init ( & client - > ctx , NULL ) ;
if ( firstclient ) {
firstclient - > prev = client ;
client - > next = firstclient ;
}
firstclient = client ;
/*
if ( lastclient ) {
client - > prev = lastclient ;
lastclient - > next = client ;
}
lastclient = client ;
*/
upsdebugx ( 2 , " Connect from %s " , client - > addr ) ;
}
/* read tcp messages and handle them */
2012-01-24 12:22:33 +02:00
static void client_readline ( nut_ctype_t * client )
2010-03-26 01:20:59 +02:00
{
char buf [ SMALLBUF ] ;
2022-07-10 10:23:45 +03:00
int i ;
ssize_t ret ;
2010-03-26 01:20:59 +02:00
2013-11-24 17:00:12 +02:00
# ifdef WITH_SSL
2010-03-26 01:20:59 +02:00
if ( client - > ssl ) {
ret = ssl_read ( client , buf , sizeof ( buf ) ) ;
2022-07-10 10:23:45 +03:00
} else
2013-11-24 17:00:12 +02:00
# endif /* WITH_SSL */
{
2010-03-26 01:20:59 +02:00
ret = read ( client - > sock_fd , buf , sizeof ( buf ) ) ;
}
if ( ret < 0 ) {
upsdebug_with_errno ( 2 , " Disconnect %s (read failure) " , client - > addr ) ;
client_disconnect ( client ) ;
return ;
}
if ( ret = = 0 ) {
upsdebugx ( 2 , " Disconnect %s (no data available) " , client - > addr ) ;
client_disconnect ( client ) ;
return ;
}
/* fragment handling code */
for ( i = 0 ; i < ret ; i + + ) {
/* add to the receive queue one by one */
switch ( pconf_char ( & client - > ctx , buf [ i ] ) )
{
case 1 :
time ( & client - > last_heard ) ; /* command received */
parse_net ( client ) ;
continue ;
case 0 :
continue ; /* haven't gotten a line yet */
default :
/* parse error */
upslogx ( LOG_NOTICE , " Parse error on sock: %s " , client - > ctx . errmsg ) ;
return ;
}
}
return ;
}
void server_load ( void )
{
stype_t * server ;
/* default behaviour if no LISTEN addres has been specified */
if ( ! firstaddr ) {
if ( opt_af ! = AF_INET ) {
listen_add ( " ::1 " , string_const ( PORT ) ) ;
}
if ( opt_af ! = AF_INET6 ) {
listen_add ( " 127.0.0.1 " , string_const ( PORT ) ) ;
}
}
for ( server = firstaddr ; server ; server = server - > next ) {
setuptcp ( server ) ;
}
2022-07-10 10:23:45 +03:00
2012-01-24 12:22:33 +02:00
/* check if we have at least 1 valid LISTEN interface */
if ( firstaddr - > sock_fd < 0 ) {
fatalx ( EXIT_FAILURE , " no listening interface available " ) ;
}
2010-03-26 01:20:59 +02:00
}
void server_free ( void )
{
stype_t * server , * snext ;
/* cleanup server fds */
for ( server = firstaddr ; server ; server = snext ) {
snext = server - > next ;
if ( server - > sock_fd ! = - 1 ) {
close ( server - > sock_fd ) ;
}
free ( server - > addr ) ;
free ( server - > port ) ;
free ( server ) ;
}
firstaddr = NULL ;
}
static void client_free ( void )
{
2012-01-24 12:22:33 +02:00
nut_ctype_t * client , * cnext ;
2010-03-26 01:20:59 +02:00
/* cleanup client fds */
for ( client = firstclient ; client ; client = cnext ) {
cnext = client - > next ;
client_disconnect ( client ) ;
}
}
2022-07-10 10:23:45 +03:00
static void driver_free ( void )
2010-03-26 01:20:59 +02:00
{
upstype_t * ups , * unext ;
for ( ups = firstups ; ups ; ups = unext ) {
2022-07-10 10:23:45 +03:00
upsdebugx ( 1 , " %s: forgetting UPS [%s] (FD %d) " ,
__func__ , ups - > name , ups - > sock_fd ) ;
2010-03-26 01:20:59 +02:00
unext = ups - > next ;
if ( ups - > sock_fd ! = - 1 ) {
close ( ups - > sock_fd ) ;
}
sstate_infofree ( ups ) ;
sstate_cmdfree ( ups ) ;
pconf_finish ( & ups - > sock_ctx ) ;
free ( ups - > fn ) ;
free ( ups - > name ) ;
free ( ups - > desc ) ;
free ( ups ) ;
}
}
static void upsd_cleanup ( void )
{
if ( strlen ( pidfn ) > 0 ) {
unlink ( pidfn ) ;
}
/* dump everything */
user_flush ( ) ;
desc_free ( ) ;
2022-07-10 10:23:45 +03:00
2010-03-26 01:20:59 +02:00
server_free ( ) ;
client_free ( ) ;
driver_free ( ) ;
2022-07-10 10:23:45 +03:00
tracking_free ( ) ;
2010-03-26 01:20:59 +02:00
free ( statepath ) ;
free ( datapath ) ;
free ( certfile ) ;
2013-11-24 17:00:12 +02:00
free ( certname ) ;
free ( certpasswd ) ;
2010-03-26 01:20:59 +02:00
free ( fds ) ;
free ( handler ) ;
}
2022-07-10 10:23:45 +03:00
static void poll_reload ( void )
2010-03-26 01:20:59 +02:00
{
2022-07-10 10:23:45 +03:00
long ret ;
2010-03-26 01:20:59 +02:00
ret = sysconf ( _SC_OPEN_MAX ) ;
2022-07-10 10:23:45 +03:00
if ( ( intmax_t ) ret < ( intmax_t ) maxconn ) {
fatalx ( EXIT_FAILURE ,
" Your system limits the maximum number of connections to %ld \n "
" but you requested %jd. The server won't start until this \n "
" problem is resolved. \n " , ret , ( intmax_t ) maxconn ) ;
}
if ( 1 > maxconn ) {
2010-03-26 01:20:59 +02:00
fatalx ( EXIT_FAILURE ,
2022-07-10 10:23:45 +03:00
" You requested %jd as maximum number of connections. \n "
" The server won't start until this problem is resolved. \n " , ( intmax_t ) maxconn ) ;
}
/* How many items can we stuff into the array? */
size_t maxalloc = SIZE_MAX / sizeof ( void * ) ;
if ( ( uintmax_t ) maxalloc < ( uintmax_t ) maxconn ) {
fatalx ( EXIT_FAILURE ,
" You requested %jd as maximum number of connections, but we can only allocate %zu. \n "
" The server won't start until this problem is resolved. \n " , ( intmax_t ) maxconn , maxalloc ) ;
}
/* The checks above effectively limit that maxconn is in size_t range */
fds = xrealloc ( fds , ( size_t ) maxconn * sizeof ( * fds ) ) ;
handler = xrealloc ( handler , ( size_t ) maxconn * sizeof ( * handler ) ) ;
}
/* instant command and setvar status tracking */
/* allocate a new status tracking entry */
int tracking_add ( const char * id )
{
tracking_t * item ;
if ( ( ! tracking_enabled ) | | ( ! id ) )
return 0 ;
item = xcalloc ( 1 , sizeof ( * item ) ) ;
item - > id = xstrdup ( id ) ;
item - > status = STAT_PENDING ;
time ( & item - > request_time ) ;
if ( tracking_list ) {
tracking_list - > prev = item ;
item - > next = tracking_list ;
}
tracking_list = item ;
return 1 ;
}
/* set status of a specific tracking entry */
int tracking_set ( const char * id , const char * value )
{
tracking_t * item , * next_item ;
/* sanity checks */
if ( ( ! tracking_list ) | | ( ! id ) | | ( ! value ) )
return 0 ;
for ( item = tracking_list ; item ; item = next_item ) {
next_item = item - > next ;
if ( ! strcasecmp ( item - > id , id ) ) {
item - > status = atoi ( value ) ;
return 1 ;
}
2010-03-26 01:20:59 +02:00
}
2022-07-10 10:23:45 +03:00
return 0 ; /* id not found! */
}
/* free a specific tracking entry */
int tracking_del ( const char * id )
{
tracking_t * item , * next_item ;
/* sanity check */
if ( ( ! tracking_list ) | | ( ! id ) )
return 0 ;
upsdebugx ( 3 , " %s: deleting id %s " , __func__ , id ) ;
for ( item = tracking_list ; item ; item = next_item ) {
next_item = item - > next ;
if ( strcasecmp ( item - > id , id ) )
continue ;
if ( item - > prev )
item - > prev - > next = item - > next ;
else
/* deleting first entry */
tracking_list = item - > next ;
if ( item - > next )
item - > next - > prev = item - > prev ;
free ( item - > id ) ;
free ( item ) ;
return 1 ;
}
return 0 ; /* id not found! */
}
/* free all status tracking entries */
void tracking_free ( void )
{
tracking_t * item , * next_item ;
/* sanity check */
if ( ! tracking_list )
return ;
upsdebugx ( 3 , " %s " , __func__ ) ;
for ( item = tracking_list ; item ; item = next_item ) {
next_item = item - > next ;
tracking_del ( item - > id ) ;
}
}
/* cleanup status tracking entries according to their age and tracking_delay */
void tracking_cleanup ( void )
{
tracking_t * item , * next_item ;
time_t now ;
/* sanity check */
if ( ! tracking_list )
return ;
time ( & now ) ;
upsdebugx ( 3 , " %s " , __func__ ) ;
for ( item = tracking_list ; item ; item = next_item ) {
next_item = item - > next ;
if ( difftime ( now , item - > request_time ) > tracking_delay ) {
tracking_del ( item - > id ) ;
}
}
}
/* get status of a specific tracking entry */
char * tracking_get ( const char * id )
{
tracking_t * item , * next_item ;
/* sanity checks */
if ( ( ! tracking_list ) | | ( ! id ) )
return " ERR UNKNOWN " ;
for ( item = tracking_list ; item ; item = next_item ) {
next_item = item - > next ;
if ( strcasecmp ( item - > id , id ) )
continue ;
switch ( item - > status )
{
case STAT_PENDING :
return " PENDING " ;
case STAT_HANDLED :
return " SUCCESS " ;
case STAT_UNKNOWN :
return " ERR UNKNOWN " ;
case STAT_INVALID :
return " ERR INVALID-ARGUMENT " ;
case STAT_FAILED :
return " ERR FAILED " ;
}
}
return " ERR UNKNOWN " ; /* id not found! */
}
/* enable general status tracking (tracking_enabled) and return its value (1). */
int tracking_enable ( void )
{
tracking_enabled = 1 ;
return tracking_enabled ;
}
/* disable general status tracking only if no client use it anymore.
* return the new value for tracking_enabled */
int tracking_disable ( void )
{
nut_ctype_t * client , * cnext ;
for ( client = firstclient ; client ; client = cnext ) {
cnext = client - > next ;
if ( client - > tracking = = 1 )
return 1 ;
}
return 0 ;
}
/* return current general status of tracking (tracking_enabled). */
int tracking_is_enabled ( void )
{
return tracking_enabled ;
}
/* UUID v4 basic implementation
* Note : ' dest ' must be at least ` UUID4_LEN ` long */
int nut_uuid_v4 ( char * uuid_str )
{
size_t i ;
uint8_t nut_uuid [ UUID4_BYTESIZE ] ;
if ( ! uuid_str )
return 0 ;
for ( i = 0 ; i < UUID4_BYTESIZE ; i + + )
nut_uuid [ i ] = ( uint8_t ) rand ( ) + ( uint8_t ) rand ( ) ;
/* set variant and version */
nut_uuid [ 6 ] = ( nut_uuid [ 6 ] & 0x0F ) | 0x40 ;
nut_uuid [ 8 ] = ( nut_uuid [ 8 ] & 0x3F ) | 0x80 ;
return snprintf ( uuid_str , UUID4_LEN ,
" %02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X " ,
nut_uuid [ 0 ] , nut_uuid [ 1 ] , nut_uuid [ 2 ] , nut_uuid [ 3 ] ,
nut_uuid [ 4 ] , nut_uuid [ 5 ] , nut_uuid [ 6 ] , nut_uuid [ 7 ] ,
nut_uuid [ 8 ] , nut_uuid [ 9 ] , nut_uuid [ 10 ] , nut_uuid [ 11 ] ,
nut_uuid [ 12 ] , nut_uuid [ 13 ] , nut_uuid [ 14 ] , nut_uuid [ 15 ] ) ;
2010-03-26 01:20:59 +02:00
}
/* service requests and check on new data */
static void mainloop ( void )
{
2022-07-10 10:23:45 +03:00
int ret ;
nfds_t i , nfds = 0 ;
2010-03-26 01:20:59 +02:00
upstype_t * ups ;
2012-01-24 12:22:33 +02:00
nut_ctype_t * client , * cnext ;
2010-03-26 01:20:59 +02:00
stype_t * server ;
time_t now ;
time ( & now ) ;
if ( reload_flag ) {
conf_reload ( ) ;
poll_reload ( ) ;
reload_flag = 0 ;
}
2022-07-10 10:23:45 +03:00
/* cleanup instcmd/setvar status tracking entries if needed */
tracking_cleanup ( ) ;
2010-03-26 01:20:59 +02:00
/* scan through driver sockets */
for ( ups = firstups ; ups & & ( nfds < maxconn ) ; ups = ups - > next ) {
/* see if we need to (re)connect to the socket */
if ( ups - > sock_fd < 0 ) {
2022-07-10 10:23:45 +03:00
upsdebugx ( 1 , " %s: UPS [%s] is not currently connected " ,
__func__ , ups - > name ) ;
2010-03-26 01:20:59 +02:00
ups - > sock_fd = sstate_connect ( ups ) ;
2022-07-10 10:23:45 +03:00
upsdebugx ( 1 , " %s: UPS [%s] is now connected as FD %d " ,
__func__ , ups - > name , ups - > sock_fd ) ;
2010-03-26 01:20:59 +02:00
continue ;
}
/* throw some warnings if it's not feeding us data any more */
if ( sstate_dead ( ups , maxage ) ) {
ups_data_stale ( ups ) ;
} else {
ups_data_ok ( ups ) ;
}
fds [ nfds ] . fd = ups - > sock_fd ;
fds [ nfds ] . events = POLLIN ;
handler [ nfds ] . type = DRIVER ;
handler [ nfds ] . data = ups ;
nfds + + ;
}
/* scan through client sockets */
for ( client = firstclient ; client ; client = cnext ) {
cnext = client - > next ;
if ( difftime ( now , client - > last_heard ) > 60 ) {
/* shed clients after 1 minute of inactivity */
2022-07-10 10:23:45 +03:00
/* FIXME: create an upsd.conf parameter (CLIENT_INACTIVITY_DELAY) */
2010-03-26 01:20:59 +02:00
client_disconnect ( client ) ;
continue ;
}
if ( nfds > = maxconn ) {
/* ignore clients that we are unable to handle */
continue ;
}
fds [ nfds ] . fd = client - > sock_fd ;
fds [ nfds ] . events = POLLIN ;
handler [ nfds ] . type = CLIENT ;
handler [ nfds ] . data = client ;
nfds + + ;
}
/* scan through server sockets */
for ( server = firstaddr ; server & & ( nfds < maxconn ) ; server = server - > next ) {
if ( server - > sock_fd < 0 ) {
continue ;
}
fds [ nfds ] . fd = server - > sock_fd ;
fds [ nfds ] . events = POLLIN ;
handler [ nfds ] . type = SERVER ;
handler [ nfds ] . data = server ;
nfds + + ;
}
2022-07-10 10:23:45 +03:00
upsdebugx ( 2 , " %s: polling %jd filedescriptors " , __func__ , ( intmax_t ) nfds ) ;
2010-03-26 01:20:59 +02:00
ret = poll ( fds , nfds , 2000 ) ;
if ( ret = = 0 ) {
upsdebugx ( 2 , " %s: no data available " , __func__ ) ;
return ;
}
if ( ret < 0 ) {
upslog_with_errno ( LOG_ERR , " %s " , __func__ ) ;
return ;
}
for ( i = 0 ; i < nfds ; i + + ) {
if ( fds [ i ] . revents & ( POLLHUP | POLLERR | POLLNVAL ) ) {
switch ( handler [ i ] . type )
{
case DRIVER :
sstate_disconnect ( ( upstype_t * ) handler [ i ] . data ) ;
break ;
case CLIENT :
2012-01-24 12:22:33 +02:00
client_disconnect ( ( nut_ctype_t * ) handler [ i ] . data ) ;
2010-03-26 01:20:59 +02:00
break ;
case SERVER :
upsdebugx ( 2 , " %s: server disconnected " , __func__ ) ;
break ;
2022-07-10 10:23:45 +03:00
# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) )
# pragma GCC diagnostic push
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT
# pragma GCC diagnostic ignored "-Wcovered-switch-default"
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE
# pragma GCC diagnostic ignored "-Wunreachable-code"
# endif
/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcovered-switch-default"
# pragma clang diagnostic ignored "-Wunreachable-code"
# endif
/* All enum cases defined as of the time of coding
* have been covered above . Handle later definitions ,
* memory corruptions and buggy inputs below . . .
*/
2010-03-26 01:20:59 +02:00
default :
upsdebugx ( 2 , " %s: <unknown> disconnected " , __func__ ) ;
break ;
2022-07-10 10:23:45 +03:00
# ifdef __clang__
# pragma clang diagnostic pop
# endif
# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) )
# pragma GCC diagnostic pop
# endif
2010-03-26 01:20:59 +02:00
}
continue ;
}
if ( fds [ i ] . revents & POLLIN ) {
switch ( handler [ i ] . type )
{
case DRIVER :
sstate_readline ( ( upstype_t * ) handler [ i ] . data ) ;
break ;
case CLIENT :
2012-01-24 12:22:33 +02:00
client_readline ( ( nut_ctype_t * ) handler [ i ] . data ) ;
2010-03-26 01:20:59 +02:00
break ;
case SERVER :
client_connect ( ( stype_t * ) handler [ i ] . data ) ;
break ;
2022-07-10 10:23:45 +03:00
# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) )
# pragma GCC diagnostic push
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT
# pragma GCC diagnostic ignored "-Wcovered-switch-default"
# endif
# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE
# pragma GCC diagnostic ignored "-Wunreachable-code"
# endif
/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcovered-switch-default"
# pragma clang diagnostic ignored "-Wunreachable-code"
# endif
/* All enum cases defined as of the time of coding
* have been covered above . Handle later definitions ,
* memory corruptions and buggy inputs below . . .
*/
2010-03-26 01:20:59 +02:00
default :
upsdebugx ( 2 , " %s: <unknown> has data available " , __func__ ) ;
break ;
2022-07-10 10:23:45 +03:00
# ifdef __clang__
# pragma clang diagnostic pop
# endif
# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) )
# pragma GCC diagnostic pop
# endif
2010-03-26 01:20:59 +02:00
}
continue ;
}
}
}
2022-07-10 10:23:45 +03:00
static void help ( const char * arg_progname )
__attribute__ ( ( noreturn ) ) ;
static void help ( const char * arg_progname )
2010-03-26 01:20:59 +02:00
{
printf ( " Network server for UPS data. \n \n " ) ;
2022-07-10 10:23:45 +03:00
printf ( " usage: %s [OPTIONS] \n " , arg_progname ) ;
2010-03-26 01:20:59 +02:00
printf ( " \n " ) ;
printf ( " -c <command> send <command> via signal to background process \n " ) ;
printf ( " commands: \n " ) ;
printf ( " - reload: reread configuration files \n " ) ;
printf ( " - stop: stop process and exit \n " ) ;
2022-07-10 10:23:45 +03:00
printf ( " -P <pid> send the signal above to specified PID (bypassing PID file) \n " ) ;
printf ( " -D raise debugging level (and stay foreground by default) \n " ) ;
printf ( " -F stay foregrounded even if no debugging is enabled \n " ) ;
printf ( " -FF stay foregrounded and still save the PID file \n " ) ;
printf ( " -B stay backgrounded even if debugging is bumped \n " ) ;
2010-03-26 01:20:59 +02:00
printf ( " -h display this help \n " ) ;
printf ( " -r <dir> chroots to <dir> \n " ) ;
2011-01-26 11:35:08 +02:00
printf ( " -q raise log level threshold \n " ) ;
2010-03-26 01:20:59 +02:00
printf ( " -u <user> switch to <user> (if started as root) \n " ) ;
printf ( " -V display the version of this software \n " ) ;
printf ( " -4 IPv4 only \n " ) ;
printf ( " -6 IPv6 only \n " ) ;
exit ( EXIT_SUCCESS ) ;
}
static void set_reload_flag ( int sig )
{
2022-07-10 10:23:45 +03:00
NUT_UNUSED_VARIABLE ( sig ) ;
2010-03-26 01:20:59 +02:00
reload_flag = 1 ;
}
static void set_exit_flag ( int sig )
{
exit_flag = sig ;
}
static void setup_signals ( void )
{
struct sigaction sa ;
sigemptyset ( & sa . sa_mask ) ;
sigaddset ( & sa . sa_mask , SIGHUP ) ;
sa . sa_flags = 0 ;
/* basic signal setup to ignore SIGPIPE */
2022-07-10 10:23:45 +03:00
# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wstrict-prototypes"
# endif
2010-03-26 01:20:59 +02:00
sa . sa_handler = SIG_IGN ;
2022-07-10 10:23:45 +03:00
# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES)
# pragma GCC diagnostic pop
# endif
2010-03-26 01:20:59 +02:00
sigaction ( SIGPIPE , & sa , NULL ) ;
/* handle shutdown signals */
sa . sa_handler = set_exit_flag ;
sigaction ( SIGINT , & sa , NULL ) ;
sigaction ( SIGQUIT , & sa , NULL ) ;
sigaction ( SIGTERM , & sa , NULL ) ;
/* handle reloading */
sa . sa_handler = set_reload_flag ;
sigaction ( SIGHUP , & sa , NULL ) ;
}
void check_perms ( const char * fn )
{
int ret ;
struct stat st ;
ret = stat ( fn , & st ) ;
if ( ret ! = 0 ) {
fatal_with_errno ( EXIT_FAILURE , " stat %s " , fn ) ;
}
/* include the x bit here in case we check a directory */
if ( st . st_mode & ( S_IROTH | S_IXOTH ) ) {
upslogx ( LOG_WARNING , " %s is world readable " , fn ) ;
}
}
int main ( int argc , char * * argv )
{
2022-07-10 10:23:45 +03:00
int i , cmd = 0 , cmdret = 0 , foreground = - 1 ;
2010-03-26 01:20:59 +02:00
char * chroot_path = NULL ;
2011-01-26 11:35:08 +02:00
const char * user = RUN_AS_USER ;
2010-03-26 01:20:59 +02:00
struct passwd * new_uid = NULL ;
2022-07-10 10:23:45 +03:00
pid_t oldpid = - 1 ;
2010-03-26 01:20:59 +02:00
progname = xbasename ( argv [ 0 ] ) ;
/* yes, xstrdup - the conf handlers call free on this later */
statepath = xstrdup ( dflt_statepath ( ) ) ;
datapath = xstrdup ( DATADIR ) ;
/* set up some things for later */
2011-01-26 11:35:08 +02:00
snprintf ( pidfn , sizeof ( pidfn ) , " %s/%s.pid " , altpidpath ( ) , progname ) ;
2010-03-26 01:20:59 +02:00
2011-01-26 11:35:08 +02:00
printf ( " Network UPS Tools %s %s \n " , progname , UPS_VERSION ) ;
2010-03-26 01:20:59 +02:00
2022-07-10 10:23:45 +03:00
while ( ( i = getopt ( argc , argv , " +h46p:qr:i:fu:Vc:P:DFB " ) ) ! = - 1 ) {
2010-03-26 01:20:59 +02:00
switch ( i ) {
case ' p ' :
case ' i ' :
fatalx ( EXIT_FAILURE , " Specifying a listening addresses with '-i <address>' and '-p <port>' \n "
" is deprecated. Use 'LISTEN <address> [<port>]' in 'upsd.conf' instead. \n "
" See 'man 8 upsd.conf' for more information. " ) ;
2022-07-10 10:23:45 +03:00
# ifndef HAVE___ATTRIBUTE__NORETURN
exit ( EXIT_FAILURE ) ; /* Should not get here in practice, but compiler is afraid we can fall through */
# endif
2011-01-26 11:35:08 +02:00
case ' q ' :
nut_log_level + + ;
break ;
2022-07-10 10:23:45 +03:00
2010-03-26 01:20:59 +02:00
case ' r ' :
chroot_path = optarg ;
break ;
2022-07-10 10:23:45 +03:00
2010-03-26 01:20:59 +02:00
case ' u ' :
user = optarg ;
break ;
2022-07-10 10:23:45 +03:00
2010-03-26 01:20:59 +02:00
case ' V ' :
/* do nothing - we already printed the banner */
exit ( EXIT_SUCCESS ) ;
case ' c ' :
if ( ! strncmp ( optarg , " reload " , strlen ( optarg ) ) )
cmd = SIGCMD_RELOAD ;
if ( ! strncmp ( optarg , " stop " , strlen ( optarg ) ) )
cmd = SIGCMD_STOP ;
/* bad command given */
if ( cmd = = 0 )
help ( progname ) ;
break ;
2022-07-10 10:23:45 +03:00
case ' P ' :
if ( ( oldpid = parsepid ( optarg ) ) < 0 )
help ( progname ) ;
break ;
2010-03-26 01:20:59 +02:00
case ' D ' :
nut_debug_level + + ;
break ;
2022-07-10 10:23:45 +03:00
case ' F ' :
if ( foreground > 0 ) {
/* specified twice to save PID file anyway */
foreground = 2 ;
} else {
foreground = 1 ;
}
break ;
case ' B ' :
foreground = 0 ;
break ;
2010-03-26 01:20:59 +02:00
2011-01-26 11:35:08 +02:00
case ' 4 ' :
2010-03-26 01:20:59 +02:00
opt_af = AF_INET ;
break ;
2011-01-26 11:35:08 +02:00
case ' 6 ' :
2010-03-26 01:20:59 +02:00
opt_af = AF_INET6 ;
break ;
2022-07-10 10:23:45 +03:00
case ' h ' :
2010-03-26 01:20:59 +02:00
default :
help ( progname ) ;
2022-07-10 10:23:45 +03:00
}
}
if ( foreground < 0 ) {
if ( nut_debug_level > 0 ) {
foreground = 1 ;
} else {
foreground = 0 ;
2010-03-26 01:20:59 +02:00
}
}
if ( cmd ) {
2022-07-10 10:23:45 +03:00
if ( oldpid < 0 ) {
cmdret = sendsignalfn ( pidfn , cmd ) ;
} else {
cmdret = sendsignalpid ( oldpid , cmd ) ;
}
exit ( ( cmdret = = 0 ) ? EXIT_SUCCESS : EXIT_FAILURE ) ;
2010-03-26 01:20:59 +02:00
}
2012-06-01 16:55:19 +03:00
/* otherwise, we are being asked to start.
* so check if a previous instance is running by sending signal ' 0 '
* ( Ie ' kill < pid > 0 ' ) */
2022-07-10 10:23:45 +03:00
if ( oldpid < 0 ) {
cmdret = sendsignalfn ( pidfn , 0 ) ;
} else {
cmdret = sendsignalpid ( oldpid , 0 ) ;
}
switch ( cmdret ) {
case 0 :
2012-06-01 16:55:19 +03:00
printf ( " Fatal error: A previous upsd instance is already running! \n " ) ;
printf ( " Either stop the previous instance first, or use the 'reload' command. \n " ) ;
exit ( EXIT_FAILURE ) ;
2022-07-10 10:23:45 +03:00
case - 3 :
case - 2 :
upslogx ( LOG_WARNING , " Could not %s PID file '%s' "
" to see if previous upsd instance is "
" already running! \n " ,
( cmdret = = - 3 ? " find " : " parse " ) ,
pidfn ) ;
break ;
case - 1 :
default :
/* Just failed to send signal, no competitor running */
break ;
2012-06-01 16:55:19 +03:00
}
2010-03-26 01:20:59 +02:00
argc - = optind ;
argv + = optind ;
if ( argc ! = 0 ) {
help ( progname ) ;
}
atexit ( upsd_cleanup ) ;
setup_signals ( ) ;
2011-01-26 11:35:08 +02:00
open_syslog ( progname ) ;
2010-03-26 01:20:59 +02:00
/* send logging to the syslog pre-background for later use */
syslogbit_set ( ) ;
/* do this here, since getpwnam() might not work in the chroot */
new_uid = get_user_pwent ( user ) ;
if ( chroot_path ) {
chroot_start ( chroot_path ) ;
}
2022-07-10 10:23:45 +03:00
/* default to system limit (may be overridden in upsd.conf) */
/* FIXME: Check for overflows (and int size of nfds_t vs. long) - see get_max_pid_t() for example */
maxconn = ( nfds_t ) sysconf ( _SC_OPEN_MAX ) ;
2010-03-26 01:20:59 +02:00
/* handle upsd.conf */
load_upsdconf ( 0 ) ; /* 0 = initial */
2022-07-10 10:23:45 +03:00
/* CLI debug level can not be smaller than debug_min specified
* in upsd . conf . Note that non - zero debug_min does not impact
* foreground running mode .
*/
if ( nut_debug_level_global > nut_debug_level )
nut_debug_level = nut_debug_level_global ;
upsdebugx ( 1 , " debug level is '%d' " , nut_debug_level ) ;
{ /* scope */
/* As documented above, the ALLOW_NO_DEVICE can be provided via
* envvars and then has higher priority than an upsd . conf setting
*/
const char * envvar = getenv ( " ALLOW_NO_DEVICE " ) ;
if ( envvar ! = NULL ) {
if ( ( ! strncasecmp ( " TRUE " , envvar , 4 ) ) | | ( ! strncasecmp ( " YES " , envvar , 3 ) ) | | ( ! strncasecmp ( " ON " , envvar , 2 ) ) | | ( ! strncasecmp ( " 1 " , envvar , 1 ) ) ) {
/* Admins of this server expressed a desire to serve
* anything on the NUT protocol , even if nothing is
* configured yet - tell the clients so , properly .
*/
allow_no_device = 1 ;
} else if ( ( ! strncasecmp ( " FALSE " , envvar , 5 ) ) | | ( ! strncasecmp ( " NO " , envvar , 2 ) ) | | ( ! strncasecmp ( " OFF " , envvar , 3 ) ) | | ( ! strncasecmp ( " 0 " , envvar , 1 ) ) ) {
/* Admins of this server expressed a desire to serve
* anything on the NUT protocol , even if nothing is
* configured yet - tell the clients so , properly .
*/
allow_no_device = 0 ;
}
}
} /* scope */
2010-03-26 01:20:59 +02:00
/* start server */
server_load ( ) ;
become_user ( new_uid ) ;
if ( chdir ( statepath ) ) {
fatal_with_errno ( EXIT_FAILURE , " Can't chdir to %s " , statepath ) ;
}
/* check statepath perms */
check_perms ( statepath ) ;
/* handle ups.conf */
read_upsconf ( ) ;
upsconf_add ( 0 ) ; /* 0 = initial */
poll_reload ( ) ;
if ( num_ups = = 0 ) {
2022-07-10 10:23:45 +03:00
if ( allow_no_device ) {
upslogx ( LOG_WARNING , " Normally at least one UPS must be defined in ups.conf, currently there are none (please configure the file and reload the service) " ) ;
} else {
fatalx ( EXIT_FAILURE , " Fatal error: at least one UPS must be defined in ups.conf " ) ;
}
2010-03-26 01:20:59 +02:00
}
/* try to bring in the var/cmd descriptions */
desc_load ( ) ;
/* handle upsd.users */
user_load ( ) ;
2022-07-10 10:23:45 +03:00
if ( ! foreground ) {
2010-03-26 01:20:59 +02:00
background ( ) ;
writepid ( pidfn ) ;
} else {
2022-07-10 10:23:45 +03:00
if ( foreground = = 2 ) {
upslogx ( LOG_WARNING , " Running as foreground process, but saving a PID file anyway " ) ;
writepid ( pidfn ) ;
} else {
upslogx ( LOG_WARNING , " Running as foreground process, not saving a PID file " ) ;
memset ( pidfn , 0 , sizeof ( pidfn ) ) ;
}
2010-03-26 01:20:59 +02:00
}
2015-04-30 16:53:36 +03:00
/* initialize SSL (keyfile must be readable by nut user) */
ssl_init ( ) ;
2010-03-26 01:20:59 +02:00
while ( ! exit_flag ) {
mainloop ( ) ;
}
2013-11-24 17:00:12 +02:00
ssl_cleanup ( ) ;
2010-03-26 01:20:59 +02:00
upslogx ( LOG_INFO , " Signal %d: exiting " , exit_flag ) ;
return EXIT_SUCCESS ;
}
2011-01-26 11:35:08 +02:00