/*
 * File...........: osasnmpd.c
 * Author(s)......: Thomas Weber <tweber@de.ibm.com>
 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2002
 *
 * History of changes:
 * 07/29 added logic '-x agentx_socket' parm 
 *        exit subagent on startup, if init_agent() fails
 *  
 * OSA-E subagent main module  
 * This subagent extends the ucd-snmp and net-snmp master agent to support
 * the Direct SNMP feature of zSeries OSA-E network cards. 
 * ucd-snmp or net-snmp AgentX support must be enabled to allow the 
 * subagent connecting to the master agent (snmpd).  
 *
 * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


/* basic includes */
#include "ibmOSAMibUtil.h"

#include <signal.h>

/* ptr to OSA Express MIB information stored in linked lists */
extern TABLE_OID* oid_list_head;

/* pipe for communication between main and blocking-ioctl thread */
int pipe_fd[ 2 ];

/* signal handler for SIGTERM or SIGINT */ 
sig_atomic_t keep_running = 1; 


RETSIGTYPE
stop_server(int a) {
	keep_running = 0;
}

static int
check_pipe_and_process( )
{
	int             numfds;
	fd_set          fdset;
	struct timeval  timeout = { LONG_MAX, 0 }, *tvp = &timeout;
	int             count;
	int             fakeblock = 0;
	char 		cmd[ 100 ];
	int		cmd_count;
	int 		do_update;
	int		i;        
                                                                                                                    
	numfds = 0;
	FD_ZERO(&fdset);
	snmp_select_info(&numfds, &fdset, tvp, &fakeblock);
	if ( fakeblock != 0) {
		/*
		 * There are no alarms registered, and the caller asked for blocking, so
		 * let select() block forever.
		 */
                                                                                                                            
		tvp = NULL;
	} 

	FD_SET( pipe_fd[ 0 ] , &fdset );

	if( pipe_fd[0] >= numfds )
		numfds = pipe_fd[ 0 ] + 1;

	count = select(numfds, &fdset, 0, 0, tvp);

	if( count <= 0 )
		return 0;

	if( FD_ISSET( pipe_fd[ 0 ], &fdset ) ) 
	{
		do_update = 0;
		cmd_count = read( pipe_fd[ 0 ], cmd, 100 );

		if( cmd_count < 0 )
		{
			keep_running = 0;
			return 1;
		} else {
			for( i = 0; i < cmd_count; i++)
			{
				switch( cmd[i] )
				{
					case 'C':
						do_update = 1;
						break;
					case 'E':
					default:
						keep_running = 0;
						return 1;
				}
			}

			if( do_update )
				update_mib_info();

		}

		return 1;
	}
                      
	return 0;                                                                                                      
}



/*
 * main routine
 */
