#pragma module chess_mib "V5.4-00" /* * © Copyright 1976, 2003 Hewlett-Packard Development Company, L.P. * * Confidential computer software. Valid license from HP and/or its * subsidiaries required for possession, use, or copying. * * Consistent with FAR 12.211 and 12.212, Commercial Computer Software, * Computer Software Documentation, and Technical Data for Commercial * Items are licensed to the U.S. Government under vendor's standard * commercial license. * * Neither HP nor any of its subsidiaries shall be liable for technical * or editorial errors or omissions contained herein. The information * in this document is provided "as is" without warranty of any kind * and is subject to change without notice. The warranties for HP * products are set forth in the express limited warranty statements * accompanying such products. Nothing herein should be construed as * constituting an additional warranty. */ /* * chess-mib.c Example Extensible SNMP Subagent * * This example sub-agent implements the wholly-fictitious * chess MIB. * */ #include #include #include #ifndef __VMS #include #else #include /* V2.0-01 */ #endif #include #include #include #include #include "esnmp.h" /* esnmp definitions */ #ifndef __VMS #define ARE_YOU_THERE_TIME 30 /* interval between ARE_YOU_THERE messages (seconds)*/ #else #define ARE_YOU_THERE_TIME 5400 /* V5.1-02 */ #endif #define RESPONSE_TIMEOUT 0 /* use the default timeout */ #define REGISTRATION_PRIORITY 2 #ifndef __VMS #define RESTARTS 3 /* Attempt to connect 3 times in the face of 'fatal' errors */ #else __VMS #define RESTARTS 10 /* sdb/ea-v2.0-00 Up to 10 for robustness */ #define ERROR_TIMEOUT 3 #ifndef PR_SLOWHZ /* V5.1-00 */ /* From */ #define PR_SLOWHZ 2 /* 2 slow timeouts per second */ #define AGENTX_VERSION /* V5.1-00 */ #endif #ifndef TCPTV_MSL /* V5.1-00 */ /* From */ #define TCPTV_MSL #define TCPTV_MSL ( 30*PR_SLOWHZ) /* max seg lifetime */ #endif #endif __VMS extern SUBTREE chess_subtree; extern int chess_initialize(void); static int usage(char *progname); static void quit(int sig); #ifdef __VMS static char *lib_error(int code); /* V2.0-02 */ #endif /***************************************************************************** | FUNCTION: main | | DESCRIPTION: | This routine does the following: | 1) Decode the runtime arguments. | 2) calls esnmp_init() to initialize and send the open. | Calls esnmp_register() for each subtree to be supported | when the response the the OPEN is received. | 3) loops waiting on a select on the socket | when an event occures, call esnmp_poll() to read | and process the incoming packet. | 4) If the select times out and no messages are pending, | send a 'Are-You-There' message. | 5) If the select times out and we are expecting a response, | then restart from the top by sending a CLOSE and an OPEN. | | From these routines I could get the following status codes: | 0) ESNMP_LIB_OK - continue, everything is fine. | -1) ESNMP_LIB_NO_CONNECTION - Cannot send OPEN to Master Agent, restart. | -2) ESNMP_LIB_BAD_REG - Registration error. | -3) ESNMP_LIB_CLOSE - Received a CLOSE message. restart. | -4) ESNMP_LIB_LOST_CONNECTION- lost connection with Master Agent, restart. | -5) ESNMP_LIB_NOTOK - negitive response. | | USAGE: examp [-d][-trace] | -d - don't be a demond | -trace - show all trace messages (won't demonize) | *****************************************************************************/ int main (int argc, char **argv) { int i; int cc; int rc = ESNMP_LIB_OK; int starts = 0; int response_pending; int subsocket = 0; fd_set fds_mask; fd_set fds_cur; struct timeval cur_timeout; int debug_level; #ifdef DAEMONIZE int daemonize = 1; /* daemonize by default */ #endif /* DAEMONIZE */ #ifdef __VMS unsigned long int selectErrCnt; /* V2.0-01 */ #endif /* initialize the signal handlers */ signal(SIGINT, quit); /* Decode the command line arguments */ debug_level = 0; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-trace") == 0) { debug_level |= TRACE; } #ifdef DAEMONIZE else if (!strcmp("-d", argv[i])) { debug_level |= WARNING; /* send messages to stdout */ daemonize = 0; /* don't be a daemon */ } #endif /* DAEMONIZE */ else { usage(argv[0]); exit(-1); } } #ifdef DAEMONIZE if (daemonize) debug_level |= DAEMON_LOG; /* send log messages to daemon log */ #endif set_debug_level(debug_level, NULL); chess_initialize(); #ifdef DAEMONIZE if (daemonize) { if (fork()) { _exit(); } else { daemon(0, 0); } } #endif /* DAEMONIZE */ /* MAIN LOOP */ #ifndef __VMS /* | Try RESTARTS times to connect to the master agent, | unless we are a duplicate. */ while ((starts++ < RESTARTS) && (rc != ESNMP_LIB_DUPLICATE)) { #else /* V2.0-02: Allow to detect CLOSE */ /* | Try RESTARTS times to connect to the master agent, | unless we are a duplicate or have received CLOSE from master. */ while ( (starts++ < RESTARTS) && (rc != ESNMP_LIB_DUPLICATE) && (rc != ESNMP_LIB_CLOSE) && (rc != ESNMP_LIB_NO_CONNECTION) /* V5.1-00 */ ) { #endif /* | Initialize and send the OPEN | V2.0-02: Do esnmp_init before trace */ rc = esnmp_init(&subsocket, argv[0]); ESNMP_LOG(TRACE, ("Initializing eSNMP connection...\n")); #ifdef __VMS /* Put routine after esnmp_init() so progname already captured. */ selectErrCnt = set_select_limit(NULL); /* V2.0-01 */ #endif if (rc == ESNMP_LIB_OK) { FD_ZERO(&fds_mask); FD_SET(subsocket, &fds_mask); response_pending = TRUE; } /* | Register our subtrees */ if (rc == ESNMP_LIB_OK) { #if 1 rc = esnmp_register( &chess_subtree, RESPONSE_TIMEOUT, REGISTRATION_PRIORITY); #else /* ** Sample esnmp_register2() call, for demo only. To use: ** - Change #if 1 to #if 0 above ** - Compile and link ** - Must send request in on a cluster address, since this ** sample does registration using the ESNMP_REG_OPT_CLUSTER ** context. ** - Also, tests registering only the range product id through num games ** data */ { #define RESPONSE_TIMEOUT 10 #define REGISTRATION_PRIORITY 10 /* #define RANGE_SUBID 12 */ /*#define RANGE_UPPER_BOUND 4 */ int status; static ESNMP_REG esnmp_reg_for_minichess; static ESNMP_REG esnmp_reg_for_interfaces2; memset(&esnmp_reg_for_minichess, 0, sizeof(ESNMP_REG)); esnmp_reg_for_minichess.subtree = &chess_subtree; esnmp_reg_for_minichess.priority = REGISTRATION_PRIORITY; esnmp_reg_for_minichess.timeout = RESPONSE_TIMEOUT; /* esnmp_reg_for_minichess.range_subid = RANGE_SUBID; */ /* esnmp_reg_for_minichess.range_upper_bound = RANGE_UPPER_BOUND; */ /* need table support for meaningful range_subid -- */ /* e.g. register 1.3.6.1.2.1.6.1.8 and want to support 3 rows, */ /* so really up to 1.3.6.1.2.1.6.3.8 */ /* would have subid ^ =8 and upper_bound 3 */ /* so 1.3.6.1.2.1.6.1.8 to 1.3.6.1.2.1.6.3.8 would be supported, */ /* not 1.3.6.1.2.1.6.4.8, 1.3.6.1.2.1.6.5.8 etc. */ /* also nothing beyond 1.3.6.1.2.1.6.3.8 !!!*/ /* assume this means the instrances, so really CAN get 1.3.6.1.2.1.6.3.8.0 ??*/ esnmp_reg_for_minichess.options |= ESNMP_REG_OPT_CLUSTER; status = esnmp_register2( &esnmp_reg_for_minichess ); if (status != ESNMP_LIB_OK) { printf("Could not queue the 'minichess' \n"); printf("subtree for registration\n"); } } #endif } /* | Keep reading the eSNMP socket, sending R-U-THERE? whenever | we timeout. esnmp_poll() will process the master's responses, | so check specifically for DUPLICATE. */ while(rc == ESNMP_LIB_OK) { #ifdef __VMS /* V5.1-00 */ char *strPtr = getenv("TCPIP$ARE_YOU_THERE_TIME"); if(strPtr) cur_timeout.tv_sec = atoi(strPtr); else #endif __VMS cur_timeout.tv_sec = ARE_YOU_THERE_TIME; cur_timeout.tv_usec= 0; fds_cur = fds_mask; cc = select(FD_SETSIZE, &fds_cur, (fd_set *) NULL, (fd_set *) NULL, &cur_timeout); if (cc == -1) { #ifdef __VMS /* ** V2.0-01 Check for known errors first; exit if found. ** V5.1-03 Add checks for SS$_UNREACHABLE and ENETDOWN. ** Note that check for EINTR is preserved from original ** UNIX code for OS_MIBS. */ if( (vaxc$errno == SS$_SHUT) || (vaxc$errno == SS$_DEVNOTMOUNT) || (vaxc$errno == SS$_UNREACHABLE) || (errno == ENETDOWN) ) { ESNMP_LOG( ERROR, ("select returned -1 on subagent sockets: %s, errno: %d vaxc$errno: %d\n", strerror(errno), errno, vaxc$errno)); ESNMP_LOG( ERROR, ("possible TCPIP shutdown/restart; exiting.\n")); exit(1); } else #endif __VMS if (errno != EINTR) { /* We have an error on the select - should not get any*/ #ifndef __VMS ESNMP_LOG( ERROR, ("select returned:%s\n",strerror(errno))); #else /* ** Keep track of how many times this error encountered. ** V2.0-01 */ selectErrCnt--; ESNMP_LOG( ERROR, ("select returned -1 on subagent sockets: %s, vaxc$errno: %d\n", strerror(errno), vaxc$errno )); /* ** If selectErrMax was initialized to 0, no limit, so first ** decrement gets it to -1. Get to 0 here only if started >0. */ if(selectErrCnt==0) { ESNMP_LOG( ERROR, ("limit of select() repetitions reached, terminating...")); exit(1); } #endif rc = ESNMP_LIB_LOST_CONNECTION; } continue; } else if (cc == 0) { /* The ARE_YOU_THERE time interval expired */ if (!response_pending) { ESNMP_LOG( TRACE, ( "time to send ARE_YOU_THERE\n")); #if 0 /* V2.0-02 */ /* ** Sample unregister call. Not intended for use ** in actual operation, for demo only. To use: ** - Change #if 0 to #if 1 ** - Compile and link ** - When program runs, instead of periodically sending ** are_you_there it will unregister the mib tree. ** First time.. .then./? */ rc = esnmp_unregister( &chess_subtree); #else rc = esnmp_are_you_there(); #endif response_pending = TRUE; } else { /* timeout waiting for a response */ rc = ESNMP_LIB_LOST_CONNECTION; /* exit the loop and restart */ } } else { /* This must be a valid message - process it */ response_pending = FALSE; #ifndef __VMS rc = esnmp_poll(); #else __VMS /* ** Reset starts for robustness ** sdb/ea-v2.0-00 */ if((rc = esnmp_poll()) == ESNMP_LIB_OK) starts = 0; #endif __VMS } } /* end Message loop */ #ifndef __VMS /* | Trace the error condition. If we got back a DUPLICATE | error stop now. Otherwise wait and attempt a retry. */ if (rc == ESNMP_LIB_DUPLICATE) { ESNMP_LOG( ERROR, ("This process is a duplicate sub-agent.")); } else { ESNMP_LOG(ERROR, ("eSNMP error: %i", rc)); sleep(ARE_YOU_THERE_TIME); } #else __VMS /* V2.0-02 | Trace the error condition. If we got back a DUPLICATE | error or a CLOSE, stop now. Custom extension subagents | may choose to try to restart after a CLOSE. | Otherwise wait and attempt a retry. Note that the curly | braces before and after ESNMP_LOG macro may be required | for proper macro preprocessing; otherwise compiler may | get confused by expanded macro braces. */ if (rc == ESNMP_LIB_DUPLICATE) { ESNMP_LOG( ERROR, ("This process is a duplicate subagent.\n")); } else if (rc == ESNMP_LIB_CLOSE) { /* V2.0-02 */ ESNMP_LOG( WARNING, ("Received CLOSE from master agent.\n")); } #ifdef AGENTX_VERSION /* V5.1-00 */ /* ** To operate with new API. If subagent is connected and then ** master terminates, close is OK, but then get new socket and ** just keep on that. Note that do not try do uncapabilities, ** if can't get to master anyway makes no sense. */ else if (rc == ESNMP_LIB_NO_CONNECTION) { ESNMP_LOG( WARNING, ("Master agent cannot be reached. Waiting to attempt reconnect.\n")); rc = esnmp_cleanup(); sleep(2*TCPTV_MSL); /* give time to get out of TIME_WAIT state */ } /* V5.1-04 ** Add special handling for lost connection, like no connection. */ else if (rc == ESNMP_LIB_LOST_CONNECTION) { ESNMP_LOG( WARNING, ("Lost connection with master agent. Waiting to attempt reconnect.\n")); rc = esnmp_cleanup(); sleep(2*TCPTV_MSL); } /* V5.1-04 ** Other reason for problem. Do cleanup as above, and sleep ** for the TIME_WAIT period instead of ERROR_TIMEOUT. Make ** message consistent with that in os_mibs and hr_mib. */ else { ESNMP_LOG(WARNING, ("Restarting protocol, eSNMP error: %s", lib_error(rc))); rc = esnmp_cleanup(); sleep(2*TCPTV_MSL); } #else /* non AgentX version */ else { /* ** V2.0-01: display text with lib_error() ** OpenVMS note: example of how can get here: ** Try a restart but the master agent went down since last ** (re)start. V5.1-01 Remove extra \n here, provided by ** lib_error(). */ ESNMP_LOG(ERROR, ("eSNMP error: %s", lib_error(rc))); sleep(ERROR_TIMEOUT); /* sdb/ea-v2.0-00 */ } #endif AGENTX_VERSION #endif __VMS } /* end restart loop */ #ifndef __VMS /* | We've retried RESTART times or we received a DUPLICATE error. | Just say no... */ ESNMP_LOG( ERROR, ("Terminating...")); esnmp_term(); #else /* V2.0-02 ** Have tried RESTART times, have received a DUPLICATE error, or a CLOSE. ** No esnmp_term() here since atexit() in esnmp_init() takes care of ** setting that up to be called. Also, adjust message depending on status. ** Note only difference is status of WARNING vs. ERROR. */ if(rc==ESNMP_LIB_CLOSE) { ESNMP_LOG( WARNING, ("Terminating...\n")); } else { /* ** Assume duplicate or true restart problem. */ ESNMP_LOG( ERROR, ("Terminating...\n")); } #endif __VMS exit(1); } /**************************************************************************** | FUNCTION: usage | | DESCRIPTION: | print the usage | | USAGE: examp [-d][-trace] | -d - don't be a daemon | -trace - show all trace messages | | NOTE: default logging is to the daemon log if the program is | daemonized. Otherwise it goes to stdout. | *****************************************************************************/ static int usage(char *name) { char *progname; #ifndef __VMS if ((progname = rindex(name,'/'))== NULL) progname = name; else progname++; (void) fprintf(stderr, "USAGE: %s [-d][-trace]\n", progname); (void) fprintf(stderr, " -d Don't Daemonize\n"); (void) fprintf(stderr, " -trace print trace messages\n"); #else /* V2.0-02 */ if ((progname = strrchr (name,']')) == NULL) progname = name; else progname++; ESNMP_LOG(NULL, ("Usage: $ chess = \"$%s\"\n", progname)); ESNMP_LOG(NULL, (" $ chess [log options]\n")); ESNMP_LOG(NULL, (" or\n")); ESNMP_LOG(NULL, (" $ mcr %s [log options]\n", progname)); ESNMP_LOG(NULL, ("\n")); ESNMP_LOG(NULL, (" log options:\n")); ESNMP_LOG(NULL, (" -warn : Log warning messages\n")); ESNMP_LOG(NULL, (" -error : Log error messages\n")); ESNMP_LOG(NULL, (" -trace : Log trace messages\n")); #endif __VMS return 1; } /**************************************************************************** | FUNCTION: quit | | DESCRIPTION: | The place to go when we get a SIGINT signal | *****************************************************************************/ static void quit(int sig) { ESNMP_LOG( TRACE, ( "SIGINT signal -exiting\n")); esnmp_term(); /* Send a CLOSE message */ exit(1); } #ifdef __VMS /* V2.0-02 */ /* From standard MIB source code; provide example for customers. V2.0-02 */ /*********************************************************************** | FUNCTION: lib_error | | DESCRIPTION: | Returns text describing the given error code. | | RETURNS: | pointer to string ************************************************************************/ static char *lib_error(int code) { /* ** V2.0-02: struct now in esnmp.h, name changed. Keep old code ** for consistency with existing applications using it as an example. */ if (code > ESNMP_LIB_OK || code < ESNMP_LIB_DUPLICATE) return("Unknown error code"); return(esnmp_lib_err[-code].text); } #endif __VMS