/***************************************************************************
 *                                                                         *
 *   GALICODG.C                                                            *
 *                                                                         *
 *   Copyright (c) 1995-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Collection of diagnostics for ICO (Pacific Softworks' Fusion).        *
 *                                                                         *
 *                                                 - R. Stein 8/29/95      *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "tcpip.h"
#include "llconfig.h"
#include "cman.h"
#include "scp.h"
#include "tcport.h"
#include "crit.h"
#include "galpkt.h"

static INT icodglb(VOID);
static VOID shotimers(VOID);
static VOID shotcp(VOID);
static VOID shoip(VOID);
static VOID shocallers(VOID);
static VOID shosock(VOID);
static VOID shopacc(VOID);
static VOID shopack(VOID);
static VOID shoroutes(VOID);
static VOID shoslipinf(INT detail);
static VOID gal_printf();

#ifdef PACSOFT
     struct nfywhy {
          u16 mask;
          CHAR *name;
     } nfywhy[]={
          {READ_NOTIFY     ,"READ"     },
          {WRITE_NOTIFY    ,"WRITE"    },
          {ACCEPT_NOTIFY   ,"ACCEPT"   },
          {CLOSE_NOTIFY    ,"CLOSE"    },
          {CONNECT_NOTIFY  ,"CONNECT"  },
          {EXCEPT_NOTIFY   ,"EXCEPT"   },
          {RSHUTDOWN_NOTIFY,"RSHUTDOWN"},
          {TIMEOUT_NOTIFY  ,"TIMEOUT"  },
          {WSHUTDOWN_NOTIFY,"WSHUTDOWN"},
          {URGENT_NOTIFY   ,"URGENT"   },
          {OOB_NOTIFY      ,"OOB"      },
     };
#define NUMWHY (sizeof(nfywhy)/sizeof(nfywhy[0]))
#endif

ULONG lstselewb=0UL;               /* selewb history                       */
ULONG lstaccewb=0UL;               /* accewb history                       */

VOID EXPORT
init__icodg(VOID)
{
     globalcmd(icodglb);
}

VOID EXPORT
initwc__icodg(VOID)
{
     init__icodg();
}

