Procházet zdrojové kódy

Added global command prefix option and ability to reload config files with SIGHUP

Ethan Galstad před 20 roky
rodič
revize
6ff905f987
7 změnil soubory, kde provedl 218 přidání a 66 odebrání
  1. 2 0
      Changelog
  2. 1 1
      SECURITY
  3. 0 1
      configure.in
  4. 8 2
      include/nrpe.h
  5. 0 1
      init-script.suse.in
  6. 23 1
      sample-config/nrpe.cfg.in
  7. 184 60
      src/nrpe.c

+ 2 - 0
Changelog

@@ -6,6 +6,8 @@ NRPE Changelog
 2.4 - ??/??/2006
 ----------------
 - Added option to allow week random seed (Gerhard Lausser)
+- Added optional command line prefix (Sean Finney)
+- Added ability to reload config file with SIGHUP
 
 
 2.3 - 01/23/2006

+ 1 - 1
SECURITY

@@ -6,7 +6,7 @@ NRPE SECURITY README
 TCP WRAPPER SUPPORT
 ===================
 
-NRPE 2.1 includes native support for TCP wrappers.  The older
+NRPE 2.x includes native support for TCP wrappers.  The older
 host access list directive was removed from the config file.
 Make sure your system supports TCP wrappers before running NRPE.
 Once you compile NRPE you can check to see if it has wrapper

+ 0 - 1
configure.in

@@ -263,7 +263,6 @@ AC_OUTPUT(Makefile src/Makefile subst)
 
 perl subst init-script
 perl subst init-script.debian
-perl subst init-script.freebsd
 perl subst init-script.suse
 perl subst sample-config/nrpe.cfg
 perl subst sample-config/nrpe.xinetd

+ 8 - 2
include/nrpe.h

@@ -2,7 +2,7 @@
  *
  * NRPE.H - NRPE Include File
  * Copyright (c) 1999-2006 Ethan Galstad (nagios@nagios.org)
- * Last Modified: 01-21-2006
+ * Last Modified: 02-03-2006
  *
  * License:
  *
@@ -40,8 +40,11 @@ int add_command(char *,char *);
 command *find_command(char *);
 void sighandler(int);
 int drop_privileges(char *,char *);
-int write_pid_file(void);
 int check_privileges(void);
+
+int write_pid_file(void);
+int remove_pid_file(void);
+
 void free_memory(void);
 int is_an_allowed_host(char *);
 int validate_request(packet *);
@@ -50,4 +53,7 @@ int process_macros(char *,char *,int);
 int my_system(char *,int,int *,char *,int);            	/* executes a command via popen(), but also protects against timeouts */
 void my_system_sighandler(int);				/* handles timeouts when executing commands via my_system() */
 
+void sighandler(int);
+void child_sighandler(int);
+
 

+ 0 - 1
init-script.suse.in

@@ -62,7 +62,6 @@ case "$1" in
 	echo -n "Shutting down nrpe:"
 	killproc -TERM $NRPEBIN
 	rc_status -v ; rc_reset
-
 	;;
     restart)
 	$0 stop

+ 23 - 1
sample-config/nrpe.cfg.in

@@ -2,7 +2,7 @@
 # Sample NRPE Config File 
 # Written by: Ethan Galstad (nagios@nagios.org)
 # 
-# Last Modified: 02-02-2006
+# Last Modified: 02-03-2006
 #
 # NOTES:
 # This is a sample configuration file for the NRPE daemon.  It needs to be
@@ -19,6 +19,7 @@
 pid_file=/var/run/nrpe.pid
 
 
+
 # PORT NUMBER
 # Port number we should wait for connections on.
 # NOTE: This must be a non-priviledged port (i.e. > 1024).
@@ -73,6 +74,27 @@ dont_blame_nrpe=0
 
 
 
+# COMMAND PREFIX
+# This option allows you to prefix all commands with a user-defined string.
+# A space is automatically added between the specified prefix string and the
+# command line from the command definition.
+#
+# *** THIS EXAMPLE MAY POSE A POTENTIAL SECURITY RISK, SO USE WITH CAUTION! ***
+# Usage scenario: 
+# Execute restricted commmands using sudo.  For this to work, you need to add
+# the nagios user to your /etc/sudoers.  An example entry for alllowing 
+# execution of the plugins from might be:
+#
+# nagios          ALL=(ALL) NOPASSWD: /usr/lib/nagios/plugins/
+#
+# This lets the nagios user run all commands in that directory (and only them)
+# without asking for a password.  If you do this, make sure you don't give
+# random users write access to that directory or its contents!
+
+# command_prefix=/usr/bin/sudo 
+
+
+
 # DEBUGGING OPTION
 # This option determines whether or not debugging messages are logged to the
 # syslog facility.