int main( int argc, char *argv[] )
{  
	/* ID for task we want to spin off */
	pid_t child_pid;

	TABLE_OID* li_ptr;
	char oidstr[MAX_OID_STR_LEN];
	char logfile[PATH_MAX + 1];
	char *pid_file = NULL;
	FILE *PID;
	int arg;

	/* default settings, may be overridden by parameters */  
	int std_err_log = 0;     /* 0=turn off stderr logging; 1=turn on */
     	                      /* if turned off; use file logging */
	int dont_zero_log = 0;   /* 0=clear logfile; 1=append to logfile */
	int dont_fork = 0;       /* 0=dont detach from shell; 1=detach */ 
	strcpy( logfile, OSAE_LOGFILE ); /* default log file */

	/* check for parameters */
	for (arg = 1; arg < argc; arg++)
	{
		if (argv[arg][0] == '-') 
		{
			switch (argv[arg][1]) {
			case 'h':
				usage();
				break;
			case 'v':
				printf( "\nVersion:     %s", AgentVersion );
				printf( "\nDescription: IBM OSA Express SNMP subagent for ucd-snmp and net-snmp\n\n" );
				exit(0); 
			case 'l':
				if (++arg == argc) 
					usage();
				if (strlen(argv[arg]) > PATH_MAX) 
				{
					fprintf( stderr, "osasnmpd: logfile path too long (limit %d chars)\n",
						PATH_MAX);
					exit(1);
				}
				strncpy(logfile, argv[arg], PATH_MAX);
				break;
			case 'A':
				dont_zero_log = 1;
				break;	    
			case 'L':
				std_err_log=1;
				break;	    
			case 'f':
				dont_fork = 1;
				break;
			case 'P':
				if (++arg == argc)
					usage();
				pid_file = argv[arg];
				break;
			case 'x':
				if (++arg == argc)
					usage();	 
				ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET, argv[arg]);
				break;
			case '-':
				switch(argv[arg][2]){
				case 'v':		
					printf( "\nVersion:     %s", AgentVersion );
					printf( "\nDescription: IBM OSA Express SNMP subagent for "
						"ucd-snmp and net-snmp\n\n" );
					exit(0);
				case 'h':
					usage();
					break;
				} /* end switch */
			default:
				fprintf( stderr, "osasnmpd: Invalid option: %s\n", argv[arg] );
				usage();
				break;
			} /* end switch */	  
		}
		else 
		{
			fprintf(stderr, "osasnmpd: Bad argument: %s\n", argv[arg]);
			exit(1);
		} /* end if */
	}  /* end-for */


	/* detach from shell (default) */
	if (!dont_fork && fork() != 0) 
	{
		exit(0);
	}
 
	/* create a pidfile if requested */ 
	if (pid_file != NULL) 
	{
		if ((PID = fopen(pid_file, "w")) == NULL) 
		{
			snmp_log_perror("fopen");
			fprintf(stderr, "osasnmpd: cannot create PIDFILE\n");
			exit(1);
		}
		else 
		{
			fprintf(PID, "%d\n", (int) getpid() );
			fclose(PID);
		}
	} /* end if */
   
	/* enable logging to stderr or logfile */ 
	if ( !std_err_log ) {
		snmp_disable_stderrlog();
		snmp_enable_filelog( logfile, dont_zero_log );	
	}
	else
	{
		snmp_enable_stderrlog();
	} /* end if */

	snmp_log( LOG_INFO, "IBM OSA-E ucd-snmp 4.2.x and net-snmp 5.0.x series subagent version %s\n",
	    AgentVersion );

	/* make us a agentx client. */
	ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1); 
	
	
	/* initialize the agent library */
	if ( init_agent("osasnmpd") != 0 )
	{
		fprintf( stderr, "osasnmpd: init_agent() failed\n"
			"osasnmpd: check subagent logfile for detailed information\n" );	      
		exit(1);
	}
	/* initialize OSA Express MIB module here */
	init_ibmOSAMib();  

	/* osasnmpd will be used to read osasnmpd.conf files. */
	init_snmp("osasnmpd");

	/* init pipe */
	if( pipe( pipe_fd ) < 0 ){
		snmp_shutdown("osasnmpd");
		exit(1);
	}

	fcntl( pipe_fd[ 0 ], F_SETFL | O_NONBLOCK );

	child_pid = fork();

	if( child_pid < 0 ){
                fprintf( stderr, "too less system resources to create a new thread\n"
                                "Cannot start subagent...exiting...\n");
                snmp_shutdown("osasnmpd");
                exit(1);
	}

	if( child_pid == 0 )
	{
		/* CHILD process */
		return monitor_interface_count();
	} else {
		/* PARENT process */
		
		/* handle termination requests (kill -TERM or kill -INT) */
		signal(SIGTERM, stop_server);
		signal(SIGINT, stop_server);

		snmp_log(LOG_INFO, "Initialization of OSA-E subagent successful...\n"); 

		/* subagent main loop, that calls agent_check_and_process() in blocking mode */
		while(keep_running) {
			if( check_pipe_and_process() == 0 )    
				agent_check_and_process(0); 
		} /* end while */

		snmp_log(LOG_INFO, "Received TERM or STOP signal...shutting down agent...\n"); 

		close( pipe_fd[ 0 ] );
		close( pipe_fd[ 1 ] );

		/* unregister all Toplevel OIDs we support */
		for( li_ptr = oid_list_head; li_ptr->next != NULL; li_ptr = li_ptr->next )    
		{
			oid_to_str_conv( (oid*) li_ptr->next->pObjid, li_ptr->next->length, oidstr );
			snmp_log(LOG_INFO, "unregister Toplevel OID .%s.....", oidstr ); 
			if ( unregister_mib( li_ptr->next->pObjid, li_ptr->next->length ) == MIB_UNREGISTERED_OK )
				snmp_log(LOG_INFO, "successful\n");
			else
				snmp_log(LOG_INFO, "failed\n");
		} /* end for */
  
		/* at shutdown time */
		kill( child_pid, SIGTERM );
		waitpid( child_pid, NULL, 0 );
	
		snmp_shutdown("osasnmpd");  
	}

	return 0;
} /* end osasnmpd */