static INT
icodglb(VOID)                      /* /ICO global command handler          */
{
#    ifdef PACSOFT
          static struct sel *testsel=NULL;
          u32 nswait;
          INT i,j,k,q;
          u16 outflags;
          u16 nfyflags;
          u16 mask;
          INT unum;
          VOID (*evthdl)();
          extern INT *sktunm;
          extern ULONG h_avail;
          extern ULONG h_size;
          extern ULONG h_mallocs;
          extern ULONG h_mfrees;
          extern ULONG h_high_watermark;
#         ifdef BPCHAIN_ON_HEAP
               extern f_heap();
#         endif
#    endif
#    ifdef IPSWITCH
          struct fd_set combin;
          static struct timeval immed;
          INT winsiz;
          INT typ,siztyp;
          INT i;
          LONG nready;
#    endif

     if (margc >= 1 && sameas(margv[0],"/ico") && haskey("SYSOP")) {
#         ifdef PACSOFT
               if (margc == 1) {
                    if (testsel == NULL) {
                         testsel=(struct sel *)alczer(NUMSOCKETS*sizeof(struct sel));
                         for (i=0 ; i < NUMSOCKETS ; i++) {
                              testsel[i].se_fd=i;
                              testsel[i].se_inflags=ALL_NOTIFY;
                         }
                    }
                    nswait=0L;
                    if (nselect(testsel,NUMSOCKETS,&nswait,(pfi_t)0,(u32)0,&tcpip_errno) < 0) {
                         prf("GALICODG:  NSELECT ERROR %d",tcpip_errno);
                         outprf(usrnum);
                    }
                    for (i=0 ; i < NUMSOCKETS ; i++) {
                         outflags=testsel[i].se_outflags;
                         unum=sktunm[i];
                         nfyflags=0;
                         for (k=0 ; k < numnfy ; k++) {
                              if (sktsel[k].se_fd == i) {
                                   nfyflags=sktsel[k].se_inflags&~BASE_NOTIFY;
                                   break;
                              }
                         }
                         if ((outflags != 0 && !(outflags&CLOSE_NOTIFY)) || nfyflags != 0) {
                              prf("Socket%3d: ",i);
                              if (unum != -1) {
                                   prf(" chan %02x",channel[unum]);
                              }
                              for (j=0 ; j < NUMWHY ; j++) {
                                   mask=nfywhy[j].mask;
                                   if (outflags&mask) {
                                        if (nfyflags&mask) {
                                             prf(" %s-READY",nfywhy[j].name);
                                        }
                                        else {
                                             prf("%s",strlwr(spr(" %s-ready",nfywhy[j].name)));
                                        }
                                   }
                                   else if (nfyflags&mask) {
                                        prf("%s",strlwr(spr(" %s-waiting",nfywhy[j].name)));
                                   }
                                   if ((nfyflags&mask) != 0) {
                                        for (q=0 ; q < TNFQUAN ; q++) {
                                             if (nfyflags&selflags[q]) {
                                                  evthdl=sktmapped(tnfidx[q],i).evthdl;
                                                  prf("(%04x:%04x)",FP_SEG(evthdl),FP_OFF(evthdl));
                                                  break;
                                             }
                                        }
                                   }
                              }
                              if (nfyflags == 0) {
                                   prf(" <IGNORED>");
                              }
                              prf("\r");
                              outprf(usrnum);
                         }
                    }
               }
               else if (sameto("H",margv[1])) {
                    if (margc == 2) {
                         prf("\nHeap Report:\n"
                             "%10s bytes in entire heap (100%%)\n"
                             "%10s bytes now in use (%d%%),\n"
                             "%10s bytes high-watermark (%d%%)\n"
                             "%10s blocks allocated\n\n",
                             commas(ul2as(h_size*sizeof(INT))),
                             commas(ul2as((h_size-h_avail)*sizeof(INT))),
                             (INT)(((h_size-h_avail)*100+h_size/2)/h_size),
                             commas(ul2as(h_high_watermark*sizeof(INT))),
                             (INT)((h_high_watermark*100+h_size/2)/h_size),
                             commas(ul2as((h_mallocs-h_mfrees))));
                         outprf(usrnum);
                    }
                    else {
#                        ifdef BPCHAIN_ON_HEAP
                              if (margc == 3 && sameas(margv[2],"FILE")) {
                                   f_heap();
                                   prf("\nHeap dump complete.  "
                                       "See REPORT.HD for details.\n");
                                   outprf(usrnum);
                              }
#                        endif
                    }
               }
               else if (sameto("TIM",margv[1])) {
                    shotimers();
                    outprf(usrnum);
               }
               else if (sameto("TCP",margv[1])) {
                    shotcp();
                    outprf(usrnum);
               }
               else if (sameto("IP",margv[1])) {
                    shoip();
                    outprf(usrnum);
               }
               else if (sameto("CA",margv[1])) {
                    shocallers();
                    outprf(usrnum);
               }
               else if (sameto("SO",margv[1])) {
                    shosock();
                    outprf(usrnum);
               }
               else if (sameto("DEBUGLOG",margv[1])) {
                    dbgtcp=!dbgtcp;
                    if (dbgtcp) {
                         prf("Enabling debug log %s\n",dbgtcpfn);
                    }
                    else {
                         prf("Disabling debug log %s\n",dbgtcpfn);
                    }
                    outprf(usrnum);
               }
               else if (sameto("PAC",margv[1])) {
                    if (margc == 3 && sameto("C",margv[2])) {
                         shopacc();
                    }
                    else {
                         shopack();
                    }
                    outprf(usrnum);
               }
               else if (sameto("ROU",margv[1])) {
                    shoroutes();
                    outprf(usrnum);
               }
               else if (sameto("SLIP",margv[1])) {
                    shoslipinf(margc == 3 && sameto("D",margv[2]));
                    outprf(usrnum);
               }
#         endif
#         ifdef IPSWITCH
               prf("Socket mask (recv): %s\n",spr("%08lX%08lX",sktfds[TNFRECV].fds_bits[1],sktfds[TNFRECV].fds_bits[0]));
               prf("Socket mask (send): %s\n",spr("%08lX%08lX",sktfds[TNFSEND].fds_bits[1],sktfds[TNFSEND].fds_bits[0]));
               combin.fds_bits[0]=sktfds[TNFRECV].fds_bits[0]|sktfds[TNFSEND].fds_bits[0];
               combin.fds_bits[1]=sktfds[TNFRECV].fds_bits[1]|sktfds[TNFSEND].fds_bits[1];
               for (i=0 ; i < FD_SETSIZE ; i++) {
                    siztyp=sizeof(typ);
                    if (sktunm[i] != -1
                     || getsockopt(i,SOL_SOCKET,SO_TYPE,(CHAR *)&typ,&siztyp) == 0) {
                         FD_SET(i,&combin);
                    }
               }
               movmem(sktfds,skttmp,sizeof(skttmp));
               immed.tv_sec=0L;
               immed.tv_usec=0L;
               select(FD_SETSIZE,&skttmp[TNFRECV],&skttmp[TNFSEND],NULL,&immed);
               for (fd_scanbeg(&combin) ; fd_scanmor() ; fd_scannxt()) {
                    prf("Socket%3d:",fdscskt);
                    if (sktunm[fdscskt] != -1) {
                         prf(" chan %02x",channel[sktunm[fdscskt]]);
                    }
                    if (FD_ISSET(fdscskt,&sktfds[TNFRECV])) {
                         prf(" receivable(%04x\\%04x)=%s",sktmapped(TNFRECV,fdscskt).evthdl,FD_ISSET(fdscskt,&skttmp[TNFRECV]) ? "YES" : "NO");
                    }
                    if (FD_ISSET(fdscskt,&sktfds[TNFSEND])) {
                         prf(" sendable(%04x\\%04x)=%s",sktmapped(TNFSEND,fdscskt).evthdl,FD_ISSET(fdscskt,&skttmp[TNFSEND]) ? "YES" : "NO");
                    }
                    if (s_ioctl(fdscskt,FIONREAD,(CHAR *)&nready) == 0 && nready > 0) {
                         prf(" incoming=%s",l2as(nready));
                    }
                    siztyp=sizeof(winsiz);
                    if (getsockopt(fdscskt,SOL_SOCKET,SO_RCVBUF,(CHAR *)&winsiz,&siztyp) == 0
                     && winsiz != rcvwin) {
                         prf(" rcvwin=%u",winsiz);
                    }
                    siztyp=sizeof(winsiz);
                    if (getsockopt(fdscskt,SOL_SOCKET,SO_SNDBUF,(CHAR *)&winsiz,&siztyp) == 0
                     && winsiz != sndwin) {
                         prf(" sndwin=%u",winsiz);
                    }
                    prf("\n");
                    outprf(usrnum);
               }
#         endif
          if (selewb != lstselewb) {
               prf("%s new EWOULDBLOCK errors reported by (n)select()\n",
                   ul2as(selewb-lstselewb));
               outprf(usrnum);
               lstselewb=selewb;
          }
          if (accewb != lstaccewb) {
               prf("%s new EWOULDBLOCK errors reported by accept()"
                   " in newcall()\n",ul2as(accewb-lstaccewb));
               outprf(usrnum);
               lstaccewb=accewb;
          }
          return(1);
     }
     return(0);
}

