/* ** File name: GETSYI_SVC_SUBS.C ** ** Copyright (c) 1991, by ** Process Software Corporation ** Framingham, Massachusetts */ /* ** This file contains the code that actually does the work that server says it ** will do. In this case the server receives a structure that contains a ** string identifying a node in the local cluster, and 1 or more strings that ** identify the items that the client wants returned. The server converts ** the code strings into integers, create a list and calls SYS$GETSYIW. The ** results are converted into strings and sent to the client. If an error ** occurs, the error code and the VMS message string are sent. See the DEC VMS ** documentation for more information on SYS$GETSYIW. ** */ /* ** Include files */ #include #include #include #include "TCPWARE_INCLUDE:RPC.H" #include "GETSYI.H" #include "GETSYI_DEF.H" /* ** Declare Functions */ thread_info *parse_rpc_args(); void encode_rpc_res(); static void cvt_12_bytes(), cvt_longw(), cvt_string(), cvt_time(), cvt_word(); /* ** Variables ** ** The array parse_table contains all of the information required to convert a ** string into the form that SYS$GETSYI requires and to convert the results ** back into a string. */ code_info parse_table[] = { /* string SYI code return length encode routine*/ {"BOOTTIME", SYI$_BOOTTIME, sizeof( quad), cvt_time}, {"CLUSTER_NODES", SYI$_CLUSTER_NODES, sizeof( word), cvt_word}, {"CPU", SYI$_CPU, sizeof( longw), cvt_longw}, #ifdef SYI$_FREE_GBLPAGES {"FREE_GBLPAGES", SYI$_FREE_GBLPAGES, sizeof( longw), cvt_longw}, #endif #ifdef SYI$_FREE_GBLSECTS {"FREE_GBLSECTS", SYI$_FREE_GBLSECTS, sizeof( longw), cvt_longw}, #endif #ifdef SYI$_HW_NAME {"HW_NAME", SYI$_HW_NAME, STR_31, cvt_string}, #endif {"NODE_HWTYPE", SYI$_NODE_HWTYPE, STR_31, cvt_string}, {"NODE_HWVERS", SYI$_NODE_HWVERS, 12, cvt_12_bytes}, {"NODE_NUMBER", SYI$_NODE_NUMBER, sizeof( longw), cvt_longw}, {"NODE_SWTYPE", SYI$_NODE_SWTYPE, STR_4, cvt_string}, {"NODE_SWVERS", SYI$_NODE_SWVERS, STR_4, cvt_string}, {"NODENAME", SYI$_NODENAME, STR_15, cvt_string}, {"PAGEFILE_FREE", SYI$_PAGEFILE_FREE, sizeof( longw), cvt_longw}, {"PAGEFILE_PAGE", SYI$_PAGEFILE_PAGE, sizeof( longw), cvt_longw}, {"SWAPFILE_FREE", SYI$_SWAPFILE_FREE, sizeof( longw), cvt_longw}, {"SWAPFILE_PAGE", SYI$_SWAPFILE_PAGE, sizeof( longw), cvt_longw}, {"VERSION", SYI$_VERSION, STR_8, cvt_string}}; /* **************************************** ** ** Function: parse_rpc_args() ** ** Abstract: ** This function converts the list of code strings that the client sends ** into a list of structures that SYS$GETSYI requires. In addition, the ** addresses of the routines that properly format the results of the ** SYS$GETSYI system call are saved for later use. ** ** A code string is compared with the strings in the parsing table. If ** a match is found, the appropriate information is copied out of the ** parse table, otherwise an error is returned. This is done for all ** the strings that the client sent. ** ** After the information about all of the strings has been obtained, ** the memory for the structure is allocated. This memory includes ** the list of item codes that SYS$GETSYI expects, the routines to ** convert the results, and all of the results buffers. ** ** Return value: ** The address of the structure containing all of the information about ** this call to the server. The status code in this structure indicates ** whether this function was successful. */ thread_info *parse_rpc_args( argsp) getsyi_args *argsp; { code_info *ptr, /* pointer into the parse table */ *end, /* addr of entry beyond end of parse table */ *cptr, /* pointer into codes */ codes[ MAX_ITEM_CODES]; /* list of selected codes */ list_3 *lptr; byte *bptr; /* addr of next free byte in memory */ int i, no_codes; /* # codes specified by the client */ longw size, /* number of bytes to allocate */ status; /* status code, used for errors only */ thread_info *thread; char *cp; no_codes = argsp->gsa_item_codes.gsa_item_codes_len; if( no_codes == 0) { status = SS$_INSFARG; /* client must specify at least one code */ goto error_return; } /* ** must always allocate the thread_info structure and a longword to terminate ** the list of item codes sent to SYS$GETSYI. */ size = sizeof( thread_info) + sizeof( longw); cptr = codes; end = &parse_table[ sizeof( parse_table) / sizeof( code_info)]; for( i = 0; i < no_codes; i++) { for( ptr = parse_table; ptr != end; ++ptr) if( strcmp( ptr->ci_name, argsp->gsa_item_codes.gsa_item_codes_val[i]) == 0) break; /* the string matches */ if( ptr >= end) { status = 0; /* string did not match anything */ goto error_return; } *cptr++ = *ptr; /* ** For each item that we are requested to retrieve, we must allocate memory for: ** the list element (list_3), the buffer (ptr->ci_len), the word to receive the ** number of bytes written by SYS$GETSYI, and the address of the routine to ** format the results. */ size += sizeof( list_3) + ptr->ci_len + sizeof( word) + sizeof(longw); } if( (thread = get_vm( size)) == 0) return( 0); thread->ti_results.gsr_status = SS$_NORMAL; thread->ti_args = argsp; thread->ti_getsyi_args = lptr = &thread->ti_routines[ no_codes]; thread->ti_no_items = no_codes; if( *argsp->gsa_node_name != 0) /* is a node specified? */ { thread->ti_node.dsc$b_dtype = DSC$K_DTYPE_T; thread->ti_node.dsc$b_class = DSC$K_CLASS_S; thread->ti_node.dsc$a_pointer = argsp->gsa_node_name; thread->ti_node.dsc$w_length = strlen( argsp->gsa_node_name); for( cp = argsp->gsa_node_name; *cp; cp++) *cp = _toupper( *cp); } else thread->ti_node.dsc$w_length = 0; bptr = (byte *)lptr + no_codes * sizeof( list_3) + sizeof( longw); cptr = codes; /* ** For each code, save the appropriate formating routine, buffer length, code, ** the buffer address and the return length address. */ for( i = 0; i < no_codes; i++, cptr++, lptr++) { thread->ti_routines[ i] = cptr->ci_routine; lptr->l_buf_len = cptr->ci_len; lptr->l_code = cptr->ci_code; lptr->l_buf_addr = bptr; bptr += cptr->ci_len; lptr->l_ret_len_addr = bptr; bptr += sizeof( word); } lptr->l_buf_len = 0; /* terminate the list of codes */ lptr->l_code = 0; return( thread); error_return: if( (thread = get_vm( sizeof( thread_info))) == 0) return( 0); thread->ti_results.gsr_status = status; thread->ti_args = argsp; thread->ti_no_items = i; /* for SS$_BADPARAM, this is the bad string */ return( thread); } /* end function parse_rpc_args() */ /* **************************************** ** ** Function: encode_rpc_res() ** ** Abstract: ** Converts the results from the form returned by SYS$GETSYI into what the ** server protocol requires (namely strings that the client will print). ** If the field gsr_status is a VMS error code (an even number), the ** message associated with the status is sent to the client. The only ** exception to this is if the status code is 0. This means that the ** client sent an invalid code string, so a message indicating which ** string caused the problem will be sent. ** ** If the argument results is non-zero, it is assumed that this structure ** and all of the strings it points to have already been allocated. If ** results is zero, memory is allocated as needed. ** ** This encoding of the results does not test for the server being unable ** to allocate memory. If this condition happens, the server will crash. */ void encode_rpc_res( ti, results) thread_info *ti; getsyi_res *results; { int i; getsyi_res *rp; list_3 *listp; rtns *rtnp; result *rsltp; item *itemp; word msg_len; DSC msg_dsc; char msg_buf[ 256 + 0]; static char bad_param[] = "parameter '%s' is invalid"; if( results) rp = results; /* synchronous, use static memory */ else rp = &ti->ti_results; /* asynch, allocate memory as needed */ _error( rp->gsr_status) { if( rp->gsr_status == 0) /* client sent bad code */ { itemp = ti->ti_args->gsa_item_codes.gsa_item_codes_val + ti->ti_no_items; if( !results) rp->getsyi_res_u.gsr_msg = get_vm( sizeof( bad_param) - 2 + strlen( *itemp)); sprintf( rp->getsyi_res_u.gsr_msg, bad_param, *itemp); } else /* get the message for the VMS status code */ { if( !results) rp->getsyi_res_u.gsr_msg = get_vm( 256 + 1); msg_dsc.dsc$w_length = 256; msg_dsc.dsc$b_dtype = DSC$K_DTYPE_T; msg_dsc.dsc$b_class = DSC$K_CLASS_S; msg_dsc.dsc$a_pointer = rp->getsyi_res_u.gsr_msg; sys$getmsg( rp->gsr_status, &msg_len, &msg_dsc, 0xF, 0); rp->getsyi_res_u.gsr_msg[ msg_len] = 0; } return; } /* ** The processing for a successful client call. All VMS success codes are ** mapped to SS$_NORMAL (1). */ if( rp->gsr_status != SS$_NORMAL) rp->gsr_status = SS$_NORMAL; rp->getsyi_res_u.gsr_info.gsi_results.gsi_results_len = ti->ti_no_items; if( !results) { rsltp = rp->getsyi_res_u.gsr_info.gsi_results.gsi_results_val = get_vm( sizeof( result) * ti->ti_no_items); for( i = ti->ti_no_items; i > 0; --i) *rsltp++ = 0; /* tell the cvt routines to allocate memory */ } rtnp = ti->ti_routines; rsltp = rp->getsyi_res_u.gsr_info.gsi_results.gsi_results_val; for( listp = ti->ti_getsyi_args; ; listp++, rtnp++, rsltp++) { if( listp->l_buf_len == 0 && listp->l_code == 0) break; (**rtnp)( listp->l_buf_addr, listp->l_ret_len_addr, rsltp); } } /* end function encode_rpc_res() */ /* **************************************** ** ** Function: free_rpc_res() ** ** Abstract: ** This function frees the memory that was allocated when building the RPC ** results. Do not call this function with the address of a static ** structure. */ void free_rpc_res( results) getsyi_res *results; { int no_items; result *rsltp; _error( results->gsr_status) { if( results->getsyi_res_u.gsr_msg) free_vm( results->getsyi_res_u.gsr_msg); return; } no_items = results->getsyi_res_u.gsr_info.gsi_results.gsi_results_len; rsltp = results->getsyi_res_u.gsr_info.gsi_results.gsi_results_val; for( ; no_items > 0; --no_items, rsltp++) free_vm( *rsltp); free_vm( results->getsyi_res_u.gsr_info.gsi_results.gsi_results_val); return; } /* end function free_rpc_res() */ /* ** The GETSYI result conversion routines ** Each of these routines is called with three arguments: ** The address of the value returned by SYS$GETSYI ** The address of the length returned by SYS$GETSYI ** The address of the result to be sent to the client. This is the address ** of an address of a character. If this argument points to a NULL ** pointer, the required memory will be allocated. ** ** None of these routines return any status information */ /* **************************************** ** ** Function: cvt_12_bytes() ** ** Abstract: ** convert an array of 12 bytes into a user readable string */ static void cvt_12_bytes( ptr, len, rsltp) byte *ptr; word *len; result *rsltp; { static readonly char string[] = "12 bytes: "; int i; char *cp; /* ** Allocate enough memory to hold the above string plus 3 characters for each ** byte. The NULL terminating byte is included by sizeof( string). */ if( *rsltp == 0) *rsltp = get_vm( sizeof( string) + 12 * 3); cp = *rsltp; strcpy( cp, string); cp += sizeof( string) - 1; ptr += 12; for( i = 11; i >= 0; --i) cp += sprintf( cp, "%02X-", *--ptr); *(--cp) = '\0'; } /* end function cvt_12_bytes() */ /* **************************************** ** ** Function: cvt_longw() ** ** Abstract: ** convert a longword into a user readable string */ static void cvt_longw( ptr, len, rsltp) longw *ptr; word *len; result *rsltp; { static readonly char string[] = "long: decimal: %10d, hex: 0x%08X, octal: %011o"; /* ** Allocate enough memory to hold the above string plus extra bytes for the ** displayed integers: 10-4 for decimal, 8-4 for hex, 11-5 for octal. */ if( *rsltp == 0) *rsltp = get_vm( sizeof( string) + 6 + 4 + 6); sprintf( *rsltp, string, *ptr, *ptr, *ptr); } /* end function cvt_longw() */ /* **************************************** ** ** Function: cvt_string() ** ** Abstract: ** Send to the client a string that contains the length of the string ** returned by SYS$GETSYI and that string itself. */ static void cvt_string( ptr, len, rsltp) char *ptr; word *len; result *rsltp; { static readonly char string[] = "string (%2d): '"; char *cp; /* ** Allocate enough memory to hold the above string plus the string returned by SYS$GETSYI. The -1 is because "%2d" is 3 bytes, not 2. */ if( *rsltp == 0) *rsltp = get_vm( (sizeof( string) - 1) + *len); cp = *rsltp; cp += sprintf( cp, string, *len); strncpy( cp, ptr, *len); cp += *len; *cp++ = '\''; *cp = '\0'; } /* end function cvt_string() */ /* **************************************** ** ** Function: cvt_time() ** ** Abstract: ** convert a time (64 bit number) into a user readable string */ static void cvt_time( ptr, len, rsltp) quad *ptr; word *len; result *rsltp; { static readonly char string[] = "time: "; word time_len; DSC time_buf; char *cp; /* ** Allocate enough memory to hold the above string plus the string returned by ** SYS$ASCTIM (23 bytes). */ if( *rsltp == 0) *rsltp = get_vm( sizeof( string) + 23); cp = *rsltp; strcpy( cp, string); cp += sizeof( string)-1; time_buf.dsc$w_length = 23; time_buf.dsc$b_dtype = DSC$K_DTYPE_T; time_buf.dsc$b_class = DSC$K_CLASS_S; time_buf.dsc$a_pointer = cp; sys$asctim( &time_len, &time_buf, ptr, 0); *(cp + time_len) = '\0'; return; } /* end function cvt_time() */ /* **************************************** ** ** Function: cvt_word() ** ** Abstract: ** convert a word into a user readable string */ static void cvt_word( ptr, len, rsltp) word *ptr; word *len; result *rsltp; { static readonly char string[] = "word: decimal: %10d, hex: 0x%04X, octal: %06o"; /* ** Allocate enough memory to hold the above string plus extra bytes for the ** displayed integers: 10-4 for decimal, 4-4 for hex, 6-4 for octal. */ if( *rsltp == 0) *rsltp = get_vm( sizeof( string) + 6 + 0 + 2); sprintf( *rsltp, string, *ptr, *ptr, *ptr); return; } /* end function cvt_word() */ /* end file GETSYI_SVC_SUBS.C */