extensible.c
上传用户:wxp200602
上传日期:2007-10-30
资源大小:4028k
文件大小:20k
源码类别:

SNMP编程

开发平台:

Unix_Linux

  1. #include <net-snmp/net-snmp-config.h>
  2. #if HAVE_STDLIB_H
  3. #include <stdlib.h>
  4. #endif
  5. #if HAVE_UNISTD_H
  6. #include <unistd.h>
  7. #endif
  8. #if HAVE_FCNTL_H
  9. #include <fcntl.h>
  10. #endif
  11. #if TIME_WITH_SYS_TIME
  12. # ifdef WIN32
  13. #  include <sys/timeb.h>
  14. # else
  15. #  include <sys/time.h>
  16. # endif
  17. # include <time.h>
  18. #else
  19. # if HAVE_SYS_TIME_H
  20. #  include <sys/time.h>
  21. # else
  22. #  include <time.h>
  23. # endif
  24. #endif
  25. #include <signal.h>
  26. #if HAVE_MACHINE_PARAM_H
  27. #include <machine/param.h>
  28. #endif
  29. #if HAVE_SYS_PARAM_H
  30. #include <sys/param.h>
  31. #endif
  32. #if HAVE_SYS_VMMETER_H
  33. #if !(defined(bsdi2) || defined(netbsd1))
  34. #include <sys/vmmeter.h>
  35. #endif
  36. #endif
  37. #if HAVE_SYS_CONF_H
  38. #include <sys/conf.h>
  39. #endif
  40. #if HAVE_ASM_PAGE_H
  41. #include <asm/page.h>
  42. #endif
  43. #if HAVE_SYS_SWAP_H
  44. #include <sys/swap.h>
  45. #endif
  46. #if HAVE_SYS_FS_H
  47. #include <sys/fs.h>
  48. #else
  49. #if HAVE_UFS_FS_H
  50. #include <ufs/fs.h>
  51. #else
  52. #if HAVE_UFS_UFS_DINODE_H
  53. #include <ufs/ufs/dinode.h>
  54. #endif
  55. #if HAVE_UFS_FFS_FS_H
  56. #include <ufs/ffs/fs.h>
  57. #endif
  58. #endif
  59. #endif
  60. #if HAVE_MTAB_H
  61. #include <mtab.h>
  62. #endif
  63. #include <sys/stat.h>
  64. #include <errno.h>
  65. #if HAVE_FSTAB_H
  66. #include <fstab.h>
  67. #endif
  68. #if HAVE_SYS_STATFS_H
  69. #include <sys/statfs.h>
  70. #endif
  71. #if HAVE_SYS_STATVFS_H
  72. #include <sys/statvfs.h>
  73. #endif
  74. #if HAVE_SYS_VFS_H
  75. #include <sys/vfs.h>
  76. #endif
  77. #if (!defined(HAVE_STATVFS)) && defined(HAVE_STATFS)
  78. #if HAVE_SYS_MOUNT_H
  79. #include <sys/mount.h>
  80. #endif
  81. #if HAVE_SYS_SYSCTL_H
  82. #include <sys/sysctl.h>
  83. #endif
  84. #define statvfs statfs
  85. #endif
  86. #if HAVE_VM_VM_H
  87. #include <vm/vm.h>
  88. #endif
  89. #if HAVE_VM_SWAP_PAGER_H
  90. #include <vm/swap_pager.h>
  91. #endif
  92. #if HAVE_SYS_FIXPOINT_H
  93. #include <sys/fixpoint.h>
  94. #endif
  95. #if HAVE_MALLOC_H
  96. #include <malloc.h>
  97. #endif
  98. #if HAVE_STRING_H
  99. #include <string.h>
  100. #endif
  101. #include <ctype.h>
  102. #if HAVE_WINSOCK_H
  103. #include <winsock.h>
  104. #endif
  105. #ifndef HAVE_STRNCASECMP
  106. int             strncasecmp(const char *s1, const char *s2, size_t n);
  107. #endif
  108. #if HAVE_DMALLOC_H
  109. #include <dmalloc.h>
  110. #endif
  111. #include <net-snmp/net-snmp-includes.h>
  112. #include <net-snmp/agent/net-snmp-agent-includes.h>
  113. #include <net-snmp/agent/auto_nlist.h>
  114. #include <net-snmp/agent/agent_callbacks.h>
  115. #include "struct.h"
  116. #include "extensible.h"
  117. #include "utilities/execute.h"
  118. #include "util_funcs.h"
  119. extern struct myproc *procwatch;        /* moved to proc.c */
  120. extern int      numprocs;       /* ditto */
  121. extern struct extensible *extens;       /* In exec.c */
  122. extern struct extensible *relocs;       /* In exec.c */
  123. extern int      numextens;      /* ditto */
  124. extern int      numrelocs;      /* ditto */
  125. extern struct extensible *passthrus;    /* In pass.c */
  126. extern int      numpassthrus;   /* ditto */
  127. extern char     sysName[];
  128. extern netsnmp_subtree *subtrees;
  129. extern struct variable2 extensible_relocatable_variables[];
  130. extern struct variable2 extensible_passthru_variables[];
  131. /*
  132.  * the relocatable extensible commands variables 
  133.  */
  134. struct variable2 extensible_relocatable_variables[] = {
  135.     {MIBINDEX, ASN_INTEGER, RONLY, var_extensible_relocatable, 1,
  136.      {MIBINDEX}},
  137.     {ERRORNAME, ASN_OCTET_STR, RONLY, var_extensible_relocatable, 1,
  138.      {ERRORNAME}},
  139.     {SHELLCOMMAND, ASN_OCTET_STR, RONLY, var_extensible_relocatable, 1,
  140.      {SHELLCOMMAND}},
  141.     {ERRORFLAG, ASN_INTEGER, RONLY, var_extensible_relocatable, 1,
  142.      {ERRORFLAG}},
  143.     {ERRORMSG, ASN_OCTET_STR, RONLY, var_extensible_relocatable, 1,
  144.      {ERRORMSG}},
  145.     {ERRORFIX, ASN_INTEGER, RWRITE, var_extensible_relocatable, 1,
  146.      {ERRORFIX}},
  147.     {ERRORFIXCMD, ASN_OCTET_STR, RONLY, var_extensible_relocatable, 1,
  148.      {ERRORFIXCMD}}
  149. };
  150. void
  151. init_extensible(void)
  152. {
  153.     struct variable2 extensible_extensible_variables[] = {
  154.         {MIBINDEX, ASN_INTEGER, RONLY, var_extensible_shell, 1,
  155.          {MIBINDEX}},
  156.         {ERRORNAME, ASN_OCTET_STR, RONLY, var_extensible_shell, 1,
  157.          {ERRORNAME}},
  158.         {SHELLCOMMAND, ASN_OCTET_STR, RONLY, var_extensible_shell, 1,
  159.          {SHELLCOMMAND}},
  160.         {ERRORFLAG, ASN_INTEGER, RONLY, var_extensible_shell, 1,
  161.          {ERRORFLAG}},
  162.         {ERRORMSG, ASN_OCTET_STR, RONLY, var_extensible_shell, 1,
  163.          {ERRORMSG}},
  164.         {ERRORFIX, ASN_INTEGER, RWRITE, var_extensible_shell, 1,
  165.          {ERRORFIX}},
  166.         {ERRORFIXCMD, ASN_OCTET_STR, RONLY, var_extensible_shell, 1,
  167.          {ERRORFIXCMD}}
  168.     };
  169.     /*
  170.      * Define the OID pointer to the top of the mib tree that we're
  171.      * registering underneath 
  172.      */
  173.     oid             extensible_variables_oid[] =
  174.         { UCDAVIS_MIB, SHELLMIBNUM, 1 };
  175.     /*
  176.      * register ourselves with the agent to handle our mib tree 
  177.      */
  178.     REGISTER_MIB("ucd-snmp/extensible", extensible_extensible_variables,
  179.                  variable2, extensible_variables_oid);
  180.     snmpd_register_config_handler("exec", extensible_parse_config,
  181.                                   extensible_free_config,
  182.                                   "[miboid] name program arguments");
  183.     snmpd_register_config_handler("sh", extensible_parse_config,
  184.                                   extensible_free_config,
  185.                                   "[miboid] name program-or-script arguments");
  186.     snmpd_register_config_handler("execfix", execfix_parse_config, NULL,
  187.                                   "exec-or-sh-name program [arguments...]");
  188.     snmp_register_callback(SNMP_CALLBACK_APPLICATION,
  189.                            SNMPD_CALLBACK_PRE_UPDATE_CONFIG,
  190.                            extensible_unregister, NULL);
  191. }
  192. extern int pass_compare(const void *a, const void *b);
  193. void
  194. extensible_parse_config(const char *token, char *cptr)
  195. {
  196.     struct extensible *ptmp, **pp;
  197.     char           *tcptr;
  198.     int            scount;
  199.     /*
  200.      * allocate and clear memory structure 
  201.      */
  202.     ptmp = (struct extensible *) calloc(1, sizeof(struct extensible));
  203.     if (ptmp == NULL)
  204.         return;                 /* XXX memory alloc error */
  205.     if (*cptr == '.')
  206.         cptr++;
  207.     if (isdigit(*cptr)) {
  208.         /*
  209.          * its a relocatable extensible mib 
  210.          */
  211.         config_perror("Warning: relocatable 'exec' format will change in a future release - See 'NET-SNMP-EXTEND-MIB' for an alternative" );
  212.         for (pp = &relocs, numrelocs++; *pp; pp = &((*pp)->next));
  213.         (*pp) = ptmp;
  214.         pp = &relocs; scount = numrelocs;
  215.     } else {
  216.         /*
  217.          * it goes in with the general extensible table 
  218.          */
  219.         for (pp = &extens, numextens++; *pp; pp = &((*pp)->next));
  220.         (*pp) = ptmp;
  221.         pp = &extens; scount = numextens;
  222.     }
  223.     /*
  224.      * the rest is pretty much handled the same 
  225.      */
  226.     if (!strncasecmp(token, "sh", 2))
  227.         ptmp->type = SHPROC;
  228.     else
  229.         ptmp->type = EXECPROC;
  230.     if (isdigit(*cptr)) {
  231.         ptmp->miblen = parse_miboid(cptr, ptmp->miboid);
  232.         while (isdigit(*cptr) || *cptr == '.')
  233.             cptr++;
  234.     }
  235.     /*
  236.      * name 
  237.      */
  238.     cptr = skip_white(cptr);
  239.     copy_nword(cptr, ptmp->name, sizeof(ptmp->name));
  240.     cptr = skip_not_white(cptr);
  241.     cptr = skip_white(cptr);
  242.     /*
  243.      * command 
  244.      */
  245.     if (cptr == NULL) {
  246.         config_perror("No command specified on line");
  247.     } else {
  248.         /*
  249.          * Support multi-element commands in shell configuration
  250.          *   lines, but truncate after the first command for 'exec'
  251.          */
  252.         for (tcptr = cptr; *tcptr != 0 && *tcptr != '#'; tcptr++)
  253.             if (*tcptr == ';' && ptmp->type == EXECPROC)
  254.                 break;
  255.         strncpy(ptmp->command, cptr, tcptr - cptr);
  256.         ptmp->command[tcptr - cptr] = 0;
  257.     }
  258. #ifdef EXECFIXCMD
  259.     sprintf(ptmp->fixcmd, EXECFIXCMD, ptmp->name);
  260. #endif
  261.     if (ptmp->miblen > 0) {
  262.         register_mib(token,
  263.                      (struct variable *) extensible_relocatable_variables,
  264.                      sizeof(struct variable2), 
  265.                      sizeof(extensible_relocatable_variables) /
  266.                      sizeof(*extensible_relocatable_variables),
  267.                      ptmp->miboid, ptmp->miblen);
  268.     }
  269.     if (scount > 1) {
  270.         int i;
  271.         struct extensible **etmp = (struct extensible **)
  272.             malloc(((sizeof(struct extensible *)) * scount));
  273.         if (etmp == NULL)
  274.             return;                 /* XXX memory alloc error */
  275.         for (i = 0, ptmp = *pp;
  276.              i < scount && ptmp != 0; i++, ptmp = ptmp->next)
  277.             etmp[i] = ptmp;
  278.         qsort(etmp, scount, sizeof(struct extensible *),
  279.               pass_compare);
  280.         *pp = (struct extensible *) etmp[0];
  281.         ptmp = (struct extensible *) etmp[0];
  282.         for (i = 0; i < scount - 1; i++) {
  283.             ptmp->next = etmp[i + 1];
  284.             ptmp = ptmp->next;
  285.         }
  286.         ptmp->next = NULL;
  287.         free(etmp);
  288.     }
  289. }
  290. int
  291. extensible_unregister(int major, int minor,
  292.                       void *serverarg, void *clientarg)
  293. {
  294.     extensible_free_config();
  295.     return 0;
  296. }
  297. void
  298. extensible_free_config(void)
  299. {
  300.     struct extensible *etmp, *etmp2;
  301.     oid    tname[MAX_OID_LEN];
  302.     int    i;
  303.     for (etmp = extens; etmp != NULL;) {
  304.         etmp2 = etmp;
  305.         etmp = etmp->next;
  306.         free(etmp2);
  307.     }
  308.     for (etmp = relocs; etmp != NULL;) {
  309.         etmp2 = etmp;
  310.         etmp = etmp->next;
  311.         /*
  312.          * The new modular API results in the column
  313.          *  objects being registered individually, so
  314.          *  they need to be unregistered individually too!
  315.          */
  316.         memset(tname, 0, MAX_OID_LEN*sizeof(oid));
  317.         memcpy(tname,  etmp2->miboid, etmp2->miblen*sizeof(oid));
  318.         for (i=1; i<4; i++) {
  319.             tname[etmp2->miblen] = i;
  320.             unregister_mib(tname, etmp2->miblen+1);
  321.         }
  322.         for (i=100; i<=103; i++) {
  323.             tname[etmp2->miblen] = i;
  324.             unregister_mib(tname, etmp2->miblen+1);
  325.         }
  326.         free(etmp2);
  327.     }
  328.     relocs = NULL;
  329.     extens = NULL;
  330.     numextens = 0;
  331.     numrelocs = 0;
  332. }
  333. #define MAXMSGLINES 1000
  334. struct extensible *extens = NULL;       /* In exec.c */
  335. struct extensible *relocs = NULL;       /* In exec.c */
  336. int             numextens = 0, numrelocs = 0;   /* ditto */
  337. /*
  338.  * var_extensible_shell(...
  339.  * Arguments:
  340.  * vp     IN      - pointer to variable entry that points here
  341.  * name    IN/OUT  - IN/name requested, OUT/name found
  342.  * length  IN/OUT  - length of IN/OUT oid's 
  343.  * exact   IN      - TRUE if an exact match was requested
  344.  * var_len OUT     - length of variable or 0 if function returned
  345.  * write_method
  346.  * 
  347.  */
  348. /*
  349.  * find a give entry in the linked list associated with a proc name 
  350.  */
  351. struct extensible *
  352. get_exec_by_name(char *name)
  353. {
  354.     struct extensible *etmp;
  355.     if (name == NULL)
  356.         return NULL;
  357.     for (etmp = extens; etmp != NULL && strcmp(etmp->name, name) != 0;
  358.          etmp = etmp->next);
  359.     if(NULL == etmp)
  360.         for (etmp = relocs; etmp != NULL && strcmp(etmp->name, name) != 0;
  361.          etmp = etmp->next);
  362.     return etmp;
  363. }
  364. void
  365. execfix_parse_config(const char *token, char *cptr)
  366. {
  367.     char            tmpname[STRMAX];
  368.     struct extensible *execp;
  369.     /*
  370.      * don't allow two entries with the same name 
  371.      */
  372.     cptr = copy_nword(cptr, tmpname, sizeof(tmpname));
  373.     if ((execp = get_exec_by_name(tmpname)) == NULL) {
  374.         config_perror("No exec entry registered for this exec name yet.");
  375.         return;
  376.     }
  377.     if (strlen(cptr) > sizeof(execp->fixcmd)) {
  378.         config_perror("fix command too long.");
  379.         return;
  380.     }
  381.     strncpy(execp->fixcmd, cptr, sizeof(execp->fixcmd));
  382.     execp->fixcmd[ sizeof(execp->fixcmd)-1 ] = 0;
  383. }
  384. u_char         *
  385. var_extensible_shell(struct variable * vp,
  386.                      oid * name,
  387.                      size_t * length,
  388.                      int exact,
  389.                      size_t * var_len, WriteMethod ** write_method)
  390. {
  391.     static struct extensible *exten = 0;
  392.     static long     long_ret;
  393.     int len;
  394.     if (header_simple_table
  395.         (vp, name, length, exact, var_len, write_method, numextens))
  396.         return (NULL);
  397.     if ((exten = get_exten_instance(extens, name[*length - 1]))) {
  398.         switch (vp->magic) {
  399.         case MIBINDEX:
  400.             long_ret = name[*length - 1];
  401.             return ((u_char *) (&long_ret));
  402.         case ERRORNAME:        /* name defined in config file */
  403.             *var_len = strlen(exten->name);
  404.             return ((u_char *) (exten->name));
  405.         case SHELLCOMMAND:
  406.             *var_len = strlen(exten->command);
  407.             return ((u_char *) (exten->command));
  408.         case ERRORFLAG:        /* return code from the process */
  409.             len = sizeof(exten->output);
  410.             if (exten->type == EXECPROC) {
  411.                 exten->result = run_exec_command( exten->command, NULL,
  412.                                                   exten->output, &len);
  413.     } else {
  414.                 exten->result = run_shell_command(exten->command, NULL,
  415.                                                   exten->output, &len);
  416.     }
  417.             long_ret = exten->result;
  418.             return ((u_char *) (&long_ret));
  419.         case ERRORMSG:         /* first line of text returned from the process */
  420.             len = sizeof(exten->output);
  421.             if (exten->type == EXECPROC) {
  422.                 exten->result = run_exec_command( exten->command, NULL,
  423.                                                   exten->output, &len);
  424.     } else {
  425.                 exten->result = run_shell_command(exten->command, NULL,
  426.                                                   exten->output, &len);
  427.     }
  428.             *var_len = strlen(exten->output);
  429.             if (exten->output[*var_len - 1] == 'n')
  430.                 exten->output[--(*var_len)] = '';
  431.             return ((u_char *) (exten->output));
  432.         case ERRORFIX:
  433.             *write_method = fixExecError;
  434.             long_return = 0;
  435.             return ((u_char *) & long_return);
  436.         case ERRORFIXCMD:
  437.             *var_len = strlen(exten->fixcmd);
  438.             return ((u_char *) exten->fixcmd);
  439.         }
  440.         return NULL;
  441.     }
  442.     return NULL;
  443. }
  444. int
  445. fixExecError(int action,
  446.              u_char * var_val,
  447.              u_char var_val_type,
  448.              size_t var_val_len,
  449.              u_char * statP, oid * name, size_t name_len)
  450. {
  451.     struct extensible *exten;
  452.     long            tmp = 0;
  453.     int             fd;
  454.     static struct extensible ex;
  455.     FILE           *file;
  456.     if ((exten = get_exten_instance(extens, name[10]))) {
  457.         if (var_val_type != ASN_INTEGER) {
  458.             snmp_log(LOG_ERR, "Wrong type != intn");
  459.             return SNMP_ERR_WRONGTYPE;
  460.         }
  461.         tmp = *((long *) var_val);
  462.         if ((tmp == 1) && (action == COMMIT) && (exten->fixcmd[0] != 0)) {
  463.             sprintf(ex.command, exten->fixcmd);
  464.             if ((fd = get_exec_output(&ex)) != -1) {
  465.                 file = fdopen(fd, "r");
  466.                 while (fgets(ex.output, sizeof(ex.output), file) != NULL);
  467.                 fclose(file);
  468.                 wait_on_exec(&ex);
  469.             }
  470.         }
  471.         return SNMP_ERR_NOERROR;
  472.     }
  473.     return SNMP_ERR_WRONGTYPE;
  474. }
  475. u_char         *
  476. var_extensible_relocatable(struct variable *vp,
  477.                            oid * name,
  478.                            size_t * length,
  479.                            int exact,
  480.                            size_t * var_len, WriteMethod ** write_method)
  481. {
  482.     int             i;
  483.     int             len;
  484.     struct extensible *exten = 0;
  485.     static long     long_ret;
  486.     static char     errmsg[STRMAX];
  487.     char            *cp, *cp1;
  488.     struct variable myvp;
  489.     oid             tname[MAX_OID_LEN];
  490.     memcpy(&myvp, vp, sizeof(struct variable));
  491.     long_ret = *length;
  492.     for (i = 1; i <= (int) numrelocs; i++) {
  493.         exten = get_exten_instance(relocs, i);
  494.         if ((int) exten->miblen == (int) vp->namelen - 1) {
  495.             memcpy(myvp.name, exten->miboid, exten->miblen * sizeof(oid));
  496.             myvp.namelen = exten->miblen;
  497.             *length = vp->namelen;
  498.             memcpy(tname, vp->name, vp->namelen * sizeof(oid));
  499.             if (!header_simple_table
  500.                 (&myvp, tname, length, -1, var_len, write_method, -1))
  501.                 break;
  502.             else
  503.                 exten = NULL;
  504.         }
  505.     }
  506.     if (i > (int) numrelocs || exten == NULL) {
  507.         *length = long_ret;
  508.         *var_len = 0;
  509.         *write_method = NULL;
  510.         return (NULL);
  511.     }
  512.     *length = long_ret;
  513.     if (header_simple_table(vp, name, length, exact, var_len, write_method,
  514.                             ((vp->magic == ERRORMSG) ? MAXMSGLINES : 1)))
  515.         return (NULL);
  516.     switch (vp->magic) {
  517.     case MIBINDEX:
  518.         long_ret = name[*length - 1];
  519.         return ((u_char *) (&long_ret));
  520.     case ERRORNAME:            /* name defined in config file */
  521.         *var_len = strlen(exten->name);
  522.         return ((u_char *) (exten->name));
  523.     case SHELLCOMMAND:
  524.         *var_len = strlen(exten->command);
  525.         return ((u_char *) (exten->command));
  526.     case ERRORFLAG:            /* return code from the process */
  527.         len = sizeof(exten->output);
  528.         if (exten->type == EXECPROC)
  529.             exten->result = run_exec_command( exten->command, NULL,
  530.                                               exten->output, &len);
  531. else
  532.             exten->result = run_shell_command(exten->command, NULL,
  533.                                               exten->output, &len);
  534.         long_ret = exten->result;
  535.         return ((u_char *) (&long_ret));
  536.     case ERRORMSG:             /* first line of text returned from the process */
  537.         len = sizeof(exten->output);
  538.         if (exten->type == EXECPROC)
  539.             exten->result = run_exec_command( exten->command, NULL,
  540.                                               exten->output, &len);
  541. else
  542.             exten->result = run_shell_command(exten->command, NULL,
  543.                                               exten->output, &len);
  544.         /*
  545.          *  Pick the output string apart into individual lines,
  546.          *  and extract the one being asked for....
  547.          */
  548.         cp1 = exten->output;
  549.         for (i = 1; i != (int) name[*length - 1]; i++) {
  550.             cp = strchr(cp1, 'n');
  551.             if (!cp) {
  552.         *var_len = 0;
  553.         /* wait_on_exec(exten); ??? */
  554.         return NULL;
  555.     }
  556.     cp1 = ++cp;
  557. }
  558.         /*
  559.          *  ... and quit if we've run off the end of the output
  560.          */
  561.         if (!*cp1) {
  562.             *var_len = 0;
  563.     return NULL;
  564. }
  565.         cp = strchr(cp1, 'n');
  566.         if (cp)
  567.             *cp = 0;
  568.         strncpy(errmsg, cp1, sizeof(errmsg));
  569.         errmsg[ sizeof(errmsg)-1 ] = 0;
  570.         *var_len = strlen(errmsg);
  571.         if (errmsg[*var_len - 1] == 'n')
  572.             errmsg[--(*var_len)] = '';
  573.         return ((u_char *) (errmsg));
  574.     case ERRORFIX:
  575.         *write_method = fixExecError;
  576.         long_return = 0;
  577.         return ((u_char *) & long_return);
  578.     case ERRORFIXCMD:
  579.         *var_len = strlen(exten->fixcmd);
  580.         return ((u_char *) exten->fixcmd);
  581.     }
  582.     return NULL;
  583. }
  584. netsnmp_subtree *
  585. find_extensible(netsnmp_subtree *tp, oid *tname, size_t tnamelen, int exact)
  586. {
  587.     size_t          tmp;
  588.     int             i;
  589.     struct extensible *exten = 0;
  590.     struct variable myvp;
  591.     oid             name[MAX_OID_LEN];
  592.     static netsnmp_subtree mysubtree[2] =
  593. { { NULL, 0, NULL, 0, NULL, 0, NULL, 0, 0, NULL, NULL, 0, 0, 0,
  594.     NULL, NULL, NULL, 0, 0, NULL, 0, 0 },
  595.   { NULL, 0, NULL, 0, NULL, 0, NULL, 0, 0, NULL, NULL, 0, 0, 0,
  596.     NULL, NULL, NULL, 0, 0, NULL, 0, 0 } };
  597.     for (i = 1; i <= (int) numrelocs; i++) {
  598.         exten = get_exten_instance(relocs, i);
  599.         if (exten->miblen != 0) {
  600.             memcpy(myvp.name, exten->miboid, exten->miblen * sizeof(oid));
  601.             memcpy(name, tname, tnamelen * sizeof(oid));
  602.             myvp.name[exten->miblen] = name[exten->miblen];
  603.             myvp.namelen = exten->miblen + 1;
  604.             tmp = exten->miblen + 1;
  605.             if (!header_simple_table(&myvp, name, &tmp, -1, 
  606.      NULL, NULL, numrelocs)) {
  607.                 break;
  608.     }
  609.         }
  610.     }
  611.     if (i > (int)numrelocs || exten == NULL) {
  612.         return (tp);
  613.     }
  614.     if (mysubtree[0].name_a != NULL) {
  615. free(mysubtree[0].name_a);
  616. mysubtree[0].name_a = NULL;
  617.     }
  618.     mysubtree[0].name_a  = snmp_duplicate_objid(exten->miboid, exten->miblen);
  619.     mysubtree[0].namelen = exten->miblen;
  620.     mysubtree[0].variables = (struct variable *)extensible_relocatable_variables;
  621.     mysubtree[0].variables_len = sizeof(extensible_relocatable_variables) /
  622.         sizeof(*extensible_relocatable_variables);
  623.     mysubtree[0].variables_width = sizeof(*extensible_relocatable_variables);
  624.     mysubtree[1].namelen = 0;
  625.     return (mysubtree);
  626. }