static VOID
shotimers(VOID)                    /* report on kernel's timers            */
{
     extern VOID (*dbg_printf_ptr)();
     VOID (*old_printf_ptr)();
     extern VOID t_pstate(VOID);
     use_critical;

     critical;
     old_printf_ptr=dbg_printf_ptr;
     dbg_printf_ptr=gal_printf;
     t_pstate();
     dbg_printf_ptr=old_printf_ptr;
     normal;
}

static VOID
shotcp(VOID)                       /* report on kernel's TCP sessions      */
{
     extern VOID (*dbg_printf_ptr)();
     VOID (*old_printf_ptr)();
     extern VOID tcp_pstate(VOID);
     use_critical;

     critical;
     old_printf_ptr=dbg_printf_ptr;
     dbg_printf_ptr=gal_printf;
     tcp_pstate();
     dbg_printf_ptr=old_printf_ptr;
     normal;
}

static VOID
shoip(VOID)                        /* report on kernel's IP situation      */
{
     extern VOID (*dbg_printf_ptr)();
     VOID (*old_printf_ptr)();
     extern VOID ip_pstate(VOID);
     use_critical;

     critical;
     old_printf_ptr=dbg_printf_ptr;
     dbg_printf_ptr=gal_printf;
     ip_pstate();
     dbg_printf_ptr=old_printf_ptr;
     normal;
}

