home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / mac / source / kboot22.zoo / kboot22.1 / cmd.c next >
C/C++ Source or Header  |  1991-02-22  |  16KB  |  630 lines

  1. #ifndef lint
  2. static char    *RCSid="$Header: /tmp_mnt/home/src/rand/etc/kboot/RCS/cmd.c,v 1.2 91/02/12 19:38:49 root Exp $";
  3. #endif lint
  4.  
  5. /*
  6.  * $Log:    cmd.c,v $
  7.  * Revision 1.2  91/02/12  19:38:49  root
  8.  * Removed unused commands.
  9.  * 
  10.  * Revision 1.1  91/01/29  17:36:59  root
  11.  * Initial revision
  12.  * 
  13.  */
  14.  
  15. /*
  16.  * cmd.c - Send commands to kboxes.
  17.  */
  18.  
  19. #include <stdio.h>
  20. #include <syslog.h>
  21. #include <netdb.h>
  22. #include <sys/types.h>
  23. #include <sys/socket.h>
  24. #include <sys/errno.h>
  25. #include <net/if.h>
  26. #include <netinet/in.h>
  27. #include <netinet/if_ether.h>
  28. #include <sys/time.h>
  29. #include <sys/timeb.h>
  30. #include "appletalk.h"
  31. #include "cmdidx.h"
  32. #include "kbox.h"
  33. #include "config.h"
  34.  
  35. extern struct hostent        *gethostbyaddr ();
  36. extern char            *inet_ntoa ();
  37. struct kbox            *get_kbox ();
  38. extern int            errno;
  39. extern char            *sys_errlist[];
  40. extern int            numkbox;
  41. extern int            debug;
  42. extern struct ether_addr    ebc;
  43. extern struct ether_header    eh;
  44. extern struct kbox        *kboxtab[HTSIZE];
  45. extern long            atalkaddr;
  46. extern unsigned char        kboxstate[STATE_LENGTH];
  47. extern unsigned char        newstate[STATE_LENGTH];
  48. extern char            *etcdir;
  49. extern unsigned char        *zonelistp;
  50.  
  51. #define HEX(i)            ((i) < 10 ? '0' + (i) : 'A' + (i) - 10)
  52.  
  53. /*
  54.  * Globals.
  55.  */
  56. int            (*recv_copyproc) () = NULL;
  57. struct ether_fastpath    fppkt;
  58. struct fp_promram    promram;
  59. int            num_resp;
  60. int            expected_resp;
  61. int            destkbox;
  62. struct kbox        *kboxaddr[MAXKBOX];
  63. struct timeval        timeout;
  64.  
  65. /*
  66.  * Receive functions.  There should be a recv_ function for each send_
  67.  * function.
  68.  */
  69.  
  70. /*
  71.  * recv_who - Process who packet.  Algorithm is to look up the ethernet
  72.  *   address in the ethers file and then add this kbox to the hash
  73.  *   table it's not already there.  It would probably be more efficient
  74.  *   to look up the ethernet address directly in a table hashed by ethernet
  75.  *   address, however recv_who isn't called very often so I won't bother.
  76.  */
  77. recv_who (p)
  78.  
  79. struct fp_packet    *p;
  80.  
  81. {
  82.   char        name[MAXHOSTNAME];
  83.   char        buf[20];
  84.   struct kbox    *kp;
  85.  
  86.   if (ether_ntohost (name, &p->fp_shost) != 0) {
  87.       logerr ("recv_who: Can't find %s in ethers table\n",
  88.           ether_ntoa (&p->fp_shost));
  89.       return;
  90.   }
  91.   if (kp = get_kbox (name)) {
  92.       if (!bcmp (&kp->ea, &p->fp_shost, sizeof(struct ether_addr))) {
  93.       if (kp->aa == AT_Broadcast) {
  94.           kp->aa = p->fp_lapsrc;
  95.           kboxaddr[kp->aa] = kp;
  96.           if (debug)
  97.               fprintf (stderr, "recv_who: Adding kbox %s %s\n",
  98.                name, ether_ntoa (&p->fp_shost));
  99.           num_resp++;
  100.       }
  101.       } else {
  102.       strcpy (buf, ether_ntoa (&kp->ea));
  103.       logerr ("recv_who: Mismatched ether address for %s: %s != %s\n",
  104.           name, buf, ether_ntoa (&p->fp_shost));
  105.       }
  106.   } else if (debug)
  107.       fprintf (stderr, "recv_who: Unknown kbox %s %s\n",
  108.            name, ether_ntoa (&p->fp_shost));
  109. }
  110.  
  111. /*
  112.  * recv_promram - Receive promram vector.  On sparc machines the
  113.  *   fp_promram structure doesn't align properly, so copy the count
  114.  *   field separately.
  115.  */
  116. recv_promram (p)
  117.  
  118. struct fp_packet    *p;
  119.  
  120. {
  121.   bcopy (p->fp_data, &promram.fpr_count, sizeof(promram.fpr_count));
  122.   bcopy (&p->fp_data[sizeof(promram.fpr_count)], &promram.fpr_jtable,
  123.      PROMRAMSIZE - sizeof(promram.fpr_count));
  124.   num_resp++;
  125. }
  126.  
  127. /*
  128.  * recv_getstate - Receive state vector.
  129.  */
  130. recv_getstate (p)
  131.  
  132. struct fp_packet    *p;
  133.  
  134. {
  135.   bcopy (&p->fp_data[FPCOPYSIZE], kboxstate, STATE_LENGTH);
  136.   if (debug) {
  137.       fprintf (stderr, "recv_getstate: Received state...\n");
  138.       show_state (stderr, kboxstate);
  139.   }
  140.   num_resp++;
  141. }
  142.  
  143. /*
  144.  * inet_ntos - Translate ip address to hostname.
  145.  */
  146. char *inet_ntos (addr)
  147.  
  148. unsigned char    *addr;
  149.  
  150. {
  151.   struct in_addr    a;
  152.   struct hostent    *he;
  153.  
  154.   bcopy (addr, &a.s_addr, 4);
  155.   if ((he = gethostbyaddr (&a, 4, AF_INET)) == NULL)
  156.       return (inet_ntoa (a));
  157.   else
  158.       return (he->h_name);
  159. }
  160.  
  161. /*
  162.  * show_state - Print state vector.
  163.  */
  164. show_state (f, s)
  165.  
  166. FILE        *f;
  167. unsigned char    s[STATE_LENGTH];
  168.  
  169. {
  170.   int    options;
  171.   int    i;
  172.   int    first;
  173.   long    l;
  174.   short    st;
  175.  
  176.   fprintf (f, "\tAppletalk: %d.%d.%d Ethertalk: %d.%d.%d IPtalk: %d.%d.%d",
  177.        s[O_ATNET], s[O_ATNET+1], s[O_NODE],
  178.        s[O_ETNET], s[O_ETNET+1], s[O_ETNODE],
  179.        s[O_UDPNET], s[O_UDPNET+1], s[O_UDPNODE]);
  180.   fprintf (f, " Bridge: %d\n", s[O_BRIDGE]);
  181.   fprintf (f, "\tEthernet: %s Name: \"%s\" File: \"%s\"\n",
  182.        ether_ntoa (&s[O_ETHER]), &s[O_NAME], &s[O_FILE]);
  183.   fprintf (f, "\tConfig: \"%s\"\n", &s[O_CONFIG]);
  184.   fprintf (f, "\tATzone: \"%s\" ETzone: \"%s\" UDPzone: \"%s\"\n",
  185.        &s[O_ATZONE], &s[O_ETZONE], &s[O_UDPZONE]);
  186.   fprintf (f, "\tPforce: %d Autoconfig: %s", s[O_PFORCE],
  187.        (s[O_AUTOCONFIG] || s[O_AUTOCONFIG+1]) ? "yes" : "no");
  188.   fprintf (f, " Autoboot: %s",
  189.        (s[O_AUTOBOOT] || s[O_AUTOBOOT+1]) ? "yes\n" : "no\n");
  190.   fprintf (f, "\tOptions: ");
  191.   bcopy (&s[O_OPTIONS], &options, sizeof(options));
  192.   if (options) {
  193.       for (first = 1, i = 0; i < 32; i++)
  194.       if ((1 << i) & options)
  195.           if (first) {
  196.           first = 0;
  197.           fprintf (f, "%d", i + 1);
  198.           } else
  199.           fprintf (f, ", %d", i+1);
  200.   } else
  201.       fprintf (f, "none");
  202.   bcopy (&s[O_SN], &l, sizeof (l));
  203.   bcopy (&s[O_TYPE], &st, sizeof (st));
  204.   fprintf (f, " Serial: %d Type: %d\n", l, st);
  205.   fprintf (f, "\tRouter: \"%s\"", inet_ntos (&s[O_IPDEFROUTER]));
  206.   fprintf (f, " ETip: \"%s\"", inet_ntos (&s[O_IPADDRESS]));
  207.   fprintf (f, " ATip: \"%s\"", inet_ntos (&s[O_ATIPADDRESS]));
  208.   fprintf (f, " Broadcast: \"%s\"\n", inet_ntos (&s[O_IPBROADCAST]));
  209.   fprintf (f, "\tSubnet: \"%s\"", inet_ntos (&s[O_IPSUBMASK]));
  210.   fprintf (f, " KIP: \"%s\"", inet_ntos (&s[O_IPKIPSERVER]));
  211.   fprintf (f, " Name: \"%s\"", inet_ntos (&s[O_NAMESERVER]));
  212.   fprintf (f, " File: \"%s\"\n", inet_ntos (&s[O_FILESERVER]));
  213.   bcopy (&s[O_LP1], &l, sizeof (l));
  214.   fprintf (f, "\tLocal1: 0x%x", l);
  215.   bcopy (&s[O_LP2], &l, sizeof (l));
  216.   fprintf (f, " Local2: 0x%x", l);
  217.   bcopy (&s[O_LP3], &l, sizeof (l));
  218.   fprintf (f, " Local3: 0x%x", l);
  219.   bcopy (&s[O_LP4], &l, sizeof (l));
  220.   fprintf (f, " Local4: 0x%x\n", l);
  221.   bcopy (&s[O_NDYNAMICS], &st, sizeof (st));
  222.   fprintf (f, "\tDynamics: %d", st);
  223.   bcopy (&s[O_NSTATICS], &st, sizeof (st));
  224.   fprintf (f, " Statics: %d", st);
  225.   bcopy (&s[O_ZONELIST], &l, sizeof (l));
  226.   fprintf (f, " Zonelist 0x%X\n", l);
  227. }
  228.  
  229. /*
  230.  * recv_gen - Generic receive routine.
  231.  */
  232. recv_gen (p)
  233.  
  234. struct fp_packet    *p;
  235.  
  236. {
  237.   num_resp++;
  238. }
  239.  
  240. /*
  241.  * recv_pkt - Call appropriate function to deal with packet from fastpath.
  242.  */
  243. recv_pkt (p)
  244.  
  245. struct fp_packet    *p;
  246.  
  247. {
  248.   char    buf[100];
  249.  
  250.   if (p->fp_laptype != FP_TYPE) {
  251.       return;
  252.   }
  253.   if (destkbox != AT_Broadcast && p->fp_lapsrc != destkbox) {
  254.     /*
  255.      * X_WHO responses from unconfigured fastpaths to broadcasts may come
  256.      * in after we've already started talking to a real fastpath, so
  257.      * just ignore all X_WHO responses with no error message.
  258.      */
  259.       if (p->fp_scmd != X_WHO)
  260.       logerr ("recv_pkt: Bogus response %d from %s\n",
  261.           p->fp_scmd, ether_ntoa (&p->fp_shost));
  262.       return;
  263.   }
  264.   if (p->fp_cmd == FP_ACK) {
  265.       num_resp++;
  266.       return;
  267.   }
  268.   if (p->fp_cmd != FP_RESP)
  269.       return;
  270.   switch (p->fp_scmd) {
  271.     case X_COPYMEM :    if (recv_copyproc)
  272.                 (*recv_copyproc) (p);
  273.             break;
  274.  
  275.     case X_PROMRAM :    recv_promram (p); break;
  276.  
  277.     case X_WHO :    recv_who (p); break;
  278.  
  279.     case X_RESET :    recv_gen ();
  280.             break;
  281.  
  282.     default :        ether_ntohost (buf, &p->fp_shost);
  283.             logerr ("recv_pkt: Received %d from %s\n",
  284.                 p->fp_scmd, buf);
  285.             break;
  286.   }
  287. }
  288.  
  289. /*
  290.  * Send procedures.  Those that have no parameters are macros that invoke
  291.  * send_gen(), defined in kbox.h.
  292.  */
  293.  
  294. /*
  295.  * send_who - Broadcast who packet.
  296.  */
  297. int send_who ()
  298.  
  299. {
  300.   int    i;
  301.  
  302.   for (i = 0; i < MAXKBOX; i++)
  303.       if (kboxaddr[i])
  304.         kboxaddr[i]->aa = AT_Broadcast;
  305.   nit_timeout (60, &timeout);
  306.   return (send_pkt (NULL, (int) FP_CMD, X_WHO, 0, 5, &timeout));
  307. }
  308.  
  309. /*
  310.  * send_boot - Download file to kbox.
  311.  */
  312. int send_boot (kp, zonedata, zonelen)
  313.  
  314. struct kbox    *kp;
  315. unsigned char    *zonedata;
  316. int        zonelen;
  317.  
  318. {
  319.   FILE    *f;
  320.   int    l;
  321.   int    ch;
  322.   int    cmd;
  323.   int    scmd;
  324.   char    fn[MAXFN];
  325.   int    size = 0;
  326.   int    lastlen;
  327.   int    lastaddr;
  328.  
  329.   sprintf (fn, "%s/%s", etcdir, kp->bootfile);
  330.   if ((f = fopen (fn, "r")) == NULL) {
  331.       logerr ("send_boot: %s to %s - %s\n", fn, kp->name, sys_errlist[errno]);
  332.       return (0);
  333.   }
  334.   nit_timeout (30, &timeout);
  335.   while ((cmd = fgetc (f)) > 0) {
  336.       if (cmd != 'S') {
  337.       logerr ("send_boot: Bad S record %d\n", cmd);
  338.       return (-1);
  339.       }
  340.       scmd = fgetc (f);
  341.       if (scmd == '8') {
  342.     /*
  343.      * With KSTAR 8.0 it is necessary to download a zonelist with
  344.      * KSTAR.  The following code converts the zonelist read from
  345.      * the config file written by the Fastpath Manager into
  346.      * s-records and sends them to the kbox.  The address where
  347.      * the zonelist is downloaded is the first even address after
  348.      * the last s-record, which is the address of the last s-record
  349.      * plus its length, rounded up to the nearest even address.
  350.      * The first s-record describes the following data, it looks like:
  351.      *
  352.      *    short num_elements;
  353.      *    struct {
  354.      *      short    type;
  355.      *      short length;
  356.      *    } element[NUMELEMENTS];
  357.      *
  358.      * For a zonelist the type is 1.  The second s-record contains
  359.      * the zonelist itself.  These extra s-records get sent right
  360.      * before the last s-record, which seems to have scmd == 8.
  361.      * Much of the information on the format of the s-records and
  362.      * their location was reverse engineered from packet traces,
  363.      * I don't guarantee this code to be correct, although it
  364.      * works for me.
  365.      */
  366.       lastlen = atox (fppkt.fastpath_data, 2);
  367.       lastaddr = atox (&fppkt.fastpath_data[2], 6);
  368.       zonelistp = (unsigned char *) (lastaddr + lastlen - 4);
  369.       if ((int) zonelistp % 2 == 1)
  370.           zonelistp++;
  371.       makeelemlist (fppkt.fastpath_data, zonelen);
  372.       if (debug)
  373.           fprintf (stderr, "send_boot: %s\n", fppkt.fastpath_data);
  374.       if (send_pkt (kp, 'S', '2', strlen(fppkt.fastpath_data),
  375.             5, &timeout) != 1)
  376.           return (0);
  377.       makezonedata (fppkt.fastpath_data, zonedata, zonelen);
  378.       if (debug)
  379.           fprintf (stderr, "send_boot: %s\n", fppkt.fastpath_data);
  380.       if (send_pkt (kp, 'S', '2', strlen(fppkt.fastpath_data),
  381.             5, &timeout) != 1)
  382.           return (0);
  383.       }
  384.       l = 0;
  385.       while ((ch = fgetc (f)) > 0 && ch != '\r' && ch != '\n')
  386.       fppkt.fastpath_data[l++] = ch;
  387.       size += l;
  388.       if (send_pkt (kp, cmd, scmd, l, 5, &timeout) != 1)
  389.       return (0);
  390.   }
  391.   if (debug)
  392.       fprintf (stderr, "send_boot: Downloaded %d bytes\n", size);
  393.   return (1);
  394. }
  395.  
  396. /*
  397.  * atox - Convert ascii string of length l to hex.
  398.  */
  399. int atox (s, l)
  400.  
  401. char    *s;
  402. int    l;
  403.  
  404. {
  405.   int    r = 0;
  406.   int    i;
  407.  
  408.   for (i = 0; i < l; i++)
  409.       r = (r << 4) | ((s[i] >= '0' && s[i] <= '9') ? s[i] - '0' :
  410.               (s[i] >= 'a' && s[i] <= 'f') ? s[i] - 'a' + 10 :
  411.               s[i] - 'A' + 10);
  412.   return (r);
  413. }
  414.  
  415. /*
  416.  * csumsrec - Compute checksum for s-record.
  417.  */
  418. unsigned char csumsrec (s, l)
  419.  
  420. unsigned char    *s;
  421. int        l;
  422.  
  423. {
  424.   unsigned char    sum;
  425.   static char    buf[3];
  426.  
  427.   sum = 0;
  428.   while (l--)
  429.       sum += *s++;
  430.   sum ^= 0xFF;
  431.   return (sum);
  432. }
  433.  
  434. /*
  435.  * makeelemlist - Make element list srecord.
  436.  */
  437. makeelemlist (p, l)
  438.  
  439. unsigned char    *p;
  440. int        l;
  441.  
  442. {
  443.   static elemlist    el = {1, 1, 0};
  444.   unsigned char        buf[BUFSIZ];
  445.  
  446.   el.bytes = l + 2;                /* Don't know why +2    */
  447.   bcopy (&zonelistp, buf, sizeof (zonelistp));    /* First byte is length */
  448.   *buf = 4 + sizeof (el);            /* so overwrite it    */
  449.   bcopy (&el, &buf[4], sizeof (el));
  450.   buf[4 + sizeof (el)] = csumsrec (buf, 4 + sizeof (el));
  451.   hexify (buf, p, 4 + sizeof (el) + 1);
  452. }
  453.  
  454. /*
  455.  * makezonedata - Make zone data srecord.
  456.  */
  457. makezonedata (p, data, l)
  458.  
  459. unsigned char    *p;
  460. unsigned char    *data;
  461. int        l;
  462.  
  463. {
  464.   unsigned char    *addr;
  465.   unsigned char    buf[BUFSIZ];
  466.  
  467.   addr = zonelistp + sizeof (elemlist);
  468.   bcopy (&addr, buf, sizeof (addr));
  469.   *buf = 4 + l;
  470.   bcopy (data, &buf[4], l);
  471.   buf[4 + l] = csumsrec (buf, 4 + l);
  472.   hexify (buf, p, 4 + l + 1);
  473. }
  474.  
  475. /*
  476.  * hexify - Convert buffer to hex ascii characters.
  477.  */
  478. hexify (buf, p, len)
  479.  
  480. unsigned char    *buf;
  481. unsigned char    *p;
  482. int        len;
  483.  
  484. {
  485.   int    i;
  486.  
  487.   for (i = 0; i < len; i++) {
  488.       p[2 * i] = HEX (buf[i] >> 4);
  489.       p[2 * i + 1] = HEX (buf[i] & 0xF);
  490.   }
  491.   p[2 * len] = '\0';
  492. }
  493.  
  494.  
  495. /*
  496.  * send_getstate - Get state vector.
  497.  */
  498. int send_getstate (kp)
  499.  
  500. struct kbox    *kp;
  501.  
  502. {
  503.   struct fp_copy    copy;
  504.  
  505.   copy.fpc_from = (char *) promram.fpr_state;
  506.   copy.fpc_to = (char *) -1;
  507.   copy.fpc_count = STATE_LENGTH;
  508.   bcopy (©, fppkt.fastpath_data, FPCOPYSIZE);
  509.   recv_copyproc = recv_getstate;
  510.   nit_timeout (30, &timeout);
  511.   return (send_pkt (kp, (int) FP_CMD, X_COPYMEM, FPCOPYSIZE, 5, &timeout));
  512. }
  513.  
  514. /*
  515.  * send_putstate - Put state vector.
  516.  */
  517. int send_putstate (kp)
  518.  
  519. struct kbox    *kp;
  520.  
  521. {
  522.   struct fp_copy    copy;
  523.  
  524.   copy.fpc_from = (char *) -1;
  525.   copy.fpc_to = (char *) promram.fpr_state;
  526.   copy.fpc_count = htons (STATE_LENGTH);
  527.   bcopy (©, fppkt.fastpath_data, FPCOPYSIZE);
  528.   newstate[O_NODE] = kboxstate[O_NODE];
  529.   bcopy (newstate, &fppkt.fastpath_data[FPCOPYSIZE], STATE_LENGTH);
  530.   if (debug) {
  531.       fprintf (stderr, "send_putstate: Sending state...\n");
  532.       show_state (stderr, newstate);
  533.   }
  534.   recv_copyproc = recv_gen;
  535.   nit_timeout (30, &timeout);
  536.   return (send_pkt (kp, (int) FP_CMD, X_COPYMEM,
  537.             FPCOPYSIZE + STATE_LENGTH, 5, &timeout));
  538. }
  539.  
  540. /*
  541.  * send_gen - Generic send command.
  542.  */
  543. int send_gen (kp, scmd, retries)
  544.  
  545. struct kbox    *kp;
  546. int        scmd;
  547. int        retries;
  548.  
  549. {
  550.   nit_timeout (60, &timeout);
  551.   return (send_pkt (kp, (int) FP_CMD, scmd, 0, retries, &timeout));
  552. }
  553.  
  554. /*
  555.  * send_pkt - Send command packet (in fppkt).  Returns # of responders,
  556.  *   or -1 on error.  If retries is 0, don't expect any response.
  557.  */
  558. int send_pkt (kp, cmd, scmd, len, retries, timeout)
  559.  
  560. struct kbox    *kp;
  561. int        cmd;
  562. int        scmd;
  563. int        len;
  564. int        retries;
  565. struct timeval    *timeout;
  566.  
  567. {
  568.   short            count;
  569.   int            h;
  570.   struct kbox        *p;
  571.   struct timeb        tstart;
  572.   struct timeb        tcur;
  573.   int            utotal;
  574.   int            ucur;
  575.  
  576.   if (kp) {
  577.       if (kp->aa == AT_Broadcast) {
  578.       logerr ("send_pkt: Invalid appletalk address for %s\n", kp->name);
  579.       return (-1);
  580.       }
  581.       destkbox = kp->aa;
  582.       ether_copy (&kboxaddr[destkbox]->ea, &eh.ether_dhost);
  583.       if (retries == 0) {
  584.       expected_resp = 0;
  585.       retries = 1;
  586.       } else
  587.       expected_resp = 1;
  588.   } else {
  589.       destkbox = AT_Broadcast;
  590.       ether_copy (&ebc, &eh.ether_dhost);
  591.       expected_resp = numkbox;
  592.   }
  593.   num_resp = 0;
  594.   fppkt.fastpath_lapdest = destkbox;
  595.   fppkt.fastpath_lapsrc = atalkaddr;
  596.   fppkt.fastpath_laptype = FP_TYPE;
  597.   fppkt.fastpath_cmd = cmd;
  598.   fppkt.fastpath_scmd = scmd;
  599.   count = htons (len + 4);
  600.   bcopy (&count, fppkt.fastpath_len, sizeof(count));
  601.   utotal = timeout->tv_sec * 1000000 + timeout->tv_usec;
  602.   for (count = 0; count < retries; count++) {
  603.       if (debug && count >= 1)
  604.       fprintf (stderr, "send_pkt: Retransmitting cmd %d scmd %d\n",
  605.            fppkt.fastpath_cmd, fppkt.fastpath_scmd);
  606.       nit_write (&eh, &fppkt, len + sizeof(struct ether_fastpath) - MAXFPPKT);
  607.       ftime (&tstart);
  608.       ucur = 0;
  609.       while (num_resp < expected_resp && utotal > ucur) {
  610.       nit_dispatch (recv_pkt, 1, NULL, NULL, timeout);
  611.       ftime (&tcur);
  612.       ucur = (tcur.time - tstart.time) * 1000000 +
  613.          (tcur.millitm - tstart.millitm) * 1000;
  614.       }
  615.       if (num_resp >= expected_resp)
  616.       break;
  617.   }
  618.   if (num_resp != expected_resp)
  619.       if (destkbox == AT_Broadcast) {
  620.           for (h = 0; h < HTSIZE; h++)
  621.               for (p = kboxtab[h]; p; p = p->nxt)
  622.               if (p->aa == AT_Broadcast)
  623.                   logerr ("send_pkt(%d,%d): No response from %s\n",
  624.                    cmd, scmd, p->name);
  625.       } else
  626.       logerr ("send_pkt(%d,%d): No response from %s\n",
  627.           cmd, scmd, kboxaddr[destkbox]->name);
  628.   return (num_resp);
  629. }
  630.