Annotation of 43BSDReno/contrib/isode-beta/doc/rfcs/smux-api.rf, revision 1.1.1.1

1.1       root        1: .\" smux-api.rf - How to export...
                      2: .de UL
                      3: .ti -.5i
                      4: -------
                      5: .br
                      6: ..
                      7: .ie \nh \{\
                      8: .ll 66 
                      9: .po 2 \}
                     10: .el \{\
                     11: .ll 62 
                     12: .pl 58
                     13: .nr sf R
                     14: .nr fm 0 \}
                     15: .he 'draft'Exporting MIBs for BSD UNIX'May 1990'
                     16: .fo 'M.T. Rose''[Page %]'
                     17: .de $0
                     18: .(x t
                     19: \\$2 \\$1
                     20: .)x
                     21: ..
                     22: .lp
                     23: .na
                     24: .nh
                     25: .ce 3
                     26: \fBHow to export a MIB module
                     27: from a BSD UNIX daemon
                     28: using the 4BSD/ISODE SMUX API\fR
                     29: .sp 1
                     30: .ce
                     31: Sun May 13 14:54:13 1990
                     32: .sp 2
                     33: .ce
                     34: Marshall T. Rose
                     35: .sp 1
                     36: .ce 4
                     37: Performance Systems International, Inc.
                     38: 11800 Sunrise Valley Drive
                     39: Suite 1100
                     40: Reston, VA  22091
                     41: .sp 1
                     42: .ce
                     43: [email protected]
                     44: .sp 5
                     45: .sh 1 "Status of this Memo"
                     46: .lp
                     47: This document defines an API for UNIX daemons wishing to implement a
                     48: MIB module on a BSD UNIX system using the 4BSD/ISODE SNMP software.
                     49: This is a local mechanism.
                     50: 
                     51: .bp
                     52: .sh 1 "The Environment"
                     53: .lp
                     54: This document gives an overview of how one modifies a UNIX program
                     55: to export a MIB module to the local SNMP agent.
                     56: .lp
                     57: All of the files necessary to interface to the SNMP agent is contained
                     58: in the ISODE source tree,
                     59: in the \fBsnmp/\fR directory.
                     60: Since this document avoids giving the actual \fIC\fR procedural
                     61: definitions,
                     62: you should familiarize yourself with the \fIlint\fR library, \fBllib-lisnmp\fR.
                     63: .lp
                     64: For the purposes of example,
                     65: throughout this document we will reference a simple UNIX daemon,
                     66: \fIunixd\fR, 
                     67: which implements a MIB module for \*(lqmbuf statistics\*(rq.
                     68: 
                     69: .sh 2 "The ISODE"
                     70: .lp
                     71: As you might have guessed,
                     72: this document assumes that you're running the ISODE on your system.
                     73: You should read the \fBREAD-ME\fR file at the base of the source tree
                     74: for instructions on how to configure, generate, and install the ISODE.
                     75: In the future, an abbreviated set of instructions, 
                     76: for those interested in running the only 4BSD/ISODE SNMP software,
                     77: will be written.
                     78: .lp
                     79: For now,
                     80: follow the \fBREAD-ME\fR,
                     81: generate the base system and SNMP, e.g.,
                     82: .sp
                     83: .in +.5i
                     84: .nf
                     85: % ./make all all-snmp
                     86: .fi
                     87: .in -.5i
                     88: .sp
                     89: and then install only the 4BSD/ISODE SNMP software:
                     90: .sp
                     91: .in +.5i
                     92: .nf
                     93: # ./make inst-all inst-snmp
                     94: .fi
                     95: .in -.5i
                     96: .sp
                     97: However, 
                     98: be sure to read the entire \fBREAD-ME\fR file,
                     99: as there are other steps involved in installing the SNMP software.
                    100: 
                    101: .bp
                    102: .sh 1 "MIB Modules"
                    103: .lp
                    104: The first thing you need to do is to define the actual managed objects
                    105: which your program will implement.
                    106: This is done by writing a \*(lqMIB module\*(rq.
                    107: The syntax of the module is defined in the Internet-standard SMI, RFC1155.
                    108: It is not the purpose of this document to describe the rules used for
                    109: writing a MIB module.
                    110: However, here are the basics to speed you on your way.
                    111: .lp
                    112: The MIB module is defined in a file called \fImodule\fR.my,
                    113: e.g., \fBunix.my\fR.
                    114: In general,
                    115: there are three kinds of MIB modules:
                    116: .np
                    117: If you are defining a MIB module for something UNIX specific,
                    118: it should probably go under the BSD UNIX MIB
                    119: (contact Marshall Rose or Keith Sklower to get a number under the UNIX
                    120: enterprise tree).
                    121: In this case,
                    122: the definition might start something like this:
                    123: .sp
                    124: .in +.5i
                    125: .nf
                    126: SendMail-MIB { iso org(3) dod(6) internet(1) private(4)
                    127:                enterprises(1) unix (4) sendmail (99) }
                    128: 
                    129: DEFINITIONS ::= BEGIN
                    130: 
                    131: IMPORTS
                    132:         unix --*, OBJECT-TYPE *--
                    133:             FROM RFC1155-SMI;
                    134: 
                    135: sendMail OBJECT IDENTIFIER ::= { unix 99 }
                    136: .fi
                    137: .in -.5i
                    138: .sp
                    139: .np
                    140: If you are defining a MIB module on a multilateral, experimental basis,
                    141: e.g., for a protocol like the NTP,
                    142: then you should contact the Internet Assigned Numbers Authority ([email protected])
                    143: and ask for an experimental number.
                    144: In this case,
                    145: the definition might start something like this:
                    146: .sp
                    147: .in +.5i
                    148: .nf
                    149: FIZBIN-MIB DEFINITIONS ::= BEGIN
                    150: 
                    151: IMPORTS
                    152:         experimental, OBJECT-TYPE
                    153:             FROM RFC1155-SMI;
                    154: 
                    155: fizBin OBJECT IDENTIFIER ::= { experimental 99 }
                    156: .fi
                    157: .in -.5i
                    158: .sp
                    159: .np
                    160: Otherwise,
                    161: if you are defining a MIB module for something specific to your
                    162: enterprise,
                    163: then you contact the Internet Assigned Numbers Authority ([email protected]) and
                    164: ask for an enterprise number (assuming you don't already have one).
                    165: In this case,
                    166: the definition might start something like this:
                    167: .sp
                    168: .in +.5i
                    169: .nf
                    170: FIZZBIN-MIB DEFINITIONS ::= BEGIN
                    171: 
                    172: IMPORTS
                    173:         enterprises, OBJECT-TYPE
                    174:             FROM RFC1155-SMI;
                    175: 
                    176: cheetah OBJECT IDENTIFIER ::= { enterprises 9999 }
                    177: 
                    178: fizBin OBJECT IDENTIFIER ::= { cheetah 1 }
                    179: .fi
                    180: .in -.5i
                    181: .sp
                    182: .lp
                    183: Regardless of the kind of MIB module,
                    184: this last OBJECT IDENTIFIER points to the root of the tree which your
                    185: agent is going to export.
                    186: .lp
                    187: Following this start,
                    188: you have the actual definitions of the MIB objects,
                    189: followed by
                    190: .sp
                    191: .in +.5i
                    192: .nf
                    193: END
                    194: .fi
                    195: .in -.5i
                    196: .sp
                    197: 
                    198: .sh 2 "Compiling MIB modules"
                    199: .lp
                    200: The next step is to compile the MIB module into a form that your
                    201: program can read.
                    202: This is done using the \fImosy\fR program:
                    203: .sp
                    204: .in +.5i
                    205: .nf
                    206: % others/mosy/xmosy \fImodule\fR.my
                    207: .fi
                    208: .in -.5i
                    209: .sp
                    210: which will create the file \fImodule\fR.defs.
                    211: In most cases you will need to prefix this file with definitions from
                    212: the root of the OBJECT IDENTIFIER tree,
                    213: e.g.,
                    214: .sp
                    215: .in +.5i
                    216: .nf
                    217: % cat $(INCDIR)isode/snmp/smi.defs \fImodule\fR.defs > \fIdaemon\fR.defs
                    218: .fi
                    219: .in -.5i
                    220: .sp
                    221: where the \fBdaemon.defs\fR file will be the one which you install with
                    222: your program binary.
                    223: 
                    224: .sh 3 "The Syntax of compiled MIB modules"
                    225: .lp
                    226: The syntax is pretty simple:
                    227: .np
                    228: Comments start with \*(lq--\*(rq or \*(lq#\*(rq at the beginning of a line.
                    229: .np
                    230: Object identifiers are defined like this:
                    231: .sp
                    232: .in +.5i
                    233: .nf
                    234: name            value
                    235: .fi
                    236: .in -.5i
                    237: .sp
                    238: e.g.,
                    239: .sp
                    240: .in +.5i
                    241: .nf
                    242: internet        iso.3.6.1
                    243: .fi
                    244: .in -.5i
                    245: .sp
                    246: .np
                    247: Object types are defined like this:
                    248: .sp
                    249: .in +.5i
                    250: .nf
                    251: name      oid       syntax         access     status
                    252: .fi
                    253: .in -.5i
                    254: .sp
                    255: e.g.,
                    256: .sp
                    257: .in +.5i
                    258: .nf
                    259: sysDescr  system.1  DisplayString  read-only  mandatory
                    260: .fi
                    261: .in -.5i
                    262: .sp
                    263: where \*(lqname\*(rq and \*(lqoid\*(rq are fairly obvious.
                    264: For the rest:
                    265: .sp
                    266: .in +.5i
                    267: .nf
                    268: \*(lqsyntax\*(rq is the name of a defined syntax;
                    269: .sp
                    270: \*(lqaccess\*(rq is one of \fIread-only\fR, \fIread-write\fR", or \fInone\fR;
                    271: and,
                    272: .sp
                    273: \*(lqstatus\*(rq is one of \fImandatory\fR, \fIoptional\fR,
                    274: \fIdeprecated\fR, or \fIobsolete\fR. 
                    275: .fi
                    276: .in -.5i
                    277: .sp
                    278: Names of objects are \fIalways\fR case-sensitive.
                    279: 
                    280: .bp
                    281: .sh 1 "SMUX Peers"
                    282: .lp
                    283: A program which exports a MIB module is termed a \*(lqSMUX peer\*(rq
                    284: (SMUX is the name of the protocol used by these programs to
                    285: communicate with an SNMP agent.)
                    286: .lp
                    287: There is a textual database,
                    288: \fB$(ETCDIR)snmpd.peers\fR,
                    289: which defines the SMUX peers known to the local SNMP agent.
                    290: The syntax of this file is pretty simple:
                    291: .np
                    292: Comments start with \*(lq#\*(rq at the beginning of a line.
                    293: .np
                    294: Each peer is identified in a single line:
                    295: .sp
                    296: .in +.5i
                    297: .nf
                    298: name        oid         password    [priority]
                    299: .fi
                    300: .in -.5i
                    301: .sp
                    302: where \*(lqname\*(rq and \*(lqoid\*(rq are fairly obvious.
                    303: For the rest:
                    304: .sp
                    305: .in +.5i
                    306: .nf
                    307: \*(lqpassword\*(rq is a string which the agent will use to
                    308: authenticate the SMUX peer;
                    309: and,
                    310: .sp
                    311: \*(lqpriority\*(rq,
                    312: if present,
                    313: is the highest priority with which the SMUX peer can register subtrees.
                    314: .fi
                    315: .in -.5i
                    316: .sp
                    317: The name/oid pairs are assigned by the authority for the BSD UNIX MIB
                    318: (contact Marshall Rose or Keith Sklower to register the name of your
                    319: program and get an for your program).
                    320: .lp
                    321: Note that this file contains things resembling passwords in the clear.
                    322: As such, it should be protected mode 0600 and owned by root.
                    323: 
                    324: .bp
                    325: .sh 1 "Daemon Skeleton"
                    326: .lp
                    327: Now it's time to modify your daemon to export the MIB.
                    328: Your source code should include these lines:
                    329: .sp
                    330: .in +.5i
                    331: .nf
                    332: #include <isode/snmp/smux.h>
                    333: #include <isode/snmp/objects.h>
                    334: #include <isode/tailor.h>
                    335: .fi
                    336: .in -.5i
                    337: .sp
                    338: which will include the definitions for:
                    339: talking to the SNMP agent via the SMUX protocol,
                    340: the object management package,
                    341: and the ISODE tailoring subsystem.
                    342: .lp
                    343: When loading your daemon,
                    344: you should add this to the end of your command to the loader:
                    345: .sp
                    346: .in +.5i
                    347: .nf
                    348: -lisnmp -lisode
                    349: .fi
                    350: .in -.5i
                    351: .sp
                    352: which includes the 4BSD/SNMP and ISODE libraries.
                    353: .lp
                    354: You should decide if you want to use the ISODE logging subsystem.
                    355: This is a convenient mechanism for using a unified logging system.
                    356: If you are modifying an already existing daemon,
                    357: you probably have your own logging package.
                    358: Otherwise,
                    359: you should consider using the ISODE subsystem.
                    360: 
                    361: .sh 2 "Declarations"
                    362: .lp
                    363: There are some global variables that your program will have to declare:
                    364: .sp
                    365: .in +.5i
                    366: .nf
                    367: int     debug = 0;
                    368: static  char   *myname = "unixd";
                    369: 
                    370: static LLog     _pgm_log = {
                    371:     "unixd.log", NULLCP, NULLCP,
                    372:     LLOG_FATAL | LLOG_EXCEPTIONS | LLOG_NOTICE,
                    373:     LLOG_FATAL, -1, LLOGCLS | LLOGCRT | LLOGZER, NOTOK
                    374: };
                    375: static  LLog   *pgm_log = &_pgm_log;
                    376: 
                    377: static  OID     subtree = NULLOID;
                    378: static  struct smuxEntry *se = NULL;
                    379: 
                    380: static  int     smux_fd = NOTOK;
                    381: static  int     rock_and_roll = 0;
                    382: static  int     dont_bother_anymore = 0;
                    383: 
                    384: static  int     quantum = 0;
                    385: .fi
                    386: .in -.5i
                    387: .sp
                    388: You only need the definition of \fI_pgm_log\fR and \fIpgm_log\fR if
                    389: you will be using the ISODE logging subsystem.
                    390: 
                    391: .sh 2 "Initialization"
                    392: .lp
                    393: When your program initializes itself,
                    394: it should contain some code like this:
                    395: .sp
                    396: .in +.5i
                    397: .nf
                    398:     if (myname = rindex (argv[0], '/'))
                    399:         myname++;
                    400:     if (myname == NULL || *myname == NULL)
                    401:         myname = argv[0];
                    402: 
                    403:     isodetailor (myname, 0);
                    404:     ll_hdinit (pgm_log, myname);
                    405: 
                    406: /* scan argv, set debug if need be... */
                    407: 
                    408:     if (debug)
                    409:         ll_dbinit (pgm_log, myname);
                    410: .fi
                    411: .in -.5i
                    412: .sp
                    413: Of course,
                    414: if you're not using the ISODE logging subsystem,
                    415: you don't have the calls to \fIll_hdinit\fR or \fIll_dbinit\fR.
                    416: .lp
                    417: After cracking argv,
                    418: your program should do the usual detaching actions.
                    419: Usually at this point,
                    420: your program reads the compiled MIB module definitions and starts
                    421: talking to the SNMP agent.
                    422: 
                    423: .sh 2 "Reading the compiled MIB module"
                    424: .lp
                    425: You program should call the \fIreadobjects\fR routine to read the module,
                    426: look-up the subtree which it will export to the SNMP agent,
                    427: and call the \fIgetsmuxEntrybyname\fR routine to find its entry in the
                    428: \fBsnmpd.peers\fR database.
                    429: Finally,
                    430: it should call a routine you define,
                    431: e.g., \fIinit_mib\fR,
                    432: to initialize it's internal MIB structures.
                    433: (We'll look at this routine in greater detail later on.)
                    434: .sp
                    435: .in +.5i
                    436: .nf
                    437:     OT      ot;
                    438: 
                    439:     if (readobjects ("unixd.defs") == NOTOK)
                    440:         error ("readobjects: %s", PY_pepy);
                    441: 
                    442:     if ((ot = text2obj ("mbuf")) == NULL)
                    443:         error ("object \"%s\" not in \"%s\"", "mbuf", "unix.defs");
                    444:     subtree = ot -> ot_name;
                    445: 
                    446:     if (se = getsmuxEntrybyname ("unixd")) == NULL)
                    447:         error ("no SMUX entry for \"%s\"", "unixd");
                    448: 
                    449:     init_mib ();
                    450: .fi
                    451: .in -.5i
                    452: .sp
                    453: If any of these routines fail,
                    454: then don't bother trying to export the MIB module.
                    455: 
                    456: .sh 2 "Talking to the SNMP agent"
                    457: .lp
                    458: All of the SMUX routines return \fINOTOK\fR on failure.
                    459: Unless otherwise noted,
                    460: these routines also return \fIOK\fR on success.
                    461: On failure, the variable
                    462: .sp
                    463: .in +.5i
                    464: .nf
                    465: extern int  smux_errno;
                    466: .fi
                    467: .in -.5i
                    468: .sp
                    469: is set to a symbolic value defined in \fBsmux.h\fR, one of:
                    470: .sp
                    471: .in +.5i
                    472: .nf
                    473: invalidOperation
                    474: parameterMissing
                    475: systemError
                    476: youLoseBig
                    477: congestion
                    478: inProgress
                    479: .fi
                    480: .in -.5i
                    481: .sp
                    482: In addition, the variable
                    483: .sp
                    484: .in +.5i
                    485: .nf
                    486: extern char smux_info[BUFSIZ];
                    487: .fi
                    488: .in -.5i
                    489: .sp
                    490: contains a printable explanation of what happened on failure.
                    491: .lp
                    492: All errors are FATAL except for \fIinProgress\fR.
                    493: This means retry your operation later on.
                    494: 
                    495: .sh 3 "Initialization"
                    496: .lp
                    497: You program should call the routine \fIsmux_init\fR,
                    498: which initializes a SMUX connection to the local SNMP agent.
                    499: This starts a TCP connection,
                    500: but most likely does not complete it.
                    501: You program will need to call an open routine (there is only one at present)
                    502: to finish the connect and establish the SMUX association.
                    503: .lp
                    504: If successful,
                    505: the return value is a file-descriptor,
                    506: suitable for use with \fIselect\fR, etc.
                    507: .lp
                    508: On failure,
                    509: \fIsmux_errno\fR will be set to one of
                    510: \fIcongestion\fR, \fIyouLoseBig\fR, or \fIsystemError\fR.
                    511: In this case,
                    512: you should probably have your program retry the operation every 5
                    513: minutes or so.
                    514: .sp
                    515: .in +.5i
                    516: .nf
                    517: if ((smux_fd = smux_init (debug)) == NOTOK)
                    518:     error ("smux_init: %s [%s]",
                    519:            smux_error (smux_errno), smux_info);
                    520: else
                    521:     rock_and_roll = 0;
                    522: .fi
                    523: .in -.5i
                    524: .sp
                    525: 
                    526: .sh 3 "Opening"
                    527: .lp
                    528: Once \fIsmux_init\fR returns OK,
                    529: you should start selecting for writability on the file-descriptor returned.
                    530: Once select says your program can write to the fd,
                    531: your program should call the routine \fIsmux_simple_open\fR,
                    532: which establishes the SMUX association.
                    533: .lp
                    534: On failure,
                    535: \fIsmux_error\fR will be set to one of
                    536: \fIparameterMissing\fR, \fIinvalidOperation\fR, \fIinProgress\fR,
                    537: \fIsystemError\fR, \fIcongestion\fR, or \fIyouLoseBig\fR.
                    538: If the error code is \fIinProgress\fR,
                    539: then your program should continue retrying the fd for writability,
                    540: and then call \fIsmux_simple_open again.
                    541: Otherwise,
                    542: your program should take the appropriate action based on the error
                    543: code returned.
                    544: .sp
                    545: .in +.5i
                    546: .nf
                    547: if (smux_simple_open (&se -> se_identity,
                    548:                       "SMUX UNIX daemon",
                    549:                       se -> se_password,
                    550:                       strlen (se -> se_password))
                    551:         == NOTOK) {
                    552:     if (smux_errno == inProgress)
                    553:         return;
                    554: 
                    555:     error ("smux_simple_open: %s [%s]", 
                    556:            smux_error (smux_errno), smux_info);
                    557:     smux_fd = NOTOK;
                    558: }
                    559: else
                    560:     rock_and_roll = 1;
                    561: .fi
                    562: .in -.5i
                    563: .sp
                    564: 
                    565: .sh 3 "Closing"
                    566: .lp
                    567: If, for some reason, your program wishes to close the SMUX
                    568: association.
                    569: There are several reasons that are allowed:
                    570: .sp
                    571: .in +.5i
                    572: .nf
                    573: goingDown
                    574: unsupportedVersion
                    575: packetFormat
                    576: protocolError
                    577: internalError
                    578: authenticationFailure
                    579: .fi
                    580: .in -.5i
                    581: .sp
                    582: On failure,
                    583: \fIsmux_error\fR will be set to one of
                    584: \fIinvalidOperation\fR, \fIcongestion\fR, or \fIyouLoseBig\fR.
                    585: 
                    586: .sh 3 "Registering Subtrees"
                    587: .lp
                    588: Once \fIsmux_simple_open\fR returns OK,
                    589: your program should register the MIB module that your daemon will
                    590: export by calling \fIsmux_register\fR.
                    591: A subtree can be registered in one of three modes:
                    592: .sp
                    593: .in +.5i
                    594: .nf
                    595: readOnly
                    596: readWrite
                    597: delete
                    598: .fi
                    599: .in -.5i
                    600: .sp
                    601: which are all fairly obvious.
                    602: .lp
                    603: Note that a return value of OK from \fIsmux_register\fR means only
                    604: that the registration request was queued for the SNMP agent via the
                    605: SMUX protocol.
                    606: Some time later the SNMP agent's response will be forthcoming.
                    607: .lp
                    608: On failure,
                    609: \fIsmux_error\fR will be set to one of 
                    610: \fIparameterMissing\fR, \fIinvalidOperation\fR, \fIcongestion\fR, or
                    611: \fIyouLoseBig\fR.
                    612: Your program should take the appropriate action based on the error
                    613: code returned.
                    614: .sp
                    615: .in +.5i
                    616: .nf
                    617: if (smux_register (subtree, -1, readOnly) == NOTOK) {
                    618:     error ("smux_register: %s [%s]",
                    619:             smux_error (smux_errno), smux_info);
                    620:     smux_fd = NOTOK;
                    621: }
                    622: .fi
                    623: .in -.5i
                    624: .sp
                    625: 
                    626: .sh 3 "Main Loop"
                    627: .lp
                    628: At this point,
                    629: your program is more or less in it's main loop
                    630: (actually, it probably entered the main loop after the very first call
                    631: to \fIsmux_init\fR).
                    632: .sp
                    633: .in +.5i
                    634: .nf
                    635: int     nfds;   /* these are set for other fd's... */
                    636: 
                    637: fd_set  ifds;
                    638: fd_set  ofds;
                    639: 
                    640:     for (;;) {
                    641:         int     n,
                    642:                 secs;
                    643:         fd_set  rfds,
                    644:                 wfds;
                    645: 
                    646:         secs = NOTOK;
                    647: 
                    648:         rfds = ifds;    /* struct copy */
                    649:         wfds = ofds;    /*   .. */
                    650: 
                    651:         if (smux_fd == NOTOK && !dont_bother_anymore)
                    652:             secs = 5 * 60L;
                    653:         else
                    654:             if (rock_and_roll)
                    655:                 FD_SET (smux_fd, &rfds);
                    656:             else
                    657:                 FD_SET (smux_fd, &wfds);
                    658:         if (smux_fd >= nfds)
                    659:             nfds = smux_fd + 1;
                    660: 
                    661:         if ((n = xselect (nfds, &rfds, &wfds, NULLFD, secs))
                    662:                 == NOTOK) {
                    663:             error ("xselect failed");
                    664: \0            ...
                    665:         }
                    666: 
                    667: /* check fd's for other purposes here... */
                    668: 
                    669:         if (smux_fd == NOTOK && !dont_bother_anymore) {
                    670:             if (n == 0) {
                    671:                 if ((smux_fd = smux_init (debug)) == NOTOK)
                    672:                     error ("smux_init: %s [%s]",
                    673:                            smux_error (smux_errno),
                    674:                            smux_info);
                    675:                 else
                    676:                     rock_and_roll = 0;
                    677:             }
                    678:         }
                    679:         else
                    680:             if (rock_and_roll) {
                    681:                 if (FD_ISSET (smux_fd, &rfds))
                    682:                     doit_smux ();
                    683:             }
                    684:             else
                    685:                 if (FD_ISSET (smux_fd, &wfds)) {
                    686:                     if (smux_simple_open (&se -> se_identity, 
                    687:                                   "SMUX UNIX daemon",
                    688:                                   se -> se_password, 
                    689:                                   strlen (se -> se_password))
                    690:                             == NOTOK) {
                    691:                         if (smux_errno != inProgress) {
                    692:                             error ("smux_simple_open: %s [%s]",
                    693:                                    smux_error (smux_errno), 
                    694:                                    smux_info);
                    695:                             smux_fd = NOTOK;
                    696:                         }
                    697:                     }
                    698:                     else {
                    699:                         rock_and_roll = 1;
                    700: 
                    701:                         if (smux_register (subtree, -1, 
                    702:                                    readOnly) == NOTOK) {
                    703:                             error ("smux_register: %s [%s]",
                    704:                                    smux_error (smux_errno),
                    705:                                    smux_info);
                    706:                             smux_fd = NOTOK;
                    707:                         }
                    708:                 }
                    709:     }
                    710: .fi
                    711: .in -.5i
                    712: .sp
                    713: So,
                    714: all that remains is to take a look at the routine \fIdoit_smux\fR
                    715: mentioned above.
                    716: This is called when select indicates the SMUX file-descriptor is ready
                    717: for reading.
                    718: 
                    719: .sh 3 "Events"
                    720: .lp
                    721: When \fIselect\fR indicates the SMUX file-descriptor is ready for
                    722: reading,
                    723: your program calls the routine \fIsmux_wait\fR to return the next
                    724: event from the SNMP agent.
                    725: .lp
                    726: Note that the event is filled-in from a static area.
                    727: On the next call to \fIsmux_init\fR, \fIsmux_close\fR, or
                    728: \fIsmux_wait\fR,
                    729: the value will be overwritten.
                    730: As such,
                    731: do not free this structure yourself.
                    732: .lp
                    733: On failure,
                    734: \fIsmux_error\fR will be set to one of 
                    735: \fIparameterMissing\fR, \fIinvalidOperation\fR, \fIinProgress\fR,
                    736: or \fIyouLoseBig\fR.
                    737: Your program should take the appropriate action based on the error
                    738: code returned.
                    739: .sp
                    740: .in +.5i
                    741: .nf
                    742:     struct type_SNMP_SMUX__PDUs *event;
                    743: 
                    744:     if (smux_wait (&event, NOTOK) == NOTOK) {
                    745:         if (smux_errno == inProgress)
                    746:             return;
                    747: 
                    748:         error ("smux_wait: %s [%s]",
                    749:                smux_error (smux_errno), smux_info);
                    750: losing: ;
                    751:         smux_fd = NOTOK;
                    752:         return;
                    753:     }
                    754: .fi
                    755: .in -.5i
                    756: .sp
                    757: Next,
                    758: your program should switch based on the actual event returned:
                    759: .sp
                    760: .in +.5i
                    761: .nf
                    762: type_SNMP_SMUX__PDUs_registerResponse
                    763: type_SNMP_SMUX__PDUs_get_request
                    764: type_SNMP_SMUX__PDUs_get__next__request
                    765: type_SNMP_SMUX__PDUs_close
                    766: .fi
                    767: .in -.5i
                    768: .sp
                    769: The actual code is fairly straight-forward:
                    770: .sp
                    771: .in +.5i
                    772: .nf
                    773: switch (event -> offset) {
                    774:     case type_SNMP_SMUX__PDUs_registerResponse:
                    775:         {
                    776:             struct type_SNMP_RRspPDU *rsp =
                    777:                         event -> un.registerResponse;
                    778: 
                    779:             if (rsp -> parm == int_SNMP_RRspPDU_failure) {
                    780:                 error ("SMUX registration of %s failed",
                    781:                        oid2ode (subtree));
                    782:                 dont_bother_anymore = 1;
                    783:                 (void) smux_close (goingDown);
                    784:                 break;
                    785:             }
                    786:         }
                    787:         if (smux_trap (int_SNMP_generic__trap_coldStart,
                    788:                        0, (struct type_SNMP_VarBindList *) 0)
                    789:                 == NOTOK) {
                    790:             error ("smux_trap: %s [%s]",
                    791:                    smux_error (smux_errno), smux_info);
                    792:             break;
                    793:         }
                    794:         return;
                    795: 
                    796:     case type_SNMP_SMUX__PDUs_get_request:
                    797:     case type_SNMP_SMUX__PDUs_get__next__request:
                    798:         get_smux (event -> un.get__request, event -> offset);
                    799:         return;
                    800: 
                    801:     case type_SNMP_SMUX__PDUs_close:
                    802:         notice ("SMUX close: %s", 
                    803:                 smux_error (event -> un.close -> parm));
                    804:         break;
                    805: 
                    806:     default:
                    807:         error ("bad SMUX operation: %d", event -> offset);
                    808:         (void) smux_close (protocolError);
                    809:         break;
                    810: }
                    811: smux_fd = NOTOK;
                    812: .fi
                    813: .in -.5i
                    814: .sp
                    815: Note the use of the \fIsmux_trap\fR routine to send a \fIcoldStart\fR
                    816: trap once the daemon has successful registered the MIB module it is exporting.
                    817: The trap codes are:
                    818: .sp
                    819: .in +.5i
                    820: .nf
                    821: int_SNMP_generic__trap_coldStart
                    822: int_SNMP_generic__trap_warmStart
                    823: int_SNMP_generic__trap_linkDown
                    824: int_SNMP_generic__trap_linkUp
                    825: int_SNMP_generic__trap_authenticationFailure
                    826: int_SNMP_generic__trap_egpNeighborLoss
                    827: int_SNMP_generic__trap_enterpriseSpecific
                    828: .fi
                    829: .in -.5i
                    830: .sp
                    831: If this routine fails,
                    832: \fIsmux_errno\fR will be set to one of
                    833: \fIinvalidOperation\fR, \fIcongestion\fR, or \fIyouLoseBig\fR.
                    834: .lp
                    835: So,
                    836: all that remains is to take a look at the routine \fIget_smux\fR which
                    837: implements the SNMP get and powerful get-next operators.
                    838: We will return to this routine once the structures and routines which
                    839: implement the managed object abstraction are explained. 
                    840: 
                    841: .bp
                    842: .sh 1 "Managed Objects"
                    843: .lp
                    844: A managed object is an abstraction with a syntax and a semantics.
                    845: The syntax defines what instances of the object look like on the network.
                    846: The semantics define what the object actually is.
                    847: 
                    848: .sh 2 "Syntax"
                    849: .lp
                    850: The syntax of MIB objects is modeled by the \fIOS\fR structure:
                    851: .sp
                    852: .in +.5i
                    853: .nf
                    854: typedef struct object_syntax {
                    855:     char   *os_name;                    /* syntax name */
                    856: 
                    857:     IFP     os_encode;                  /* data -> PE */
                    858:     IFP     os_decode;                  /* PE -> data */
                    859:     IFP     os_free;                    /* free data */
                    860: 
                    861:     IFP     os_parse;                   /* str -> data */
                    862:     IFP     os_print;                   /* data -> tty */
                    863: 
                    864: \0        ...
                    865: }               object_syntax, *OS;
                    866: #define NULLOS  ((OS) 0)
                    867: .fi
                    868: .in -.5i
                    869: .sp
                    870: The syntaxes defined by the Internet-standard MIB are already implemented:
                    871: .sp
                    872: .in +.5i
                    873: .nf
                    874: .ta \w'NetworkAddress  'u
                    875: syntax  structure
                    876: INTEGER integer
                    877: OctetString     struct qbuf (OCTET STRING)
                    878: ObjectID        struct OIDentifier (OBJECT IDENTIFIER)
                    879: NULL    char
                    880: DisplayString   struct qbuf
                    881: IpAddress       struct sockaddr_in
                    882: NetworkAddress  struct sockaddr_in
                    883: Counter integer
                    884: Gauge   integer
                    885: TimeTicks       integer
                    886: ClnpAddress     struct sockaddr_iso
                    887: .re
                    888: .fi
                    889: .in -.5i
                    890: .sp
                    891: where \*(lqsyntax\*(rq is the name appearing in a compiled MIB module,
                    892: and \*(lqstructure\*(rq is the \fIC\fR language data type
                    893: corresponding to the object's syntax.
                    894: .lp
                    895: To take a syntax name and get back the structure, use the routine
                    896: \fItext2syn\fR.
                    897: 
                    898: .sh 3 "Abstractions for Standard Syntaxes"
                    899: .lp
                    900: Here are the structures and routine used to implement the low-level
                    901: MIB abstractions.
                    902: 
                    903: .sh 4 "integer"
                    904: .lp
                    905: This is used for the \fIINTEGER\fR, \fICounter\fR, \fIGauge\fR, and
                    906: \fITimeTicks\fR syntax.
                    907: .lp
                    908: The definition is:
                    909: .sp
                    910: .in +.5i
                    911: .nf
                    912: typedef int integer;
                    913: .fi
                    914: .in -.5i
                    915: .sp
                    916: which is hardly surprising.
                    917: 
                    918: .sh 4 "struct qbuf"
                    919: .lp
                    920: This is used for the \fIOctetString\fR (OBJECT IDENTIFIER)
                    921: and \fIDisplayString\fR syntaxes.
                    922: .lp
                    923: The definition is:
                    924: .sp
                    925: .in +.5i
                    926: .nf
                    927: struct qbuf {
                    928:     struct qbuf *qb_forw;   /* doubly-linked list */
                    929:     struct qbuf *qb_back;   /*   .. */
                    930: 
                    931:     int     qb_len;         /* length of data */
                    932:     char   *qb_data;        /* current pointer into data */
                    933:     char    qb_base[1];     /* extensible... */
                    934: };
                    935: .fi
                    936: .in -.5i
                    937: .sp
                    938: The macro \fIQBFREE\fR is used to traverse \fIqb_forw\fR to free all
                    939: qbufs in the ring:
                    940: .sp
                    941: .in +.5i
                    942: .nf
                    943: QBFREE (qb)
                    944: struct qbuf *qb;
                    945: .fi
                    946: .in -.5i
                    947: .sp
                    948: To allocate a new string from the qbuf use:
                    949: .sp
                    950: .in +.5i
                    951: .nf
                    952: char *qb2str (q)
                    953: struct qbuf *q
                    954: .fi
                    955: .in -.5i
                    956: .sp
                    957: The string is NULL-terminated,
                    958: but there may be other NULLs in the string to foil things like \fIstrlen\fR.
                    959: .lp
                    960: To allocate a new qbuf of \fIlen\fR octets from string \fIs\fR,
                    961: use:
                    962: .sp
                    963: .in +.5i
                    964: .nf
                    965: struct qbuf str2qb (s, len, head)
                    966: char    *s;
                    967: int      len,
                    968:          head;
                    969: .fi
                    970: .in -.5i
                    971: .sp
                    972: (\fIhead\fR should always be \fI1\fR).
                    973: .lp
                    974: To free an allocated qbuf, use \fIqb_free\fR which calls \fIQBFREE\fR
                    975: and then \fIfree\fR on its argument.
                    976: 
                    977: .sh 4 "struct OIDentifier"
                    978: .lp
                    979: This is used for the \fIObjectID\fR (OBJECT IDENTIFIER) syntax.
                    980: The definition is:
                    981: .sp
                    982: .in +.5i
                    983: .nf
                    984: typedef struct OIDentifier {
                    985:     int     oid_nelem;  /* number of sub-identifiers */
                    986: 
                    987:     unsigned int *oid_elements;
                    988:        /* the (ordered) list of sub-identifiers */
                    989: }                       OIDentifier, *OID;
                    990: #define NULLOID ((OID) 0)
                    991: .fi
                    992: .in -.5i
                    993: .sp
                    994: To compare two \fIOIDs\fR, use
                    995: .sp
                    996: .in +.5i
                    997: .nf
                    998: int     oid_cmp (p, q)
                    999: OID     p,
                   1000:         q;
                   1001: .fi
                   1002: .in -.5i
                   1003: .sp
                   1004: which returns \fI-1\fR if \fIp<q\fR, \fI1\fR if \fIp>q\fR, \fI0\fR otherwise.
                   1005: .lp
                   1006: To allocate a new \fIOID\fR and copy it from another, use \fIoid_cpy\fR.
                   1007: .lp
                   1008: To free an allocated \fIOID\fR, use \fIoid_free\fR.
                   1009: .lp
                   1010: To take an \fIOID\fR and produce a string in numeric form use
                   1011: .sp
                   1012: .in +.5i
                   1013: .nf
                   1014: char   *sprintoid (oid)
                   1015: OID     oid;
                   1016: .fi
                   1017: .in -.5i
                   1018: .sp
                   1019: The result is returned in a static area.
                   1020: The inverse routine is:
                   1021: .sp
                   1022: .in +.5i
                   1023: .nf
                   1024: OID     str2oid (s)
                   1025: char    *s;
                   1026: .fi
                   1027: .in -.5i
                   1028: .sp
                   1029: which returns an OID from a static area
                   1030: 
                   1031: .sh 4 "struct sockaddr_in"
                   1032: .lp
                   1033: This is used for the \fIIpAddress\fR and \fINetworkAddress\fR syntaxes.
                   1034: Presumably you are overly familiar with this structure.
                   1035: 
                   1036: .sh 4 "struct sockaddr_iso"
                   1037: .lp
                   1038: This is used for the \fIClnpAddress\fR syntax.
                   1039: If your are not running a BSD/OSI system,
                   1040: don't worry about this.
                   1041: 
                   1042: .sh 3 "Defining a new Syntax"
                   1043: .lp
                   1044: The routine \fIadd_syntax\fR is used to define a new syntax.
                   1045: (if this routine returns \fINOTOK\fR,
                   1046: then the internal syntax table has overflowed;
                   1047: adjust the constant \fIMAXSYN\fR in the file \fBsyntax.c\fR).
                   1048: The first argument is the name of the syntax.
                   1049: The other five are pointers to integer-valued routines that are called
                   1050: to operate on an opaque pointer:
                   1051: .sp
                   1052: .in +.5i
                   1053: .nf
                   1054: /* returns OK if x is encoded into pe (allocates pe),
                   1055:    NOTOK otherwise */
                   1056: 
                   1057: f_encode (x, pe)                
                   1058: pointer *x;                             
                   1059: PE      *pe;                    
                   1060: 
                   1061: 
                   1062: /* returns OK if pe is decoded into x (allocates x),
                   1063:    NOTOK otherwise */
                   1064: 
                   1065: f_decode (x, pe)                
                   1066: pointer **x;                    
                   1067: PE        pe;                   
                   1068: 
                   1069: 
                   1070: /* free's an allocated x (from f_decode or f_parse) */
                   1071: 
                   1072: f_free (x)
                   1073: pointer *x;
                   1074: 
                   1075: 
                   1076: /* returns OK if s is decoded into x (allocates x),
                   1077:    NOTOK otherwise */
                   1078: 
                   1079: f_parse (x, s)
                   1080: pointer **x;
                   1081: char     *s;
                   1082: 
                   1083: 
                   1084: /* prints x on the user's tty */
                   1085: 
                   1086: f_print (x, os)
                   1087: pointer *x;
                   1088: OS       os;
                   1089: .fi
                   1090: .in -.5i
                   1091: .sp
                   1092: Presentation elements (PEs) are discussed later on.
                   1093: 
                   1094: .sh 2 "Objects"
                   1095: .lp
                   1096: MIB objects are modeled by the \fIOT\fR structure:
                   1097: .sp
                   1098: .in +.5i
                   1099: .nf
                   1100: typedef struct object_type {
                   1101:     char   *ot_text;                /* OBJECT DESCRIPTOR */
                   1102:     char   *ot_id;                  /* OBJECT IDENTIFIER */
                   1103:     OID     ot_name;                /*   .. */
                   1104: 
                   1105:     OS      ot_syntax;              /* SYNTAX */
                   1106: 
                   1107:     int     ot_access;              /* ACCESS */
                   1108: #define OT_NONE         0x00
                   1109: #define OT_RDONLY       0x01
                   1110: #define OT_RDWRITE      0x02
                   1111: 
                   1112:     int     ot_status;              /* STATUS */
                   1113: #define OT_OBSOLETE     0x00
                   1114: #define OT_MANDATORY    0x01
                   1115: #define OT_OPTIONAL     0x02
                   1116: #define OT_DEPRECATED   0x03
                   1117: 
                   1118: \0    ...
                   1119: }               object_type, *OT;
                   1120: #define NULLOT  ((OT) 0)
                   1121: .fi
                   1122: .in -.5i
                   1123: .sp
                   1124: There are lots of routines to manipulate MIB objects.
                   1125: .lp
                   1126: The routine \fIname2obj\fR takes an object identifier and returns the
                   1127: object type, either exact or prefix, e.g.,
                   1128: .sp
                   1129: .in +.5i
                   1130: .nf
                   1131: name2obj ( OID { ipRouteDest.0.0.0.0 } )
                   1132: .fi
                   1133: .in -.5i
                   1134: .sp
                   1135: returns the object type for \*(lqipRouteDest\*(rq
                   1136: .lp
                   1137: The routine \fItext2obj\fR takes a string and returns the exact object type,
                   1138: e.g.,
                   1139: .sp
                   1140: .in +.5i
                   1141: .nf
                   1142: text2obj (\*(lqipRouteDest\*(rq)
                   1143: .fi
                   1144: .in -.5i
                   1145: .sp
                   1146: will succeed, but
                   1147: .sp
                   1148: .in +.5i
                   1149: .nf
                   1150: text2obj (\*(lqipRouteDest.0.0.0.0\*(rq)
                   1151: .fi
                   1152: .in -.5i
                   1153: .sp
                   1154: will fail.
                   1155: .lp
                   1156: The routine \fItext2oid\fR takes a string and returns the object identifier
                   1157: associated with the corresponding object.
                   1158: The string can be numeric (e.g., \*(lq1.3.6.1\*(rq),
                   1159: symbolic (e.g., \*(lqinternet\*(rq),
                   1160: or symbolic.numeric (e.g., \*(lqiso.3.6.1\*(rq).
                   1161: .lp
                   1162: The routine \fIoid2ode\fR takes an \fIOID\fR and returns a string suitable
                   1163: for pretty-printing, in the form symbolic or symbolic.numeric.
                   1164: 
                   1165: .sh 2 "Instances"
                   1166: .lp
                   1167: MIB instances are modeled by the \fIOI\fR structure:
                   1168: .sp
                   1169: .in +.5i
                   1170: .nf
                   1171: typedef struct object_instance {
                   1172:     OID     oi_name;                /* instance OID */
                   1173: 
                   1174:     OT      oi_type;                /* prototype */
                   1175: }               object_instance, *OI;
                   1176: #define NULLOI  ((OI) 0)
                   1177: .fi
                   1178: .in -.5i
                   1179: .sp
                   1180: There are lots of routines to manipulate MIB instances.
                   1181: .lp
                   1182: The routine \fIname2inst\fR takes a variable name and returns the
                   1183: corresponding instance, e.g.,
                   1184: .sp
                   1185: .in +.5i
                   1186: .nf
                   1187: name2inst ( OID { ipRouteDest.0.0.0.0 } )
                   1188: .fi
                   1189: .in -.5i
                   1190: .sp
                   1191: will return an \fIOI\fR with \fIoi_name\fR set to its argument and
                   1192: \fIoi_type\fR set to the object type for \*(lqipRouteDest\*(rq.
                   1193: .lp
                   1194: The routine \fInext2inst\fR finds the closest object type before the
                   1195: variable name and returns an \fIOI\fR corresponding to that object type.
                   1196: .lp
                   1197: The routine \fItext2inst\fR first calls \fItext2oid\fR to get the \fIOID\fR
                   1198: corresponding to the argument, then calls \fIname2obj\fR to get the
                   1199: type associated with that \fIOID\fR.
                   1200: 
                   1201: .bp
                   1202: .sh 1 "Linkage"
                   1203: .lp
                   1204: It is now time to tie up all the loose ends.
                   1205: When your program starts,
                   1206: it needs to establish some linkage between the objects defined in the
                   1207: compiled MIB module and the data structures in your program.
                   1208: Further,
                   1209: when the SNMP agent asks to manipulate the object instances in the MIB
                   1210: module your program exported (e.g., using the powerful SNMP get-next
                   1211: operator), you need to have a small protocol engine to field these requests.
                   1212: .lp
                   1213: The way the linkage is established is to associate a \fIC\fR routine
                   1214: with each of the leaf objects defined in the compiled MIB module.
                   1215: When the protocol engine fields a request from the SNMP agent,
                   1216: it will invoke that routine to \*(lqdo the right thing\*(rq.
                   1217: Typically,
                   1218: for each table in the compiled MIB module,
                   1219: you define a \fIC\fR routine to handle requests for the leaf objects
                   1220: of that table.
                   1221: In addition,
                   1222: there is usually one more routine defined to handle those leaf objects
                   1223: not found under any one table.
                   1224: For each \fIC\fR routine you define,
                   1225: you define a group of constant integer symbols which identify a leaf
                   1226: object that is handled by that routine
                   1227: (e.g., for each routine, you start numbering the symbols starting at
                   1228: \fI0\fR and going up).
                   1229: By convention,
                   1230: these symbols have the same name as the leaf objects.
                   1231: 
                   1232: .sh 2 "Initialization"
                   1233: .lp
                   1234: Earlier it was noted that your program will probably call a routine
                   1235: called \fIinit_mib\fR which initializes it's internal MIB structures.
                   1236: The routine should look something like this:
                   1237: .sp
                   1238: .in +.5i
                   1239: .nf
                   1240: register OT     ot;
                   1241: 
                   1242: if (ot = text2obj ("mbufS"))
                   1243:     ot -> ot_getfnx = o_mbuf,
                   1244:     ot -> ot_info = (caddr_t) mbufS;
                   1245: 
                   1246: \0...
                   1247: 
                   1248: if (ot = text2obj ("mbufType"))
                   1249:     ot -> ot_getfnx = o_mbufType,
                   1250:     ot -> ot_info = (caddr_t) mbufType;
                   1251: .fi
                   1252: .in -.5i
                   1253: .sp
                   1254: where we can imagine that \fIo_mbuf\fR and \fIo_mbufType\fR are
                   1255: routines that are defined in your program,
                   1256: and \fImbufS\fR and \fImbufType\fR are constant symbols defined with
                   1257: those routines.
                   1258: 
                   1259: .sh 2 "Generic Event Handling"
                   1260: .lp
                   1261: Earlier it was noted that your program will probably call a routine
                   1262: called \fIget_smux\fR which implements the SNMP get and powerful
                   1263: get-next operators.
                   1264: The routine should look something like this:
                   1265: .sp
                   1266: .in +.5i
                   1267: .nf
                   1268: static  get_smux (pdu, offset)
                   1269: register struct type_SNMP_GetRequest__PDU *pdu;
                   1270: int     offset;
                   1271: {
                   1272:     int     idx,
                   1273:             status;
                   1274:     object_instance ois;
                   1275:     register struct type_SNMP_VarBindList *vp;
                   1276: 
                   1277:     quantum = pdu -> request__id;
                   1278:     idx = 0;
                   1279:     for (vp = pdu -> variable__bindings; vp; vp = vp -> next) {
                   1280:         register OI     oi;
                   1281:         register OT     ot;
                   1282:         register struct type_SNMP_VarBind *v = vp -> VarBind;
                   1283: 
                   1284:         idx++;
                   1285: 
                   1286:         if (offset == type_SNMP_SMUX__PDUs_get__next__request) {
                   1287:             if ((oi = name2inst (v -> name)) == NULLOI
                   1288:                     && (oi = next2inst (v -> name)) == NULLOI)
                   1289:                 goto no_name;
                   1290: 
                   1291:             if ((ot = oi -> oi_type) -> ot_getfnx == NULLIFP)
                   1292:                 goto get_next;
                   1293:         }
                   1294:         else
                   1295:             if ((oi = name2inst (v -> name)) == NULLOI
                   1296:                     || (ot = oi -> oi_type) -> ot_getfnx
                   1297:                             == NULLIFP) {
                   1298: no_name: ;
                   1299:                 pdu -> error__status =
                   1300:                         int_SNMP_error__status_noSuchName;
                   1301:                 goto out;
                   1302:             }
                   1303: 
                   1304: try_again: ;
                   1305:         switch (ot -> ot_access) {
                   1306:             case OT_NONE:
                   1307:                 if (offset ==
                   1308:                        type_SNMP_SMUX__PDUs_get__next__request)
                   1309:                     goto get_next;
                   1310:                 goto no_name;
                   1311: 
                   1312:             case OT_RDONLY:
                   1313:                 if (offset ==
                   1314:                        type_SNMP_SMUX__PDUs_set__request) {
                   1315:                     pdu -> error__status =
                   1316:                                 int_SNMP_error__status_readOnly;
                   1317:                     goto out;
                   1318:                 }
                   1319:                 break;
                   1320: 
                   1321:             case OT_RDWRITE:
                   1322:                 break;
                   1323:         }
                   1324:                 
                   1325:         switch (status = (*ot -> ot_getfnx) (oi, v, offset)) {
                   1326:             case NOTOK:     /* get-next wants a bump */
                   1327: get_next: ;
                   1328:                 oi = &ois;
                   1329:                 for (;;) {
                   1330:                     if ((ot = ot -> ot_next) == NULLOT) {
                   1331:                         pdu -> error__status =
                   1332:                               int_SNMP_error__status_noSuchName;
                   1333:                         goto out;
                   1334:                     }
                   1335:                     oi -> oi_name =
                   1336:                                (oi -> oi_type = ot) -> ot_name;
                   1337:                     if (ot -> ot_getfnx)
                   1338:                         goto try_again;
                   1339:                 }
                   1340: 
                   1341:             case int_SNMP_error__status_noError:
                   1342:                 break;
                   1343: 
                   1344:             default:
                   1345:                 pdu -> error__status = status;
                   1346:                 goto out;
                   1347:         }
                   1348:     }
                   1349:     idx = 0;
                   1350: 
                   1351: out: ;
                   1352:     pdu -> error__index = idx;
                   1353: 
                   1354:     if (smux_response (pdu) == NOTOK) {
                   1355:         error ("smux_response: %s [%s]",
                   1356:                smux_error (smux_errno), smux_info);
                   1357:         smux_fd = NOTOK;
                   1358:     }
                   1359: }
                   1360: .fi
                   1361: .in -.5i
                   1362: .sp
                   1363: The actual code is fairly straight-forward:
                   1364: First,
                   1365: the variable \fIquantum\fR is set to the request ID for this transaction.
                   1366: The SMUX protocol requires that this number change for each SNMP
                   1367: operation that the SNMP agent fields,
                   1368: so your program can use it as a cookie to see when it should re-read
                   1369: kernel variables.
                   1370: (A single SNMP operation received by the SNMP agent might result in
                   1371: multiple SMUX protocol transactions with your program.)
                   1372: .lp
                   1373: Next, the code loops through the list of variables requested.
                   1374: The object instance is determined and loaded into \fIoi\fRn
                   1375: and the corresponding object type is loaded into \fIot\fR,
                   1376: and the access is checked.
                   1377: .lp
                   1378: Finally, the user-defined routine is invoked.
                   1379: This routine returns one of these values:
                   1380: .sp
                   1381: .in +.5i
                   1382: .nf
                   1383: NOTOK
                   1384: int_SNMP_error__status_noError
                   1385: int_SNMP_error__status_tooBig
                   1386: int_SNMP_error__status_noSuchName
                   1387: int_SNMP_error__status_badValue
                   1388: int_SNMP_error__status_readOnly
                   1389: int_SNMP_error__status_genErr
                   1390: .fi
                   1391: .in -.5i
                   1392: .sp
                   1393: the first value is returned only if the powerful get-next operator is
                   1394: being invoked and the routine didn't have any more object instances
                   1395: for the object type in question.
                   1396: The remainder are all self-explanatory.
                   1397: .lp
                   1398: Once an answer is returned,
                   1399: the loop either continues or is broken and a response is written back
                   1400: using the \fIsmux_response\fR routine.
                   1401: On failure,
                   1402: \fIsmux_error\fR will be set to one of
                   1403: \fIparameterMissing\fR, \fIinvalidOperation\fR, or \fIyouLoseBig\fR.
                   1404: 
                   1405: .sh 2 "Specific Event Handling: Outside a table"
                   1406: .lp
                   1407: Now let's look at one of these user-defined routines,
                   1408: which handles leaf objects that are not part of a table:
                   1409: .sp
                   1410: .in +.5i
                   1411: .nf
                   1412: static  int     lastq = -1;
                   1413: static  struct mbstat mbstat;
                   1414: 
                   1415: 
                   1416: #define mbufS           0
                   1417: #define mbufClusters    1
                   1418: \0...
                   1419: #define mbufFrees       6
                   1420: 
                   1421: 
                   1422: static int  o_mbuf (oi, v, offset)
                   1423: OI      oi;
                   1424: register struct type_SNMP_VarBind *v;
                   1425: int     offset;
                   1426: {
                   1427:     int     ifvar;
                   1428:     register struct mbstat *m = &mbstat;
                   1429:     register OID    oid = oi -> oi_name;
                   1430:     register OT     ot = oi -> oi_type;
                   1431: 
                   1432:     ifvar = (int) ot -> ot_info;
                   1433:     switch (offset) {
                   1434:         case type_SNMP_SMUX__PDUs_get__request:
                   1435:             if (oid -> oid_nelem !=
                   1436:                         ot -> ot_name -> oid_nelem + 1
                   1437:                     || oid -> oid_elements[oid -> oid_nelem - 1]
                   1438:                             != 0)
                   1439:                 return int_SNMP_error__status_noSuchName;
                   1440:             break;
                   1441: 
                   1442:         case type_SNMP_SMUX__PDUs_get__next__request:
                   1443:             if (oid -> oid_nelem
                   1444:                     == ot -> ot_name -> oid_nelem) {
                   1445:                 OID     new;
                   1446: 
                   1447:                 if ((new = oid_extend (oid, 1)) == NULLOID)
                   1448:                     return int_SNMP_error__status_genErr;
                   1449:                 new -> oid_elements[new -> oid_nelem - 1] = 0;
                   1450: 
                   1451:                 if (v -> name)
                   1452:                     free_SNMP_ObjectName (v -> name);
                   1453:                 v -> name = new;
                   1454:             }
                   1455:             else
                   1456:                 return NOTOK;
                   1457:             break;
                   1458: 
                   1459:         default:
                   1460:             return int_SNMP_error__status_genErr;
                   1461:     }
                   1462: 
                   1463:     if (quantum != lastq) {
                   1464:         lastq = quantum;
                   1465: 
                   1466:         if (getkmem (nl + N_MBSTAT, (caddr_t) m, sizeof *m)
                   1467:                 == NOTOK)
                   1468:             return int_SNMP_error__status_genErr;
                   1469:     }
                   1470: 
                   1471:     switch (ifvar) {
                   1472:         case mbufS:
                   1473:             return o_integer (oi, v, m -> m_mbufs);
                   1474: 
                   1475:         case mbufClusters:
                   1476:             return o_integer (oi, v, m -> m_clusters);
                   1477: 
                   1478: \0...
                   1479: 
                   1480:         case mbufFrees:
                   1481:             return o_integer (oi, v, m -> m_mbfree);
                   1482: 
                   1483:         default:
                   1484:             return int_SNMP_error__status_noSuchName;
                   1485:     }
                   1486: }
                   1487: .fi
                   1488: .in -.5i
                   1489: .sp
                   1490: The actual code is fairly straight-forward:
                   1491: First,
                   1492: the constant offsets are defined.
                   1493: Then, the \fIo_mbuf\fR routine is defined.
                   1494: .lp
                   1495: The first action is to determine which leaf object is being referenced.
                   1496: This corresponding symbolic constant is placed in the variable \fIifvar\fR.
                   1497: Then a switch is made based on the operation.
                   1498: .np
                   1499: For the get operation,
                   1500: all instances are identified by the object type followed by \*(lq.0\*(rq
                   1501: (e.g., \*(lqmbufS.0\*(rq).
                   1502: So,
                   1503: the code checks to see if the \fIOID\fR associated with the object
                   1504: instance is exactly one longer than the \fIOID\fR associated with the
                   1505: object type,
                   1506: and that the extra sub-identifier has the value \fI0\fR.
                   1507: .np
                   1508: For the powerful get-next operation,
                   1509: there are really two cases,
                   1510: depending on whether some instance identifier is present.
                   1511: If some instance identifier is present,
                   1512: then for a non-tabular leaf object,
                   1513: the next variable belongs to some other object type,
                   1514: so the routine simply returns the value \fINOTOK\fR,
                   1515: and the \fIget_smux\fR routine will find the next object accordingly.
                   1516: Otherwise,
                   1517: if no instance is identified,
                   1518: a new \fIOID\fR is constructed and initialized.
                   1519: The old \fIOID\fR is free'd and the new one inserted in its place.
                   1520: .lp
                   1521: Now that the correct instance has been identified,
                   1522: a check is made to see if \fIkmem\fR should be consulted.
                   1523: (Obviously other programs might consult other data stores.)
                   1524: Finally,
                   1525: the instance value is encoded and the routine returns.
                   1526: 
                   1527: .sh 3 "Instance handling"
                   1528: .lp
                   1529: Several routines are provided to encode instance values:
                   1530: .lp
                   1531: The \fIo_number\fR routine encodes an integer value,
                   1532: such as an INTEGER, Counter, Guage, or TimeTick.
                   1533: The macro \fIo_integer\fR is simply a synonym for \fIo_number\fR.
                   1534: .lp
                   1535: The \fIo_string\fR routine encodes a string value,
                   1536: such as an OctetString or DisplayString,
                   1537: e.g.,
                   1538: .sp
                   1539: .in +.5i
                   1540: .nf
                   1541: o_string (oi, v, "lo0", strlen ("lo0"));
                   1542: .fi
                   1543: .in -.5i
                   1544: .sp
                   1545: or
                   1546: .sp
                   1547: .in +.5i
                   1548: .nf
                   1549: o_string (oi, v, ether_addr, sizeof ether_addr);
                   1550: .fi
                   1551: .in -.5i
                   1552: .sp
                   1553: .lp
                   1554: The \fIo_specific\fR routine takes a structure corresponding to a
                   1555: syntax,
                   1556: such as an \fIOID\fR for ObjectID,
                   1557: and does the encoding,
                   1558: e.g.,
                   1559: .sp
                   1560: .in +.5i
                   1561: .nf
                   1562: OID nullSpecific = ...;
                   1563: 
                   1564: o_specific (oi, v, nullSpecific);
                   1565: .fi
                   1566: .in +.5i
                   1567: .sp
                   1568: 
                   1569: .sh 3 "A special case"
                   1570: .lp
                   1571: A special user-defined routine is provided for those cases for
                   1572: leaf-objects containing information that is initialized on start-up,
                   1573: \fIo_generic\fR.
                   1574: For example:
                   1575: .sp
                   1576: .in +.5i
                   1577: .nf
                   1578: char    buffer[BUFSIZ];
                   1579: 
                   1580: if (ot = text2obj ("sysName"))
                   1581:     ot -> ot_getfnx = o_generic,
                   1582:     ot -> ot_info = (caddr_t) sysName;
                   1583: 
                   1584: (void) gethostname (buffer, sizeof buffer);
                   1585: (void) (*ot -> ot_syntax -> os_parse)
                   1586:                 ((struct qbuf **) &ot -> ot_info, buffer);
                   1587: .fi
                   1588: .in -.5i
                   1589: .sp
                   1590: The idea is that the \fIot_info\fR field contains a pointer to a
                   1591: data structure corresponding to the syntax of the object.
                   1592: Since all of the structures are rather strange (except for integers),
                   1593: \fIo_generic\fR probably won't receive a lot of use.
                   1594: 
                   1595: .sh 2 "Specific Event Handling: Inside a table"
                   1596: .lp
                   1597: Now let's look at one of these user-defined routines,
                   1598: which handles leaf objects that are part of a table:
                   1599: .sp
                   1600: .in +.5i
                   1601: .nf
                   1602: #define mbufType        0
                   1603: #define mbufAllocates   1
                   1604: 
                   1605: 
                   1606: static int  o_mbufType (oi, v, offset)
                   1607: OI      oi;
                   1608: register struct type_SNMP_VarBind *v;
                   1609: int     offset;
                   1610: {
                   1611:     int     ifnum,
                   1612:             ifvar;
                   1613:     register struct mbstat *m = &mbstat;
                   1614:     register OID    oid = oi -> oi_name;
                   1615:     register OT     ot = oi -> oi_type;
                   1616: 
                   1617:     ifvar = (int) ot -> ot_info;
                   1618:     switch (offset) {
                   1619:         case type_SNMP_SMUX__PDUs_get__request:
                   1620:             if (oid -> oid_nelem
                   1621:                     != ot -> ot_name -> oid_nelem + 1)
                   1622:                 return int_SNMP_error__status_noSuchName;
                   1623:            ifnum = 
                   1624:                   oid -> oid_elements[oid -> oid_nelem - 1];
                   1625:             if (ifvar >= sizeof m -> m_mtypes /
                   1626:                             sizeof m -> m_mtypes[0])
                   1627:                 return int_SNMP_error__status_noSuchName;
                   1628:             break;
                   1629: 
                   1630:         case type_SNMP_SMUX__PDUs_get__next__request:
                   1631:             if (oid -> oid_nelem
                   1632:                     == ot -> ot_name -> oid_nelem) {
                   1633:                 OID     new;
                   1634: 
                   1635:                 ifnum = 0;
                   1636: 
                   1637:                 if ((new = oid_extend (oid, 1)) == NULLOID)
                   1638:                     return int_SNMP_error__status_genErr;
                   1639:                 new -> oid_elements[new -> oid_nelem - 1] =
                   1640:                                ifnum;
                   1641: 
                   1642:                 if (v -> name)
                   1643:                     free_SNMP_ObjectName (v -> name);
                   1644:                 v -> name = new;
                   1645:             }
                   1646:             else {
                   1647:                 int     i = ot -> ot_name -> oid_nelem;
                   1648: 
                   1649:                ifnum = oid -> oid_elements[i] + 1;
                   1650:                 if (ifnum >= sizeof m -> m_mtypes /
                   1651:                                 sizeof m -> m_mtypes[0])
                   1652:                     return NOTOK;
                   1653: 
                   1654:                 oid -> oid_elements[i] = ifnum;
                   1655:                 oid -> oid_nelem = i + 1;
                   1656:             }
                   1657:             break;
                   1658: 
                   1659:         default:
                   1660:             return int_SNMP_error__status_genErr;
                   1661:     }
                   1662: 
                   1663:     if (quantum != lastq) {
                   1664:         lastq = quantum;
                   1665: 
                   1666:         if (getkmem (nl + N_MBSTAT, (caddr_t) m, sizeof *m)
                   1667:                 == NOTOK)
                   1668:             return int_SNMP_error__status_genErr;
                   1669:     }
                   1670: 
                   1671:     switch (ifvar) {
                   1672:         case mbufType:
                   1673:             return o_integer (oi, v, ifnum);
                   1674: 
                   1675:         case mbufAllocates:
                   1676:             return o_integer (oi, v,
                   1677:                               m -> m_mtypes[ifnum]);
                   1678: 
                   1679:         default:
                   1680:             return int_SNMP_error__status_noSuchName;
                   1681:     }
                   1682: }
                   1683: .fi
                   1684: .in -.5i
                   1685: .sp
                   1686: The actual code is fairly straight-forward:
                   1687: First,
                   1688: the constant offsets are defined.
                   1689: Then, the \fIo_mbufType\fR routine is defined.
                   1690: .lp
                   1691: The first action is to determine which leaf object is being referenced.
                   1692: This corresponding symbolic constant is placed in the variable \fIifvar\fR.
                   1693: Then a switch is made based on the operation.
                   1694: Because these are tabular objects,
                   1695: the instance refers to a row in the table
                   1696: (and the leaf object refers to a column in the table, of course).
                   1697: .np
                   1698: For the get operation,
                   1699: all instances are identified by the object type followed by
                   1700: \*(lq.\fIrowno\fR\*(rq
                   1701: (e.g., \*(lqmbufAllocates.10\*(rq) refers to the value in the
                   1702: \fImbufAllocates\fR column of the thenth row).
                   1703: So,
                   1704: the code checks to see if the \fIOID\fR associated with the object
                   1705: instance is exactly one longer than the \fIOID\fR associated with the
                   1706: object type,
                   1707: and that the extra sub-identifier is within the range 0..rowno-1.
                   1708: .np
                   1709: For the powerful get-next operation,
                   1710: there are really two cases,
                   1711: depending on whether some instance identifier is present.
                   1712: If no instance is identified,
                   1713: a new \fIOID\fR is constructed and initialized for the first row
                   1714: (row 0) of the table. 
                   1715: The old \fIOID\fR is free'd and the new one inserted in its place.
                   1716: Otherwise,
                   1717: if some instance identifier is present,
                   1718: then the corresponding row must be identified,
                   1719: the row number incremented, and a check made to see if this is still
                   1720: in range
                   1721: (if not, \fINOTOK\fR is returned to signal that the next object type
                   1722: should be used).
                   1723: Otherwise,
                   1724: the \fIOID\fR is updated in place.
                   1725: .lp
                   1726: Now that the correct instance has been identified,
                   1727: a check is made to see if \fIkmem\fR should be consulted.
                   1728: (Obviously other programs might consult other data stores.)
                   1729: Finally,
                   1730: the instance value is encoded and the routine returns.
                   1731: 
                   1732: .sh 3 "The general case"
                   1733: .lp
                   1734: Obviously,
                   1735: this is a simple example of table handling.
                   1736: In general, the table will be implemented via linked lists,
                   1737: sorted according to object instance.
                   1738: In this case, 
                   1739: there is usually a special routine that is called to get a particular
                   1740: instance or the next instance.
                   1741: Take a look at the routines \fIget_tbent\fR and \fIo_smuxTree\fR in
                   1742: the file \fIsnmpd.c\fR.
                   1743: These implement the \fIsmuxTree\fR table.
                   1744: 
                   1745: .\" some day talk about:
                   1746: .\"     PEs
                   1747: .\"     mediaddr2oid
                   1748: 
                   1749: .bp
                   1750: .lp
                   1751: \fBTable of Contents\fR
                   1752: .sp 2
                   1753: .xp t

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.