static VOID
shocallers(VOID)                   /* report on socket's creators          */
{
     extern VOID (*dbg_printf_ptr)();
     VOID (*old_printf_ptr)();
     extern VOID so_pcallers(VOID);
     use_critical;

     critical;
     old_printf_ptr=dbg_printf_ptr;
     dbg_printf_ptr=gal_printf;
     so_pcallers();
     dbg_printf_ptr=old_printf_ptr;
     normal;
}

static VOID
shosock(VOID)                      /* report on kernel's sockets           */
{
     extern VOID (*dbg_printf_ptr)();
     VOID (*old_printf_ptr)();
     extern VOID so_pstate(VOID);
     use_critical;

     critical;
     old_printf_ptr=dbg_printf_ptr;
     dbg_printf_ptr=gal_printf;
     so_pstate();
     dbg_printf_ptr=old_printf_ptr;
     normal;
}

static VOID
shoroutes(VOID)                    /* report on kernel's routing table     */
{
     extern VOID (*dbg_printf_ptr)();
     VOID (*old_printf_ptr)();
     extern VOID fr_pstate(VOID);
     use_critical;

     critical;
     old_printf_ptr=dbg_printf_ptr;
     dbg_printf_ptr=gal_printf;
     fr_pstate();
     dbg_printf_ptr=old_printf_ptr;
     normal;
}

static VOID
shoslipinf(                        /* report on user slip handling         */
INT detail)                        /*   1=expanded detail 0=normal         */
{
     INT unum;
     struct in_addr userip;
     u32 errn;
     INT nopen;
     INT nclosed;
     INT nudp;
     INT nunused;
     INT idx;
     cman_proxport *pxp;
     extern u32 pacseconds;
     LONG cntovr;

     prf("\r");
     for (unum=0 ; unum < nterms ; unum++) {
          if (inscp(unum)) {
               prf("CH%02x %s /%s %s bytes",
                   channel[unum],
                   scpusr(unum)->realuid,
                   nametbl[scpinf[unum].idx],
                   commas(ul2as(scpusr(unum)->nbytes)));
               if (scpinf[unum].flags&SCPDYN) {
                    userip.s_addr=ip_of_chan[unum];
                    prf(" %s dynamic",inet_ntoa(userip));
               }
               else if (scpinf[unum].flags&SCPPRX) {
                    userip.s_addr=cman_tbp[unum].proxip;
                    prf(" [%s] proxy",inet_ntoa(userip));
                    prf(", magic ports:");
                    nudp=nopen=nclosed=nunused=0;
                    for (pxp=&proxport[unum*maxpxp],idx=0 ; idx < maxpxp ; idx++,pxp++) {
                         if (pxp->flags&PXPUDP) {
                              nudp++;
                              if (detail) {
                                   prf("\r     UDP (%u) <-> %u, age=%s",
                                       pxp->fakeport,pxp->realport,ul2as(pacseconds-pxp->lasttime));
                              }
                         }
                         else if (pxp->flags&(PXPUOPN+PXPROPN)) {
                              if (detail) {
                                   prf("\r     TCP (%u) <-> %u, age=%s",
                                       pxp->fakeport,pxp->realport,ul2as(pacseconds-pxp->lasttime));
                                   if (!(pxp->flags&PXPUOPN)) {
                                        prf(", (awaiting remote close)");
                                   }
                                   if (!(pxp->flags&PXPROPN)) {
                                        prf(", (awaiting user close)");
                                   }
                              }
                              nopen++;
                         }
                         else if (pxp->realport != 0) {
                              if (detail) {
                                   prf("\r     TCP closed (%u) <-> %u, age=%s",
                                       pxp->fakeport,pxp->realport,ul2as(pacseconds-pxp->lasttime));
                              }
                              nclosed++;
                         }
                         else {
                              nunused++;
                         }
                    }
                    if (detail) {
                         prf("\r    ");
                    }
                    if (nopen > 0) {
                         prf(" %d opn",nopen);
                    }
                    if (nclosed > 0) {
                         prf(" %d cls",nclosed);
                    }
                    if (nudp > 0) {
                         prf(" %d udp",nudp);
                    }
                    if (nunused > 0) {
                         prf(" %d unu",nunused);
                    }
                    if ((errn=cman_tbp[unum].errspur) != 0UL) {
                         prf(", %s SPURIOUS",ul2as(errn));
                    }
                    if ((errn=cman_tbp[unum].errtoom) != 0UL) {
                         prf(", %s TOOMANY",ul2as(errn));
                    }
                    if ((errn=cman_tbp[unum].errprot) != 0UL) {
                         prf(", %s UNKPROTS",ul2as(errn));
                    }
                    if ((errn=cman_tbp[unum].errfrag) != 0UL) {
                         prf(", %s FRAGMENT",ul2as(errn));
                    }
               }
               else {
                    userip.s_addr=ip_of_chan[unum];
                    prf(" %s assigned",inet_ntoa(userip));
               }
               if ((cntovr=bturep(unum,CNTOVR)) != 0L) {
                    if (usroff(unum)->flags&NON550) {
                         prf(" %s 16450 UART overflows",l2as(cntovr));
                    }
                    else {
                         prf(" %s 16550 FIFO overflows",l2as(cntovr));
                    }
               }
               prf("\r");
          }
     }
}

