/* * n e t u t l - netlnk */ /*)LIBRARY */ #ifdef DOCUMENTATION Title netlnk network link functions for Decus C Index netlnk network link functions for Decus C synopsis .s.nf _#include .s NIOV *opn_net(nfp) int (*nfp)(); .s clsnet() .s NIOV *con_net(dest_node, dest_task) char *dest_node, *dest_task; .s NIOV *con_obj(dest_node, dest_obj) char *dest_node; int dest_obj; .s NIOV *acc_net(con_buf_ptr) struct a_conblk *con_buf_ptr; .s dsc_net(np, msg) NIOV *np; char *msg; .s.f Description This group of functions allows openning a Decnet network, reading network data, and establishing and maintaining links. .s Opn_net() connects a task to the network. It must be issued before any other network calls. See the DECnet documentation on OPN$. .s Opn_net() takes one argument, the address of a function to handle network messages. If the argument is NULL, network messages will still be dequeued from the netword data queue, but will be discarded. .s If specified, the network message function will be called as follows: .s.nf netmsg(iosb, msg) int iosb[2]; /* stat block from GND$ call */ char *msg; /* message from GND$ */ .s.f You should be familiar with how GND$ returns data before using this function. The msg pointer points to a buffer large enough to accept a full connect request message and can be passed directly to the acc_net() function. .s Your netmsg() function is called at ast state, and the buffer pointed to by msg is allocated dynamically. It is freed when your function returns. If you need to save any data from the message buffer, copy it to your own buffer before returning. .s Cls_net() closes the network data queue and returns the NIOV data structure and lun allocated for the link. Closing the network link while any data links are still up will cause unpredictable problems. .s Con_net() will attempt to connect the the remote node and task specified in the arguments. If it succeeds, it returns an NIOV pointer. If the connect fails, NULL is returned and the error condition is returned in $$nerr. You should declare $$nerr as: .s.nf extern int $$nerr; .s.f if you wish to use it. The pointer returned by con_net() is required for network IO functions such as put_net() and get_net(). .s Con_obj() is similar to con_net(), but is used to connect to remote number network objects. .s Acc_net() will attempt to accept a connect request from another node. It is normally called with a connect buffer retrieved from the task's network data queue. .s Acc_net() returns an NIOV pointer if successful, and NULL on error. The error condition is availible from $$nerr. .s Dsc_net() disconnects a logical link. The NIOV pointer must point to an open logical link, if not, unpredicatable errors will result. .s You may pass a message to the other end of the link when disconnecting by passing a pointer to the message in msg. Set the msg argument to 0 if no message is needed. .s Dsc_net() frees the NIOV structure and lun used for the link. The memory area pointed to by np is returned to free memory, don't try to reuse np. Implementation Details These routines try to mimic the Decus C interface to the file system. NIOV is described in stdio.h. .s NIOV data structures are allocated with malloc and freed as necessary. .s Be sure you understand how the user supplied network message function is called. The current implimentation is an attempt to provide the greatest flexibility while still hiding as much of the gory network detail as possible. .s The internal function getmsg() will check for the NT.DSC, NT.ABT, and NT.ABO and free the appropriate NIOV structures and luns AFTER calling the user supplied message function. Bugs The routines try to do some error checking, but if a bad NIOV pointer is passed to a function, many strange things could happen, not the least of which is clobbering random areas of memory. Be careful. Edit History 01 05/08/85 hjj fixed bad check for NDA in gndw. #endif #include #include #include extern int $$nlun; /* number of luns available (C RTL) */ int $$nerr; /* define $$nerr here. We assume any code */ /* using $$nerr will call one of these routines */ NIOV *$$$ndq; /* pointer to network que niov */ static int (*netmsg)(); /* pointer to network queue msg handler */ /* functions */ NIOV *opn_net(nfp) int (*nfp)(); /* pointer to msg handler */ { NIOV *np_alo(); /* returns a pointer to a new NIOV */ extern word cmpast(); extern word netast(); if (($$$ndq = np_alo()) == NULL) return(NULL); $$nerr = opnwnt($$$ndq->lun, $$$ndq->efn, $$$ndq->iosb, 0, 0, 0); dirtst($$nerr, $$$ndq->iosb, "opnw failed"); if (nfp) netmsg = nfp; else netmsg = NULL; $$nerr = spawnt($$$ndq->lun, $$$ndq->efn, $$$ndq->iosb, &cmpast, &netast); dirtst($$nerr, $$$ndq->iosb, "spaw failed"); return($$$ndq); } #ifdef LUNDEBUG pniov(msg, np) NIOV *np; { printf("%s", msg); printf("lun %d efn %d rem_nam |%.8s| rem_tsk |%.8s| type %d\n", np->lun, np->efn, np->rem_nam, np->rem_task, np->type); printf("status %o iosb %o %o\n", np->status, np->iosb[0], np->iosb[1]); printf("$$luns[0-7] %o, %o, %o, %o, %o, %o, %o, %o\n", $$luns[0], $$luns[1], $$luns[2], $$luns[3], $$luns[4], $$luns[5], $$luns[6], $$luns[7]); printf("$$luns[8-15] %o, %o, %o, %o, %o, %o, %o, %o\n", $$luns[8], $$luns[9], $$luns[10], $$luns[11], $$luns[12], $$luns[13], $$luns[14], $$luns[15]); } #endif cls_net() { dsar(); $$nerr = clswnt($$$ndq->lun, $$$ndq->efn, $$$ndq->iosb, 0); dirtst($$nerr, $$$ndq->iosb, "clsw failed"); np_fre($$$ndq); /* free up NIOV and lun */ enar(); return(1); } NIOV *con_net(dest_node, dest_task) char *dest_node, *dest_task; { NIOV *np; struct c_conblk *conp; if ((np = np_alo()) == NULL) return(NULL); if ((conp = malloc(sizeof (struct c_conblk))) == NULL) { $$nerr = IE_NBF; /* no room left */ return(NULL); } conb1(conp, dest_node, dest_task); /* fill con req block */ $$nerr = conwnt(np->lun, np->efn, np->iosb, 0, conp, (sizeof (struct c_conblk)), 0, 0, 0, 0); if ($$nerr < 0) { np_fre(np); /* free up NIOV and lun */ np = NULL; } else if (($$nerr = np->iosb[0]) != 1) { /* check status */ np_fre(np); /* free up NIOV and lun */ np = NULL; } if (np) { /* fill in niov data if connect succeeded */ np->type = conp->n_rot; /* copy type */ if (np->type == 0) { /* if remote is a task, not object */ copy(np->rem_nam, conp->n_rnd, 6); /* copy remote node */ copy(np->rem_task, conp->n_rde, ((conp->n_rdec <= 16)?conp->n_rdec:16)); /* copy remote task name */ } np->status |= LINKUP; } mfree(conp); /* give back con block */ return(np); } NIOV *con_obj(dest_node, dest_obj) char *dest_node; int dest_obj; { NIOV *np; struct c_conblk *conp; if ((np = np_alo()) == NULL) return(NULL); if ((conp = malloc(sizeof (struct c_conblk))) == NULL) { $$nerr = IE_NBF; /* no room left */ return(NULL); } conb0(conp, dest_node, dest_obj); /* fill con req block */ $$nerr = conwnt(np->lun, np->efn, np->iosb, 0, conp, (sizeof (struct c_conblk)), 0, 0, 0, 0); if ($$nerr < 0) { np_fre(np); /* free up NIOV and lun */ np = NULL; } else if (($$nerr = np->iosb[0]) != 1) { /* check status */ np_fre(np); /* free up NIOV and lun */ np = NULL; } if (np) { /* fill in niov data if connect succeeded */ np->type = conp->n_rot; /* copy type */ if (np->type == 0) { /* if remote is a task, not object */ copy(np->rem_nam, conp->n_rnd, 6); /* copy remoet node */ copy(np->rem_task, conp->n_rde, ((conp->n_rdec <= 16)?conp->n_rdec:16)); /* copy remote task name */ } np->status |= LINKUP; } mfree(conp); /* give back con block */ return(np); } NIOV *acc_net(cp) struct a_conblk *cp; { struct a_conblk *acp; NIOV *np; int aloc_flag; aloc_flag = 0; /* no memory allocated yet */ if (cp) acp = cp; else { if ((acp = malloc(sizeof (struct a_conblk))) == NULL) { $$nerr = IE_NBF; /* no room left */ return(NULL); } aloc_flag = 1; /* mark memory allocated */ } if ((np = np_alo()) == NULL) { if (aloc_flag) mfree(acp); return(NULL); } $$nerr = accwnt(np->lun, np->efn, np->iosb, 0, acp, (sizeof (struct a_conblk)), 0, 0); if ($$nerr < 0) { np_fre(np); /* free up NIOV and lun */ np = NULL; } else if (($$nerr = np->iosb[0]) != 1) { /* check status */ np_fre(np); /* free up NIOV and lun */ np = NULL; } if (np) { /* if no error */ np->type = acp->n_sot; /* copy type */ if (np->type == 0) { /* if remote is a task, not object */ copy(np->rem_nam, acp->n_snd, 6); /* copy remote node */ copy(np->rem_task, acp->n_sde, ((acp->n_sdec <= 16)?acp->n_sdec:16)); /* copy remote task name */ } np->status |= LINKUP; } if (aloc_flag) mfree(acp); return(np); } dsc_net(np, msg) NIOV *np; char *msg; { if (np == 0) return(0); $$nerr = dscwnt(np->lun, np->efn, np->iosb, 0, msg, ((strlen(msg) <= 16)?strlen(msg):16)); if ($$nerr == 1) $$nerr = np->iosb[0]; /* copy completion status */ np_fre(np); /* free up NIOV and lun */ np = NULL; return(($$nerr == 1)?1:0); } /************************************************************** internal functions from here on **************************************************************/ static NIOV *np_alo() /* allocate an NIOV with a free lun */ { NIOV *np; unsigned *lp, *a_lun(); if ((np = malloc(sizeof NIOV)) == NULL) { /* get an NIOV */ $$nerr = IE_NBF; /* no room left */ return(NULL); } zero(np, sizeof NIOV); if ((lp = a_lun(&np->lun)) == NULL) { /* get a free lun */ $$nerr = IE_ILU; /* no luns left */ return(NULL); } *lp = np; /* insert the NIOV in the lun table */ np->efn = np->lun; /* lun and efn are the same */ dirtst(alunx(np->lun, "NS", 0), 0, "link alun failed"); return(np); } static np_fre(np) NIOV *np; { $$luns[np->lun-2] = 0; /* zero out lun slot */ mfree(np); } static unsigned *a_lun(lnum) int *lnum; { unsigned *lp; /* lun address pointer */ int lcnt; lp = $$luns; for (lcnt = 0; lcnt < $$nlun; lcnt++, lp++) if (*lp == NULL) { *lnum = lcnt + 2; /* free luns start at lun 2 */ return(lp); } return(NULL); } static int conb1(conp, node, task) /* fmt 1 connect block */ struct c_conblk *conp; char *node, *task; { int i, len; zero(conp, (sizeof (struct c_conblk))); fill(conp->n_rnd, 040, 6); /* put spaces in node name */ fill(conp->n_rde, 040, 16); /* and destination task */ len = strlen(node); copy(conp->n_rnd, node, (len > 6 ? 6 : len)); for (i = 0; i < 6; i++) conp->n_rnd[i] = toupper(conp->n_rnd[i]); conp->n_rdec = ((len = strlen(task)) > 16) ? 16 : len; copy(conp->n_rde, task, conp->n_rdec); for (i = 0; i < 16; i++) conp->n_rde[i] = toupper(conp->n_rde[i]); conp->n_rfm = 1; /* type 1 descriptor */ conp->n_rot = 0; /* object type 0 */ return(1); } static int conb0(conp, node, obj) /* fmt 0 connect block */ struct c_conblk *conp; char *node; int obj; { int i, len; zero(conp, (sizeof (struct c_conblk))); fill(conp->n_rnd, 040, 6); /* put spaces in node name */ len = strlen(node); copy(conp->n_rnd, node, (len > 6 ? 6 : len)); for (i = 0; i < 6; i++) conp->n_rnd[i] = toupper(conp->n_rnd[i]); conp->n_rfm = 0; /* type 0 descriptor */ conp->n_rot = obj; /* fill in object type */ return(1); } static word cmpast() { byte *piosb; /* pointer to iosb */ astset(); piosb = gtdp(0); /* point to iosb */ dirtst(1, piosb, "spaw failed"); piosb += 2; /* move 1 word */ if (*piosb > 0) get_msg(*piosb); astx(1); } static word netast() { astset(); get_msg(1); astx(0); } static int get_msg(msgcnt) int msgcnt; { byte *bp; struct a_conblk *acp; if ((acp = malloc(sizeof (struct a_conblk))) == NULL) { $$nerr = IE_NBF; /* no room left */ return(NULL); } bp = $$$ndq->iosb; while (msgcnt-- > 0) { $$nerr = gndwnt($$$ndq->lun, $$$ndq->efn, $$$ndq->iosb, 0, acp, (sizeof (struct a_conblk))); if (($$nerr == 1) && ($$$ndq->iosb[0] == (IE_NDA & 0377))) return(1); /* no data waiting */ dirtst($$nerr, $$$ndq->iosb, "gndw failed"); switch (*(bp+1)) { case NT_DSC: case NT_ABT: case NT_ABO: { if (netmsg != 0) (*netmsg)($$$ndq->iosb, acp); nl_fre(*(bp+3)); break; } default: { if (netmsg != 0) (*netmsg)($$$ndq->iosb, acp); } } } mfree(acp); return(1); } static nl_fre(lun) /* free NIOV and lun given lun */ int lun; { NIOV *np; np = $$luns[lun-2]; /* get possible np */ if (np->lun == lun) np_fre(np); /* free if ok */ else fatal("attempt to free noexistant lun in netlnk getmsg", 0, 0); } static int dirtst(dstat, pio, msg) int dstat; char *pio, *msg; { if ((dstat >= 0) && ((pio == 0) || (*pio == 1))) return(0); fatal(msg, dstat, pio); } static int fatal(msg, dsw, fiosb) char *msg; word dsw, fiosb[2]; { tmsg('F', "%s: \ndsw:%o, fiosb:%o:%o\n", msg, dsw, fiosb[0], fiosb[1]); exit(4); }