DOCUMENT:Q214572 21-AUG-2001 [sna] TITLE :How To Retrieve Link Information For SNA Server Programmatically PRODUCT :Microsoft SNA Server PROD/VER::3.0,4.0 OPER/SYS: KEYWORDS:kbfile kbProgramming kbSamplekbfaq ====================================================================== ------------------------------------------------------------------------------- The information in this article applies to: - Microsoft SNA Server, versions 3.0, 4.0 ------------------------------------------------------------------------------- SUMMARY ======= SNA Server currently does not expose any APIs directly to gather link status information. However, the APPC DISPLAY management verb does allow some information to be gathered on the status of links that are not in an inactive or "On Demand" state. The following code is a console application that will display the status of these links in an SNA domain. It can be easily adapted to monitor to see if a link is "missing" in the list, which would indicate that the link has gone inactive, or to monitor the other status indicators (for example, pending). MORE INFORMATION ================ The following header file contains all the includes, macros, and functions used by the test program. With SNA Server 4.0 and it's multinode support, up to 64 nodes are supported in 1 SNA domain. In your program, you may want to reduce the MAXNODES value to the number of nodes/Sna Server computers you have in your domain. Also, the DISPLAY management verb supports a maximum buffer size of 64 KB. You may also want to reduce this value in your program. Do not attempt to exceed this value as unpredictable results may occur. In addition, be sure to link with the correct library files (Wincsv32.lib and Wappc32.lib). // linktest.h #if !defined(LINKTEST_H_INCLUDED_) #define LINKTEST_H_INCLUDED_ #include "wincsv.h" #include "winappc.h" #include "winmgt.h" #define CLEARDISPVCB memset(dispvcbptr,0,sizeof(dispvcb)) #define ZERODATABUFFER ZeroMemory( &DataBuffer, sizeof(DataBuffer) ) #define SWAPWORD(x) ((unsigned short)MAKEWORD(HIBYTE(x),LOBYTE(x))) #define SWAPDWORD(x) ((unsigned long)MAKELONG(SWAPWORD(HIWORD(x)),SWAPWORD(LOWORD(x)))) #define MAXNODES 64 #define BUFFERSIZE 65535 #define WinAPPCVERSION 0x0001 typedef union dispvcb_u { struct appc_hdr hdr; struct display display; } dispvcb_t; typedef struct dbstruct { unsigned char dbuf[BUFFERSIZE]; } dbufstruct; typedef struct namestructag { char boxname[MAXNODES][32]; char nodename[MAXNODES][8]; char shortname[MAXNODES][18]; int nodenumber; } namestruct; #endif //LINKTEST_H_INCLUDED_ What follows is the main code segment for the test program. Each function is documented as to what it is doing. //SNAnls.cpp #include #include #include "linktest.h" /*****************************************************************************/ /* ConvertEToA - Converts EBCDIC string to ASCII using Convert verb */ /*****************************************************************************/ void ConvertEToA(IN OUT UCHAR *string, int length) { convert cnvt; memset((char*)&cnvt,0,sizeof(cnvt)); cnvt.opcode = SV_CONVERT; cnvt.direction = SV_EBCDIC_TO_ASCII; cnvt.char_set = SV_A; cnvt.len = length; cnvt.source = string; cnvt.target = string; ACSSVC_C((long)(char *) &cnvt); } /*****************************************************************************/ /* EBC_to_ASCII - Converts EBCDIC string to ASCII using Convert verb */ /*****************************************************************************/ void EBC_to_ASCII(char *dest, unsigned char *src) { unsigned char temp[8]; FillMemory( temp, sizeof(temp), 0); memcpy( temp, src, 8); // convert from EBCDIC A to ASCII ConvertEToA(temp,8); ZeroMemory(dest,sizeof(dest) ); strncpy( dest, (char*)temp, 8); strncpy( dest + 8, "\0", 1); return; } /*****************************************************************************/ /* show_usage - displays command line usage /*****************************************************************************/ void show_usage() { printf("\nLinkTest\n"); printf("This application is a simple commmand line application\n"); printf("that returns the status of all link services in a SNA\n"); printf("Domain that are currently active or pending.\n"); printf("\nNOTE: The Display Management verbs do NOT return status\n"); printf("on link services that are marked as unavailable or ON DEMAND\n"); printf("\n\n"); return; } /*****************************************************************************/ /* DisplayVerbFailed - Prints to console Primary and Secondary Return Codes */ /* if AP_OK is not returned. If the primary RC = COMM_SUBSYSTEM_NOT_LOADED */ /* (F004) this will also print a message that the SNA Server service may not */ /* be running */ /*****************************************************************************/ void DisplayVerbFailed(unsigned short prim_rc, unsigned long sec_rc) { printf("\nDisplay Verb Primary RC = %X\n",SWAPWORD(prim_rc) ); printf("Display Verb Secondary RC = %X\n",SWAPDWORD(sec_rc)); // if actual return code is 1264 // - nt pickes this up as 4F0 - AP_COMM_SUBSYSTEM_NOT_LOADED (F004) // lets display an message that the snaservr service may not be running // this is usually the cause that I've seen within the application if (prim_rc == 1264) { printf("\n*** The SNA Server Service may not be running.\n"); printf(" Plese check all services on the SNA Server\n\n"); } } /*****************************************************************************/ /* DSPAppcError - This uses the GetAppcReturnCode API to display a text */ /* that can help in troublshooting the failure. This also checks to see if */ /* the buffersize is too small to return the information requested */ /*****************************************************************************/ void DSPAppcError(dispvcb_t *dispvcb) { char errorbuf[1024]; GetAppcReturnCode((struct appc_hdr *) &(dispvcb->hdr), 1024, (unsigned char *)errorbuf); printf("\n%s\n",errorbuf); // if actual return code is 1b4 (sna picks up as B4010000) before // converting it then let's trace the error and other parameters. if (dispvcb->display.secondary_rc == 3019964416) { printf("\nBUFFER IS TO SMALL\n"); printf("Area Needed is %lu bytes\n",dispvcb->display.area_needed); printf("Area Returned is %lu bytes\n",dispvcb->display.display_len); printf("Area Available is %i bytes\n",BUFFERSIZE); } } /*****************************************************************************/ /* SetLength - removes terminating spaces and trash from string variable */ /* and ensures there is a terminating null */ /*****************************************************************************/ void SetLength(char *string, int length) { char *p = NULL; if ((p=(char *)memchr(string,' ',length)) != NULL) { *p='\0'; } else { strncpy( string + length, "\0", 1); } } /*****************************************************************************/ /* SetupString - copy's string to dest and set's length with null terminator */ /*****************************************************************************/ void SetupString(char *dest, char *src, int length) { ZeroMemory(dest,sizeof(dest) ); strncpy(dest, src, length); SetLength(dest, length); } /*****************************************************************************/ /* GetActiveServers - calls the Display management verb to retrieve a list */ /* of active servers in the SNA Domain. */ /*****************************************************************************/ void GetActiveServers(dispvcb_t *dispvcb, dbufstruct *DataBuffer) { dispvcb->display.opcode = AP_DISPLAY; dispvcb->display.init_sect_len = (unsigned long)&(((DISPLAY *)0)->sna_global_info_ptr); dispvcb->display.num_sections = 0x10; dispvcb->display.buffer_len = sizeof(DataBuffer->dbuf); dispvcb->display.buffer_ptr = DataBuffer->dbuf; dispvcb->display.lu_def_info = AP_NO; strcpy( (char *)DataBuffer->dbuf, "CSEXTNIDCSLISTND" ); APPC( (long)dispvcb ); } /*****************************************************************************/ /* GetNodes - fills in gNames with a list of available servers/nodes in the */ /* SNA Domain . */ /*****************************************************************************/ bool GetNodes(namestruct *Names) { char buffer1[40], buffer2[40]; int i, pos; char ch[1]; dispvcb_t dispvcb; char *dispvcbptr; dbufstruct DataBuffer; dispvcbptr=(char *)&dispvcb; CLEARDISPVCB; ZERODATABUFFER; GetActiveServers(&dispvcb, &DataBuffer); if( dispvcb.display.primary_rc != AP_OK ) { // call failed, display message DisplayVerbFailed(dispvcb.display.primary_rc, dispvcb.display.secondary_rc); DSPAppcError(&dispvcb); return false; } else { unsigned short *pw = (unsigned short *)DataBuffer.dbuf; char *lpData; if( (*pw > 0) && (*pw < MAXNODES+1)) { lpData = (char *)&DataBuffer.dbuf[2]; for( i = 0; i < *pw; i++ ) { memcpy(&Names->nodename[i][0],(lpData + i*40), 8); memcpy(&Names->boxname[i][0],(lpData + i*40 + 8),32); // Now we have to create the shortname for the boxname // This will convert "SERVNAME 02" // to "SERVNAME.02" ZeroMemory(buffer1,sizeof(buffer1) ); ZeroMemory(buffer2,sizeof(buffer2) ); memcpy( (char*)buffer1, &Names->boxname[i][0],32); pos = strcspn( buffer1, " " ); strncpy( buffer2, buffer1, pos ); ch[0] = buffer1[16]; if ( ch[0] == 48) // if this is a ZERO ("0") { strncpy(buffer2 + pos, ".", 1); pos++; strncpy( buffer2 + pos, ch, 1); pos++; ch[0] = buffer1[17]; strncpy( buffer2 + pos, ch, 1); } SetupString( (char*)buffer1, buffer2,18); memcpy(&Names->shortname[i][0], buffer1, 18); } } else { if (*pw < 1) { printf("\nUnable to locate any Active Servers!\n"); return false; } else { // SNA only support 64 nodes printf("\nMore than 64 nodes found\n"); return false; } } Names->nodenumber = *pw; } return true; } /*****************************************************************************/ /* GetDefInfo - calls the Display Management verb to get the link_info for a */ /* particular server/node in the SNA Domain. */ /*****************************************************************************/ void GetDefInfo(dispvcb_t *dispvcb, dbufstruct *DataBuffer, char boxname[]) { dispvcb->display.opcode = AP_DISPLAY; dispvcb->display.init_sect_len = (unsigned long)&(((DISPLAY *)0)->sna_global_info_ptr); dispvcb->display.num_sections = 0x10; dispvcb->display.buffer_len = sizeof(DataBuffer->dbuf); dispvcb->display.buffer_ptr = DataBuffer->dbuf; strcpy( (char*)DataBuffer->dbuf, "CSEXTNID" ); memcpy( DataBuffer->dbuf + 8, boxname,32 ); dispvcb->display.link_info = AP_YES; APPC( (long)dispvcb ); } /*****************************************************************************/ /* EnumerateLinkInfo - This function goes through the link_info_section and */ /* prints out the information returned for each link service (connection) */ /* found. */ /*****************************************************************************/ void EnumerateLinkInfo(dispvcb_t *dispvcb) { LINK_INFO_SECT *link_info; LINK_OVERLAY *link_overlay; unsigned short i; char buffer1[20]; link_info = dispvcb->display.link_info_ptr; link_overlay = (LINK_OVERLAY *)((char *)link_info + link_info->link_init_sect_len); if (link_info->num_links == 0) { printf("No Active Links found.\n"); } else { for (i = 0; i < link_info->num_links; i++) { EBC_to_ASCII( (char*)buffer1, link_overlay->link_id); printf("\tConnection Name: %s\n",buffer1); SetupString( (char*)buffer1, (char*)link_overlay->dlc_name,8); printf("\t\tDLC Name: %s",buffer1); if (link_overlay->inbound_outbound == AP_OUTBOUND) { printf(" ,Outbound\n"); } else { printf(" ,Inbound\n"); } switch (link_overlay->state) { case AP_CONALS_PND: printf("\t\tLink state: AP_CONALS_PND\n"); break; case AP_XID_PND: printf("\t\tLink state: AP_XID_PND\n"); break; case AP_CONTACT_PND: printf("\t\tLink state: AP_CONTACT_PND\n"); break; case AP_CONTACTED: printf("\t\tLink state: AP_CONTACTED\n"); break; case AP_DISC_PND: printf("\t\tLink state: AP_DISC_PND\n"); break; case AP_DISC_RQ: printf("\t\tLink state: AP_DISC_RQ\n"); break; } printf("\t\tActive Sessions: %i\n",link_overlay->num_sessions); link_overlay=(LINK_OVERLAY *)((char *)link_overlay + link_overlay->link_entry_len); } // for } //end else printf("\n");; } /*****************************************************************************/ /* DoLinkInfo - This function does most of the work. It loops through each */ /* node, calling GetDefInfo and EnumerateLinkInfo to display the status of */ /* all available links in the domain. */ /*****************************************************************************/ bool DoLinkInfo(namestruct *Names) { int loop; dbufstruct DataBuffer; dispvcb_t dispvcb; char *dispvcbptr; dispvcbptr=(char *)&dispvcb; // clear buffer and call display verb for link_info // for each boxname found in Names for (loop = 0; loop < Names->nodenumber; loop++) { CLEARDISPVCB; ZERODATABUFFER; GetDefInfo(&dispvcb, &DataBuffer, Names->boxname[loop]); //link_info if( dispvcb.display.primary_rc != AP_OK ) { DisplayVerbFailed(dispvcb.display.primary_rc, dispvcb.display.secondary_rc); DSPAppcError(&dispvcb); return false; } else { printf("\nBoxName:Server: %.8s:%s\n",Names->nodename[loop],Names->shortname[loop]); EnumerateLinkInfo(&dispvcb); } //end else } //end for loop return true; } /*****************************************************************************/ /* main - main procedure for program */ /*****************************************************************************/ void main( int argc, char *argv[]) { namestruct gNames; int iRet; WAPPCDATA AppcInfo; if (argc == 2 && ( stricmp(_strupr(argv[1]),"/H") || stricmp(_strupr(argv[1]),"-H")) ) { show_usage(); return; } if( (iRet = WinAPPCStartup(WinAPPCVERSION, &AppcInfo)) != 0 ) { printf( "\nWinAPPCStartup Failed, error %d\n", iRet ); return; } else if ( GetNodes(&gNames) ) // did it return true? { // we have now populated the gNames structure // so we can now get the rest of what we need if (DoLinkInfo(&gNames)) { printf("Completed\n"); } else { printf("DoLinkInfo failed\n"); } } WinAPPCCleanup(); } Example output is shown below for a single server that has three nodes configured. The first node has three connections, the second and third nodes have one connection each. Notice that only two connections are shown for the first node here. This is because one of the connections was set up for "On Demand", and was not in an active state. BoxName:Server: SNASERVR:CHARLIEE Connection Name: PRT DLC Name: SDLC ,Outbound Link state: AP_CONTACTED Active Sessions: 2 Connection Name: CONNECT1 DLC Name: IBMTRNET ,Outbound Link state: AP_CONTACT_PND Active Sessions: 0 BoxName:Server: SNASRV02:CHARLIEE.02 Connection Name: CONNECT2 DLC Name: IBMTRNET ,Outbound Link state: AP_CONTACTED Active Sessions: 0 BoxName:Server: SNASRV03:CHARLIEE.03 Connection Name: CONNECT3 DLC Name: IBMTRNET ,Outbound Link state: AP_CONTACT_PND Active Sessions: 0 Completed For additional information about how to retrieve 3270 LU information, please see the following article in the Microsoft Knowledge Base: Q214629 How To Retrieve 3270 LU Information For SNA Server Programmatically For information on how to use the NLS API to do EBCDIC - ASCII conversion, please see the following article in the Microsoft Knowledge Base: Q214649 How To Program EBCDIC - ASCII Conversion Using The NLS API For additional programming information, please see the SNA SDK documentation that is included with SNA Server. Additional query words: ====================================================================== Keywords : kbfile kbProgramming kbSample kbfaq Technology : kbAudDeveloper kbSNAServSearch kbSNAServ300 kbSNAServ400 Version : :3.0,4.0 Issue type : kbhowto ============================================================================= THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY. Copyright Microsoft Corporation 2001.