From 5e3aa0ea7c3fabc4b95b463130a05e012e9a6ef1 Mon Sep 17 00:00:00 2001 From: joepun76 Date: Tue, 1 Feb 2022 14:02:41 -0500 Subject: [PATCH 1/8] initial Signed-off-by: joepun76 --- build/build_zss.sh | 3 + build/build_zss64.sh | 3 + c/datasetjson.c | 395 +--------------------------------- c/dsutils.c | 417 ++++++++++++++++++++++++++++++++++++ c/jesService.c | 101 +++++++++ c/jesrequestsjson.c | 490 +++++++++++++++++++++++++++++++++++++++++++ c/zss.c | 1 + h/dsutils.h | 76 +++++++ h/jesService.h | 30 +++ h/jesrequestsjson.h | 36 ++++ 10 files changed, 1161 insertions(+), 391 deletions(-) create mode 100644 c/dsutils.c create mode 100644 c/jesService.c create mode 100644 c/jesrequestsjson.c create mode 100644 h/dsutils.h create mode 100644 h/jesService.h create mode 100644 h/jesrequestsjson.h diff --git a/build/build_zss.sh b/build/build_zss.sh index 27152ec0a..5cf7c13d3 100755 --- a/build/build_zss.sh +++ b/build/build_zss.sh @@ -138,7 +138,10 @@ if c89 \ ${ZSS}/c/unixFileService.c \ ${ZSS}/c/datasetService.c \ ${ZSS}/c/datasetjson.c \ + ${ZSS}/c/dsutils.c \ ${ZSS}/c/envService.c \ + ${ZSS}/c/jesService.c \ + ${ZSS}/c/jesrequestsjson.c \ ${ZSS}/c/zosDiscovery.c \ ${ZSS}/c/securityService.c \ ${ZSS}/c/registerProduct.c \ diff --git a/build/build_zss64.sh b/build/build_zss64.sh index d71018379..c99fe4bae 100755 --- a/build/build_zss64.sh +++ b/build/build_zss64.sh @@ -113,7 +113,10 @@ if ! c89 \ ${ZSS}/c/unixFileService.c \ ${ZSS}/c/datasetService.c \ ${ZSS}/c/datasetjson.c \ + ${ZSS}/c/dsutils.c \ ${ZSS}/c/envService.c \ + ${ZSS}/c/jesService.c \ + ${ZSS}/c/jesrequestsjson.c \ ${ZSS}/c/zosDiscovery.c \ ${ZSS}/c/securityService.c \ ${ZSS}/c/registerProduct.c \ diff --git a/c/datasetjson.c b/c/datasetjson.c index 07483263d..14d08801f 100644 --- a/c/datasetjson.c +++ b/c/datasetjson.c @@ -40,6 +40,7 @@ #include "jcsi.h" #include "impersonation.h" #include "dynalloc.h" +#include "dsutils.h" #include "utils.h" #include "vsam.h" #include "qsam.h" @@ -47,107 +48,18 @@ #define INDEXED_DSCB 96 -static char defaultDatasetTypesAllowed[3] = {'A','D','X'}; + static char clusterTypesAllowed[3] = {'C','D','I'}; /* TODO: support 'I' type DSNs */ static int clusterTypesCount = 3; static char *datasetStart = "//'"; -static char *defaultCSIFields[] ={ "NAME ", "TYPE ", "VOLSER "}; -static int defaultCSIFieldCount = 3; + + static char *defaultVSAMCSIFields[] ={"AMDCIREC", "AMDKEY ", "ASSOC ", "VSAMTYPE"}; static int defaultVSAMCSIFieldCount = 4; static char vsamCSITypes[5] = {'R', 'D', 'G', 'I', 'C'}; -static char getRecordLengthType(char *dscb); -static int getMaxRecordLength(char *dscb); - -const static int DSCB_TRACE = FALSE; - -typedef struct DatasetName_tag { - char value[44]; /* space-padded */ -} DatasetName; - -typedef struct DatasetMemberName_tag { - char value[8]; /* space-padded */ -} DatasetMemberName; - -typedef struct DDName_tag { - char value[8]; /* space-padded */ -} DDName; - -typedef struct Volser_tag { - char value[6]; /* space-padded */ -} Volser; - -static int getVolserForDataset(const DatasetName *dataset, Volser *volser); static bool memberExists(char* dsName, DynallocMemberName daMemberName); - -static int getLreclOrRespondError(HttpResponse *response, const DatasetName *dsn, const char *ddPath) { - int lrecl = 0; - - FileInfo info; - int returnCode; - int reasonCode; - FILE *in = fopen(ddPath, "r"); - if (in == NULL) { - respondWithError(response,HTTP_STATUS_NOT_FOUND,"File could not be opened or does not exist"); - return 0; - } - - Volser volser; - memset(&volser.value, ' ', sizeof(volser.value)); - - int volserSuccess = getVolserForDataset(dsn, &volser); - int handledThroughDSCB = FALSE; - - if (!volserSuccess){ - - char dscb[INDEXED_DSCB] = {0}; - int rc = obtainDSCB1(dsn->value, sizeof(dsn->value), - volser.value, sizeof(volser.value), - dscb); - if (rc == 0){ - if (DSCB_TRACE){ - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "DSCB for %.*s found\n", sizeof(dsn->value), dsn->value); - dumpbuffer(dscb,INDEXED_DSCB); - } - - lrecl = getMaxRecordLength(dscb); - char recordType = getRecordLengthType(dscb); - if (recordType == 'U'){ - fclose(in); - respondWithError(response, HTTP_STATUS_BAD_REQUEST,"Undefined-length dataset"); - return 0; - } - handledThroughDSCB = TRUE; - } - } - if (!handledThroughDSCB){ - FileInfo info; - fldata_t fileinfo = {0}; - char filenameOutput[100]; - int returnCode = fldata(in,filenameOutput,&fileinfo); - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "FLData request rc=0x%x\n",returnCode); - if (!returnCode) { - if (fileinfo.__recfmU) { - fclose(in); - respondWithError(response, HTTP_STATUS_BAD_REQUEST, - "Undefined-length dataset"); - return 0; - } - lrecl = fileinfo.__maxreclen; - } else { - fclose(in); - respondWithError(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, - "Could not read dataset information"); - return 0; - } - } - fclose(in); - - return lrecl; -} - /* TODO this duplicates a lot of stremDataset. Thinking of putting conditionals on streamDataset writing to json stream, but then function becomes misleading. */ @@ -550,73 +462,6 @@ static bool isSupportedWriteDsorg(char *dscb, bool *isPds) { } } -static int obtainDSCB1(const char *dsname, unsigned int dsnameLength, - const char *volser, unsigned int volserLength, - char *dscb1) { - -#define DSCB1_SIZE 96 - -#define SVC27_WORKAREA_SIZE 140 -#define SVC27_OPCODE_SEARCH 0xC100 -#define SVC27_OPCODE_SEEK 0xC080 - -#define SVC27_OPTION_NO_TIOT_ENQ 0x80 -#define SVC27_OPTION_NO_DUMMY_DSCB1 0x40 -#define SVC27_OPTION_NO_CAT_ALLOC 0x20 -#define SVC27_OPTION_HIDE_NAME 0x10 -#define SVC27_OPTION_EADSCB_OK 0x08 - - ALLOC_STRUCT31( - STRUCT31_NAME(mem31), - STRUCT31_FIELDS( - char dsnameSpacePadded[44]; - char volserSpacePadded[6]; - char workArea[SVC27_WORKAREA_SIZE]; - __packed struct { - int operationCode : 16; - int option : 8; - int dscbNumber : 8; - char * __ptr32 dsname; - char * __ptr32 volser; - char * __ptr32 workArea; - } parmList; - ) - ); - - memset(mem31->dsnameSpacePadded, ' ', sizeof(mem31->dsnameSpacePadded)); - memcpy(mem31->dsnameSpacePadded, dsname, dsnameLength); - memset(mem31->volserSpacePadded, ' ', sizeof(mem31->volserSpacePadded)); - memcpy(mem31->volserSpacePadded, volser, volserLength); - - memset(mem31->workArea, 0, sizeof(mem31->workArea)); - - mem31->parmList.operationCode = SVC27_OPCODE_SEARCH; - mem31->parmList.option = SVC27_OPTION_EADSCB_OK; - mem31->parmList.dscbNumber = 0; - mem31->parmList.dsname = mem31->dsnameSpacePadded; - mem31->parmList.volser = mem31->volserSpacePadded; - mem31->parmList.workArea = mem31->workArea; - - int rc = 0; - - __asm( - " LA 1,0(%1) \n" - " SVC 27 \n" - " ST 15,%0 \n" - : "=m"(rc) - : "r"(&mem31->parmList) - : "r0", "r15" - ); - - memcpy(dscb1, mem31->workArea, DSCB1_SIZE); - - FREE_STRUCT31( - STRUCT31_NAME(mem31) - ); - - return rc; -} - #ifdef __ZOWE_OS_ZOS void addDetailedDatasetMetadata(char *datasetName, int nameLength, char *volser, int volserLength, @@ -728,167 +573,6 @@ void addMemberedDatasetMetadata(char *datasetName, int nameLength, #ifdef __ZOWE_OS_ZOS -#define DSPATH_PREFIX "//\'" -#define DSPATH_SUFFIX "\'" - -static bool isDatasetPathValid(const char *path) { - - /* Basic check. The fopen() dataset path format is //'dsn(member)' */ - - /* Min dataset file name: - * //' - 3 - * min ds name - 1 - * ' - 1 - */ -#define PATH_MIN_LENGTH 5 - - /* Max dataset file name: - * //' - 3 - * man ds name - 44 - * ( - 1 - * max member name - 8 - * ) - 1 - * ' - 1 - */ -#define PATH_MAX_LENGTH 58 - -#define DSN_MIN_LENGTH 1 -#define DSN_MAX_LENGTH 44 -#define MEMBER_MIN_LENGTH 1 -#define MEMBER_MAX_LENGTH 8 - - size_t pathLength = strlen(path); - - if (pathLength < PATH_MIN_LENGTH || PATH_MAX_LENGTH < pathLength) { - return false; - } - - if (memcmp(path, DSPATH_PREFIX, strlen(DSPATH_PREFIX))) { - return false; - } - if (memcmp(&path[pathLength - 1], DSPATH_SUFFIX, strlen(DSPATH_SUFFIX))) { - return false; - } - - const char *dsnStart = path + strlen(DSPATH_PREFIX); - const char *dsnEnd = path + pathLength - 1 - strlen(DSPATH_SUFFIX); - - const char *leftParen = strchr(dsnStart, '('); - const char *rightParen = strchr(dsnStart, ')'); - - if (!leftParen ^ !rightParen) { - return false; - } - - if (leftParen) { - - ptrdiff_t dsnLength = leftParen - dsnStart; - if (dsnLength < DSN_MIN_LENGTH || DSN_MAX_LENGTH < dsnLength) { - return false; - } - - if (rightParen != dsnEnd) { - return false; - } - - ptrdiff_t memberNameLength = rightParen - leftParen - 1; - if (memberNameLength < MEMBER_MIN_LENGTH || MEMBER_MAX_LENGTH < memberNameLength) { - return false; - } - - } else { - - ptrdiff_t dsnLength = dsnEnd - dsnStart + 1; - if (dsnLength < DSN_MIN_LENGTH || DSN_MAX_LENGTH < dsnLength) { - return false; - } - - } - -#undef PATH_MIN_LENGTH -#undef PATH_MAX_LENGTH - -#undef DSN_MIN_LENGTH -#undef DSN_MAX_LENGTH -#undef MEMBER_MIN_LENGTH -#undef MEMBER_MAX_LENGTH - - return true; - -} - -static void extractDatasetAndMemberName(const char *datasetPath, - DatasetName *dsn, - DatasetMemberName *memberName) { - - memset(&dsn->value, ' ', sizeof(dsn->value)); - memset(&memberName->value, ' ', sizeof(memberName->value)); - - size_t pathLength = strlen(datasetPath); - - const char *dsnStart = datasetPath + strlen(DSPATH_PREFIX); - const char *leftParen = strchr(datasetPath, '('); - - if (leftParen) { - memcpy(dsn->value, dsnStart, leftParen - dsnStart); - const char *rightParen = strchr(datasetPath, ')'); - memcpy(memberName->value, leftParen + 1, rightParen - leftParen - 1); - } else { - memcpy(dsn->value, dsnStart, - pathLength - strlen(DSPATH_PREFIX""DSPATH_SUFFIX)); - } - - for (int i = 0; i < sizeof(dsn->value); i++) { - dsn->value[i] = toupper(dsn->value[i]); - } - - for (int i = 0; i < sizeof(memberName->value); i++) { - memberName->value[i] = toupper(memberName->value[i]); - } - -} - -#undef DSPATH_PREFIX -#undef DSPATH_SUFFIX - -static void respondWithDYNALLOCError(HttpResponse *response, - int rc, int sysRC, int sysRSN, - const DynallocDatasetName *dsn, - const DynallocMemberName *member, - const char *site) { - - if (rc == RC_DYNALLOC_SVC99_FAILED && sysRC == 4) { - - if (sysRSN == 0x020C0000 || sysRSN == 0x02100000) { - respondWithMessage(response, HTTP_STATUS_FORBIDDEN, - "Dataset \'%44.44s(%8.8s)\' busy (%s)", - dsn->name, member->name, site); - return; - } - - if (sysRSN == 0x02180000) { - respondWithMessage(response, HTTP_STATUS_NOT_FOUND, - "Device not available for dataset \'%44.44s(%8.8s)\' " - "(%s)", dsn->name, member->name, site); - return; - } - - if (sysRSN == 0x023C0000) { - respondWithMessage(response, HTTP_STATUS_NOT_FOUND, - "Catalog not available for dataset \'%44.44s(%8.8s)\' " - "(%s)", dsn->name, member->name, site); - return; - } - - } - - respondWithMessage(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, - "DYNALLOC failed with RC = %d, DYN RC = %d, RSN = 0x%08X, " - "dsn=\'%44.44s(%8.8s)\', (%s)", rc, sysRC, sysRSN, - dsn->name, member->name, site); - -} - #define IS_DAMEMBER_EMPTY($member) \ (!memcmp(&($member), &(DynallocMemberName){" "}, sizeof($member))) @@ -1299,77 +983,6 @@ static void updateVSAMDatasetWithJSON(HttpResponse *response, JsonObject *json, #define NATIVE_CODEPAGE CCSID_EBCDIC_1047 #endif -/* - Dataset only, no wildcards or pds members accepted - beware that this should be wrapped with authentication - */ -static int getVolserForDataset(const DatasetName *dataset, Volser *volser) { - if (dataset == NULL){ - return -1; - } - int length = sizeof(dataset->value); - - char dsnNullTerm[45] = {0}; - memcpy(dsnNullTerm, dataset->value, sizeof(dataset->value)); - - int lParenIndex = indexOf(dsnNullTerm, length, '(', 0); - int asterixIndex = indexOf(dsnNullTerm, length, '*', 0); - int dollarIndex = indexOf(dsnNullTerm, length, '$', 0); - if (lParenIndex > -1 || asterixIndex > -1 || dollarIndex > -1){ - return -1; - } - csi_parmblock * __ptr32 returnParms = (csi_parmblock* __ptr32)safeMalloc31(sizeof(csi_parmblock),"CSI ParmBlock"); - EntryDataSet *entrySet = returnEntries(dsnNullTerm, defaultDatasetTypesAllowed,3, 0, defaultCSIFields, defaultCSIFieldCount, NULL, NULL, returnParms); - - EntryData *entry = entrySet->entries ? entrySet->entries[0] : NULL; - int rc = -1; - if (entry){ - char *fieldData = (char*)entry+sizeof(EntryData); - unsigned short *fieldLengthArray = ((unsigned short *)((char*)entry+sizeof(EntryData))); - char *fieldValueStart = (char*)entry+sizeof(EntryData)+defaultCSIFieldCount*sizeof(short); - for (int j=0; jvalue,fieldValueStart,6); - rc = 0; - break; - } - fieldValueStart += fieldLengthArray[j]; - } - } - - for (int i = 0; i < entrySet->length; i++){ - EntryData *currentEntry = entrySet->entries[i]; - int fieldDataLength = currentEntry->data.fieldInfoHeader.totalLength; - int entrySize = sizeof(EntryData)+fieldDataLength-4; - safeFree((char*)(currentEntry),entrySize); - } - if (entrySet->entries != NULL) { - safeFree((char*)(entrySet->entries),sizeof(EntryData*)*entrySet->size); - entrySet->entries = NULL; - } - safeFree((char*)entrySet,sizeof(EntryDataSet)); - safeFree31((char*)returnParms,sizeof(csi_parmblock)); - return rc; -} - -static char getRecordLengthType(char *dscb){ - int posOffset = 44; - int recfm = dscb[84 - posOffset]; - if ((recfm & 0xc0) == 0xc0){ - return 'U'; - } - else if (recfm & 0x80){ - return 'F'; - } - else if (recfm & 0x40){ - return 'V'; - } -} - -static int getMaxRecordLength(char *dscb){ - int posOffset = 44; - int lrecl = (dscb[88-posOffset] << 8) | dscb[89-posOffset]; - return lrecl; -} void updateDataset(HttpResponse* response, char* absolutePath, int jsonMode) { #ifdef __ZOWE_OS_ZOS diff --git a/c/dsutils.c b/c/dsutils.c new file mode 100644 index 000000000..5e63b80f7 --- /dev/null +++ b/c/dsutils.c @@ -0,0 +1,417 @@ +#ifdef METTLE +/* HAS NOT BEEN COMPILED WITH METTLE BEFORE */ +#else +#include +#include +#include +#include +#include +#include +#include +#include "zowetypes.h" +#include "alloc.h" +#include "utils.h" +#include "json.h" +#include "bpxnet.h" +#include "logging.h" +#include "unixfile.h" +#ifdef __ZOWE_OS_ZOS +#include "zos.h" +#endif + +#include "charsets.h" + +#include "socketmgmt.h" +#include "httpserver.h" +#include "datasetjson.h" +#include "pdsutil.h" +#include "jcsi.h" +#include "impersonation.h" +#include "dynalloc.h" +#include "utils.h" +#include "vsam.h" +#include "qsam.h" +#include "icsf.h" + + + +#include "dsutils.h" + + +#ifdef __ZOWE_OS_ZOS +/* + Dataset only, no wildcards or pds members accepted - beware that this should be wrapped with authentication + */ +int getVolserForDataset(const DatasetName *dataset, Volser *volser) { + if (dataset == NULL){ + return -1; + } + int length = sizeof(dataset->value); + + char dsnNullTerm[45] = {0}; + memcpy(dsnNullTerm, dataset->value, sizeof(dataset->value)); + + int lParenIndex = indexOf(dsnNullTerm, length, '(', 0); + int asterixIndex = indexOf(dsnNullTerm, length, '*', 0); + int dollarIndex = indexOf(dsnNullTerm, length, '$', 0); + if (lParenIndex > -1 || asterixIndex > -1 || dollarIndex > -1){ + return -1; + } + csi_parmblock * __ptr32 returnParms = (csi_parmblock* __ptr32)safeMalloc31(sizeof(csi_parmblock),"CSI ParmBlock"); + EntryDataSet *entrySet = returnEntries(dsnNullTerm, defaultDatasetTypesAllowed,3, 0, defaultCSIFields, defaultCSIFieldCount, NULL, NULL, returnParms); + + EntryData *entry = entrySet->entries ? entrySet->entries[0] : NULL; + int rc = -1; + if (entry){ + char *fieldData = (char*)entry+sizeof(EntryData); + unsigned short *fieldLengthArray = ((unsigned short *)((char*)entry+sizeof(EntryData))); + char *fieldValueStart = (char*)entry+sizeof(EntryData)+defaultCSIFieldCount*sizeof(short); + for (int j=0; jvalue,fieldValueStart,6); + rc = 0; + break; + } + fieldValueStart += fieldLengthArray[j]; + } + } + + for (int i = 0; i < entrySet->length; i++){ + EntryData *currentEntry = entrySet->entries[i]; + int fieldDataLength = currentEntry->data.fieldInfoHeader.totalLength; + int entrySize = sizeof(EntryData)+fieldDataLength-4; + safeFree((char*)(currentEntry),entrySize); + } + if (entrySet->entries != NULL) { + safeFree((char*)(entrySet->entries),sizeof(EntryData*)*entrySet->size); + entrySet->entries = NULL; + } + safeFree((char*)entrySet,sizeof(EntryDataSet)); + safeFree31((char*)returnParms,sizeof(csi_parmblock)); + return rc; +} + +char getRecordLengthType(char *dscb){ + int posOffset = 44; + int recfm = dscb[84 - posOffset]; + if ((recfm & 0xc0) == 0xc0){ + return 'U'; + } + else if (recfm & 0x80){ + return 'F'; + } + else if (recfm & 0x40){ + return 'V'; + } +} + +int getMaxRecordLength(char *dscb){ + int posOffset = 44; + int lrecl = (dscb[88-posOffset] << 8) | dscb[89-posOffset]; + return lrecl; +} + +int obtainDSCB1(const char *dsname, unsigned int dsnameLength, + const char *volser, unsigned int volserLength, + char *dscb1) { + +#define DSCB1_SIZE 96 + +#define SVC27_WORKAREA_SIZE 140 +#define SVC27_OPCODE_SEARCH 0xC100 +#define SVC27_OPCODE_SEEK 0xC080 + +#define SVC27_OPTION_NO_TIOT_ENQ 0x80 +#define SVC27_OPTION_NO_DUMMY_DSCB1 0x40 +#define SVC27_OPTION_NO_CAT_ALLOC 0x20 +#define SVC27_OPTION_HIDE_NAME 0x10 +#define SVC27_OPTION_EADSCB_OK 0x08 + + ALLOC_STRUCT31( + STRUCT31_NAME(mem31), + STRUCT31_FIELDS( + char dsnameSpacePadded[44]; + char volserSpacePadded[6]; + char workArea[SVC27_WORKAREA_SIZE]; + __packed struct { + int operationCode : 16; + int option : 8; + int dscbNumber : 8; + char * __ptr32 dsname; + char * __ptr32 volser; + char * __ptr32 workArea; + } parmList; + ) + ); + + memset(mem31->dsnameSpacePadded, ' ', sizeof(mem31->dsnameSpacePadded)); + memcpy(mem31->dsnameSpacePadded, dsname, dsnameLength); + memset(mem31->volserSpacePadded, ' ', sizeof(mem31->volserSpacePadded)); + memcpy(mem31->volserSpacePadded, volser, volserLength); + + memset(mem31->workArea, 0, sizeof(mem31->workArea)); + + mem31->parmList.operationCode = SVC27_OPCODE_SEARCH; + mem31->parmList.option = SVC27_OPTION_EADSCB_OK; + mem31->parmList.dscbNumber = 0; + mem31->parmList.dsname = mem31->dsnameSpacePadded; + mem31->parmList.volser = mem31->volserSpacePadded; + mem31->parmList.workArea = mem31->workArea; + + int rc = 0; + + __asm( + " LA 1,0(%1) \n" + " SVC 27 \n" + " ST 15,%0 \n" + : "=m"(rc) + : "r"(&mem31->parmList) + : "r0", "r15" + ); + + memcpy(dscb1, mem31->workArea, DSCB1_SIZE); + + FREE_STRUCT31( + STRUCT31_NAME(mem31) + ); + + return rc; +} + + +int getLreclOrRespondError(HttpResponse *response, + const DatasetName *dsn, + const char *ddPath){ + + int lrecl = 0; + + FileInfo info; + int returnCode; + int reasonCode; + FILE *in = fopen(ddPath, "r"); + if (in == NULL) { + respondWithError(response,HTTP_STATUS_NOT_FOUND,"File could not be opened or does not exist"); + return 0; + } + + Volser volser; + memset(&volser.value, ' ', sizeof(volser.value)); + + int volserSuccess = getVolserForDataset(dsn, &volser); + int handledThroughDSCB = FALSE; + + if (!volserSuccess){ + + char dscb[INDEXED_DSCB] = {0}; + int rc = obtainDSCB1(dsn->value, sizeof(dsn->value), + volser.value, sizeof(volser.value), + dscb); + if (rc == 0){ + if (DSCB_TRACE){ + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "DSCB for %.*s found\n", sizeof(dsn->value), dsn->value); + dumpbuffer(dscb,INDEXED_DSCB); + } + + lrecl = getMaxRecordLength(dscb); + char recordType = getRecordLengthType(dscb); + if (recordType == 'U'){ + fclose(in); + respondWithError(response, HTTP_STATUS_BAD_REQUEST,"Undefined-length dataset"); + return 0; + } + handledThroughDSCB = TRUE; + } + } + if (!handledThroughDSCB){ + FileInfo info; + fldata_t fileinfo = {0}; + char filenameOutput[100]; + int returnCode = fldata(in,filenameOutput,&fileinfo); + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "FLData request rc=0x%x\n",returnCode); + if (!returnCode) { + if (fileinfo.__recfmU) { + fclose(in); + respondWithError(response, HTTP_STATUS_BAD_REQUEST, + "Undefined-length dataset"); + return 0; + } + lrecl = fileinfo.__maxreclen; + } else { + fclose(in); + respondWithError(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, + "Could not read dataset information"); + return 0; + } + } + fclose(in); + + return lrecl; +} + +#define DSPATH_PREFIX "//\'" +#define DSPATH_SUFFIX "\'" + +bool isDatasetPathValid(const char *path) { + + /* Basic check. The fopen() dataset path format is //'dsn(member)' */ + + /* Min dataset file name: + * //' - 3 + * min ds name - 1 + * ' - 1 + */ +#define PATH_MIN_LENGTH 5 + + /* Max dataset file name: + * //' - 3 + * man ds name - 44 + * ( - 1 + * max member name - 8 + * ) - 1 + * ' - 1 + */ +#define PATH_MAX_LENGTH 58 + +#define DSN_MIN_LENGTH 1 +#define DSN_MAX_LENGTH 44 +#define MEMBER_MIN_LENGTH 1 +#define MEMBER_MAX_LENGTH 8 + + size_t pathLength = strlen(path); + + if (pathLength < PATH_MIN_LENGTH || PATH_MAX_LENGTH < pathLength) { + return false; + } + + if (memcmp(path, DSPATH_PREFIX, strlen(DSPATH_PREFIX))) { + return false; + } + if (memcmp(&path[pathLength - 1], DSPATH_SUFFIX, strlen(DSPATH_SUFFIX))) { + return false; + } + + const char *dsnStart = path + strlen(DSPATH_PREFIX); + const char *dsnEnd = path + pathLength - 1 - strlen(DSPATH_SUFFIX); + + const char *leftParen = strchr(dsnStart, '('); + const char *rightParen = strchr(dsnStart, ')'); + + if (!leftParen ^ !rightParen) { + return false; + } + + if (leftParen) { + + ptrdiff_t dsnLength = leftParen - dsnStart; + if (dsnLength < DSN_MIN_LENGTH || DSN_MAX_LENGTH < dsnLength) { + return false; + } + + if (rightParen != dsnEnd) { + return false; + } + + ptrdiff_t memberNameLength = rightParen - leftParen - 1; + if (memberNameLength < MEMBER_MIN_LENGTH || MEMBER_MAX_LENGTH < memberNameLength) { + return false; + } + + } else { + + ptrdiff_t dsnLength = dsnEnd - dsnStart + 1; + if (dsnLength < DSN_MIN_LENGTH || DSN_MAX_LENGTH < dsnLength) { + return false; + } + + } + +#undef PATH_MIN_LENGTH +#undef PATH_MAX_LENGTH + +#undef DSN_MIN_LENGTH +#undef DSN_MAX_LENGTH +#undef MEMBER_MIN_LENGTH +#undef MEMBER_MAX_LENGTH + + return true; + +} + +#define DSPATH_PREFIX "//\'" +#define DSPATH_SUFFIX "\'" + +void extractDatasetAndMemberName(const char *datasetPath, + DatasetName *dsn, + DatasetMemberName *memberName) { + + memset(&dsn->value, ' ', sizeof(dsn->value)); + memset(&memberName->value, ' ', sizeof(memberName->value)); + + size_t pathLength = strlen(datasetPath); + + const char *dsnStart = datasetPath + strlen(DSPATH_PREFIX); + const char *leftParen = strchr(datasetPath, '('); + + if (leftParen) { + memcpy(dsn->value, dsnStart, leftParen - dsnStart); + const char *rightParen = strchr(datasetPath, ')'); + memcpy(memberName->value, leftParen + 1, rightParen - leftParen - 1); + } else { + memcpy(dsn->value, dsnStart, + pathLength - strlen(DSPATH_PREFIX""DSPATH_SUFFIX)); + } + + for (int i = 0; i < sizeof(dsn->value); i++) { + dsn->value[i] = toupper(dsn->value[i]); + } + + for (int i = 0; i < sizeof(memberName->value); i++) { + memberName->value[i] = toupper(memberName->value[i]); + } + +} + +#undef DSPATH_PREFIX +#undef DSPATH_SUFFIX + +void respondWithDYNALLOCError(HttpResponse *response, + int rc, int sysRC, int sysRSN, + const DynallocDatasetName *dsn, + const DynallocMemberName *member, + const char *site) { + + if (rc == RC_DYNALLOC_SVC99_FAILED && sysRC == 4) { + + if (sysRSN == 0x020C0000 || sysRSN == 0x02100000) { + respondWithMessage(response, HTTP_STATUS_FORBIDDEN, + "Dataset \'%44.44s(%8.8s)\' busy (%s)", + dsn->name, member->name, site); + return; + } + + if (sysRSN == 0x02180000) { + respondWithMessage(response, HTTP_STATUS_NOT_FOUND, + "Device not available for dataset \'%44.44s(%8.8s)\' " + "(%s)", dsn->name, member->name, site); + return; + } + + if (sysRSN == 0x023C0000) { + respondWithMessage(response, HTTP_STATUS_NOT_FOUND, + "Catalog not available for dataset \'%44.44s(%8.8s)\' " + "(%s)", dsn->name, member->name, site); + return; + } + + } + + respondWithMessage(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, + "DYNALLOC failed with RC = %d, DYN RC = %d, RSN = 0x%08X, " + "dsn=\'%44.44s(%8.8s)\', (%s)", rc, sysRC, sysRSN, + dsn->name, member->name, site); + +} + +#endif /* __ZOWE_OS_ZOS */ + +#endif /* not METTLE - the whole module */ diff --git a/c/jesService.c b/c/jesService.c new file mode 100644 index 000000000..d7e671663 --- /dev/null +++ b/c/jesService.c @@ -0,0 +1,101 @@ + + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + + +#ifdef METTLE +#include +#include +#include +#include +#include +#include "metalio.h" +#include "qsam.h" +#else +#include +#include +#include +#include +#include +#endif + +#include "zowetypes.h" +#include "alloc.h" +#include "bpxnet.h" +#include "zos.h" +#include "utils.h" +#include "socketmgmt.h" + +#include "httpserver.h" +#include "dataservice.h" +#include "json.h" +#include "datasetjson.h" +#include "logging.h" +#include "zssLogging.h" + +#include "datasetService.h" +#include "jesService.h" + +#ifdef __ZOWE_OS_ZOS + +static int serveJesRequests(HttpService *service, HttpResponse *response){ + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG2, "begin %s\n", __FUNCTION__); + HttpRequest *request = response->request; + + if (!strcmp(request->method, methodPUT)) { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG, "Submit Job/s from jcl content or dataset.\n"); + responseWithSubmitJobs(response, TRUE); + } + else { + jsonPrinter *out = respondWithJsonPrinter(response); + + setContentType(response, "text/json"); + setResponseStatus(response, 405, "Method Not Allowed"); + addStringHeader(response, "Server", "jdmfws"); + addStringHeader(response, "Transfer-Encoding", "chunked"); + addStringHeader(response, "Allow", "GET, PUT, DELETE"); + writeHeader(response); + + jsonStart(out); + jsonEnd(out); + + finishResponse(response); + } + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG2, "end %s\n", __FUNCTION__); + return 0; +} + +void installJesService(HttpServer *server) { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_INFO, ZSS_LOG_INSTALL_MSG, "jes requests"); + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG2, "begin %s\n", __FUNCTION__); + + HttpService *httpService = makeGeneratedService("jes", "/jes/**"); + httpService->authType = SERVICE_AUTH_NATIVE_WITH_SESSION_TOKEN; + httpService->runInSubtask = TRUE; + httpService->doImpersonation = TRUE; + httpService->serviceFunction = serveJesRequests; + httpService->paramSpecList = makeStringParamSpec("force",SERVICE_ARG_OPTIONAL, NULL); + registerHttpService(server, httpService); +} + +#endif /* __ZOWE_OS_ZOS */ + + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + diff --git a/c/jesrequestsjson.c b/c/jesrequestsjson.c new file mode 100644 index 000000000..47c0286aa --- /dev/null +++ b/c/jesrequestsjson.c @@ -0,0 +1,490 @@ + + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + +#ifdef METTLE +/* HAS NOT BEEN COMPILED WITH METTLE BEFORE */ +#else +#include +#include +#include +#include +#include +#include +/* #include */ +#include "zowetypes.h" +#include "alloc.h" +#include "utils.h" +#include "json.h" +#include "bpxnet.h" +#include "logging.h" +#include "unixfile.h" +#ifdef __ZOWE_OS_ZOS +#include "zos.h" +#endif + +#include "charsets.h" + +#include "socketmgmt.h" +#include "httpserver.h" +#include "datasetjson.h" +#include "pdsutil.h" +#include "jcsi.h" +#include "impersonation.h" +#include "dynalloc.h" +#include "utils.h" +#include "vsam.h" +#include "qsam.h" +#include "dsutils.h" +#include "jesrequestsjson.h" + +#pragma linkage(ZWESSI, OS) + +#define INDEXED_DSCB 96 +#define JCL_RECLEN 80 +#define RPL_RET_OFFSET 60 +#define JOB_ID_LEN 8 +#define DDNAME_LEN 8 +#define ERROR_BUF_LEN 256 +#define MSG_BUF_LEN 132 + +#define JOB_ID_EMPTY "????????" +#define CHECK_BIT(var,pos) (((var)>>(pos)) & 1) + +#define IS_DAMEMBER_EMPTY($member) \ + (!memcmp(&($member), &(DynallocMemberName){" "}, sizeof($member))) + +#ifndef NATIVE_CODEPAGE +#define NATIVE_CODEPAGE CCSID_EBCDIC_1047 +#endif + + + + +void responseWithJobDetails(HttpResponse* response, char* jobID, int jsonMode) { +#ifdef __ZOWE_OS_ZOS + + HttpRequest *request = response->request; + + jsonPrinter *jPrinter = respondWithJsonPrinter(response); + setResponseStatus(response, 200, "OK"); + setDefaultJSONRESTHeaders(response); + + writeHeader(response); + + jsonStart(jPrinter); + + jsonAddUnterminatedString(jPrinter, "jobId", jobID, 8); + + jsonEnd(jPrinter); + finishResponse(response); + +#else /* not __ZOWE_OS_ZOS */ + + /* Currently nothing else has "datasets" */ + /* TBD: Is it really necessary to provide this empty array? + It seems like the safest approach, not knowing anyting about the client.. + */ + +#endif /* __ZOWE_OS_ZOS */ +} + +static int getJobID(HttpResponse* response, RPLCommon *rpl){ +#ifdef __ZOWE_OS_ZOS + char *jobID; + + __asm( + " ENDREQ RPL=(%0) \n" + : + :"r"(rpl) + :); + jobID = (char *) rpl + RPL_RET_OFFSET; + + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "jobID_Length (%d)\n", strlen(jobID)); + if (!strlen(jobID)) { + respondWithMessage(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, + "BAD_REQUEST failed with RC = %d, Submit input data does not start with a valid job line", 12); + } + else { + responseWithJobDetails(response, jobID, TRUE); + } + + return 0; +#endif /* __ZOWE_OS_ZOS */ +} + + +void responseWithSubmitJCLContents(HttpResponse* response, char* jclLines) { +#ifdef __ZOWE_OS_ZOS + + int i=0; + int j=0; + + while(jclLines[i]!='\0') + { + if(jclLines[i]=='\n') { + jclLines[i]='\0'; + j++; + } + i++; + } + + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "number of lines (%d-%s-%d)\n", j, jclLines, strlen(jclLines)); + + int macrfParms = 0; + int rplParms = 0; + int rc = 0; + char ddnameResult[DDNAME_LEN + 1] = " "; + char errorBuffer[ERROR_BUF_LEN + 1]; + unsigned int maxlrecl = JCL_RECLEN; + unsigned int maxbuffer = maxlrecl; + + char *outACB = NULL; + RPLCommon *rpl = NULL; + + rc = AllocIntReader("A", ddnameResult, errorBuffer); + + outACB = openACB(ddnameResult, ACB_MODE_OUTPUT, macrfParms, 0, rplParms, maxlrecl, maxbuffer); + + if (outACB) { + rpl = (RPLCommon *)(outACB+RPL_COMMON_OFFSET+8); + if (rpl) { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "rpl ok\n"); + } + else { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "rpl error\n"); + } + } + else { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "ACB error ok\n"); + } + + int maxRecordLength = 80; + char *jobID; + char *tJCL; + int recordLength; + tJCL = jclLines; + for ( i = 0; i < j; i++) { + recordLength = strlen(tJCL); + if (recordLength > maxRecordLength) { + respondWithError(response, HTTP_STATUS_BAD_REQUEST,"Invalid jcl line length 80"); + return; + recordLength = maxRecordLength; + } + tJCL = tJCL + recordLength + 1; + } + + /*passed record length check and type check*/ + int bytesWritten = 0; + int recordsWritten = 0; + char recordBuffer[maxRecordLength+1]; + + char *record = jclLines; + + for (int i = 0; i < j; i++) { + int recordLength = strlen(record); + if (recordLength == 0) { //this is a hack, which will be removed as we move away from fwrite + record = " "; + recordLength = 1; + } + snprintf (recordBuffer, sizeof(recordBuffer), "%-*s", maxRecordLength, record); + + /* output to INTRDR */ + putRecord(outACB, recordBuffer, maxRecordLength); + record = record + recordLength + 1; + } + + rc = getJobID(response, rpl); + closeACB(outACB, ACB_MODE_OUTPUT); + +#endif /* __ZOWE_OS_ZOS */ + +} + +void responseWithSubmitJobs(HttpResponse* response, int jsonMode) { +#ifdef __ZOWE_OS_ZOS + + if (jsonMode != TRUE) { /*TODO add support for updating files with raw bytes instead of JSON*/ + respondWithError(response, HTTP_STATUS_BAD_REQUEST,"Cannot update file without JSON formatted record request"); + return; + } + + HttpRequest *request = response->request; + HttpRequestParam *forceParam = getCheckedParam(request,"force"); + char *forceArg = (forceParam ? forceParam->stringValue : NULL); + bool force = (forceArg != NULL && !strcmp(forceArg,"true")); + + FileInfo info; + int returnCode; + int reasonCode; + char fileName[49]; + + char *contentBody = response->request->contentBody; + int bodyLength = strlen(contentBody); + + char *convertedBody = safeMalloc(bodyLength*4,"writeDatasetConvert"); + int conversionBufferLength = bodyLength*4; + int translationLength; + int outCCSID = NATIVE_CODEPAGE; + + returnCode = convertCharset(contentBody, + bodyLength, + CCSID_UTF_8, + CHARSET_OUTPUT_USE_BUFFER, + &convertedBody, + conversionBufferLength, + outCCSID, + NULL, + &translationLength, + &reasonCode); + + if(returnCode == 0) { + int blockSize = 0x10000; + int maxBlockCount = (translationLength*2)/blockSize; + if (!maxBlockCount){ + maxBlockCount = 0x10; + } + ShortLivedHeap *slh = makeShortLivedHeap(blockSize,maxBlockCount); + char errorBuffer[2048]; + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "Submit Job: before JSON parse dataLength=0x%x\n",bodyLength); + Json *json = jsonParseUnterminatedString(slh, + convertedBody, translationLength, + errorBuffer, sizeof(errorBuffer)); + if (json) { + if (jsonIsObject(json)) { + JsonObject *jsonObject = jsonAsObject(json); + char *fileValue = jsonObjectGetString(jsonObject,"file"); + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "fileValue...(%s)\n", fileValue); + if (fileValue) { + snprintf(fileName, sizeof(fileName), "//'%s'", fileValue); + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "filename...(%s)\n", fileName); + respondWithSubmitDataset(response, fileName, TRUE); + } + else { + char *jclValue = jsonObjectGetString(jsonObject,"jcl"); + if (jclValue) { + if (jclValue[0] != '/' | jclValue[1] != '/') { + respondWithMessage(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, + "BAD_REQUEST failed with RC = %d, Submit input data does not start with a slash", 8); + } + else { + responseWithSubmitJCLContents(response, jclValue); + } + } + else { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "jclValue...(%s)\n", jclValue); + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "No jcl Value...(%s)\n", jclValue); + respondWithError(response, HTTP_STATUS_BAD_REQUEST,"POST body No jcl value"); + } + + } + /* updateDatasetWithJSON(response, jsonObject, absolutePath, etag, force); */ + } else { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "*** INTERNAL ERROR *** message is JSON, but not an object\n"); + } + } + else { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "UPDATE DATASET: body was not JSON!\n"); + respondWithError(response, HTTP_STATUS_BAD_REQUEST,"POST body could not be parsed as JSON format"); + } + SLHFree(slh); + } + else { + respondWithError(response,HTTP_STATUS_INTERNAL_SERVER_ERROR,convertedBody); + } + safeFree(convertedBody,conversionBufferLength); + +#endif /* __ZOWE_OS_ZOS */ +} + + +static void respondWithSubmitDatasetInternal(HttpResponse* response, + const char *datasetPath, + const DatasetName *dsn, + const DDName *ddName, + int jsonMode) { +#ifdef __ZOWE_OS_ZOS + HttpRequest *request = response->request; + + char ddPath[16]; + snprintf(ddPath, sizeof(ddPath), "DD:%8.8s", ddName->value); + + int lrecl = getLreclOrRespondError(response, dsn, ddPath); + if (!lrecl) { + return; + } + + if (lrecl){ + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "Streaming data to INTRDR for %s\n", datasetPath); + + /* jsonStart(jPrinter); */ + int status = streamDatasetToINTRDR(response, ddPath, lrecl, TRUE); + /* jsonEnd(jPrinter); */ + } + +#endif /* __ZOWE_OS_ZOS */ +} + +void respondWithSubmitDataset(HttpResponse* response, char* absolutePath, int jsonMode) { +#ifdef __ZOWE_OS_ZOS + + HttpRequest *request = response->request; + + if (!isDatasetPathValid(absolutePath)) { + respondWithError(response, HTTP_STATUS_BAD_REQUEST, "Invalid dataset name"); + return; + } + + DatasetName dsn; + DatasetMemberName memberName; + extractDatasetAndMemberName(absolutePath, &dsn, &memberName); + + DynallocDatasetName daDsn; + DynallocMemberName daMember; + memcpy(daDsn.name, dsn.value, sizeof(daDsn.name)); + memcpy(daMember.name, memberName.value, sizeof(daMember.name)); + DynallocDDName daDDname = {.name = JOB_ID_EMPTY}; + + int daRC = RC_DYNALLOC_OK, daSysRC = 0, daSysRSN = 0; + daRC = dynallocAllocDataset( + &daDsn, + IS_DAMEMBER_EMPTY(daMember) ? NULL : &daMember, + &daDDname, + DYNALLOC_DISP_SHR, + DYNALLOC_ALLOC_FLAG_NO_CONVERSION | DYNALLOC_ALLOC_FLAG_NO_MOUNT, + &daSysRC, &daSysRSN + ); + + if (daRC != RC_DYNALLOC_OK) { + zowelog(NULL, LOG_COMP_DATASERVICE, ZOWE_LOG_DEBUG, + "error: ds alloc dsn=\'%44.44s\', member=\'%8.8s\', dd=\'%8.8s\'," + " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", + daDsn.name, daMember.name, daDDname.name, daRC, daSysRC, daSysRSN); + respondWithDYNALLOCError(response, daRC, daSysRC, daSysRSN, + &daDsn, &daMember, "r"); + return; + } + + DDName ddName; + memcpy(&ddName.value, &daDDname.name, sizeof(ddName.value)); + + respondWithSubmitDatasetInternal(response, absolutePath, &dsn, &ddName, jsonMode); + + daRC = dynallocUnallocDatasetByDDName(&daDDname, DYNALLOC_UNALLOC_FLAG_NONE, + &daSysRC, &daSysRSN); + if (daRC != RC_DYNALLOC_OK) { + zowelog(NULL, LOG_COMP_DATASERVICE, ZOWE_LOG_DEBUG, + "error: ds unalloc dsn=\'%44.44s\', member=\'%8.8s\', dd=\'%8.8s\'," + " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", + daDsn.name, daMember.name, daDDname.name, daRC, daSysRC, daSysRSN, "read"); + } +#endif /* __ZOWE_OS_ZOS */ +} + +int streamDatasetToINTRDR(HttpResponse* response, char *filename, int recordLength, int jsonMode){ +#ifdef __ZOWE_OS_ZOS + // Note: to allow processing of zero-length records set _EDC_ZERO_RECLEN=Y + + int macrfParms = 0; + int rplParms = 0; + int rc = 0; + char ddnameResult[DDNAME_LEN + 1] = " "; + char errorBuffer[ERROR_BUF_LEN + 1]; + unsigned int maxlrecl = JCL_RECLEN; + unsigned int maxbuffer = maxlrecl; + + char *outACB = NULL; + RPLCommon *rpl = NULL; + + rc = AllocIntReader("A", ddnameResult, errorBuffer); + + outACB = openACB(ddnameResult, ACB_MODE_OUTPUT, macrfParms, 0, rplParms, maxlrecl, maxbuffer); + + if (outACB) { + rpl = (RPLCommon *)(outACB+RPL_COMMON_OFFSET+8); + if (rpl) { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "rpl ok\n"); + } + else { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "rpl error\n"); + } + } + else { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "ACB error ok\n"); + } + + int defaultSize = DATA_STREAM_BUFFER_SIZE; + FILE *in; + if (recordLength < 1){ + recordLength = defaultSize; + in = fopen(filename,"rb"); + } + else { + in = fopen(filename,"rb, type=record"); + } + + int bufferSize = recordLength+1; + char buffer[bufferSize]; + char record[bufferSize]; + char jobID[9]; + int contentLength = 0; + int bytesRead = 0; + if (in) { + while (!feof(in)){ + bytesRead = fread(buffer,1,recordLength,in); + if (bytesRead > 0 && !ferror(in)) { + memset(record, ' ', bytesRead); + memcpy(record, buffer, bytesRead - 1); + putRecord(outACB, record, bytesRead); + contentLength = contentLength + bytesRead; + } else if (bytesRead == 0 && !feof(in) && !ferror(in)) { + // empty record + /* jsonAddString(jPrinter, NULL, ""); */ + } else if (ferror(in)) { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "Error reading DSN=%s, rc=%d\n", filename, bytesRead); + break; + } + } + + rc = getJobID(response, rpl); + closeACB(outACB, ACB_MODE_OUTPUT); + fclose(in); + + } + else { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "FAILED TO OPEN FILE\n"); + } + +#else /* not __ZOWE_OS_ZOS */ + + /* Currently nothing else has "datasets" */ + /* TBD: Is it really necessary to provide this empty array? + It seems like the safest approach, not knowing anyting about the client.. + */ + + int contentLength = 0; +#endif /* not __ZOWE_OS_ZOS */ + return contentLength; +} + +#endif /* not METTLE - the whole module */ + + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + diff --git a/c/zss.c b/c/zss.c index 63452e83f..33a6e399d 100644 --- a/c/zss.c +++ b/c/zss.c @@ -1604,6 +1604,7 @@ int main(int argc, char **argv){ installUnixFileTableOfContentsService(server); /* This needs to be registered last */ #ifdef __ZOWE_OS_ZOS loadCsi(); + installJesService(server); installVSAMDatasetContentsService(server); installDatasetMetadataService(server); installDatasetContentsService(server); diff --git a/h/dsutils.h b/h/dsutils.h new file mode 100644 index 000000000..e5dbf6bc9 --- /dev/null +++ b/h/dsutils.h @@ -0,0 +1,76 @@ + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + +#ifndef __DSUTILS__ +#define __DSUTILS__ 1 + +#include "httpserver.h" +#include "dynalloc.h" + + +#define INDEXED_DSCB 96 + +static char defaultDatasetTypesAllowed[3] = {'A','D','X'}; +static char *defaultCSIFields[] ={ "NAME ", "TYPE ", "VOLSER "}; +static int defaultCSIFieldCount = 3; + +const int DSCB_TRACE = FALSE; + + +typedef struct DatasetMemberName_tag { + char value[8]; /* space-padded */ +} DatasetMemberName; + +typedef struct DDName_tag { + char value[8]; /* space-padded */ +} DDName; + +typedef struct DatasetName_tag { + char value[44]; /* space-padded */ +} DatasetName; + +typedef struct Volser_tag { + char value[6]; /* space-padded */ +} Volser; + +int getVolserForDataset(const DatasetName *dataset, Volser *volser); +char getRecordLengthType(char *dscb); +int getMaxRecordLength(char *dscb); + +int obtainDSCB1(const char *dsname, unsigned int dsnameLength, + const char *volser, unsigned int volserLength, + char *dscb1); +int getLreclOrRespondError(HttpResponse *response, + const DatasetName *dsn, + const char *ddPath); +bool isDatasetPathValid(const char *path); + +void extractDatasetAndMemberName(const char *datasetPath, + DatasetName *dsn, + DatasetMemberName *memberName); +void respondWithDYNALLOCError(HttpResponse *response, + int rc, int sysRC, int sysRSN, + const DynallocDatasetName *dsn, + const DynallocMemberName *member, + const char *site); +#endif + + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + diff --git a/h/jesService.h b/h/jesService.h new file mode 100644 index 000000000..2c6d42d49 --- /dev/null +++ b/h/jesService.h @@ -0,0 +1,30 @@ + + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + +#ifndef __JES_SERVICE_H__ +#define __JES_SERVICE_H__ + +void installJesService(HttpServer *server); + +#endif /* __JES_SERVICE_H__ */ + + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + diff --git a/h/jesrequestsjson.h b/h/jesrequestsjson.h new file mode 100644 index 000000000..07c5f081f --- /dev/null +++ b/h/jesrequestsjson.h @@ -0,0 +1,36 @@ + + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + +#ifndef __JESREQUESTSJSON__ +#define __JESREQUESTSJSON__ 1 + + +#include "stcbase.h" +#include "json.h" +#include "xml.h" +#include "jcsi.h" + +void respondWithSubmitDataset(HttpResponse* response, char* absolutePath, int jsonMode); +int streamDatasetToINTRDR(HttpResponse* response, char *filename, int recordLength, int jsonMode); + +#endif + + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ \ No newline at end of file From 1afc9d23d02c198478fb98af88c734fd09c571a6 Mon Sep 17 00:00:00 2001 From: 1000TurquoisePogs Date: Mon, 21 Mar 2022 17:10:43 -0400 Subject: [PATCH 2/8] Update pointer Signed-off-by: 1000TurquoisePogs --- deps/zowe-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/zowe-common-c b/deps/zowe-common-c index 6be2ba4c9..314b65a8d 160000 --- a/deps/zowe-common-c +++ b/deps/zowe-common-c @@ -1 +1 @@ -Subproject commit 6be2ba4c9b1969427edd4ed4a69236dfe864a401 +Subproject commit 314b65a8d96df39d0573883a8e122c6f45bf3d69 From e76fe2b0d5bb1e9c5724e5c7a99b2ff145216793 Mon Sep 17 00:00:00 2001 From: Aditya Ranshinge Date: Tue, 22 Mar 2022 16:41:32 +0530 Subject: [PATCH 3/8] configure log component Signed-off-by: Aditya Ranshinge --- c/zss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/c/zss.c b/c/zss.c index db2e162c5..ebf865a99 100644 --- a/c/zss.c +++ b/c/zss.c @@ -400,11 +400,11 @@ static void configureAndSetComponentLogLevel(LogComponentsMap *logComponent, Jso memset(logCompName, '\0', logCompLen); strcpy(logCompName, logCompPrefix); strcat(logCompName, logComponent->name); + logConfigureComponent(NULL, logComponent->compID, (char *)logComponent->name, + LOG_DEST_PRINTF_STDOUT, ZOWE_LOG_INFO); if (jsonObjectHasKey(logLevels, logCompName)) { logLevel = jsonObjectGetNumber(logLevels, logCompName); if (isLogLevelValid(logLevel)) { - logConfigureComponent(NULL, logComponent->compID, (char *)logComponent->name, - LOG_DEST_PRINTF_STDOUT, ZOWE_LOG_INFO); logSetLevel(NULL, logComponent->compID, logLevel); } } From bbd29cc5b25d4f01bfd9949266a1266b3cfa3f35 Mon Sep 17 00:00:00 2001 From: ojelis Date: Mon, 16 May 2022 16:28:27 -0400 Subject: [PATCH 4/8] Updated and addressed review points related to PR402 Signed-off-by: ojelis --- build/build_zss.sh | 1 - build/build_zss64.sh | 1 - c/datasetjson.c | 46 ++-- c/dsutils.c | 67 +++--- c/jesService.c | 457 ++++++++++++++++++++++++++++++++++++++-- c/jesrequestsjson.c | 490 ------------------------------------------- h/dsutils.h | 114 ++++++++-- h/jesService.h | 1 + h/jesrequestsjson.h | 36 ---- 9 files changed, 596 insertions(+), 617 deletions(-) delete mode 100644 c/jesrequestsjson.c delete mode 100644 h/jesrequestsjson.h diff --git a/build/build_zss.sh b/build/build_zss.sh index 5cf7c13d3..f94978a66 100755 --- a/build/build_zss.sh +++ b/build/build_zss.sh @@ -141,7 +141,6 @@ if c89 \ ${ZSS}/c/dsutils.c \ ${ZSS}/c/envService.c \ ${ZSS}/c/jesService.c \ - ${ZSS}/c/jesrequestsjson.c \ ${ZSS}/c/zosDiscovery.c \ ${ZSS}/c/securityService.c \ ${ZSS}/c/registerProduct.c \ diff --git a/build/build_zss64.sh b/build/build_zss64.sh index c99fe4bae..084dce6f9 100755 --- a/build/build_zss64.sh +++ b/build/build_zss64.sh @@ -116,7 +116,6 @@ if ! c89 \ ${ZSS}/c/dsutils.c \ ${ZSS}/c/envService.c \ ${ZSS}/c/jesService.c \ - ${ZSS}/c/jesrequestsjson.c \ ${ZSS}/c/zosDiscovery.c \ ${ZSS}/c/securityService.c \ ${ZSS}/c/registerProduct.c \ diff --git a/c/datasetjson.c b/c/datasetjson.c index 14d08801f..9b18d3c22 100644 --- a/c/datasetjson.c +++ b/c/datasetjson.c @@ -470,7 +470,7 @@ void addDetailedDatasetMetadata(char *datasetName, int nameLength, int isPDS = FALSE; zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "Going to check dataset %s attributes\n",datasetName); char dscb[INDEXED_DSCB] = {0}; - int rc = obtainDSCB1(datasetName, nameLength, + int rc = dsutilsObtainDSCB1(datasetName, nameLength, volser, volserLength, dscb); if (rc == 0){ @@ -497,7 +497,7 @@ void addMemberedDatasetMetadata(char *datasetName, int nameLength, int isPDS = FALSE; char dscb[INDEXED_DSCB] = {0}; - int rc = obtainDSCB1(datasetName, nameLength, + int rc = dsutilsObtainDSCB1(datasetName, nameLength, volser, volserLength, dscb); @@ -594,11 +594,11 @@ static void updateDatasetWithJSONInternal(HttpResponse* response, Volser volser; memset(&volser.value, ' ', sizeof(volser.value)); - int volserSuccess = getVolserForDataset(dsn, &volser); + int volserSuccess = dsutilsGetVolserForDataset(dsn, &volser); if (!volserSuccess){ char dscb[INDEXED_DSCB] = {0}; - int rc = obtainDSCB1(dsn->value, sizeof(dsn->value), + int rc = dsutilsObtainDSCB1(dsn->value, sizeof(dsn->value), volser.value, sizeof(volser.value), dscb); if (rc == 0){ @@ -626,8 +626,8 @@ static void updateDatasetWithJSONInternal(HttpResponse* response, } } - maxRecordLength = getMaxRecordLength(dscb); - char recordType = getRecordLengthType(dscb); + maxRecordLength = dsutilsGetMaxRecordLength(dscb); + char recordType = dsutilsGetRecordLengthType(dscb); if (recordType == 'F'){ isFixed = TRUE; } else if (recordType == 'U') { @@ -803,7 +803,7 @@ static void updateDatasetWithJSON(HttpResponse *response, JsonObject *json, char HttpRequest *request = response->request; - if (!isDatasetPathValid(datasetPath)) { + if (!dsutilsIsDatasetPathValid(datasetPath)) { respondWithError(response, HTTP_STATUS_BAD_REQUEST, "Invalid dataset name"); return; } @@ -815,7 +815,7 @@ static void updateDatasetWithJSON(HttpResponse *response, JsonObject *json, char DatasetName dsn; DatasetMemberName memberName; - extractDatasetAndMemberName(datasetPath, &dsn, &memberName); + dsutilsExtractDatasetAndMemberName(datasetPath, &dsn, &memberName); DynallocDatasetName daDsn; DynallocMemberName daMember; @@ -838,7 +838,7 @@ static void updateDatasetWithJSON(HttpResponse *response, JsonObject *json, char "error: ds alloc dsn=\'%44.44s\', member=\'%8.8s\', dd=\'%8.8s\'," " rc=%d sysRC=%d, sysRSN=0x%08X (update)\n", daDsn.name, daMember.name, daDDname.name, daRC, daSysRC, daSysRSN, "update"); - respondWithDYNALLOCError(response, daRC, daSysRC, daSysRSN, + dsutilsRespondWithDYNALLOCError(response, daRC, daSysRC, daSysRSN, &daDsn, &daMember, "w"); return; } @@ -856,7 +856,7 @@ static void updateDatasetWithJSON(HttpResponse *response, JsonObject *json, char int eTagRC = 0; if (!force) { //do not write dataset if current contents do not match contents client expected, unless forced int eTagReturnLength = 0; - int lrecl = getLreclOrRespondError(response, &dsn, ddPath); + int lrecl = dsutilsGetLreclOrRespondError(response, &dsn, ddPath); if (lrecl) { char *eTag = getDatasetETag(ddPath, lrecl, &eTagRC, &eTagReturnLength); zowelog(NULL, LOG_COMP_DATASERVICE, ZOWE_LOG_INFO, "Given etag=%s, current etag=%s\n",lastEtag, eTag); @@ -1062,14 +1062,14 @@ void updateDataset(HttpResponse* response, char* absolutePath, int jsonMode) { void deleteDatasetOrMember(HttpResponse* response, char* absolutePath) { #ifdef __ZOWE_OS_ZOS HttpRequest *request = response->request; - if (!isDatasetPathValid(absolutePath)) { + if (!dsutilsIsDatasetPathValid(absolutePath)) { respondWithError(response, HTTP_STATUS_BAD_REQUEST, "Invalid dataset name"); return; } DatasetName datasetName; DatasetMemberName memberName; - extractDatasetAndMemberName(absolutePath, &datasetName, &memberName); + dsutilsExtractDatasetAndMemberName(absolutePath, &datasetName, &memberName); DynallocDatasetName daDatasetName; DynallocMemberName daMemberName; memcpy(daDatasetName.name, datasetName.value, sizeof(daDatasetName.name)); @@ -1105,7 +1105,7 @@ void deleteDatasetOrMember(HttpResponse* response, char* absolutePath) { " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", daDatasetName.name, daMemberName.name, daDDName.name, daReturnCode, daSysReturnCode, daSysReasonCode); - respondWithDYNALLOCError(response, daReturnCode, daSysReturnCode, + dsutilsRespondWithDYNALLOCError(response, daReturnCode, daSysReturnCode, daSysReasonCode, &daDatasetName, &daMemberName, "r"); return; @@ -1124,7 +1124,7 @@ void deleteDatasetOrMember(HttpResponse* response, char* absolutePath) { " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", daDatasetName.name, daMemberName.name, daDDName.name, daReturnCode, daSysReturnCode, daSysReasonCode); - respondWithDYNALLOCError(response, daReturnCode, daSysReturnCode, + dsutilsRespondWithDYNALLOCError(response, daReturnCode, daSysReturnCode, daSysReasonCode, &daDatasetName, &daMemberName, "r"); return; @@ -1192,7 +1192,7 @@ void deleteDatasetOrMember(HttpResponse* response, char* absolutePath) { " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", daDatasetName.name, daMemberName.name, daDDName.name, daReturnCode, daSysReturnCode, daSysReasonCode); - respondWithDYNALLOCError(response, daReturnCode, daSysReturnCode, + dsutilsRespondWithDYNALLOCError(response, daReturnCode, daSysReturnCode, daSysReasonCode, &daDatasetName, &daMemberName, "r"); return; @@ -1266,7 +1266,7 @@ char getCSIType(char* absolutePath) { DatasetName datasetName; DatasetMemberName memberName; - extractDatasetAndMemberName(absolutePath, &datasetName, &memberName); + dsutilsExtractDatasetAndMemberName(absolutePath, &datasetName, &memberName); char dsNameNullTerm[DATASET_NAME_LEN + 1] = {0}; memcpy(dsNameNullTerm, datasetName.value, sizeof(datasetName.value)); @@ -1294,7 +1294,7 @@ void deleteVSAMDataset(HttpResponse* response, char* absolutePath) { #ifdef __ZOWE_OS_ZOS HttpRequest *request = response->request; - if (!isDatasetPathValid(absolutePath)) { + if (!dsutilsIsDatasetPathValid(absolutePath)) { respondWithError(response, HTTP_STATUS_BAD_REQUEST, "Invalid dataset name"); return; } @@ -1428,7 +1428,7 @@ static void respondWithDatasetInternal(HttpResponse* response, char ddPath[16]; snprintf(ddPath, sizeof(ddPath), "DD:%8.8s", ddName->value); - int lrecl = getLreclOrRespondError(response, dsn, ddPath); + int lrecl = dsutilsGetLreclOrRespondError(response, dsn, ddPath); if (!lrecl) { return; } @@ -1454,14 +1454,14 @@ void respondWithDataset(HttpResponse* response, char* absolutePath, int jsonMode HttpRequest *request = response->request; - if (!isDatasetPathValid(absolutePath)) { + if (!dsutilsIsDatasetPathValid(absolutePath)) { respondWithError(response, HTTP_STATUS_BAD_REQUEST, "Invalid dataset name"); return; } DatasetName dsn; DatasetMemberName memberName; - extractDatasetAndMemberName(absolutePath, &dsn, &memberName); + dsutilsExtractDatasetAndMemberName(absolutePath, &dsn, &memberName); DynallocDatasetName daDsn; DynallocMemberName daMember; @@ -1484,7 +1484,7 @@ void respondWithDataset(HttpResponse* response, char* absolutePath, int jsonMode "error: ds alloc dsn=\'%44.44s\', member=\'%8.8s\', dd=\'%8.8s\'," " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", daDsn.name, daMember.name, daDDname.name, daRC, daSysRC, daSysRSN); - respondWithDYNALLOCError(response, daRC, daSysRC, daSysRSN, + dsutilsRespondWithDYNALLOCError(response, daRC, daSysRC, daSysRSN, &daDsn, &daMember, "r"); return; } @@ -1840,7 +1840,7 @@ void respondWithDatasetMetadata(HttpResponse *response) { char *absDsPathTemp = stringConcatenate(response->slh, "//'", percentDecoded); char *absDsPath = stringConcatenate(response->slh, absDsPathTemp, "'"); - if(!isDatasetPathValid(absDsPath)){ + if(!dsutilsIsDatasetPathValid(absDsPath)){ respondWithError(response,HTTP_STATUS_BAD_REQUEST,"Invalid dataset path"); return; } @@ -1852,7 +1852,7 @@ void respondWithDatasetMetadata(HttpResponse *response) { DatasetMemberName memName; int memberNameLength = 0; - extractDatasetAndMemberName(absDsPath, &dsnName, &memName); + dsutilsExtractDatasetAndMemberName(absDsPath, &dsnName, &memName); memberNameLength = (unsigned int)rParenIndex - (unsigned int)lParenIndex -1; HttpRequestParam *addQualifiersParam = getCheckedParam(request,"addQualifiers"); diff --git a/c/dsutils.c b/c/dsutils.c index 5e63b80f7..081fe554a 100644 --- a/c/dsutils.c +++ b/c/dsutils.c @@ -1,40 +1,33 @@ + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + #ifdef METTLE -/* HAS NOT BEEN COMPILED WITH METTLE BEFORE */ +#error Metal C is not supported #else #include #include #include #include #include -#include #include #include "zowetypes.h" #include "alloc.h" #include "utils.h" -#include "json.h" -#include "bpxnet.h" #include "logging.h" -#include "unixfile.h" #ifdef __ZOWE_OS_ZOS #include "zos.h" #endif - -#include "charsets.h" - -#include "socketmgmt.h" -#include "httpserver.h" -#include "datasetjson.h" -#include "pdsutil.h" #include "jcsi.h" -#include "impersonation.h" #include "dynalloc.h" #include "utils.h" -#include "vsam.h" -#include "qsam.h" -#include "icsf.h" - - - #include "dsutils.h" @@ -42,7 +35,7 @@ /* Dataset only, no wildcards or pds members accepted - beware that this should be wrapped with authentication */ -int getVolserForDataset(const DatasetName *dataset, Volser *volser) { +int dsutilsGetVolserForDataset(const DatasetName *dataset, Volser *volser) { if (dataset == NULL){ return -1; } @@ -91,7 +84,7 @@ int getVolserForDataset(const DatasetName *dataset, Volser *volser) { return rc; } -char getRecordLengthType(char *dscb){ +char dsutilsGetRecordLengthType(char *dscb){ int posOffset = 44; int recfm = dscb[84 - posOffset]; if ((recfm & 0xc0) == 0xc0){ @@ -105,13 +98,13 @@ char getRecordLengthType(char *dscb){ } } -int getMaxRecordLength(char *dscb){ +int dsutilsGetMaxRecordLength(char *dscb){ int posOffset = 44; int lrecl = (dscb[88-posOffset] << 8) | dscb[89-posOffset]; return lrecl; } -int obtainDSCB1(const char *dsname, unsigned int dsnameLength, +int dsutilsObtainDSCB1(const char *dsname, unsigned int dsnameLength, const char *volser, unsigned int volserLength, char *dscb1) { @@ -179,7 +172,7 @@ int obtainDSCB1(const char *dsname, unsigned int dsnameLength, } -int getLreclOrRespondError(HttpResponse *response, +int dsutilsGetLreclOrRespondError(HttpResponse *response, const DatasetName *dsn, const char *ddPath){ @@ -190,20 +183,20 @@ int getLreclOrRespondError(HttpResponse *response, int reasonCode; FILE *in = fopen(ddPath, "r"); if (in == NULL) { - respondWithError(response,HTTP_STATUS_NOT_FOUND,"File could not be opened or does not exist"); + respondWithError(response, HTTP_STATUS_NOT_FOUND, "File could not be opened or does not exist"); return 0; } Volser volser; memset(&volser.value, ' ', sizeof(volser.value)); - int volserSuccess = getVolserForDataset(dsn, &volser); + int volserSuccess = dsutilsGetVolserForDataset(dsn, &volser); int handledThroughDSCB = FALSE; if (!volserSuccess){ char dscb[INDEXED_DSCB] = {0}; - int rc = obtainDSCB1(dsn->value, sizeof(dsn->value), + int rc = dsutilsObtainDSCB1(dsn->value, sizeof(dsn->value), volser.value, sizeof(volser.value), dscb); if (rc == 0){ @@ -212,8 +205,8 @@ int getLreclOrRespondError(HttpResponse *response, dumpbuffer(dscb,INDEXED_DSCB); } - lrecl = getMaxRecordLength(dscb); - char recordType = getRecordLengthType(dscb); + lrecl = dsutilsGetMaxRecordLength(dscb); + char recordType = dsutilsGetRecordLengthType(dscb); if (recordType == 'U'){ fclose(in); respondWithError(response, HTTP_STATUS_BAD_REQUEST,"Undefined-length dataset"); @@ -251,7 +244,7 @@ int getLreclOrRespondError(HttpResponse *response, #define DSPATH_PREFIX "//\'" #define DSPATH_SUFFIX "\'" -bool isDatasetPathValid(const char *path) { +bool dsutilsIsDatasetPathValid(const char *path) { /* Basic check. The fopen() dataset path format is //'dsn(member)' */ @@ -340,7 +333,7 @@ bool isDatasetPathValid(const char *path) { #define DSPATH_PREFIX "//\'" #define DSPATH_SUFFIX "\'" -void extractDatasetAndMemberName(const char *datasetPath, +void dsutilsExtractDatasetAndMemberName(const char *datasetPath, DatasetName *dsn, DatasetMemberName *memberName) { @@ -374,7 +367,7 @@ void extractDatasetAndMemberName(const char *datasetPath, #undef DSPATH_PREFIX #undef DSPATH_SUFFIX -void respondWithDYNALLOCError(HttpResponse *response, +void dsutilsRespondWithDYNALLOCError(HttpResponse *response, int rc, int sysRC, int sysRSN, const DynallocDatasetName *dsn, const DynallocMemberName *member, @@ -415,3 +408,13 @@ void respondWithDYNALLOCError(HttpResponse *response, #endif /* __ZOWE_OS_ZOS */ #endif /* not METTLE - the whole module */ + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ \ No newline at end of file diff --git a/c/jesService.c b/c/jesService.c index d7e671663..44dd5c9ee 100644 --- a/c/jesService.c +++ b/c/jesService.c @@ -10,27 +10,19 @@ Copyright Contributors to the Zowe Project. */ - -#ifdef METTLE -#include -#include -#include -#include -#include -#include "metalio.h" -#include "qsam.h" -#else #include #include #include #include #include -#endif + +#include +#include +#include #include "zowetypes.h" #include "alloc.h" #include "bpxnet.h" -#include "zos.h" #include "utils.h" #include "socketmgmt.h" @@ -44,6 +36,42 @@ #include "datasetService.h" #include "jesService.h" +#include "unixfile.h" +#ifdef __ZOWE_OS_ZOS +#include "zos.h" +#endif + +#include "charsets.h" +#include "pdsutil.h" +#include "jcsi.h" +#include "impersonation.h" +#include "dynalloc.h" +#include "vsam.h" +#include "qsam.h" +#include "dsutils.h" + +#pragma linkage(ZWESSI, OS) + +#define INDEXED_DSCB 96 +#define JCL_RECLEN 80 +#define RPL_RET_OFFSET 60 +#define JOB_ID_LEN 8 +#define DDNAME_LEN 8 +#define ERROR_BUF_LEN 256 +#define MSG_BUF_LEN 132 +#define MAXCC_8 8 +#define MAXCC_12 12 + +#define JOB_ID_EMPTY "????????" +#define CHECK_BIT(var,pos) (((var)>>(pos)) & 1) + +#define IS_DAMEMBER_EMPTY($member) \ + (!memcmp(&($member), &(DynallocMemberName){" "}, sizeof($member))) + +#ifndef NATIVE_CODEPAGE +#define NATIVE_CODEPAGE CCSID_EBCDIC_1047 +#endif + #ifdef __ZOWE_OS_ZOS static int serveJesRequests(HttpService *service, HttpResponse *response){ @@ -52,7 +80,7 @@ static int serveJesRequests(HttpService *service, HttpResponse *response){ if (!strcmp(request->method, methodPUT)) { zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG, "Submit Job/s from jcl content or dataset.\n"); - responseWithSubmitJobs(response, TRUE); + responseJesServiceWithSubmitJobs(response, TRUE); } else { jsonPrinter *out = respondWithJsonPrinter(response); @@ -61,7 +89,7 @@ static int serveJesRequests(HttpService *service, HttpResponse *response){ setResponseStatus(response, 405, "Method Not Allowed"); addStringHeader(response, "Server", "jdmfws"); addStringHeader(response, "Transfer-Encoding", "chunked"); - addStringHeader(response, "Allow", "GET, PUT, DELETE"); + addStringHeader(response, "Allow", "PUT"); writeHeader(response); jsonStart(out); @@ -82,12 +110,409 @@ void installJesService(HttpServer *server) { httpService->runInSubtask = TRUE; httpService->doImpersonation = TRUE; httpService->serviceFunction = serveJesRequests; - httpService->paramSpecList = makeStringParamSpec("force",SERVICE_ARG_OPTIONAL, NULL); + httpService->paramSpecList = makeStringParamSpec("force", SERVICE_ARG_OPTIONAL, NULL); registerHttpService(server, httpService); } -#endif /* __ZOWE_OS_ZOS */ +static char* getJobID(RPLCommon *rpl){ + + __asm( + " ENDREQ RPL=(%0) \n" + : + :"r"(rpl) + :); + + return (char *) rpl + RPL_RET_OFFSET; +} + +static void responseWithJobDetails(HttpResponse* response, char* jobID, int jsonMode) { + + HttpRequest *request = response->request; + + jsonPrinter *jPrinter = respondWithJsonPrinter(response); + setResponseStatus(response, 200, "OK"); + setDefaultJSONRESTHeaders(response); + + writeHeader(response); + + jsonStart(jPrinter); + + jsonAddUnterminatedString(jPrinter, "jobId", jobID, strlen(jobID)); + + jsonEnd(jPrinter); + finishResponse(response); +} + +static int validateJCLlengthIsCorrect(char *uJobLines, int numOfLines, int maxRecordLength){ + + int recordLength; + int lineCount=0; + char *tJCL; + tJCL = uJobLines; + + for (lineCount = 0; lineCount < numOfLines; lineCount++) { + recordLength = strlen(tJCL); + if (recordLength > maxRecordLength) { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "Error JCL line details - (%d-%s-%d)\n", lineCount, tJCL, strlen(tJCL)); + return -1; + } + tJCL = tJCL + recordLength + 1; + } +return 0; +} + +static void responseWithSubmitJCLContents(HttpResponse* response, const char* jclLines) { + + int jobLineLength = strlen(jclLines) + 1; + char *jobLines = safeMalloc(jobLineLength, "copyConstjclLines"); + strcpy(jobLines, jclLines); + + int jclLinecnt=0; + int numLines=0; + + while(jobLines[jclLinecnt]!='\0') + { + if(jobLines[jclLinecnt]=='\n') { + jobLines[jclLinecnt]='\0'; + numLines++; + } + jclLinecnt++; + } + + int returnOutputValidJCL = 0; + int maxRecordLength = 80; + returnOutputValidJCL = validateJCLlengthIsCorrect(jobLines, numLines, maxRecordLength); + + if (returnOutputValidJCL != 0){ + respondWithError(response, HTTP_STATUS_BAD_REQUEST, "Invalid jcl line length 80"); + } + + int macrfParms = 0; + int rplParms = 0; + int rc = 0; + char ddnameResult[DDNAME_LEN + 1] = " "; + char errorBuffer[ERROR_BUF_LEN + 1]; + unsigned int maxLrecl = JCL_RECLEN; + unsigned int maxBuffer = maxLrecl; + + char *outACB = NULL; + RPLCommon *rpl = NULL; + + rc = AllocIntReader("A", ddnameResult, errorBuffer); + + if (rc != 0) { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "SubmitJCLContents - Allocate Internal Reader error\n"); + respondWithError(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Allocate Internal Reader error"); + } + + outACB = openACB(ddnameResult, ACB_MODE_OUTPUT, macrfParms, 0, rplParms, maxLrecl, maxBuffer); + + if (outACB) { + rpl = (RPLCommon *)(outACB+RPL_COMMON_OFFSET+8); + } + else { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "SubmitJCLContents - ACB error ok\n"); + respondWithError(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, "ACB error"); + } + + /*passed record length check and type check*/ + int bytesWritten = 0; + int recordsWritten = 0; + char recordBuffer[maxRecordLength+1]; + + char *record = jobLines; + char *jobID; + + for (int jclLinecnt = 0; jclLinecnt < numLines; jclLinecnt++) { + int recordLength = strlen(record); + if (recordLength == 0) { + record = " "; + recordLength = 1; + } + snprintf (recordBuffer, sizeof(recordBuffer), "%-*s", maxRecordLength, record); + + /* output to INTRDR */ + putRecord(outACB, recordBuffer, maxRecordLength); + record = record + recordLength + 1; + } + + jobID = getJobID(rpl); + + char uJobID[JOB_ID_LEN + 1]; + sscanf(jobID, "%s", uJobID); + + if (!strlen(uJobID)){ + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "SubmitJCLContents - RPL error\n"); + respondWithMessage(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, + "BAD_REQUEST failed with RC = %d, Submit input data does not start with a valid job line", MAXCC_12); + } + + responseWithJobDetails(response, uJobID, TRUE); + + closeACB(outACB, ACB_MODE_OUTPUT); + + safeFree(jobLines, jobLineLength); +} + +static void respondWithSubmitDatasetInternal(HttpResponse* response, + const char *datasetPath, + const DatasetName *dsn, + const DDName *ddName, + int jsonMode) { + + HttpRequest *request = response->request; + + char ddPath[16]; + snprintf(ddPath, sizeof(ddPath), "DD:%8.8s", ddName->value); + + int lrecl = dsutilsGetLreclOrRespondError(response, dsn, ddPath); + if (!lrecl) { + return; + } + + if (lrecl){ + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "Streaming data to INTRDR for %s\n", datasetPath); + + int macrfParms = 0; + int rplParms = 0; + int rc = 0; + int recordLength = lrecl; + char ddnameResult[DDNAME_LEN + 1] = " "; + char errorBuffer[ERROR_BUF_LEN + 1]; + unsigned int maxLrecl = JCL_RECLEN; + unsigned int maxBuffer = maxLrecl; + + char *outACB = NULL; + RPLCommon *rpl = NULL; + + rc = AllocIntReader("A", ddnameResult, errorBuffer); + + if (rc != 0) { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "SubmitDatasetInternal - Allocate Internal Reader error\n"); + respondWithError(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Allocate Internal Reader error"); + } + + outACB = openACB(ddnameResult, ACB_MODE_OUTPUT, macrfParms, 0, rplParms, maxLrecl, maxBuffer); + + if (outACB) { + rpl = (RPLCommon *)(outACB+RPL_COMMON_OFFSET+8); + } + else { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "SubmitDatasetInternal - ACB error\n"); + respondWithError(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, "ACB error"); + } + + int defaultSize = DATA_STREAM_BUFFER_SIZE; + FILE *in; + if (lrecl < 1){ + recordLength = defaultSize; + in = fopen(ddPath,"rb"); + } + else { + in = fopen(ddPath,"rb, type=record"); + } + + int bufferSize = DATA_STREAM_BUFFER_SIZE + 1; + char buffer[bufferSize]; + char record[bufferSize]; + char *jobID; + int contentLength = 0; + int bytesRead = 0; + if (in) { + while (!feof(in)){ + bytesRead = fread(buffer,1,recordLength,in); + if (bytesRead > 0 && !ferror(in)) { + memset(record, ' ', bytesRead); + memcpy(record, buffer, bytesRead - 1); + putRecord(outACB, record, bytesRead); + contentLength = contentLength + bytesRead; + } + else if (ferror(in)) { + int inFileError = ferror(in); + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "Error reading DSN=%s, rc=%d, error no=%d\n", ddPath, bytesRead, inFileError); + break; + } + } + + jobID = getJobID(rpl); + + char uJobID[JOB_ID_LEN + 1]; + sscanf(jobID, "%s", uJobID); + + + if (!strlen(uJobID)){ + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "SubmitDatasetInternal - RPL error\n"); + respondWithMessage(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, + "BAD_REQUEST failed with RC = %d, Submit input data does not start with a valid job line", MAXCC_12); + } + + responseWithJobDetails(response, uJobID, TRUE); + + closeACB(outACB, ACB_MODE_OUTPUT); + fclose(in); + + } + else { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "Failed to open dataset\n"); + } + } +} + +static void respondWithSubmitDataset(HttpResponse* response, char* absolutePath, int jsonMode) { + + HttpRequest *request = response->request; + + if (!dsutilsIsDatasetPathValid(absolutePath)) { + respondWithError(response, HTTP_STATUS_BAD_REQUEST, "Invalid dataset name"); + return; + } + + DatasetName dsn; + DatasetMemberName memberName; + dsutilsExtractDatasetAndMemberName(absolutePath, &dsn, &memberName); + + DynallocDatasetName daDsn; + DynallocMemberName daMember; + memcpy(daDsn.name, dsn.value, sizeof(daDsn.name)); + memcpy(daMember.name, memberName.value, sizeof(daMember.name)); + DynallocDDName daDDname = {.name = JOB_ID_EMPTY}; + + int daRC = RC_DYNALLOC_OK, daSysRC = 0, daSysRSN = 0; + daRC = dynallocAllocDataset( + &daDsn, + IS_DAMEMBER_EMPTY(daMember) ? NULL : &daMember, + &daDDname, + DYNALLOC_DISP_SHR, + DYNALLOC_ALLOC_FLAG_NO_CONVERSION | DYNALLOC_ALLOC_FLAG_NO_MOUNT, + &daSysRC, &daSysRSN + ); + + if (daRC != RC_DYNALLOC_OK) { + zowelog(NULL, LOG_COMP_DATASERVICE, ZOWE_LOG_DEBUG, + "error: ds alloc dsn=\'%44.44s\', member=\'%8.8s\', dd=\'%8.8s\'," + " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", + daDsn.name, daMember.name, daDDname.name, daRC, daSysRC, daSysRSN); + dsutilsRespondWithDYNALLOCError(response, daRC, daSysRC, daSysRSN, + &daDsn, &daMember, "r"); + return; + } + + DDName ddName; + memcpy(&ddName.value, &daDDname.name, sizeof(ddName.value)); + + respondWithSubmitDatasetInternal(response, absolutePath, &dsn, &ddName, jsonMode); + + daRC = dynallocUnallocDatasetByDDName(&daDDname, DYNALLOC_UNALLOC_FLAG_NONE, + &daSysRC, &daSysRSN); + if (daRC != RC_DYNALLOC_OK) { + zowelog(NULL, LOG_COMP_DATASERVICE, ZOWE_LOG_DEBUG, + "error: ds unalloc dsn=\'%44.44s\', member=\'%8.8s\', dd=\'%8.8s\'," + " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", + daDsn.name, daMember.name, daDDname.name, daRC, daSysRC, daSysRSN, "read"); + } +} + +void responseJesServiceWithSubmitJobs(HttpResponse* response, int jsonMode) { + + if (jsonMode != TRUE) { /*TODO add support for updating files with raw bytes instead of JSON*/ + respondWithError(response, HTTP_STATUS_BAD_REQUEST, "Cannot update file without JSON formatted record request"); + return; + } + + HttpRequest *request = response->request; + HttpRequestParam *forceParam = getCheckedParam(request,"force"); + char *forceArg = (forceParam ? forceParam->stringValue : NULL); + bool force = (forceArg != NULL && !strcmp(forceArg,"true")); + + /*FileInfo info;*/ + int returnCode; + int reasonCode; + char fileName[49]; + + char *contentBody = response->request->contentBody; + int bodyLength = strlen(contentBody); + + if (bodyLength){ + + int totalBodyLength = bodyLength*4; + char *convertedBody = safeMalloc(totalBodyLength, "writeDatasetConvert"); + int conversionBufferLength = totalBodyLength; + int translationLength; + int outCCSID = NATIVE_CODEPAGE; + + returnCode = convertCharset(contentBody, + bodyLength, + CCSID_UTF_8, + CHARSET_OUTPUT_USE_BUFFER, + &convertedBody, + conversionBufferLength, + outCCSID, + NULL, + &translationLength, + &reasonCode); + + if(returnCode == 0) { + int blockSize = 0x10000; + int maxBlockCount = (translationLength*2)/blockSize; + if (!maxBlockCount){ + maxBlockCount = 0x10; + } + ShortLivedHeap *slh = makeShortLivedHeap(blockSize, maxBlockCount); + char errorBuffer[2048]; + zowelog(NULL, LOG_COMP_RESTFILE, ZOWE_LOG_DEBUG, "Submit Job: before JSON parse dataLength=%d\n", bodyLength); + Json *json = jsonParseUnterminatedString(slh, + convertedBody, translationLength, + errorBuffer, sizeof(errorBuffer)); + if (json) { + if (jsonIsObject(json)) { + JsonObject *jsonObject = jsonAsObject(json); + char *fileValue = jsonObjectGetString(jsonObject, "file"); + + if (fileValue) { + snprintf(fileName, sizeof(fileName), "//'%s'", fileValue); + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "fileValue...(%s)\n", fileValue); + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "fileName...(%s)\n", fileName); + respondWithSubmitDataset(response, fileName, TRUE); + } + else { + char *jclValue = jsonObjectGetString(jsonObject, "jcl"); + if (jclValue) { + int cmpResult = strncmp(jclValue, "//", 2); + if (cmpResult != 0) { + respondWithMessage(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, + "BAD_REQUEST failed with RC = %d, Submit input data does not start with a slash", MAXCC_8); + } + else { + responseWithSubmitJCLContents(response, jclValue); + } + } + else { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "No jcl Value\n"); + respondWithError(response, HTTP_STATUS_BAD_REQUEST, "POST body No jcl value"); + } + } + } else { + zowelog(NULL, LOG_COMP_RESTFILE, ZOWE_LOG_DEBUG, "*** INTERNAL ERROR *** message is JSON, but not an object\n"); + } + } + else { + zowelog(NULL, LOG_COMP_RESTFILE, ZOWE_LOG_DEBUG, "UPDATE the content, body was not JSON!\n"); + respondWithError(response, HTTP_STATUS_BAD_REQUEST, "POST body could not be parsed as JSON format"); + } + SLHFree(slh); + } + else { + respondWithError(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Could not translate character set"); + } + safeFree(convertedBody,conversionBufferLength); + } + else { + zowelog(NULL, LOG_COMP_RESTFILE, ZOWE_LOG_DEBUG, "Check the content body\n"); + respondWithError(response, HTTP_STATUS_BAD_REQUEST, "Content body is empty"); + } +} +#endif /* not __ZOWE_OS_ZOS */ /* This program and the accompanying materials are diff --git a/c/jesrequestsjson.c b/c/jesrequestsjson.c deleted file mode 100644 index 47c0286aa..000000000 --- a/c/jesrequestsjson.c +++ /dev/null @@ -1,490 +0,0 @@ - - -/* - This program and the accompanying materials are - made available under the terms of the Eclipse Public License v2.0 which accompanies - this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html - - SPDX-License-Identifier: EPL-2.0 - - Copyright Contributors to the Zowe Project. -*/ - -#ifdef METTLE -/* HAS NOT BEEN COMPILED WITH METTLE BEFORE */ -#else -#include -#include -#include -#include -#include -#include -/* #include */ -#include "zowetypes.h" -#include "alloc.h" -#include "utils.h" -#include "json.h" -#include "bpxnet.h" -#include "logging.h" -#include "unixfile.h" -#ifdef __ZOWE_OS_ZOS -#include "zos.h" -#endif - -#include "charsets.h" - -#include "socketmgmt.h" -#include "httpserver.h" -#include "datasetjson.h" -#include "pdsutil.h" -#include "jcsi.h" -#include "impersonation.h" -#include "dynalloc.h" -#include "utils.h" -#include "vsam.h" -#include "qsam.h" -#include "dsutils.h" -#include "jesrequestsjson.h" - -#pragma linkage(ZWESSI, OS) - -#define INDEXED_DSCB 96 -#define JCL_RECLEN 80 -#define RPL_RET_OFFSET 60 -#define JOB_ID_LEN 8 -#define DDNAME_LEN 8 -#define ERROR_BUF_LEN 256 -#define MSG_BUF_LEN 132 - -#define JOB_ID_EMPTY "????????" -#define CHECK_BIT(var,pos) (((var)>>(pos)) & 1) - -#define IS_DAMEMBER_EMPTY($member) \ - (!memcmp(&($member), &(DynallocMemberName){" "}, sizeof($member))) - -#ifndef NATIVE_CODEPAGE -#define NATIVE_CODEPAGE CCSID_EBCDIC_1047 -#endif - - - - -void responseWithJobDetails(HttpResponse* response, char* jobID, int jsonMode) { -#ifdef __ZOWE_OS_ZOS - - HttpRequest *request = response->request; - - jsonPrinter *jPrinter = respondWithJsonPrinter(response); - setResponseStatus(response, 200, "OK"); - setDefaultJSONRESTHeaders(response); - - writeHeader(response); - - jsonStart(jPrinter); - - jsonAddUnterminatedString(jPrinter, "jobId", jobID, 8); - - jsonEnd(jPrinter); - finishResponse(response); - -#else /* not __ZOWE_OS_ZOS */ - - /* Currently nothing else has "datasets" */ - /* TBD: Is it really necessary to provide this empty array? - It seems like the safest approach, not knowing anyting about the client.. - */ - -#endif /* __ZOWE_OS_ZOS */ -} - -static int getJobID(HttpResponse* response, RPLCommon *rpl){ -#ifdef __ZOWE_OS_ZOS - char *jobID; - - __asm( - " ENDREQ RPL=(%0) \n" - : - :"r"(rpl) - :); - jobID = (char *) rpl + RPL_RET_OFFSET; - - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "jobID_Length (%d)\n", strlen(jobID)); - if (!strlen(jobID)) { - respondWithMessage(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, - "BAD_REQUEST failed with RC = %d, Submit input data does not start with a valid job line", 12); - } - else { - responseWithJobDetails(response, jobID, TRUE); - } - - return 0; -#endif /* __ZOWE_OS_ZOS */ -} - - -void responseWithSubmitJCLContents(HttpResponse* response, char* jclLines) { -#ifdef __ZOWE_OS_ZOS - - int i=0; - int j=0; - - while(jclLines[i]!='\0') - { - if(jclLines[i]=='\n') { - jclLines[i]='\0'; - j++; - } - i++; - } - - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "number of lines (%d-%s-%d)\n", j, jclLines, strlen(jclLines)); - - int macrfParms = 0; - int rplParms = 0; - int rc = 0; - char ddnameResult[DDNAME_LEN + 1] = " "; - char errorBuffer[ERROR_BUF_LEN + 1]; - unsigned int maxlrecl = JCL_RECLEN; - unsigned int maxbuffer = maxlrecl; - - char *outACB = NULL; - RPLCommon *rpl = NULL; - - rc = AllocIntReader("A", ddnameResult, errorBuffer); - - outACB = openACB(ddnameResult, ACB_MODE_OUTPUT, macrfParms, 0, rplParms, maxlrecl, maxbuffer); - - if (outACB) { - rpl = (RPLCommon *)(outACB+RPL_COMMON_OFFSET+8); - if (rpl) { - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "rpl ok\n"); - } - else { - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "rpl error\n"); - } - } - else { - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "ACB error ok\n"); - } - - int maxRecordLength = 80; - char *jobID; - char *tJCL; - int recordLength; - tJCL = jclLines; - for ( i = 0; i < j; i++) { - recordLength = strlen(tJCL); - if (recordLength > maxRecordLength) { - respondWithError(response, HTTP_STATUS_BAD_REQUEST,"Invalid jcl line length 80"); - return; - recordLength = maxRecordLength; - } - tJCL = tJCL + recordLength + 1; - } - - /*passed record length check and type check*/ - int bytesWritten = 0; - int recordsWritten = 0; - char recordBuffer[maxRecordLength+1]; - - char *record = jclLines; - - for (int i = 0; i < j; i++) { - int recordLength = strlen(record); - if (recordLength == 0) { //this is a hack, which will be removed as we move away from fwrite - record = " "; - recordLength = 1; - } - snprintf (recordBuffer, sizeof(recordBuffer), "%-*s", maxRecordLength, record); - - /* output to INTRDR */ - putRecord(outACB, recordBuffer, maxRecordLength); - record = record + recordLength + 1; - } - - rc = getJobID(response, rpl); - closeACB(outACB, ACB_MODE_OUTPUT); - -#endif /* __ZOWE_OS_ZOS */ - -} - -void responseWithSubmitJobs(HttpResponse* response, int jsonMode) { -#ifdef __ZOWE_OS_ZOS - - if (jsonMode != TRUE) { /*TODO add support for updating files with raw bytes instead of JSON*/ - respondWithError(response, HTTP_STATUS_BAD_REQUEST,"Cannot update file without JSON formatted record request"); - return; - } - - HttpRequest *request = response->request; - HttpRequestParam *forceParam = getCheckedParam(request,"force"); - char *forceArg = (forceParam ? forceParam->stringValue : NULL); - bool force = (forceArg != NULL && !strcmp(forceArg,"true")); - - FileInfo info; - int returnCode; - int reasonCode; - char fileName[49]; - - char *contentBody = response->request->contentBody; - int bodyLength = strlen(contentBody); - - char *convertedBody = safeMalloc(bodyLength*4,"writeDatasetConvert"); - int conversionBufferLength = bodyLength*4; - int translationLength; - int outCCSID = NATIVE_CODEPAGE; - - returnCode = convertCharset(contentBody, - bodyLength, - CCSID_UTF_8, - CHARSET_OUTPUT_USE_BUFFER, - &convertedBody, - conversionBufferLength, - outCCSID, - NULL, - &translationLength, - &reasonCode); - - if(returnCode == 0) { - int blockSize = 0x10000; - int maxBlockCount = (translationLength*2)/blockSize; - if (!maxBlockCount){ - maxBlockCount = 0x10; - } - ShortLivedHeap *slh = makeShortLivedHeap(blockSize,maxBlockCount); - char errorBuffer[2048]; - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "Submit Job: before JSON parse dataLength=0x%x\n",bodyLength); - Json *json = jsonParseUnterminatedString(slh, - convertedBody, translationLength, - errorBuffer, sizeof(errorBuffer)); - if (json) { - if (jsonIsObject(json)) { - JsonObject *jsonObject = jsonAsObject(json); - char *fileValue = jsonObjectGetString(jsonObject,"file"); - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "fileValue...(%s)\n", fileValue); - if (fileValue) { - snprintf(fileName, sizeof(fileName), "//'%s'", fileValue); - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "filename...(%s)\n", fileName); - respondWithSubmitDataset(response, fileName, TRUE); - } - else { - char *jclValue = jsonObjectGetString(jsonObject,"jcl"); - if (jclValue) { - if (jclValue[0] != '/' | jclValue[1] != '/') { - respondWithMessage(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, - "BAD_REQUEST failed with RC = %d, Submit input data does not start with a slash", 8); - } - else { - responseWithSubmitJCLContents(response, jclValue); - } - } - else { - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "jclValue...(%s)\n", jclValue); - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "No jcl Value...(%s)\n", jclValue); - respondWithError(response, HTTP_STATUS_BAD_REQUEST,"POST body No jcl value"); - } - - } - /* updateDatasetWithJSON(response, jsonObject, absolutePath, etag, force); */ - } else { - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "*** INTERNAL ERROR *** message is JSON, but not an object\n"); - } - } - else { - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "UPDATE DATASET: body was not JSON!\n"); - respondWithError(response, HTTP_STATUS_BAD_REQUEST,"POST body could not be parsed as JSON format"); - } - SLHFree(slh); - } - else { - respondWithError(response,HTTP_STATUS_INTERNAL_SERVER_ERROR,convertedBody); - } - safeFree(convertedBody,conversionBufferLength); - -#endif /* __ZOWE_OS_ZOS */ -} - - -static void respondWithSubmitDatasetInternal(HttpResponse* response, - const char *datasetPath, - const DatasetName *dsn, - const DDName *ddName, - int jsonMode) { -#ifdef __ZOWE_OS_ZOS - HttpRequest *request = response->request; - - char ddPath[16]; - snprintf(ddPath, sizeof(ddPath), "DD:%8.8s", ddName->value); - - int lrecl = getLreclOrRespondError(response, dsn, ddPath); - if (!lrecl) { - return; - } - - if (lrecl){ - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "Streaming data to INTRDR for %s\n", datasetPath); - - /* jsonStart(jPrinter); */ - int status = streamDatasetToINTRDR(response, ddPath, lrecl, TRUE); - /* jsonEnd(jPrinter); */ - } - -#endif /* __ZOWE_OS_ZOS */ -} - -void respondWithSubmitDataset(HttpResponse* response, char* absolutePath, int jsonMode) { -#ifdef __ZOWE_OS_ZOS - - HttpRequest *request = response->request; - - if (!isDatasetPathValid(absolutePath)) { - respondWithError(response, HTTP_STATUS_BAD_REQUEST, "Invalid dataset name"); - return; - } - - DatasetName dsn; - DatasetMemberName memberName; - extractDatasetAndMemberName(absolutePath, &dsn, &memberName); - - DynallocDatasetName daDsn; - DynallocMemberName daMember; - memcpy(daDsn.name, dsn.value, sizeof(daDsn.name)); - memcpy(daMember.name, memberName.value, sizeof(daMember.name)); - DynallocDDName daDDname = {.name = JOB_ID_EMPTY}; - - int daRC = RC_DYNALLOC_OK, daSysRC = 0, daSysRSN = 0; - daRC = dynallocAllocDataset( - &daDsn, - IS_DAMEMBER_EMPTY(daMember) ? NULL : &daMember, - &daDDname, - DYNALLOC_DISP_SHR, - DYNALLOC_ALLOC_FLAG_NO_CONVERSION | DYNALLOC_ALLOC_FLAG_NO_MOUNT, - &daSysRC, &daSysRSN - ); - - if (daRC != RC_DYNALLOC_OK) { - zowelog(NULL, LOG_COMP_DATASERVICE, ZOWE_LOG_DEBUG, - "error: ds alloc dsn=\'%44.44s\', member=\'%8.8s\', dd=\'%8.8s\'," - " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", - daDsn.name, daMember.name, daDDname.name, daRC, daSysRC, daSysRSN); - respondWithDYNALLOCError(response, daRC, daSysRC, daSysRSN, - &daDsn, &daMember, "r"); - return; - } - - DDName ddName; - memcpy(&ddName.value, &daDDname.name, sizeof(ddName.value)); - - respondWithSubmitDatasetInternal(response, absolutePath, &dsn, &ddName, jsonMode); - - daRC = dynallocUnallocDatasetByDDName(&daDDname, DYNALLOC_UNALLOC_FLAG_NONE, - &daSysRC, &daSysRSN); - if (daRC != RC_DYNALLOC_OK) { - zowelog(NULL, LOG_COMP_DATASERVICE, ZOWE_LOG_DEBUG, - "error: ds unalloc dsn=\'%44.44s\', member=\'%8.8s\', dd=\'%8.8s\'," - " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", - daDsn.name, daMember.name, daDDname.name, daRC, daSysRC, daSysRSN, "read"); - } -#endif /* __ZOWE_OS_ZOS */ -} - -int streamDatasetToINTRDR(HttpResponse* response, char *filename, int recordLength, int jsonMode){ -#ifdef __ZOWE_OS_ZOS - // Note: to allow processing of zero-length records set _EDC_ZERO_RECLEN=Y - - int macrfParms = 0; - int rplParms = 0; - int rc = 0; - char ddnameResult[DDNAME_LEN + 1] = " "; - char errorBuffer[ERROR_BUF_LEN + 1]; - unsigned int maxlrecl = JCL_RECLEN; - unsigned int maxbuffer = maxlrecl; - - char *outACB = NULL; - RPLCommon *rpl = NULL; - - rc = AllocIntReader("A", ddnameResult, errorBuffer); - - outACB = openACB(ddnameResult, ACB_MODE_OUTPUT, macrfParms, 0, rplParms, maxlrecl, maxbuffer); - - if (outACB) { - rpl = (RPLCommon *)(outACB+RPL_COMMON_OFFSET+8); - if (rpl) { - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "rpl ok\n"); - } - else { - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "rpl error\n"); - } - } - else { - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "ACB error ok\n"); - } - - int defaultSize = DATA_STREAM_BUFFER_SIZE; - FILE *in; - if (recordLength < 1){ - recordLength = defaultSize; - in = fopen(filename,"rb"); - } - else { - in = fopen(filename,"rb, type=record"); - } - - int bufferSize = recordLength+1; - char buffer[bufferSize]; - char record[bufferSize]; - char jobID[9]; - int contentLength = 0; - int bytesRead = 0; - if (in) { - while (!feof(in)){ - bytesRead = fread(buffer,1,recordLength,in); - if (bytesRead > 0 && !ferror(in)) { - memset(record, ' ', bytesRead); - memcpy(record, buffer, bytesRead - 1); - putRecord(outACB, record, bytesRead); - contentLength = contentLength + bytesRead; - } else if (bytesRead == 0 && !feof(in) && !ferror(in)) { - // empty record - /* jsonAddString(jPrinter, NULL, ""); */ - } else if (ferror(in)) { - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "Error reading DSN=%s, rc=%d\n", filename, bytesRead); - break; - } - } - - rc = getJobID(response, rpl); - closeACB(outACB, ACB_MODE_OUTPUT); - fclose(in); - - } - else { - zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "FAILED TO OPEN FILE\n"); - } - -#else /* not __ZOWE_OS_ZOS */ - - /* Currently nothing else has "datasets" */ - /* TBD: Is it really necessary to provide this empty array? - It seems like the safest approach, not knowing anyting about the client.. - */ - - int contentLength = 0; -#endif /* not __ZOWE_OS_ZOS */ - return contentLength; -} - -#endif /* not METTLE - the whole module */ - - -/* - This program and the accompanying materials are - made available under the terms of the Eclipse Public License v2.0 which accompanies - this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html - - SPDX-License-Identifier: EPL-2.0 - - Copyright Contributors to the Zowe Project. -*/ - diff --git a/h/dsutils.h b/h/dsutils.h index e5dbf6bc9..b8dd9522b 100644 --- a/h/dsutils.h +++ b/h/dsutils.h @@ -24,40 +24,118 @@ static int defaultCSIFieldCount = 3; const int DSCB_TRACE = FALSE; - +/** + * @brief Contains the name of a member in a dataset. + */ typedef struct DatasetMemberName_tag { char value[8]; /* space-padded */ } DatasetMemberName; +/** + * @brief Contains what DD name is used. + */ typedef struct DDName_tag { char value[8]; /* space-padded */ } DDName; +/** + * @brief Contains the name of a dataset. + */ typedef struct DatasetName_tag { char value[44]; /* space-padded */ } DatasetName; +/** + * @brief Contains the serial volume of a dataset. + */ typedef struct Volser_tag { char value[6]; /* space-padded */ } Volser; -int getVolserForDataset(const DatasetName *dataset, Volser *volser); -char getRecordLengthType(char *dscb); -int getMaxRecordLength(char *dscb); - -int obtainDSCB1(const char *dsname, unsigned int dsnameLength, - const char *volser, unsigned int volserLength, - char *dscb1); -int getLreclOrRespondError(HttpResponse *response, - const DatasetName *dsn, - const char *ddPath); -bool isDatasetPathValid(const char *path); - -void extractDatasetAndMemberName(const char *datasetPath, - DatasetName *dsn, - DatasetMemberName *memberName); -void respondWithDYNALLOCError(HttpResponse *response, - int rc, int sysRC, int sysRSN, +/** + * @brief Gets the serial volume of a dataset. + * + * @param dataset dataset to be called when the function is + * invoked. To be validated first and get its value for memcpy. + * @param volser To pass its value and to be used in memcpy. + * @return 0 is returned when the function has no error. + * -1 is returned when an error occurred + */ +int dsutilsGetVolserForDataset(const DatasetName *dataset, Volser *volser); + +/** + * @brief Gets the record format of a dataset. + * + * @param dscb a parameter to get the details of the physical + * characteristics of a dataset. + * @return U is returned when Undefined-length dataset. + * F is returned when it is a fixed block and V for variable block. + */ +char dsutilsGetRecordLengthType(const char *dscb); + +/** + * @brief Gets the max record length of a dataset. + * + * @param dscb a parameter to get the details of the physical + * characteristics of a dataset. + * @return lrecl is the maximum length of a dataset . + */ +int dsutilsGetMaxRecordLength(const char *dscb); + +/** + * @brief Gets the Data Set Control Block of a dataset. + * + * @param dsname a parameter to get the dataset name. + * @param dsnamelength a parameter to get the dataset length. + * @param volser a parameter to get dataset the volume serial. + * @param volserlength a parameter to get the volume serial length. + * @param dscb1 a parameter to get the details of the physical + * characteristics of a dataset. + * @return 0 is successful obtaining the dscb. + */ +int dsutilsObtainDSCB1(const char *dsname, unsigned int dsnameLength, const char *volser, unsigned int volserLength, char *dscb1); + +/** + * @brief Gets the length of a dataset or respond an error. + * + * @param response a parameter to get a response when this + * function encountered an error + * @param dsn a parameter to get the dataset name + * @param ddpath a parameter to get dataset path + * @return lrecl return the length of a dataset + * 0 this function encountered error + */ +int dsutilsGetLreclOrRespondError(HttpResponse *response, const DatasetName *dsn, const char *ddPath); + +/** + * @brief Validate if the dataset path is valid. + * + * @param path a parameter to get the dataset path + * @return true if the path is valid and false if is not + */ +bool dsutilsIsDatasetPathValid(const char *path); + +/** + * @brief Extract dataset name and member name + * + * @param datasetpath a parameter to get the dataset path + * @param dsn a parameter to get the dataset name + * @param membername a parameter to get the member name + */ +void dsutilsExtractDatasetAndMemberName(const char *datasetPath, DatasetName *dsn, DatasetMemberName *memberName); + +/** + * @brief validation if allocation is error and respond + * + * @param response a parameter to get the + * @param rc a parameter to get the + * @param sysrc a parameter to get + * @param sysrsn a parameter to get + * @param dsn a parameter to get + * @param member a parameter to get the + * @param site a parameter to get the + */ +void dsutilsRespondWithDYNALLOCError(HttpResponse *response, int rc, int sysRC, int sysRSN, const DynallocDatasetName *dsn, const DynallocMemberName *member, const char *site); diff --git a/h/jesService.h b/h/jesService.h index 2c6d42d49..c2a5a0c8c 100644 --- a/h/jesService.h +++ b/h/jesService.h @@ -14,6 +14,7 @@ #define __JES_SERVICE_H__ void installJesService(HttpServer *server); +void responseJesServiceWithSubmitJobs(HttpResponse* response, int jsonMode); #endif /* __JES_SERVICE_H__ */ diff --git a/h/jesrequestsjson.h b/h/jesrequestsjson.h deleted file mode 100644 index 07c5f081f..000000000 --- a/h/jesrequestsjson.h +++ /dev/null @@ -1,36 +0,0 @@ - - -/* - This program and the accompanying materials are - made available under the terms of the Eclipse Public License v2.0 which accompanies - this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html - - SPDX-License-Identifier: EPL-2.0 - - Copyright Contributors to the Zowe Project. -*/ - -#ifndef __JESREQUESTSJSON__ -#define __JESREQUESTSJSON__ 1 - - -#include "stcbase.h" -#include "json.h" -#include "xml.h" -#include "jcsi.h" - -void respondWithSubmitDataset(HttpResponse* response, char* absolutePath, int jsonMode); -int streamDatasetToINTRDR(HttpResponse* response, char *filename, int recordLength, int jsonMode); - -#endif - - -/* - This program and the accompanying materials are - made available under the terms of the Eclipse Public License v2.0 which accompanies - this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html - - SPDX-License-Identifier: EPL-2.0 - - Copyright Contributors to the Zowe Project. -*/ \ No newline at end of file From 930c92f3d8a90021f527323d771a8b38f07b1aa3 Mon Sep 17 00:00:00 2001 From: ojelis Date: Fri, 17 Jun 2022 16:27:15 -0400 Subject: [PATCH 5/8] update submodule to the latest Signed-off-by: ojelis --- deps/zowe-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/zowe-common-c b/deps/zowe-common-c index 314b65a8d..418fa4bc9 160000 --- a/deps/zowe-common-c +++ b/deps/zowe-common-c @@ -1 +1 @@ -Subproject commit 314b65a8d96df39d0573883a8e122c6f45bf3d69 +Subproject commit 418fa4bc96ce43d9a06ba398a4149bd071489ea1 From cb382053687a6c1fe1c6354c1e3839e549de488c Mon Sep 17 00:00:00 2001 From: ojelis Date: Tue, 28 Jun 2022 16:15:29 -0400 Subject: [PATCH 6/8] Addressed additional review points Signed-off-by: ojelis --- c/datasetjson.c | 77 ++++++++++++++++++++++++++++++++++++++++++------- c/dsutils.c | 56 ++++------------------------------- c/jesService.c | 54 ++++++++++++++++++++++++++++++---- h/dsutils.h | 24 ++------------- 4 files changed, 121 insertions(+), 90 deletions(-) diff --git a/c/datasetjson.c b/c/datasetjson.c index 9b18d3c22..7abfd2e12 100644 --- a/c/datasetjson.c +++ b/c/datasetjson.c @@ -68,7 +68,7 @@ static char *getDatasetETag(char *filename, int recordLength, int *rc, int *eTag #ifdef __ZOWE_OS_ZOS int rcEtag = 0; int eTagLength = 0; - + // Note: to allow processing of zero-length records set _EDC_ZERO_RECLEN=Y int defaultSize = DATA_STREAM_BUFFER_SIZE; FILE *in; @@ -573,6 +573,44 @@ void addMemberedDatasetMetadata(char *datasetName, int nameLength, #ifdef __ZOWE_OS_ZOS +static void respondWithDYNALLOCError(HttpResponse *response, + int rc, int sysRC, int sysRSN, + const DynallocDatasetName *dsn, + const DynallocMemberName *member, + const char *site) { + + if (rc == RC_DYNALLOC_SVC99_FAILED && sysRC == 4) { + + if (sysRSN == 0x020C0000 || sysRSN == 0x02100000) { + respondWithMessage(response, HTTP_STATUS_FORBIDDEN, + "Dataset \'%44.44s(%8.8s)\' busy (%s)", + dsn->name, member->name, site); + return; + } + + if (sysRSN == 0x02180000) { + respondWithMessage(response, HTTP_STATUS_NOT_FOUND, + "Device not available for dataset \'%44.44s(%8.8s)\' " + "(%s)", dsn->name, member->name, site); + return; + } + + if (sysRSN == 0x023C0000) { + respondWithMessage(response, HTTP_STATUS_NOT_FOUND, + "Catalog not available for dataset \'%44.44s(%8.8s)\' " + "(%s)", dsn->name, member->name, site); + return; + } + + } + + respondWithMessage(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, + "DYNALLOC failed with RC = %d, DYN RC = %d, RSN = 0x%08X, " + "dsn=\'%44.44s(%8.8s)\', (%s)", rc, sysRC, sysRSN, + dsn->name, member->name, site); + +} + #define IS_DAMEMBER_EMPTY($member) \ (!memcmp(&($member), &(DynallocMemberName){" "}, sizeof($member))) @@ -838,7 +876,7 @@ static void updateDatasetWithJSON(HttpResponse *response, JsonObject *json, char "error: ds alloc dsn=\'%44.44s\', member=\'%8.8s\', dd=\'%8.8s\'," " rc=%d sysRC=%d, sysRSN=0x%08X (update)\n", daDsn.name, daMember.name, daDDname.name, daRC, daSysRC, daSysRSN, "update"); - dsutilsRespondWithDYNALLOCError(response, daRC, daSysRC, daSysRSN, + respondWithDYNALLOCError(response, daRC, daSysRC, daSysRSN, &daDsn, &daMember, "w"); return; } @@ -856,7 +894,18 @@ static void updateDatasetWithJSON(HttpResponse *response, JsonObject *json, char int eTagRC = 0; if (!force) { //do not write dataset if current contents do not match contents client expected, unless forced int eTagReturnLength = 0; - int lrecl = dsutilsGetLreclOrRespondError(response, &dsn, ddPath); + int lrecl = dsutilsGetLreclOr(&dsn, ddPath); + if (lrecl == -1) { + respondWithError(response, HTTP_STATUS_NOT_FOUND, "File could not be opened or does not exist"); + } + else if (lrecl == -2){ + respondWithError(response, HTTP_STATUS_BAD_REQUEST, "Undefined-length dataset"); + } + else if (lrecl == -3){ + respondWithError(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, + "Could not read dataset information"); + } + if (lrecl) { char *eTag = getDatasetETag(ddPath, lrecl, &eTagRC, &eTagReturnLength); zowelog(NULL, LOG_COMP_DATASERVICE, ZOWE_LOG_INFO, "Given etag=%s, current etag=%s\n",lastEtag, eTag); @@ -1105,7 +1154,7 @@ void deleteDatasetOrMember(HttpResponse* response, char* absolutePath) { " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", daDatasetName.name, daMemberName.name, daDDName.name, daReturnCode, daSysReturnCode, daSysReasonCode); - dsutilsRespondWithDYNALLOCError(response, daReturnCode, daSysReturnCode, + respondWithDYNALLOCError(response, daReturnCode, daSysReturnCode, daSysReasonCode, &daDatasetName, &daMemberName, "r"); return; @@ -1124,7 +1173,7 @@ void deleteDatasetOrMember(HttpResponse* response, char* absolutePath) { " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", daDatasetName.name, daMemberName.name, daDDName.name, daReturnCode, daSysReturnCode, daSysReasonCode); - dsutilsRespondWithDYNALLOCError(response, daReturnCode, daSysReturnCode, + respondWithDYNALLOCError(response, daReturnCode, daSysReturnCode, daSysReasonCode, &daDatasetName, &daMemberName, "r"); return; @@ -1192,7 +1241,7 @@ void deleteDatasetOrMember(HttpResponse* response, char* absolutePath) { " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", daDatasetName.name, daMemberName.name, daDDName.name, daReturnCode, daSysReturnCode, daSysReasonCode); - dsutilsRespondWithDYNALLOCError(response, daReturnCode, daSysReturnCode, + respondWithDYNALLOCError(response, daReturnCode, daSysReturnCode, daSysReasonCode, &daDatasetName, &daMemberName, "r"); return; @@ -1428,9 +1477,16 @@ static void respondWithDatasetInternal(HttpResponse* response, char ddPath[16]; snprintf(ddPath, sizeof(ddPath), "DD:%8.8s", ddName->value); - int lrecl = dsutilsGetLreclOrRespondError(response, dsn, ddPath); - if (!lrecl) { - return; + int lrecl = dsutilsGetLreclOr(dsn, ddPath); + if (lrecl == -1) { + respondWithError(response, HTTP_STATUS_NOT_FOUND, "File could not be opened or does not exist"); + } + else if (lrecl == -2){ + respondWithError(response, HTTP_STATUS_BAD_REQUEST, "Undefined-length dataset"); + } + else if (lrecl == -3){ + respondWithError(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, + "Could not read dataset information"); } jsonPrinter *jPrinter = respondWithJsonPrinter(response); @@ -1484,7 +1540,7 @@ void respondWithDataset(HttpResponse* response, char* absolutePath, int jsonMode "error: ds alloc dsn=\'%44.44s\', member=\'%8.8s\', dd=\'%8.8s\'," " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", daDsn.name, daMember.name, daDDname.name, daRC, daSysRC, daSysRSN); - dsutilsRespondWithDYNALLOCError(response, daRC, daSysRC, daSysRSN, + respondWithDYNALLOCError(response, daRC, daSysRC, daSysRSN, &daDsn, &daMember, "r"); return; } @@ -2143,4 +2199,3 @@ void respondWithHLQNames(HttpResponse *response, MetadataQueryCache *metadataQue Copyright Contributors to the Zowe Project. */ - diff --git a/c/dsutils.c b/c/dsutils.c index 081fe554a..a6ce58c0d 100644 --- a/c/dsutils.c +++ b/c/dsutils.c @@ -26,7 +26,6 @@ #include "zos.h" #endif #include "jcsi.h" -#include "dynalloc.h" #include "utils.h" #include "dsutils.h" @@ -172,8 +171,7 @@ int dsutilsObtainDSCB1(const char *dsname, unsigned int dsnameLength, } -int dsutilsGetLreclOrRespondError(HttpResponse *response, - const DatasetName *dsn, +int dsutilsGetLreclOr(const DatasetName *dsn, const char *ddPath){ int lrecl = 0; @@ -183,8 +181,7 @@ int dsutilsGetLreclOrRespondError(HttpResponse *response, int reasonCode; FILE *in = fopen(ddPath, "r"); if (in == NULL) { - respondWithError(response, HTTP_STATUS_NOT_FOUND, "File could not be opened or does not exist"); - return 0; + return -1; } Volser volser; @@ -209,8 +206,7 @@ int dsutilsGetLreclOrRespondError(HttpResponse *response, char recordType = dsutilsGetRecordLengthType(dscb); if (recordType == 'U'){ fclose(in); - respondWithError(response, HTTP_STATUS_BAD_REQUEST,"Undefined-length dataset"); - return 0; + return -2; } handledThroughDSCB = TRUE; } @@ -224,16 +220,12 @@ int dsutilsGetLreclOrRespondError(HttpResponse *response, if (!returnCode) { if (fileinfo.__recfmU) { fclose(in); - respondWithError(response, HTTP_STATUS_BAD_REQUEST, - "Undefined-length dataset"); - return 0; + return -2; } lrecl = fileinfo.__maxreclen; } else { fclose(in); - respondWithError(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, - "Could not read dataset information"); - return 0; + return -3; } } fclose(in); @@ -367,44 +359,6 @@ void dsutilsExtractDatasetAndMemberName(const char *datasetPath, #undef DSPATH_PREFIX #undef DSPATH_SUFFIX -void dsutilsRespondWithDYNALLOCError(HttpResponse *response, - int rc, int sysRC, int sysRSN, - const DynallocDatasetName *dsn, - const DynallocMemberName *member, - const char *site) { - - if (rc == RC_DYNALLOC_SVC99_FAILED && sysRC == 4) { - - if (sysRSN == 0x020C0000 || sysRSN == 0x02100000) { - respondWithMessage(response, HTTP_STATUS_FORBIDDEN, - "Dataset \'%44.44s(%8.8s)\' busy (%s)", - dsn->name, member->name, site); - return; - } - - if (sysRSN == 0x02180000) { - respondWithMessage(response, HTTP_STATUS_NOT_FOUND, - "Device not available for dataset \'%44.44s(%8.8s)\' " - "(%s)", dsn->name, member->name, site); - return; - } - - if (sysRSN == 0x023C0000) { - respondWithMessage(response, HTTP_STATUS_NOT_FOUND, - "Catalog not available for dataset \'%44.44s(%8.8s)\' " - "(%s)", dsn->name, member->name, site); - return; - } - - } - - respondWithMessage(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, - "DYNALLOC failed with RC = %d, DYN RC = %d, RSN = 0x%08X, " - "dsn=\'%44.44s(%8.8s)\', (%s)", rc, sysRC, sysRSN, - dsn->name, member->name, site); - -} - #endif /* __ZOWE_OS_ZOS */ #endif /* not METTLE - the whole module */ diff --git a/c/jesService.c b/c/jesService.c index 44dd5c9ee..718865ad4 100644 --- a/c/jesService.c +++ b/c/jesService.c @@ -265,9 +265,16 @@ static void respondWithSubmitDatasetInternal(HttpResponse* response, char ddPath[16]; snprintf(ddPath, sizeof(ddPath), "DD:%8.8s", ddName->value); - int lrecl = dsutilsGetLreclOrRespondError(response, dsn, ddPath); - if (!lrecl) { - return; + int lrecl = dsutilsGetLreclOr(dsn, ddPath); + if (lrecl == -1) { + respondWithError(response, HTTP_STATUS_NOT_FOUND, "File could not be opened or does not exist"); + } + else if (lrecl == -2){ + respondWithError(response, HTTP_STATUS_BAD_REQUEST, "Undefined-length dataset"); + } + else if (lrecl == -3){ + respondWithError(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, + "Could not read dataset information"); } if (lrecl){ @@ -358,6 +365,43 @@ static void respondWithSubmitDatasetInternal(HttpResponse* response, } } +static void respondDYNALLOCerror(HttpResponse *response, + int rc, int sysRC, int sysRSN, + const DynallocDatasetName *dsn, + const DynallocMemberName *member, + const char *site) { + + if (rc == RC_DYNALLOC_SVC99_FAILED && sysRC == 4) { + + if (sysRSN == 0x020C0000 || sysRSN == 0x02100000) { + respondWithMessage(response, HTTP_STATUS_FORBIDDEN, + "Dataset \'%44.44s(%8.8s)\' busy (%s)", + dsn->name, member->name, site); + return; + } + + if (sysRSN == 0x02180000) { + respondWithMessage(response, HTTP_STATUS_NOT_FOUND, + "Device not available for dataset \'%44.44s(%8.8s)\' " + "(%s)", dsn->name, member->name, site); + return; + } + + if (sysRSN == 0x023C0000) { + respondWithMessage(response, HTTP_STATUS_NOT_FOUND, + "Catalog not available for dataset \'%44.44s(%8.8s)\' " + "(%s)", dsn->name, member->name, site); + return; + } + + } + + respondWithMessage(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, + "DYNALLOC failed with RC = %d, DYN RC = %d, RSN = 0x%08X, " + "dsn=\'%44.44s(%8.8s)\', (%s)", rc, sysRC, sysRSN, + dsn->name, member->name, site); +} + static void respondWithSubmitDataset(HttpResponse* response, char* absolutePath, int jsonMode) { HttpRequest *request = response->request; @@ -392,9 +436,8 @@ static void respondWithSubmitDataset(HttpResponse* response, char* absolutePath, "error: ds alloc dsn=\'%44.44s\', member=\'%8.8s\', dd=\'%8.8s\'," " rc=%d sysRC=%d, sysRSN=0x%08X (read)\n", daDsn.name, daMember.name, daDDname.name, daRC, daSysRC, daSysRSN); - dsutilsRespondWithDYNALLOCError(response, daRC, daSysRC, daSysRSN, + respondDYNALLOCerror(response, daRC, daSysRC, daSysRSN, &daDsn, &daMember, "r"); - return; } DDName ddName; @@ -523,4 +566,3 @@ void responseJesServiceWithSubmitJobs(HttpResponse* response, int jsonMode) { Copyright Contributors to the Zowe Project. */ - diff --git a/h/dsutils.h b/h/dsutils.h index b8dd9522b..7b08f5237 100644 --- a/h/dsutils.h +++ b/h/dsutils.h @@ -13,8 +13,6 @@ #define __DSUTILS__ 1 #include "httpserver.h" -#include "dynalloc.h" - #define INDEXED_DSCB 96 @@ -98,14 +96,12 @@ int dsutilsObtainDSCB1(const char *dsname, unsigned int dsnameLength, const char /** * @brief Gets the length of a dataset or respond an error. * - * @param response a parameter to get a response when this - * function encountered an error * @param dsn a parameter to get the dataset name * @param ddpath a parameter to get dataset path * @return lrecl return the length of a dataset - * 0 this function encountered error + * or this function encountered error */ -int dsutilsGetLreclOrRespondError(HttpResponse *response, const DatasetName *dsn, const char *ddPath); +int dsutilsGetLreclOr(const DatasetName *dsn, const char *ddPath); /** * @brief Validate if the dataset path is valid. @@ -124,21 +120,6 @@ bool dsutilsIsDatasetPathValid(const char *path); */ void dsutilsExtractDatasetAndMemberName(const char *datasetPath, DatasetName *dsn, DatasetMemberName *memberName); -/** - * @brief validation if allocation is error and respond - * - * @param response a parameter to get the - * @param rc a parameter to get the - * @param sysrc a parameter to get - * @param sysrsn a parameter to get - * @param dsn a parameter to get - * @param member a parameter to get the - * @param site a parameter to get the - */ -void dsutilsRespondWithDYNALLOCError(HttpResponse *response, int rc, int sysRC, int sysRSN, - const DynallocDatasetName *dsn, - const DynallocMemberName *member, - const char *site); #endif @@ -151,4 +132,3 @@ void dsutilsRespondWithDYNALLOCError(HttpResponse *response, int rc, int sysRC, Copyright Contributors to the Zowe Project. */ - From 4306e49257397de67b4beda1c519caef8a7a25b6 Mon Sep 17 00:00:00 2001 From: ojcelis Date: Wed, 28 Sep 2022 10:30:24 -0400 Subject: [PATCH 7/8] fixed issue with 64 bit Signed-off-by: ojcelis --- c/jesService.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/c/jesService.c b/c/jesService.c index 718865ad4..3b1a6aaca 100644 --- a/c/jesService.c +++ b/c/jesService.c @@ -117,7 +117,15 @@ void installJesService(HttpServer *server) { static char* getJobID(RPLCommon *rpl){ __asm( - " ENDREQ RPL=(%0) \n" +#ifdef _LP64 + " SAM31 \n" + " SYSSTATE AMODE64=NO \n" +#endif + " ENDREQ RPL=(%0) \n" +#ifdef _LP64 + " SAM64 \n" + " SYSSTATE AMODE64=YES \n" +#endif : :"r"(rpl) :); @@ -218,10 +226,12 @@ static void responseWithSubmitJCLContents(HttpResponse* response, const char* jc /*passed record length check and type check*/ int bytesWritten = 0; int recordsWritten = 0; - char recordBuffer[maxRecordLength+1]; - - char *record = jobLines; + int recordBuffersize = maxRecordLength + 1; + char *recordBuffer = safeMalloc(recordBuffersize, "recordBufferSubmitContent"); + char *record = safeMalloc(recordBuffersize, "recordSubmitContent"); char *jobID; + + record = jobLines; for (int jclLinecnt = 0; jclLinecnt < numLines; jclLinecnt++) { int recordLength = strlen(record); @@ -229,7 +239,7 @@ static void responseWithSubmitJCLContents(HttpResponse* response, const char* jc record = " "; recordLength = 1; } - snprintf (recordBuffer, sizeof(recordBuffer), "%-*s", maxRecordLength, record); + snprintf (recordBuffer, recordBuffersize, "%-*s", maxRecordLength, record); /* output to INTRDR */ putRecord(outACB, recordBuffer, maxRecordLength); @@ -249,6 +259,9 @@ static void responseWithSubmitJCLContents(HttpResponse* response, const char* jc responseWithJobDetails(response, uJobID, TRUE); + safeFree(recordBuffer,recordBuffersize); + safeFree(record,recordBuffersize); + closeACB(outACB, ACB_MODE_OUTPUT); safeFree(jobLines, jobLineLength); @@ -320,10 +333,9 @@ static void respondWithSubmitDatasetInternal(HttpResponse* response, } int bufferSize = DATA_STREAM_BUFFER_SIZE + 1; - char buffer[bufferSize]; - char record[bufferSize]; + char *buffer = safeMalloc(bufferSize, "bufferSubmitDataset"); + char *record = safeMalloc(bufferSize, "recordSubmitDataset"); char *jobID; - int contentLength = 0; int bytesRead = 0; if (in) { while (!feof(in)){ @@ -332,7 +344,6 @@ static void respondWithSubmitDatasetInternal(HttpResponse* response, memset(record, ' ', bytesRead); memcpy(record, buffer, bytesRead - 1); putRecord(outACB, record, bytesRead); - contentLength = contentLength + bytesRead; } else if (ferror(in)) { int inFileError = ferror(in); @@ -352,9 +363,9 @@ static void respondWithSubmitDatasetInternal(HttpResponse* response, respondWithMessage(response, HTTP_STATUS_INTERNAL_SERVER_ERROR, "BAD_REQUEST failed with RC = %d, Submit input data does not start with a valid job line", MAXCC_12); } - responseWithJobDetails(response, uJobID, TRUE); - + safeFree(buffer,bufferSize); + safeFree(record,bufferSize); closeACB(outACB, ACB_MODE_OUTPUT); fclose(in); From 4b02de58d1a3c56577e360aed8177f3d008f582f Mon Sep 17 00:00:00 2001 From: ojcelis Date: Wed, 28 Sep 2022 13:33:04 -0400 Subject: [PATCH 8/8] update zowe-common-c to the latest Signed-off-by: ojcelis --- deps/zowe-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/zowe-common-c b/deps/zowe-common-c index 418fa4bc9..9fd9d7654 160000 --- a/deps/zowe-common-c +++ b/deps/zowe-common-c @@ -1 +1 @@ -Subproject commit 418fa4bc96ce43d9a06ba398a4149bd071489ea1 +Subproject commit 9fd9d76543c6185d00173dd05f4c0c2b98bf57c6