static VOID
gal_printf(
CHAR *msg,
LONG p1,
LONG p2,
LONG p3,
LONG p4,
LONG p5,
LONG p6)
{
     extern VOID pac_sprintf();
     INT nprf,nmsg;

     nprf=strlen(prfbuf);
     pac_sprintf(vdatmp,msg,p1,p2,p3,p4,p5,p6);
     nmsg=strlen(vdatmp);
     if (nmsg > vdasiz) {
          catastro("GALICODG vdatmp overflow by %d bytes!",nmsg-vdasiz);
     }
     if (nprf+nmsg+1 < outbsz-1) {
          prf("%s",vdatmp);
     }
     else if (nprf+5+1 < outbsz-1) {
          prf("etc.\r");
     }
}

/*--- packet driver statistics ---*/

INT (*old_pktd_incoming)(CHAR *,UINT);

#define MAXSIZ 1514

ULONG *etbl;
UINT lstovf;

INT
diag_pktd_incoming(
CHAR *buffer,
UINT nbytes)
{
     dsairp();
     if (nbytes > MAXSIZ) {
          etbl[MAXSIZ+1]++;
          lstovf=nbytes;
     }
     else {
          etbl[nbytes]++;
     }
     enairp();
     return((*old_pktd_incoming)(buffer,nbytes));
}

static VOID
shopacc(VOID)
{
     static GBOOL inited=FALSE;
     INT i;
     ULONG pax,totpax,totbyt;

     if (!inited) {
          etbl=(ULONG *)alczer((MAXSIZ+2)*sizeof(ULONG));
          dsairp();
          old_pktd_incoming=pktd_incoming;
          pktd_incoming=diag_pktd_incoming;
          enairp();
          prf("Packet statistics initialized.\r");
          inited=TRUE;
          return;
     }
     totpax=totbyt=0UL;
     prf("Size Packets\r---- -------\r");
     for (i=0 ; i <= MAXSIZ+1 ; i++) {
          dsairp();
          pax=etbl[i];
          enairp();
          if (pax > 0L) {
               prf("%4d %7s\r",i,commas(ul2as(pax)));
               totpax+=pax;
               totbyt+=pax*i;
          }
     }
     if (pax > 0L) {
          prf("             (last invalid size was %u)\r",lstovf);
     }
     prf("     -------\r%12s total packets\r",commas(ul2as(totpax)));
     prf("%12s total bytes\r",commas(ul2as(totbyt)));
     if (totpax > 0UL) {
          prf("%12s average packet size\r",ul2as((totbyt+(totpax>>1))/totpax));
     }
}

VOID
shopack(VOID)
{
     INT sl;

     prf("\r");
     for (sl=0 ; sl < numslots ; sl++) {
          prf("%u-byte slot, %s packets, %u of %u pulled",
              slotinfo[sl].sizpkt,
              commas(ul2as(slotinfo[sl].nused)),
              slotinfo[sl].nwant-slotinfo[sl].namin,
              slotinfo[sl].nwant);
          if (slotinfo[sl].nskip > 0UL) {
               prf(", %s SKIPPED",commas(ul2as(slotinfo[sl].nskip)));
          }
          prf("\r");
     }
     if (pkt_dropheap > 0UL) {
          prf("%s PACKETS DROPPED\r",commas(ul2as(pkt_dropheap)));
     }
     prf("Packet receiver:  %s calls, %s queued, %s dropped.\n",
         commas(ul2as(pkt_rcalls)),
         commas(ul2as(pkt_rque)),
         commas(ul2as(pkt_rdrop)));
}