+ 184 - 60
src/nrpe.c

@@ -4,7 +4,7 @@
  * Copyright (c) 1999-2006 Ethan Galstad (nagios@nagios.org)
  * License: GPL
  *
- * Last Modified: 02-02-2006
+ * Last Modified: 02-03-2006
  *
  * Command line: nrpe -c <config_file> [--inetd | --daemon]
  *
@@ -52,6 +52,7 @@ int     server_port=DEFAULT_SERVER_PORT;
 char    server_address[16]="0.0.0.0";
 int     socket_timeout=DEFAULT_SOCKET_TIMEOUT;
 int     command_timeout=DEFAULT_COMMAND_TIMEOUT;
+char    *command_prefix=NULL;
 
 command *command_list=NULL;
 
@@ -64,6 +65,9 @@ int     allow_arguments=FALSE;
 
 int     allow_weak_random_seed=FALSE;
 
+int     sigrestart=FALSE;
+int     sigshutdown=FALSE;
+
 int     show_help=FALSE;
 int     show_license=FALSE;
 int     show_version=FALSE;
@@ -168,22 +172,13 @@ int main(int argc, char **argv){
 		config_file[sizeof(config_file)-1]='\x0';
 	        }
 
-	/* read the config file */
-	result=read_config_file(config_file);	
-
-	/* exit if there are errors... */
-	if(result==ERROR){
-		syslog(LOG_ERR,"Config file '%s' contained errors, bailing out...",config_file);
-		return STATE_CRITICAL;
-		}
+        /* generate the CRC 32 table */
+        generate_crc32_table();
 
 	/* initialize macros */
 	for(x=0;x<MAX_COMMAND_ARGUMENTS;x++)
 		macro_argv[x]=NULL;
 
