diff --git a/build/build_zss.sh b/build/build_zss.sh index 227ad6344..e6865718e 100755 --- a/build/build_zss.sh +++ b/build/build_zss.sh @@ -226,7 +226,9 @@ xlc \ ${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/zosDiscovery.c \ ${ZSS}/c/securityService.c \ ${ZSS}/c/registerProduct.c \ diff --git a/build/build_zss64.sh b/build/build_zss64.sh index 2816fa2ed..0860e346d 100755 --- a/build/build_zss64.sh +++ b/build/build_zss64.sh @@ -227,7 +227,9 @@ 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/zosDiscovery.c \ ${ZSS}/c/securityService.c \ ${ZSS}/c/registerProduct.c \ diff --git a/c/datasetjson.c b/c/datasetjson.c index cdbb70774..f24dcf56d 100644 --- a/c/datasetjson.c +++ b/c/datasetjson.c @@ -41,6 +41,7 @@ #include "jcsi.h" #include "impersonation.h" #include "dynalloc.h" +#include "dsutils.h" #include "utils.h" #include "vsam.h" #include "qsam.h" @@ -53,109 +54,20 @@ #define CLASS_WRITER_SIZE 8 #define TOTAL_TEXT_UNITS 23 -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 getDSCB(DatasetName *dsName, char* dscb, int bufferSize); static int setDatasetAttributesForCreation(JsonObject *object, int *configsCount, TextUnit **inputTextUnit); - -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. */ @@ -164,7 +76,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; @@ -558,73 +470,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, @@ -633,7 +478,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){ @@ -660,7 +505,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); @@ -736,129 +581,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, @@ -918,11 +640,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){ @@ -950,8 +672,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') { @@ -1127,7 +849,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; } @@ -1139,7 +861,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; @@ -1180,7 +902,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 = getLreclOrRespondError(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); @@ -1307,77 +1040,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 @@ -1457,14 +1119,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)); @@ -1661,7 +1323,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)); @@ -1689,7 +1351,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; } @@ -1823,9 +1485,16 @@ static void respondWithDatasetInternal(HttpResponse* response, char ddPath[16]; snprintf(ddPath, sizeof(ddPath), "DD:%8.8s", ddName->value); - int lrecl = getLreclOrRespondError(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); @@ -1849,14 +1518,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; @@ -2235,7 +1904,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; } @@ -2247,7 +1916,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"); @@ -2911,4 +2580,3 @@ void newDataset(HttpResponse* response, char* absolutePath, int jsonMode){ Copyright Contributors to the Zowe Project. */ - diff --git a/c/dsutils.c b/c/dsutils.c new file mode 100644 index 000000000..a6ce58c0d --- /dev/null +++ b/c/dsutils.c @@ -0,0 +1,374 @@ + +/* + 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 +#error Metal C is not supported +#else +#include +#include +#include +#include +#include +#include +#include "zowetypes.h" +#include "alloc.h" +#include "utils.h" +#include "logging.h" +#ifdef __ZOWE_OS_ZOS +#include "zos.h" +#endif +#include "jcsi.h" +#include "utils.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 dsutilsGetVolserForDataset(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 dsutilsGetRecordLengthType(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 dsutilsGetMaxRecordLength(char *dscb){ + int posOffset = 44; + int lrecl = (dscb[88-posOffset] << 8) | dscb[89-posOffset]; + return lrecl; +} + +int dsutilsObtainDSCB1(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 dsutilsGetLreclOr(const DatasetName *dsn, + const char *ddPath){ + + int lrecl = 0; + + FileInfo info; + int returnCode; + int reasonCode; + FILE *in = fopen(ddPath, "r"); + if (in == NULL) { + return -1; + } + + Volser volser; + memset(&volser.value, ' ', sizeof(volser.value)); + + int volserSuccess = dsutilsGetVolserForDataset(dsn, &volser); + int handledThroughDSCB = FALSE; + + if (!volserSuccess){ + + char dscb[INDEXED_DSCB] = {0}; + int rc = dsutilsObtainDSCB1(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 = dsutilsGetMaxRecordLength(dscb); + char recordType = dsutilsGetRecordLengthType(dscb); + if (recordType == 'U'){ + fclose(in); + return -2; + } + 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); + return -2; + } + lrecl = fileinfo.__maxreclen; + } else { + fclose(in); + return -3; + } + } + fclose(in); + + return lrecl; +} + +#define DSPATH_PREFIX "//\'" +#define DSPATH_SUFFIX "\'" + +bool dsutilsIsDatasetPathValid(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 dsutilsExtractDatasetAndMemberName(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 + +#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 new file mode 100644 index 000000000..3b1a6aaca --- /dev/null +++ b/c/jesService.c @@ -0,0 +1,579 @@ + + +/* + 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. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "zowetypes.h" +#include "alloc.h" +#include "bpxnet.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" + +#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){ + 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"); + responseJesServiceWithSubmitJobs(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", "PUT"); + 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); +} + +static char* getJobID(RPLCommon *rpl){ + + __asm( +#ifdef _LP64 + " SAM31 \n" + " SYSSTATE AMODE64=NO \n" +#endif + " ENDREQ RPL=(%0) \n" +#ifdef _LP64 + " SAM64 \n" + " SYSSTATE AMODE64=YES \n" +#endif + : + :"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; + 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); + if (recordLength == 0) { + record = " "; + recordLength = 1; + } + snprintf (recordBuffer, recordBuffersize, "%-*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); + + safeFree(recordBuffer,recordBuffersize); + safeFree(record,recordBuffersize); + + 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 = 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){ + 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 = safeMalloc(bufferSize, "bufferSubmitDataset"); + char *record = safeMalloc(bufferSize, "recordSubmitDataset"); + char *jobID; + 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); + } + 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); + safeFree(buffer,bufferSize); + safeFree(record,bufferSize); + closeACB(outACB, ACB_MODE_OUTPUT); + fclose(in); + + } + else { + zowelog(NULL, LOG_COMP_RESTDATASET, ZOWE_LOG_DEBUG, "Failed to open dataset\n"); + } + } +} + +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; + + 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); + respondDYNALLOCerror(response, daRC, daSysRC, daSysRSN, + &daDsn, &daMember, "r"); + } + + 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 + 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 78cb023bc..e35f7e853 100644 --- a/c/zss.c +++ b/c/zss.c @@ -1862,6 +1862,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..7b08f5237 --- /dev/null +++ b/h/dsutils.h @@ -0,0 +1,134 @@ + +/* + 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" + +#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; + +/** + * @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; + +/** + * @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 dsn a parameter to get the dataset name + * @param ddpath a parameter to get dataset path + * @return lrecl return the length of a dataset + * or this function encountered error + */ +int dsutilsGetLreclOr(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); + +#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..c2a5a0c8c --- /dev/null +++ b/h/jesService.h @@ -0,0 +1,31 @@ + + +/* + 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); +void responseJesServiceWithSubmitJobs(HttpResponse* response, int jsonMode); + +#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. +*/ +