/*
 * 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 master agent to support
 * the Direct SNMP feature of zSeries OSA-E network cards. 
 * ucd-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 "ibmOSAMib.h"

#include <signal.h>

/* file descriptor for the proc filesystem ioctl's */
int proc_fd;

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

sig_atomic_t keep_running = 1; 
sig_atomic_t interfaces_changed = 0;

/* signal handler for SIGTERM or SIGINT */ 
RETSIGTYPE
stop_server(int a) {
	keep_running = 0;
}

/* signal handler for SIGUSR1 
 * which is send by the kernel if the interface information changes
		 */
RETSIGTYPE
on_interfaces_changed( int a )
{
	interfaces_changed++;
}

/*
 * main routine
 */
int main( int argc, char *argv[] )
{  
	TABLE_OID* li_ptr;
	char oidstr[MAX_OID_STR_LEN];
	char logfile[PATH_MAX + 1];
	char *pid_file = NULL;
	FILE *PID;
	int arg;
	struct sigaction act;
	int res;

	/* 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", RELEASE_STRING );
				printf( "\nDescription: IBM OSA Express SNMP subagent for ucd-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();	 
#ifdef NETSNMP5
				netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET, argv[arg]);
#else
				ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET, argv[arg]);
#endif
				break;
			case '-':
				switch(argv[arg][2]){
				case 'v':		
					printf( "\nVersion:     %s", RELEASE_STRING);
					printf( "\nDescription: IBM OSA Express SNMP subagent for "
						"ucd-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 */
   
	/* open proc file, which is also used by other functions in ibmOSAMib.c */
	proc_fd = open( QETH_PROCFILE, O_RDONLY );
        if ( proc_fd < 0 )
	{
		fprintf( stderr, "open(%s) failed - reason %s\n",
				QETH_PROCFILE, strerror( errno ) );
		
		exit( 1 );
	}
   
	/* 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 subagent version %s\n",
		   RELEASE_STRING );

	/* make us a agentx client. */
#ifdef NETSNMP5
	netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1); 
#else
	ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1);
#endif	
	
	/* 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");


	act.sa_flags = 0;
	sigemptyset( &act.sa_mask );

	/* handle termination requests (kill -TERM or kill -INT) */
	act.sa_handler = stop_server;
	if( sigaction( SIGTERM, &act, NULL ) < 0 ){
		fprintf( stderr, "sigaction( SIGTERM, ... ) failed - reason %s\n",
				strerror( errno ) );
		
		exit( 1 );
	}

	act.sa_handler = stop_server;
	if( sigaction( SIGINT, &act, NULL ) < 0 ){
		fprintf( stderr, "sigaction( SIGINT, ... ) failed - reason %s\n",
				strerror( errno ) );

		exit( 1 );
	}

	/* handle iterface count change requests ( kill -SIGUSR1 ) */
	act.sa_handler = on_interfaces_changed;
	if( sigaction( SIGUSR1, &act, NULL ) ){
		fprintf( stderr, "sigaction( SIGUSR1, ... ) failed - reason %s\n",
				strerror( errno ) );
		
		exit( 1 );
	}
	/* register our process to receive SIGUSR1 on interface changes */
	if( ioctl( proc_fd, QETH_IOCPROC_REGISTER, QETH_UPDATE_MIB_SIGNAL) < 0 )
	{
		fprintf( stderr, "ioctl(%s) failed - reason %s\n",
				 QETH_PROCFILE, strerror( errno ) );
		exit( 1 );
	}

		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( interfaces_changed > 0 )
		{
			interfaces_changed = 0;
			update_mib_info();			
		} else {	
			agent_check_and_process(1); 
		}
		} /* end while */

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


		/* 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 ( (res = 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 %d\n",res);
		} /* end for */
  
		snmp_shutdown("osasnmpd");  

	return 0;
} /* end osasnmpd */