-        /* generate the CRC 32 table */
-        generate_crc32_table();
-
 #ifdef HAVE_SSL
 	/* initialize SSL */
 	if(use_ssl==TRUE){
@@ -236,6 +231,15 @@ int main(int argc, char **argv){
 	/* if we're running under inetd... */
 	if(use_inetd==TRUE){
 
+		/* read the config file */
+		result=read_config_file(config_file);	
+
+		/* exit if there are errors... */
+		if(result==ERROR){
+			syslog(LOG_ERR,"Config file '%s' contained errors, bailing out...",config_file);
+			return STATE_CRITICAL;
+		        }
+
 		/* make sure we're not root */
 		check_privileges();
 
@@ -253,16 +257,6 @@ int main(int argc, char **argv){
 		/* we're a daemon - set up a new process group */
 		setsid();
 
-		/* ignore SIGHUP */
-		signal(SIGHUP, SIG_IGN);
-
-		/* write pid file */
-		if(write_pid_file()==ERROR)
-			return STATE_CRITICAL;
-
-		chdir("/");
-		/*umask(0);*/
-
 		/* close standard file descriptors */
 		close(0);
 		close(1);
@@ -273,14 +267,66 @@ int main(int argc, char **argv){
 		open("/dev/null",O_WRONLY);
 		open("/dev/null",O_WRONLY);
 
+		chdir("/");
+		/*umask(0);*/
+
+		/* handle signals */
+		signal(SIGQUIT,sighandler);
+		signal(SIGTERM,sighandler);
+		signal(SIGHUP,sighandler);
+
+		/* log info to syslog facility */
+		syslog(LOG_NOTICE,"Starting up daemon");
+
+		/* read the config file */
+		result=read_config_file(config_file);	
+
+		/* exit if there are errors... */
+		if(result==ERROR){
+			syslog(LOG_ERR,"Config file '%s' contained errors, bailing out...",config_file);
+			return STATE_CRITICAL;
+		        }
+
+		/* write pid file */
+		if(write_pid_file()==ERROR)
+			return STATE_CRITICAL;
+		
 		/* drop privileges */
 		drop_privileges(nrpe_user,nrpe_group);
 
 		/* make sure we're not root */
 		check_privileges();
 
-		/* wait for connections */
-		wait_for_connections();
+		do{
+
+			/* reset flags */
+			sigrestart=FALSE;
+			sigshutdown=FALSE;
+
+			/* wait for connections */
+			wait_for_connections();
+
+			/* free all memory we allocated */
+			free_memory();
+
+			if(sigrestart==TRUE){
+
+				/* read the config file */
+				result=read_config_file(config_file);	
+
+				/* exit if there are errors... */
+				if(result==ERROR){
+					syslog(LOG_ERR,"Config file '%s' contained errors, bailing out...",config_file);
+					return STATE_CRITICAL;
+				        }
+			        }
+	
+		        }while(sigrestart==TRUE && sigshutdown==FALSE);
+
+		/* remove pid file */
+		remove_pid_file();
+
+		syslog(LOG_NOTICE,"Daemon shutdown\n");
 	        }
 
 #ifdef HAVE_SSL
@@ -389,6 +435,8 @@ int read_config_file(char *filename){
 				return ERROR;
 			        }
 		        }
+		else if(!strcmp(varname,"command_prefix"))
+			command_prefix=strdup(varvalue);
 
 		else if(!strcmp(varname,"server_address")){
                         strncpy(server_address,varvalue,sizeof(server_address) - 1);
@@ -595,6 +643,9 @@ void wait_for_connections(void){
 	        exit(STATE_CRITICAL);
 		}
 
+	/* socket should be non-blocking */
+	fcntl(sock,F_SETFL,O_NONBLOCK);
+
         /* set the reuse address flag so we don't get errors when restarting */
         flag=1;
         if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag))<0){
@@ -627,22 +678,13 @@ void wait_for_connections(void){
 	        exit(STATE_CRITICAL);
 		}
 
-	/* log info to syslog facility */
-        syslog(LOG_NOTICE,"Starting up daemon");
-
 	/* log warning about command arguments */
 #ifdef ENABLE_COMMAND_ARGUMENTS
 	if(allow_arguments==TRUE)
 		syslog(LOG_NOTICE,"Warning: Daemon is configured to accept command arguments from clients!");
 #endif
 
-	/* Trap signals */
-	signal(SIGQUIT,sighandler);
-	signal(SIGTERM,sighandler);
-
-	if(debug==TRUE){
-		syslog(LOG_DEBUG,"Listening for connections on port %d\n",htons(myname.sin_port));
-	        }
+	syslog(LOG_INFO,"Listening for connections on port %d\n",htons(myname.sin_port));
 
 	/* listen for connection requests - fork() if we get one */
 	while(1){
@@ -657,6 +699,10 @@ void wait_for_connections(void){
 			timeout.tv_usec=500000;
 			retval=select(sock+1,&fdread,NULL,&fdread,&timeout);
 
+			/* bail out if necessary */
+			if(sigrestart==TRUE || sigshutdown==TRUE)
+				break;
+
 			/* error */
 			if(retval<0)
 				continue;
@@ -671,6 +717,10 @@ void wait_for_connections(void){
 				if(errno==ENOBUFS)
 					continue;
 
+				/* bail out if necessary */
+				if(sigrestart==TRUE || sigshutdown==TRUE)
+					break;
+
 				/* retry */
 				if(errno==EWOULDBLOCK || errno==EINTR)
 					continue;
@@ -683,6 +733,10 @@ void wait_for_connections(void){
 			break;
 		        }
 
+		/* bail out if necessary */
+		if(sigrestart==TRUE || sigshutdown==TRUE)
+			break;
+
 		/* child process should handle the connection */
     		pid=fork();
     		if(pid==0){
@@ -703,6 +757,11 @@ void wait_for_connections(void){
 					return;
 				        }
 
+				/* handle signals */
+				signal(SIGQUIT,child_sighandler);
+				signal(SIGTERM,child_sighandler);
+				signal(SIGHUP,child_sighandler);
+
 				/* grandchild does not need to listen for connections, so close the socket */
 				close(sock);  
 
@@ -757,7 +816,7 @@ void wait_for_connections(void){
 				/* close socket prior to exiting */
 				close(new_sd);
 
-				return;
+				exit(STATE_OK);
     			        }
 
 			/* first child returns immediately, grandchild is inherited by INIT process -> no zombies... */
@@ -775,6 +834,9 @@ void wait_for_connections(void){
 		        }
   		}
 
+	/* close the socket we're listening on */
+	close(sock);
+
 	return;
 	}
 
@@ -941,7 +1003,10 @@ void handle_connection(int sock){
 		else{
 
 			/* process command line */
-			strncpy(raw_command,temp_command->command_line,sizeof(raw_command)-1);
+			if(command_prefix==NULL)
+				strncpy(raw_command,temp_command->command_line,sizeof(raw_command)-1);
+			else
+				snprintf(raw_command,sizeof(raw_command)-1,"%s %s",command_prefix,temp_command->command_line);
 			raw_command[sizeof(raw_command)-1]='\x0';
 			process_macros(raw_command,processed_command,sizeof(processed_command));
 
@@ -1033,21 +1098,6 @@ void handle_connection(int sock){
 
 
 
-/* handle signals */
-void sighandler(int sig){
-
-	/* free all memory we allocated */
-	free_memory();
-	
-	/* terminate... */
-	exit(0);
-
-	/* so the compiler doesn't complain.. */
-	return;
-        }
-
-
-
 /* free all allocated memory */
 void free_memory(void){
 	command *this_command;
@@ -1057,18 +1107,21 @@ void free_memory(void){
 	this_command=command_list;
 	while(this_command!=NULL){
 		next_command=this_command->next;
-		free(this_command->command_name);
-		free(this_command->command_line);
+		if(this_command->command_name)
+			free(this_command->command_name);
+		if(this_command->command_line)
+			free(this_command->command_line);
 		free(this_command);
 		this_command=next_command;
 	        }
 
+	command_list=NULL;
+
 	return;
         }
 
 
 
-
 /* executes a system command via popen(), but protects against timeouts */
 int my_system(char *command,int timeout,int *early_timeout,char *output,int output_length){
         pid_t pid;
@@ -1296,10 +1349,10 @@ int drop_privileges(char *user, char *group){
 		else
 			uid=(uid_t)atoi(user);
 			
-#ifdef HAVE_INITGROUPS
-
+		/* set effective user ID if other than current EUID */
 		if(uid!=geteuid()){
 
+#ifdef HAVE_INITGROUPS
 			/* initialize supplementary groups */
 			if(initgroups(user,gid)==-1){
 				if(errno==EPERM)
@@ -1309,11 +1362,11 @@ int drop_privileges(char *user, char *group){
 					return ERROR;
 			                }
 	                        }
-		        }
 #endif
 
-		if(setuid(uid)==-1)
-			syslog(LOG_ERR,"Warning: Could not set effective UID=%d",(int)uid);
+			if(setuid(uid)==-1)
+				syslog(LOG_ERR,"Warning: Could not set effective UID=%d",(int)uid);
+		        }
 	        }
 
 	return OK;
@@ -1362,7 +1415,25 @@ int write_pid_file(void){
 		close(fd);
 	        }
 	else{
-		syslog(LOG_ERR,"Cannot write to pidfile '%s'.",pid_file);
+		syslog(LOG_ERR,"Cannot write to pidfile '%s' - check your privileges.",pid_file);
+	        }
+
+	return OK;
+        }
+
+
+
+/* remove pid file */
+int remove_pid_file(void){
+
+	/* no pid file was specified */
+	if(pid_file==NULL)
+		return OK;
+
+	/* remove existing pid file */
+	if(unlink(pid_file)==-1){
+		syslog(LOG_ERR,"Cannot remove pidfile '%s' - check your privileges.",pid_file);
+		return ERROR;
 	        }
 
 	return OK;
@@ -1388,6 +1459,59 @@ int check_privileges(void){
 
 
 
+/* handle signals (parent process) */
+void sighandler(int sig){
+	static char *sigs[]={"EXIT","HUP","INT","QUIT","ILL","TRAP","ABRT","BUS","FPE","KILL","USR1","SEGV","USR2","PIPE","ALRM","TERM","STKFLT","CHLD","CONT","STOP","TSTP","TTIN","TTOU","URG","XCPU","XFSZ","VTALRM","PROF","WINCH","IO","PWR","UNUSED","ZERR","DEBUG",(char *)NULL};
+	int i;
+	char temp_buffer[MAX_INPUT_BUFFER];
+
+	if(sig<0)
+		sig=-sig;
+
+	for(i=0;sigs[i]!=(char *)NULL;i++);
+
+	sig%=i;
+
+	/* we received a SIGHUP, so restart... */
+	if(sig==SIGHUP){
+
+		sigrestart=TRUE;
+
+		syslog(LOG_NOTICE,"Caught SIGHUP - restarting...\n");
+	        }
+
+	/* else begin shutting down... */
+	if(sig==SIGTERM){
+
+		/* if shutdown is already true, we're in a signal trap loop! */
+		if(sigshutdown==TRUE)
+			exit(STATE_CRITICAL);
+
+		sigshutdown=TRUE;
+
+		syslog(LOG_NOTICE,"Caught SIG%s - shutting down...\n",sigs[sig]);
+	        }
+
+	return;
+        }
+
+
+
+/* handle signals (child processes) */
+void child_sighandler(int sig){
+
+	/* free all memory we allocated */
+	free_memory();
+
+	/* terminate */
+	exit(0);
+	
+	/* so the compiler doesn't complain... */
+	return;
+        }
+
+
+
 /* tests whether or not a client request is valid */
 int validate_request(packet *pkt){
         u_int32_t packet_crc32;