467 lines
12 KiB
C
467 lines
12 KiB
C
|
/******************************************************************************
|
||
|
*
|
||
|
* worker-ping.c - Nagios Core 4 worker to handle ping checke
|
||
|
*
|
||
|
* Program: Nagios Core
|
||
|
* License: GPL
|
||
|
*
|
||
|
* First Written: 01-03-2013 (start of development)
|
||
|
*
|
||
|
* Description:
|
||
|
*
|
||
|
* License:
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 as
|
||
|
* published by the Free Software Foundation.
|
||
|
*
|
||
|
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#include "config.h"
|
||
|
#include "workers.h"
|
||
|
|
||
|
/* Local macro definitions */
|
||
|
#define PING_WORKER_VERSION "0.2"
|
||
|
#define PING_WORKER_MODIFICATION_DATE "02-Feb-13"
|
||
|
|
||
|
#define PW_OK 0
|
||
|
#define PW_ERROR 1
|
||
|
|
||
|
/* Local function declarations */
|
||
|
int daemon_init( void);
|
||
|
int drop_privileges( char *, char *);
|
||
|
void print_license( void);
|
||
|
void print_usage( char *);
|
||
|
void print_version( void);
|
||
|
void parse_worker_command_line( int, char **, char **, int *, char **,
|
||
|
char **, char **);
|
||
|
static int worker( const char *);
|
||
|
|
||
|
/* Everything starts here */
|
||
|
main( int argc, char **argv, char **env) {
|
||
|
int daemon_mode = FALSE;
|
||
|
char *worker_socket;
|
||
|
char *worker_user = ( char *)0;
|
||
|
char *worker_group = ( char *)0;
|
||
|
|
||
|
if( FALSE == daemon_mode) {
|
||
|
printf( "Greetings from the ping worker.\n");
|
||
|
}
|
||
|
|
||
|
parse_worker_command_line( argc, argv, env, &daemon_mode, &worker_socket,
|
||
|
&worker_user, &worker_group);
|
||
|
|
||
|
if( FALSE == daemon_mode) {
|
||
|
print_version();
|
||
|
printf( "Worker socket is %s.\n", worker_socket);
|
||
|
}
|
||
|
|
||
|
/* drop privileges */
|
||
|
if( drop_privileges( worker_user, worker_group) == PW_ERROR) {
|
||
|
fprintf( stderr, "Failed to drop privileges. Aborting.\n");
|
||
|
exit( 1);
|
||
|
}
|
||
|
|
||
|
if( TRUE == daemon_mode) {
|
||
|
if( daemon_init() == ERROR) {
|
||
|
fprintf( stderr,
|
||
|
"Bailing out due to failure to daemonize. (PID=%d)\n",
|
||
|
( int)getpid());
|
||
|
exit( 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Enter the worker code */
|
||
|
if( worker( worker_socket)) {
|
||
|
exit( 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void parse_worker_command_line( int argc, char **argv, char **env,
|
||
|
int *daemon_mode, char **worker_socket, char **worker_user,
|
||
|
char **worker_group) {
|
||
|
int c = 0;
|
||
|
int display_usage = FALSE;
|
||
|
int display_license = FALSE;
|
||
|
|
||
|
#ifdef HAVE_GETOPT_H
|
||
|
int option_index = 0;
|
||
|
static struct option long_options[] = {
|
||
|
{ "help", no_argument, 0, 'h'},
|
||
|
{ "version", no_argument, 0, 'V'},
|
||
|
{ "license", no_argument, 0, 'V'},
|
||
|
{ "daemon", no_argument, 0, 'd'},
|
||
|
{ "worker", required_argument, 0, 'W'},
|
||
|
{ "user", required_argument, 0, 'u'},
|
||
|
{ "group", required_argument, 0, 'g'},
|
||
|
{ 0, 0, 0, 0}
|
||
|
};
|
||
|
#define getopt( argc, argv, o) getopt_long( argc, argv, o, long_options, &option_index)
|
||
|
#endif
|
||
|
|
||
|
/* get all command line arguments */
|
||
|
while( 1) {
|
||
|
c = getopt( argc, argv, "+:hVdW:u:g:");
|
||
|
|
||
|
if( -1 == c || EOF == c) break;
|
||
|
|
||
|
switch( c) {
|
||
|
|
||
|
case '?': /* usage */
|
||
|
case 'h':
|
||
|
display_usage = TRUE;
|
||
|
break;
|
||
|
|
||
|
case 'V': /* version */
|
||
|
display_license = TRUE;
|
||
|
break;
|
||
|
|
||
|
case 'd': /* daemon mode */
|
||
|
*daemon_mode = TRUE;
|
||
|
break;
|
||
|
|
||
|
case 'W':
|
||
|
*worker_socket = optarg;
|
||
|
break;
|
||
|
|
||
|
case 'u':
|
||
|
*worker_user = optarg;
|
||
|
break;
|
||
|
|
||
|
case 'g':
|
||
|
*worker_group = optarg;
|
||
|
break;
|
||
|
|
||
|
case ':':
|
||
|
printf( "Missing argument for command line option '%c'.\n\n",
|
||
|
optopt);
|
||
|
print_usage( argv[ 0]);
|
||
|
exit( 1);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
printf( "Unknown command line option '%c'.\n\n", c);
|
||
|
print_usage( argv[ 0]);
|
||
|
exit( 1);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( TRUE == display_license) {
|
||
|
print_version();
|
||
|
print_license();
|
||
|
exit( 0);
|
||
|
}
|
||
|
|
||
|
if( TRUE == display_usage) {
|
||
|
print_usage( argv[ 0]);
|
||
|
exit( 0);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void print_license( void) {
|
||
|
printf( "\nThis program is free software; you can redistribute it and/or modify\n");
|
||
|
printf( "it under the terms of the GNU General Public License version 2 as\n");
|
||
|
printf( "published by the Free Software Foundation.\n\n");
|
||
|
printf( "This program is distributed in the hope that it will be useful,\n");
|
||
|
printf( "but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
|
||
|
printf( "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
|
||
|
printf( "GNU General Public License for more details.\n\n");
|
||
|
printf( "You should have received a copy of the GNU General Public License\n");
|
||
|
printf( "along with this program; if not, write to the Free Software\n");
|
||
|
printf( "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");
|
||
|
}
|
||
|
|
||
|
void print_usage( char *program_name) {
|
||
|
printf( "\nUsage: %s [-?|-h|--help] [-V|--version] [-d]\n", program_name);
|
||
|
printf( " -W /path/to/socket -u user -g group\n");
|
||
|
printf( "\n");
|
||
|
printf( "Options:\n");
|
||
|
printf( "\n");
|
||
|
printf( " -d, --daemon Starts the ping worker in daemon mode,\n");
|
||
|
printf( " instead of as a foreground process\n");
|
||
|
printf( " -W, --worker /path/to/socket Act as a worker for an already running daemon\n");
|
||
|
printf( " -u, --user user Drop privileges to the specified user\n");
|
||
|
printf( " -g, --group group Drop privileges to the specified user\n");
|
||
|
printf( "\n");
|
||
|
printf( "Visit the Nagios website at https://www.nagios.org/ for bug fixes, new\n");
|
||
|
printf( "releases, online documentation, FAQs, information on subscribing to\n");
|
||
|
printf( "the mailing lists, and commercial support options for Nagios.\n");
|
||
|
printf( "\n");
|
||
|
}
|
||
|
|
||
|
void print_version( void) {
|
||
|
printf( "\nNagios Core 4 Ping Worker %s\n", PING_WORKER_VERSION);
|
||
|
printf( "Copyright (c) 2013-present Nagios Core Development Team\n");
|
||
|
printf( " and Community Contributors\n");
|
||
|
printf( "Last Modified: %s\n", PING_WORKER_MODIFICATION_DATE);
|
||
|
printf( "License: GPL\n");
|
||
|
printf( "Website: https://www.nagios.org\n");
|
||
|
}
|
||
|
|
||
|
static int worker( const char *path)
|
||
|
{
|
||
|
int sd, ret;
|
||
|
char response[128];
|
||
|
|
||
|
/*set_loadctl_defaults();*/
|
||
|
|
||
|
sd = nsock_unix( path, NSOCK_TCP | NSOCK_CONNECT);
|
||
|
if( sd < 0) {
|
||
|
printf( "Failed to connect to query socket '%s': %s: %s\n",
|
||
|
path, nsock_strerror( sd), strerror( errno));
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
ret = nsock_printf_nul( sd, "@wproc register name=Core Ping Worker %d;pid=%d;plugin=check_ping", getpid(), getpid());
|
||
|
if( ret < 0) {
|
||
|
printf( "Failed to register as worker.\n");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
ret = read( sd, response, 3);
|
||
|
if( ret != 3) {
|
||
|
printf( "Failed to read response from wproc manager\n");
|
||
|
return 1;
|
||
|
}
|
||
|
if( memcmp( response, "OK", 3)) {
|
||
|
read( sd, response + 3, sizeof(response) - 4);
|
||
|
response[ sizeof( response) - 2] = 0;
|
||
|
printf( "Failed to register with wproc manager: %s\n", response);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
enter_worker( sd, start_cmd);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int drop_privileges( char *user, char *group) {
|
||
|
uid_t uid = -1;
|
||
|
gid_t gid = -1;
|
||
|
struct group *grp = NULL;
|
||
|
struct passwd *pw = NULL;
|
||
|
int result = PW_OK;
|
||
|
|
||
|
/* only drop privileges if we're running as root, so we don't
|
||
|
interfere with being debugged while running as some random user */
|
||
|
if( getuid() != 0) {
|
||
|
return PW_OK;
|
||
|
}
|
||
|
|
||
|
/* set effective group ID */
|
||
|
if( NULL != group) {
|
||
|
|
||
|
/* see if this is a group name */
|
||
|
if( strspn( group, "0123456789") < strlen( group)) {
|
||
|
grp = ( struct group *)getgrnam( group);
|
||
|
if( NULL != grp) {
|
||
|
gid = ( gid_t)(grp->gr_gid);
|
||
|
}
|
||
|
else {
|
||
|
fprintf( stderr,
|
||
|
"Warning: Could not get group entry for '%s'\n", group);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* else we were passed the GID */
|
||
|
else {
|
||
|
gid = ( gid_t)atoi( group);
|
||
|
}
|
||
|
|
||
|
/* set effective group ID if other than current EGID */
|
||
|
if( gid != getegid()) {
|
||
|
if( setgid( gid) == -1) {
|
||
|
fprintf( stderr, "Warning: Could not set effective GID=%d\n",
|
||
|
( int)gid);
|
||
|
result = PW_ERROR;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* set effective user ID */
|
||
|
if( NULL != user) {
|
||
|
|
||
|
/* see if this is a user name */
|
||
|
if( strspn( user, "0123456789") < strlen( user)) {
|
||
|
pw = ( struct passwd *)getpwnam( user);
|
||
|
if( NULL != pw) {
|
||
|
uid = ( uid_t)(pw->pw_uid);
|
||
|
}
|
||
|
else {
|
||
|
fprintf( stderr,
|
||
|
"Warning: Could not get passwd entry for '%s'\n", user);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* else we were passed the UID */
|
||
|
else {
|
||
|
uid = ( uid_t)atoi( user);
|
||
|
}
|
||
|
|
||
|
#ifdef HAVE_INITGROUPS
|
||
|
|
||
|
if( uid != geteuid()) {
|
||
|
|
||
|
/* initialize supplementary groups */
|
||
|
if( initgroups( user, gid) == -1) {
|
||
|
if( EPERM == errno) {
|
||
|
fprintf( stderr, "Warning: Unable to change supplementary "
|
||
|
"groups using initgroups() -- I hope you know what "
|
||
|
"you're doing\n");
|
||
|
}
|
||
|
else {
|
||
|
fprintf( stderr, "Warning: Possibly root user failed "
|
||
|
"dropping privileges with initgroups()\n");
|
||
|
return PW_ERROR;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
if( setuid( uid) == -1) {
|
||
|
fprintf( stderr, "Warning: Could not set effective UID=%d\n",
|
||
|
( int)uid);
|
||
|
result = PW_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
int daemon_init( void) {
|
||
|
pid_t pid = -1;
|
||
|
int pidno = 0;
|
||
|
int lockfile = 0;
|
||
|
int val = 0;
|
||
|
char buf[256];
|
||
|
struct flock lock;
|
||
|
char *homedir = NULL;
|
||
|
|
||
|
#ifdef RLIMIT_CORE
|
||
|
struct rlimit limit;
|
||
|
#endif
|
||
|
|
||
|
/* change working directory. scuttle home if we're dumping core */
|
||
|
homedir = getenv( "HOME");
|
||
|
#ifdef DAEMON_DUMPS_CORE
|
||
|
if( NULL != homedir) {
|
||
|
chdir( homedir);
|
||
|
}
|
||
|
else {
|
||
|
#endif
|
||
|
chdir( "/");
|
||
|
#ifdef DAEMON_DUMPS_CORE
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
umask( S_IWGRP | S_IWOTH);
|
||
|
|
||
|
/* close existing stdin, stdout, stderr */
|
||
|
close( 0);
|
||
|
close( 1);
|
||
|
close( 2);
|
||
|
|
||
|
/* THIS HAS TO BE DONE TO AVOID PROBLEMS WITH STDERR BEING REDIRECTED TO SERVICE MESSAGE PIPE! */
|
||
|
/* re-open stdin, stdout, stderr with known values */
|
||
|
open( "/dev/null", O_RDONLY);
|
||
|
open( "/dev/null", O_WRONLY);
|
||
|
open( "/dev/null", O_WRONLY);
|
||
|
|
||
|
#ifdef USE_LOCKFILE
|
||
|
lockfile = open( lock_file,
|
||
|
O_RDWR | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
|
||
|
|
||
|
if( lockfile < 0) {
|
||
|
logit( NSLOG_RUNTIME_ERROR, TRUE, "Failed to obtain lock on file %s: %s\n", lock_file, strerror(errno));
|
||
|
logit( NSLOG_PROCESS_INFO | NSLOG_RUNTIME_ERROR, TRUE, "Bailing out due to errors encountered while attempting to daemonize... (PID=%d)", (int)getpid());
|
||
|
|
||
|
exit( 1);
|
||
|
}
|
||
|
|
||
|
/* see if we can read the contents of the lockfile */
|
||
|
if(( val = read( lockfile, buf, ( size_t)10)) < 0) {
|
||
|
logit(NSLOG_RUNTIME_ERROR, TRUE, "Lockfile exists but cannot be read");
|
||
|
exit( 1);
|
||
|
}
|
||
|
|
||
|
/* we read something - check the PID */
|
||
|
if( val > 0) {
|
||
|
if(( val = sscanf( buf, "%d", &pidno)) < 1) {
|
||
|
logit( NSLOG_RUNTIME_ERROR, TRUE, "Lockfile '%s' does not contain a valid PID (%s)", lock_file, buf);
|
||
|
exit( 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* check for SIGHUP */
|
||
|
if( val == 1 && ( pid = ( pid_t)pidno) == getpid()) {
|
||
|
close( lockfile);
|
||
|
return OK;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* exit on errors... */
|
||
|
if(( pid = fork()) < 0) {
|
||
|
return( PW_ERROR);
|
||
|
}
|
||
|
|
||
|
/* parent process goes away.. */
|
||
|
else if((int)pid != 0) {
|
||
|
exit( 0);
|
||
|
}
|
||
|
|
||
|
/* child continues... */
|
||
|
|
||
|
/* child becomes session leader... */
|
||
|
setsid();
|
||
|
|
||
|
#ifdef USE_LOCKFILE
|
||
|
/* place a file lock on the lock file */
|
||
|
lock.l_type = F_WRLCK;
|
||
|
lock.l_start = 0;
|
||
|
lock.l_whence = SEEK_SET;
|
||
|
lock.l_len = 0;
|
||
|
if( fcntl( lockfile, F_SETLK, &lock) < 0) {
|
||
|
if( EACCES == errno || EAGAIN == errno) {
|
||
|
fcntl( lockfile, F_GETLK, &lock);
|
||
|
logit( NSLOG_RUNTIME_ERROR, TRUE, "Lockfile '%s' looks like its already held by another instance of Nagios (PID %d). Bailing out...", lock_file, (int)lock.l_pid);
|
||
|
}
|
||
|
else {
|
||
|
logit(NSLOG_RUNTIME_ERROR, TRUE, "Cannot lock lockfile '%s': %s. Bailing out...", lock_file, strerror(errno));
|
||
|
|
||
|
}
|
||
|
exit( 1);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* prevent daemon from dumping a core file... */
|
||
|
#if defined( RLIMIT_CORE) && defined( DAEMON_DUMPS_CORE)
|
||
|
getrlimit( RLIMIT_CORE, &limit);
|
||
|
limit.rlim_cur = 0;
|
||
|
setrlimit( RLIMIT_CORE, &limit);
|
||
|
#endif
|
||
|
|
||
|
#ifdef USE_LOCKFILE
|
||
|
/* write PID to lockfile... */
|
||
|
lseek( lockfile, 0, SEEK_SET);
|
||
|
ftruncate( lockfile, 0);
|
||
|
sprintf( buf, "%d\n", ( int)getpid());
|
||
|
write( lockfile, buf, strlen( buf));
|
||
|
|
||
|
/* make sure lock file stays open while program is executing... */
|
||
|
val = fcntl( lockfile, F_GETFD, 0);
|
||
|
val |= FD_CLOEXEC;
|
||
|
fcntl( lockfile, F_SETFD, val);
|
||
|
#endif
|
||
|
|
||
|
return PW_OK;
|
||
|
}
|