#ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #ifdef __cplusplus } #endif typedef struct {short buflen, /* Length of output buffer */ itmcode; /* Item code */ void *buffer; /* Buffer address */ void *retlen; /* Return length address */ } ITMLST; typedef struct {char *ItemName; /* Name of the item we're getting */ unsigned short *ReturnLength; /* Pointer to the return */ /* buffer length */ void *ReturnBuffer; /* generic pointer to the returned */ /* data */ int ReturnType; /* The type of data in the return */ /* buffer */ int ItemListEntry; /* Index of the entry in the item */ /* list we passed to syscall */ } FetchedItem; /* Use this keep track of the items in the */ /* 'grab everything' GETDVI call */ #define user_bit_test(a, b, c) \ { \ if (c & UIC$M_##b) \ hv_store(a, #b, strlen(#b), &sv_yes, 0); \ else \ hv_store(a, #b, strlen(#b), &sv_no, 0);} /* Macro to expand out entries for generic_bitmap_encode */ #define UAI_D(a) { if (!strncmp(FlagName, #a, FlagLen)) { \ EncodedValue[0] = EncodedValue[0] | DMT$M_##a; \ break; \ } \ } /* Macro to define an entry in the user info array */ #define UAI_ENT(a, b, c) {#a, UAI$_##a, b, c} #define IS_STRING 1 #define IS_LONGWORD 2 #define IS_QUADWORD 3 #define IS_WORD 4 #define IS_BYTE 5 #define IS_VMSDATE 6 #define IS_BITMAP 7 /* Each bit in the return value indicates something */ #define IS_ENUM 8 /* Each returned value has a name, and we ought to */ /* return the name instead of the value */ #define IS_ODD 9 /* A catchall */ #define IS_COUNTSTRING 10 /* A counted string--first byte's the count */ struct GenericID { char *ItemName; /* Pointer to the item name */ int SyscallValue; /* Value to use in the getDVI item list */ int BufferLen; /* Length the return va buf needs to be. (no nul */ /* terminators, so must be careful with the return */ /* values. */ int ReturnType; /* Type of data the item returns */ }; struct GenericID UserInfoList[] = { UAI_ENT(ACCOUNT, 32, IS_STRING), UAI_ENT(ASTLM, 2, IS_WORD), UAI_ENT(BATCH_ACCESS_P, 3, IS_ODD), UAI_ENT(BATCH_ACCESS_S, 3, IS_ODD), UAI_ENT(BIOLM, 2, IS_WORD), UAI_ENT(BYTLM, 4, IS_LONGWORD), UAI_ENT(CLITABLES, 32, IS_COUNTSTRING), UAI_ENT(CPUTIM, 4, IS_LONGWORD), UAI_ENT(DEFCLI, 32, IS_COUNTSTRING), UAI_ENT(DEFDEV, 32, IS_COUNTSTRING), UAI_ENT(DEFDIR, 64, IS_COUNTSTRING), UAI_ENT(DEF_PRIV, 8, IS_QUADWORD), UAI_ENT(DFWSCNT, 4, IS_LONGWORD), UAI_ENT(DIOLM, 2, IS_WORD), UAI_ENT(DIALUP_ACCESS_P, 3, IS_ODD), UAI_ENT(DIALUP_ACCESS_S, 3, IS_ODD), UAI_ENT(ENCRYPT, 1, IS_ENUM), UAI_ENT(ENCRYPT2, 1, IS_ENUM), UAI_ENT(ENQLM, 2, IS_WORD), UAI_ENT(EXPIRATION, 8, IS_VMSDATE), UAI_ENT(FILLM, 2, IS_WORD), UAI_ENT(FLAGS, 4, IS_BITMAP), UAI_ENT(JTQUOTA, 4, IS_LONGWORD), UAI_ENT(LASTLOGIN_I, 8, IS_VMSDATE), UAI_ENT(LASTLOGIN_N, 8, IS_VMSDATE), UAI_ENT(LGICMD, 64, IS_COUNTSTRING), UAI_ENT(LOCAL_ACCESS_P, 3, IS_ODD), UAI_ENT(LOCAL_ACCESS_S, 3, IS_ODD), UAI_ENT(LOGFAILS, 2, IS_WORD), UAI_ENT(MAXACCTJOBS, 2, IS_WORD), UAI_ENT(MAXDETACH, 2, IS_WORD), UAI_ENT(MAXJOBS, 2, IS_WORD), UAI_ENT(NETWORK_ACCESS_P, 3, IS_ODD), UAI_ENT(NETWORK_ACCESS_S, 3, IS_ODD), UAI_ENT(OWNER, 32, IS_COUNTSTRING), UAI_ENT(PBYTLM, 4, IS_LONGWORD), UAI_ENT(PGFLQUOTA, 4, IS_LONGWORD), UAI_ENT(PRCCNT, 4, IS_LONGWORD), UAI_ENT(PRI, 1, IS_BYTE), UAI_ENT(PRIV, 8, IS_QUADWORD), UAI_ENT(PWD, 8, IS_STRING), UAI_ENT(PWD_DATE, 8, IS_VMSDATE), UAI_ENT(PWD_LENGTH, 4, IS_LONGWORD), UAI_ENT(PWD_LIFETIME, 8, IS_VMSDATE), UAI_ENT(PWD2, 8, IS_QUADWORD), UAI_ENT(PWD2_DATE, 8, IS_VMSDATE), UAI_ENT(QUEPRI, 1, IS_BYTE), UAI_ENT(REMOTE_ACCESS_P, 3, IS_ODD), UAI_ENT(REMOTE_ACCESS_S, 3, IS_ODD), UAI_ENT(SALT, 2, IS_WORD), UAI_ENT(SHRFILLM, 2, IS_WORD), UAI_ENT(TQCNT, 2, IS_WORD), UAI_ENT(UIC, 4, IS_LONGWORD), UAI_ENT(USER_DATA, 255, IS_STRING), UAI_ENT(WSEXTENT, 4, IS_LONGWORD), UAI_ENT(WSQUOTA, 4, IS_LONGWORD), {NULL, 0, 0, 0} }; char *MonthNames[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} ; /* Globals to track how many different pieces of info we can return, as */ /* well as how much space we'd need to grab to store it. */ static int UserInfoCount = 0; static int UserInfoMallocSize = 0; /* Macro to fill in a 'traditional' item-list entry */ #define init_itemlist(ile, length, code, bufaddr, retlen_addr) \ { \ (ile)->buflen = (length); \ (ile)->itmcode = (code); \ (ile)->buffer = (bufaddr); \ (ile)->retlen = (retlen_addr) ;} /* Take a pointer to a bitmap hash (like decode_bitmap gives), an item */ /* code, and a pointer to the output buffer and encode the bitmap */ void generic_bitmap_encode(HV * FlagHV, int ItemCode, void *Buffer) { char *FlagName; I32 FlagLen; int *EncodedValue; /* Pointer to an integer /* Shut Dec C up */ FlagName = NULL; /* Buffer's a pointer to an integer array, really it is */ EncodedValue = Buffer; /* Initialize our hash iterator */ hv_iterinit(FlagHV); /* Rip through the hash */ while (hv_iternextsv(FlagHV, &FlagName, &FlagLen)) { switch (ItemCode) { default: croak("Invalid item specified"); } } } /* Take a pointer to an itemlist, a hashref, and some flags, and build up */ /* an itemlist from what's in the hashref. Buffer space for the items is */ /* allocated, as are the length shorts and stuff. If the hash entries have */ /* values, those values are copied into the buffers, too. Returns the */ /* number of items stuck in the itemlist */ int build_itemlist(struct GenericID InfoList[], ITMLST *ItemList, HV *HashRef) { /* standard, dopey index variable */ int i = 0; int ItemListIndex = 0; char *TempCharPointer; unsigned int TempStrLen; int TempNameLen; SV *TempSV; unsigned short *TempLen; int ItemCode; char *TempBuffer; char StringLength; int BufferLength; long TempLong; struct dsc$descriptor_s TimeStringDesc; int Status; int CopyData; for(i = 0; InfoList[i].ItemName; i++) { TempNameLen = strlen(InfoList[i].ItemName); /* Figure out some stuff. Avoids duplication, and makes the macro */ /* expansion of init_itemlist a little easier */ ItemCode = InfoList[i].SyscallValue; CopyData = TRUE; switch(InfoList[i].ReturnType) { /* Quadwords are treated as strings for right now */ case IS_QUADWORD: case IS_COUNTSTRING: case IS_STRING: /* Allocate us some buffer space */ Newz(NULL, TempBuffer, InfoList[i].BufferLen, char); Newz(NULL, TempLen, 1, unsigned short); BufferLength = InfoList[i].BufferLen; /* Set the string buffer to spaces */ memset(TempBuffer, ' ', InfoList[i].BufferLen); /* If we're copying data, then fetch it and stick it in the buffer */ if (CopyData) { TempSV = *hv_fetch(HashRef, InfoList[i].ItemName, TempNameLen, FALSE); TempCharPointer = SvPV(TempSV, TempStrLen); /* If there was something in the SV, then copy it over */ if (TempStrLen) { BufferLength = TempStrLen < InfoList[i].BufferLen ? TempStrLen : InfoList[i].BufferLen; Copy(TempCharPointer, TempBuffer, BufferLength, char); } } init_itemlist(&ItemList[ItemListIndex], BufferLength, ItemCode, TempBuffer, TempLen); break; case IS_VMSDATE: /* Allocate us some buffer space */ Newz(NULL, TempBuffer, InfoList[i].BufferLen, char); Newz(NULL, TempLen, 1, unsigned short); if (CopyData) { TempSV = *hv_fetch(HashRef, InfoList[i].ItemName, TempNameLen, FALSE); TempCharPointer = SvPV(TempSV, TempStrLen); /* Fill in the time string descriptor */ TimeStringDesc.dsc$a_pointer = TempCharPointer; TimeStringDesc.dsc$w_length = TempStrLen; TimeStringDesc.dsc$b_dtype = DSC$K_DTYPE_T; TimeStringDesc.dsc$b_class = DSC$K_CLASS_S; /* Convert from an ascii rep to a VMS quadword date structure */ Status = sys$bintim(&TimeStringDesc, TempBuffer); if (Status != SS$_NORMAL) { croak("Error converting time!"); } } init_itemlist(&ItemList[ItemListIndex], InfoList[i].BufferLen, ItemCode, TempBuffer, TempLen); break; case IS_LONGWORD: /* Allocate us some buffer space */ Newz(NULL, TempBuffer, InfoList[i].BufferLen, char); Newz(NULL, TempLen, 1, unsigned short); if (CopyData) { TempSV = *hv_fetch(HashRef, InfoList[i].ItemName, TempNameLen, FALSE); TempLong = SvIVX(TempSV); /* Set the value */ *TempBuffer = TempLong; } init_itemlist(&ItemList[ItemListIndex], InfoList[i].BufferLen, ItemCode, TempBuffer, TempLen); break; case IS_BITMAP: /* Allocate us some buffer space */ Newz(NULL, TempBuffer, InfoList[i].BufferLen, char); Newz(NULL, TempLen, 1, unsigned short); if (CopyData) { TempSV = *hv_fetch(HashRef, InfoList[i].ItemName, TempNameLen, FALSE); /* Is the SV an integer? If so, then we'll use that value. */ /* Otherwise we'll assume that it's a hashref of the sort that */ /* generic_bitmap_decode gives */ if (SvIOK(TempSV)) { TempLong = SvIVX(TempSV); /* Set the value */ *TempBuffer = TempLong; } else { generic_bitmap_encode((HV *)SvRV(TempSV), ItemCode, TempBuffer); } } init_itemlist(&ItemList[ItemListIndex], InfoList[i].BufferLen, ItemCode, TempBuffer, TempLen); break; default: croak("Unknown item type found!"); break; } ItemListIndex++; } return(ItemListIndex); } /* Takes an item list pointer and a count of items, and frees the buffer */ /* memory and length buffer memory */ void tear_down_itemlist(ITMLST *ItemList, int NumItems) { int i; for(i=0; i < NumItems; i++) { if(ItemList[i].buffer != NULL) Safefree(ItemList[i].buffer); if(ItemList[i].retlen != NULL) Safefree(ItemList[i].retlen); } } char * de_enum(int UAICode, int UAIVal) { switch(UAICode) { case UAI$_ENCRYPT: switch (UAIVal) { case UAI$C_AD_II: return "AD_II"; case UAI$C_PURDY: return "PURDY"; case UAI$C_PURDY_V: return "PURDY_V"; case UAI$C_PURDY_S: return "PURDY_S"; default: return "Unknown"; } case UAI$_ENCRYPT2: switch (UAIVal) { case UAI$C_AD_II: return "AD_II"; case UAI$C_PURDY: return "PURDY"; case UAI$C_PURDY_V: return "PURDY_V"; case UAI$C_PURDY_S: return "PURDY_S"; default: return "Unknown"; } default: return "Unknown"; } } void tote_up_items() { for(UserInfoCount = 0; UserInfoList[UserInfoCount].ItemName; UserInfoCount++) { /* While we're here, we might as well get a generous estimate of how */ /* much space we'll need for all the buffers */ UserInfoMallocSize += UserInfoList[UserInfoCount].BufferLen; /* Add in a couple extra, just to be safe */ UserInfoMallocSize += 8; } } /* This routine takes a DVI item list ID and the value that wants to be */ /* de-enumerated and returns a pointer to an SV with the de-enumerated name */ /* in it */ SV * enum_name(long UAICode, long UAIVal) { SV *WorkingSV = newSV(10); char TempSprintfBuffer[512]; switch(UAICode) { case UAI$_ENCRYPT: case UAI$_ENCRYPT2: switch (UAIVal) { case UAI$C_AD_II: sv_setpv(WorkingSV, "AD_II"); break; case UAI$C_PURDY: sv_setpv(WorkingSV, "PURDY"); break; case UAI$C_PURDY_V: sv_setpv(WorkingSV, "PURDY_V"); break; case UAI$C_PURDY_S: sv_setpv(WorkingSV, "PURDY_S"); break; default: sprintf(TempSprintfBuffer, "Unknown encrypt type (%i)",UAIVal); sv_setpv(WorkingSV, TempSprintfBuffer); break; } break; default: sv_setpv(WorkingSV, "Unknown"); } return WorkingSV; } /* This routine gets passed a pre-cleared array that's big enough for all */ /* the pieces we'll fill in, and that has the input parameter stuck in */ /* entry 0. We allocate the memory and fill in the rest of the array, and */ /* pass back a hash that has all the return results in it. */ SV * generic_getuai_call(ITMLST *ListOItems, int InfoCount, SV *UserName) { FetchedItem *OurDataList; unsigned short *ReturnLengths; int i, LocalIndex; unsigned int PVLength; int status; HV *AllPurposeHV; SV *ReturnedSV; unsigned short ReturnedTime[7]; char AsciiTime[100]; char QuadWordString[65]; char *TempCharPointer; short *TempWordPointer; long *TempLongPointer; __int64 *TempQuadPointer; char *TempStringBuffer; char StringLength; long EnumVal; struct dsc$descriptor_s UserDesc; UserDesc.dsc$a_pointer = SvPV(UserName, PVLength); UserDesc.dsc$w_length = PVLength; UserDesc.dsc$b_dtype = DSC$K_DTYPE_T; UserDesc.dsc$b_class = DSC$K_CLASS_S; LocalIndex = 0; /* Allocate the local tracking array */ OurDataList = malloc(sizeof(FetchedItem) * InfoCount); memset(OurDataList, 0, sizeof(FetchedItem) * InfoCount); /* We also need room for the buffer lengths */ ReturnLengths = malloc(sizeof(short) * InfoCount); memset(ReturnLengths, 0, sizeof(short) * InfoCount); /* Fill in the item list and the tracking list */ for (i = 0; UserInfoList[i].ItemName; i++) { /* Allocate the return data buffer and zero it. Can be oddly sized, so we use the system malloc instead of New */ OurDataList[LocalIndex].ReturnBuffer = malloc(UserInfoList[i].BufferLen); memset(OurDataList[LocalIndex].ReturnBuffer, 0, UserInfoList[i].BufferLen); /* Note some important stuff (like what we're doing) in our local */ /* tracking array */ OurDataList[LocalIndex].ItemName = UserInfoList[i].ItemName; OurDataList[LocalIndex].ReturnLength = &ReturnLengths[LocalIndex]; OurDataList[LocalIndex].ReturnType = UserInfoList[i].ReturnType; OurDataList[LocalIndex].ItemListEntry = i; /* Fill in the item list */ init_itemlist(&ListOItems[LocalIndex], UserInfoList[i].BufferLen, UserInfoList[i].SyscallValue, OurDataList[LocalIndex].ReturnBuffer, &ReturnLengths[LocalIndex]); /* Increment the local index */ LocalIndex++; } /* Make the GETQUIW call */ status = sys$getuai(0, NULL, &UserDesc, ListOItems, NULL, NULL, NULL); /* Did it go OK? */ if (status == SS$_NORMAL) { /* Looks like it */ AllPurposeHV = newHV(); for (i = 0; i < LocalIndex; i++) { switch(OurDataList[i].ReturnType) { case IS_ODD: case IS_STRING: /* copy the return string into a temporary buffer with C's string */ /* handling routines. For some reason $GETQUI returns values with */ /* embedded nulls and bogus lengths, which is really */ /* strange. Anyway, this is a cheap way to see how long the */ /* string is without doing a strlen(), which might fall off the */ /* end of the world */ TempStringBuffer = malloc(*(OurDataList[i].ReturnLength) + 1); memset(TempStringBuffer, 0, *(OurDataList[i].ReturnLength) + 1); strncpy(TempStringBuffer, OurDataList[i].ReturnBuffer, *(OurDataList[i].ReturnLength)); if (strlen(TempStringBuffer) < *OurDataList[i].ReturnLength) *OurDataList[i].ReturnLength = strlen(TempStringBuffer); free(TempStringBuffer); /* Check to make sure we got something back, otherwise set the */ /* value to undef */ if (*OurDataList[i].ReturnLength) { hv_store(AllPurposeHV, OurDataList[i].ItemName, strlen(OurDataList[i].ItemName), newSVpv(OurDataList[i].ReturnBuffer, *(OurDataList[i].ReturnLength)), 0); } else { hv_store(AllPurposeHV, OurDataList[i].ItemName, strlen(OurDataList[i].ItemName), &PL_sv_undef, 0); } break; case IS_COUNTSTRING: /* Check to make sure we got something back, otherwise set the */ /* value to undef */ if (*OurDataList[i].ReturnLength) { StringLength = *(char *)OurDataList[i].ReturnBuffer; hv_store(AllPurposeHV, OurDataList[i].ItemName, strlen(OurDataList[i].ItemName), newSVpv((char *)OurDataList[i].ReturnBuffer + 1, StringLength), 0); } else { hv_store(AllPurposeHV, OurDataList[i].ItemName, strlen(OurDataList[i].ItemName), &PL_sv_undef, 0); } break; case IS_VMSDATE: sys$numtim(ReturnedTime, OurDataList[i].ReturnBuffer); sprintf(AsciiTime, "%02hi-%s-%hi %02hi:%02hi:%02hi.%hi", ReturnedTime[2], MonthNames[ReturnedTime[1] - 1], ReturnedTime[0], ReturnedTime[3], ReturnedTime[4], ReturnedTime[5], ReturnedTime[6]); hv_store(AllPurposeHV, OurDataList[i].ItemName, strlen(OurDataList[i].ItemName), newSVpv(AsciiTime, 0), 0); break; case IS_ENUM: if ( 1 == *OurDataList[i].ReturnLength) { EnumVal = *(char *)OurDataList[i].ReturnBuffer; } else { if ( 4 == *OurDataList[i].ReturnLength) { EnumVal = *(short *)OurDataList[i].ReturnBuffer; } else { if ( 4 == *OurDataList[i].ReturnLength) { EnumVal = *(long *)OurDataList[i].ReturnBuffer; } } } hv_store(AllPurposeHV, OurDataList[i].ItemName, strlen(OurDataList[i].ItemName), enum_name(UserInfoList[i].SyscallValue, EnumVal), 0); break; case IS_BITMAP: case IS_LONGWORD: TempLongPointer = OurDataList[i].ReturnBuffer; hv_store(AllPurposeHV, OurDataList[i].ItemName, strlen(OurDataList[i].ItemName), newSViv(*TempLongPointer), 0); break; case IS_WORD: TempWordPointer = OurDataList[i].ReturnBuffer; hv_store(AllPurposeHV, OurDataList[i].ItemName, strlen(OurDataList[i].ItemName), newSViv(*TempWordPointer), 0); break; case IS_BYTE: TempCharPointer = OurDataList[i].ReturnBuffer; hv_store(AllPurposeHV, OurDataList[i].ItemName, strlen(OurDataList[i].ItemName), newSViv(*TempCharPointer), 0); break; case IS_QUADWORD: TempQuadPointer = OurDataList[i].ReturnBuffer; sprintf(QuadWordString, "%llu", *TempQuadPointer); hv_store(AllPurposeHV, OurDataList[i].ItemName, strlen(OurDataList[i].ItemName), newSVpv(QuadWordString, 0), 0); break; } } /* Set the returned status and return the HV we built */ ReturnedSV = newRV_noinc((SV *) AllPurposeHV); } else { /* Well, things weren't fine and dandy. */ if (SS$_BADPARAM == status) printf("Badparam\n"); if (RMS$_RSZ == status) printf("RSZ\n"); if (SS$_ACCVIO == status) printf("ACCVIO\n"); SETERRNO(EVMSERR, status); ReturnedSV = &PL_sv_undef; } /* Free up our allocated memory */ for(i = 0; i < InfoCount; i++) { free(OurDataList[i].ReturnBuffer); } free(OurDataList); free(ReturnLengths); return(ReturnedSV); } MODULE = VMS::User PACKAGE = VMS::User void user_info(UserName) SV *UserName PPCODE: { ITMLST *ListOItems; unsigned short ReturnBufferLength = 0; unsigned int UserFlags; unsigned short UserFlagsLength; unsigned int Status; unsigned int ReturnedUAIStatus; unsigned int SubType; /* If we've not gotten the count of items, go get it now */ if (UserInfoCount == 0) { tote_up_items(); } /* We need room for our item list */ ListOItems = malloc(sizeof(ITMLST) * (UserInfoCount + 1)); memset(ListOItems, 0, sizeof(ITMLST) * (UserInfoCount + 1)); /* Make the call to the generic fetcher and make it the return */ /* value. We don't need to go messing with the item list, since what we */ /* used for the last call is OK to pass along to this one. */ XPUSHs(generic_getuai_call(ListOItems, UserInfoCount, UserName)); /* Give back the allocated item list memory */ free(ListOItems); }