diff --git a/CHANGELOG.md b/CHANGELOG.md index c105de66e..e669afff5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Zowe Common C Changelog +## `2.11.0` + +- WTO printing methods have been moved to zos.c to be more available as utilities (for ex: for the Launcher) + ## `2.10.0` - Feature: The configmgr can now use the 'zos' module in YAML config templates. The 'zos' module is only added when run on ZOS. For a list of available functions, see https://github.com/zowe/zowe-install-packaging/blob/v2.x/staging/build/zwe/types/%40qjstypes/zos.d.ts (#384) diff --git a/build/configmgr.proj.env b/build/configmgr.proj.env index 93945819e..06d81e370 100644 --- a/build/configmgr.proj.env +++ b/build/configmgr.proj.env @@ -1,5 +1,5 @@ PROJECT="configmgr" -VERSION=2.10.0 +VERSION=2.11.0 DEPS="QUICKJS LIBYAML" QUICKJS="quickjs" diff --git a/c/dynalloc.c b/c/dynalloc.c index 73fcb24d1..3898e59e8 100644 --- a/c/dynalloc.c +++ b/c/dynalloc.c @@ -874,30 +874,32 @@ int setTextUnit(int type, int size, char* stringValue, int numValue, int key, } } +typedef TextUnit *__ptr32 *__ptr32 TextUnitPtrArray; + int dynallocNewDataset(TextUnit **inputTextUnit, int inputTextUnitCount, int *reasonCode) { ALLOC_STRUCT31( STRUCT31_NAME(below2G), STRUCT31_FIELDS( DynallocParms parms; - TextUnit ** __ptr32 textUnits; + TextUnitPtrArray textUnits; ) ); - below2G->textUnits = (TextUnit **)safeMalloc31(sizeof(TextUnit*) * inputTextUnitCount, "Text units array"); + below2G->textUnits = (TextUnitPtrArray)safeMalloc31(sizeof(TextUnit*__ptr32) * inputTextUnitCount, "Text units array"); if(below2G->textUnits == NULL) { return -1; } DynallocParms *parms = &below2G->parms; dynallocParmsInit(parms); - dynallocParmsSetTextUnits(parms, (TextUnit * __ptr32 *)below2G->textUnits, inputTextUnitCount); + dynallocParmsSetTextUnits(parms, (TextUnitPtrArray)below2G->textUnits, inputTextUnitCount); int rc; do { rc = 0; for (int i = 0; i < inputTextUnitCount; i++) { - below2G->textUnits[i] = inputTextUnit[i]; + below2G->textUnits[i] = (TextUnit *__ptr32)inputTextUnit[i]; if (below2G->textUnits[i] == NULL) { rc = -1; break; diff --git a/c/httpserver.c b/c/httpserver.c index f88962e18..729988432 100644 --- a/c/httpserver.c +++ b/c/httpserver.c @@ -2653,19 +2653,23 @@ static int safAuthenticate(HttpService *service, HttpRequest *request, AuthRespo } else if (authDataFound){ ACEE *acee = NULL; strupcase(request->username); /* upfold username */ + if (!(request->flags & HTTP_REQUEST_NO_PASSWORD)) { + zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG3, "Password is null. Calling safAuthenticate without a password.\n"); + } else { #ifdef ENABLE_DANGEROUS_AUTH_TRACING #ifdef METTLE - printf("SAF auth for user: '%s'\n", request->username); + printf("SAF auth for user: '%s'\n", request->username); #else - printf("u: '%s' p: '%s'\n",request->username,request->password); + printf("u: '%s' p: '%s'\n",request->username,request->password); #endif #endif - if (isLowerCasePasswordAllowed() || isPassPhrase(request->password)) { - zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG3, "mixed-case system or a pass phrase, not upfolding password\n"); - /* don't upfold password */ - } else { - zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG3, "non-mixed-case system, not a pass phrase, upfolding password\n"); - strupcase(request->password); /* upfold password */ + if (isLowerCasePasswordAllowed() || isPassPhrase(request->password)) { + zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG3, "mixed-case system or a pass phrase, not upfolding password\n"); + /* don't upfold password */ + } else { + zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG3, "non-mixed-case system, not a pass phrase, upfolding password\n"); + strupcase(request->password); /* upfold password */ + } } #if APF_AUTHORIZED @@ -2675,10 +2679,17 @@ static int safAuthenticate(HttpService *service, HttpRequest *request, AuthRespo CrossMemoryServerName *privilegedServerName = getConfiguredProperty(service->server, HTTP_SERVER_PRIVILEGED_SERVER_PROPERTY); int pwdCheckRC = 0, pwdCheckRSN = 0; - pwdCheckRC = zisCheckUsernameAndPassword(privilegedServerName, - request->username, request->password, &status); - authResponse->type = AUTH_TYPE_RACF; - authResponse->responseDetails.safStatus = status.safStatus; + if (!(request->flags & HTTP_REQUEST_NO_PASSWORD)) { + pwdCheckRC = zisCheckUsernameAndPassword(privilegedServerName, + request->username, request->password, &status); + authResponse->type = AUTH_TYPE_RACF; + authResponse->responseDetails.safStatus = status.safStatus; + } else { + pwdCheckRC = zisCheckUsername(privilegedServerName, + request->username, &status); + authResponse->type = AUTH_TYPE_RACF; + authResponse->responseDetails.safStatus = status.safStatus; + } if (pwdCheckRC != 0) { #ifdef DEBUG_AUTH @@ -3142,7 +3153,7 @@ static int serviceAuthNativeWithSessionToken(HttpService *service, HttpRequest * int authDataFound = FALSE; HttpHeader *authenticationHeader = getHeader(request,"Authorization"); char *tokenCookieText = getCookieValue(request,getSessionTokenCookieName(service)); - + zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG3, "serviceAuthNativeWithSessionToken: authenticationHeader 0x%p\n", "extractFunction 0x%p\n", @@ -3162,9 +3173,54 @@ static int serviceAuthNativeWithSessionToken(HttpService *service, HttpRequest * if (service->authExtractionFunction(service, request) == 0){ authDataFound = TRUE; } + } + } + +#define TLS_CLIENT_CERTIFICATE_MAX_LENGTH 65536 + + char *clientCertificate = safeMalloc(TLS_CLIENT_CERTIFICATE_MAX_LENGTH, "Client Certificate"); + unsigned int clientCertificateLength = 0; + + int rc = getClientCertificate(response->socket->tlsSocket->socketHandle, clientCertificate, TLS_CLIENT_CERTIFICATE_MAX_LENGTH, &clientCertificateLength); + if (rc != 0) { + zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG, "getClientCertificate - %d.\n", rc); + } + +#ifdef ENABLE_DANGEROUS_AUTH_TRACING + /* We probably don't want to dump their certificate, right? */ + dumpbuffer(clientCertificate, clientCertificateLength); +#endif + + if (rc == 0 && clientCertificateLength > 0) { + zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG, "There is a client certificate attached to the request.\n"); + /* + * We don't want to do this if we already found authentication data. + */ + if (authDataFound == FALSE) { +#define TLS_USERID_LENGTH 9 + char userid[TLS_USERID_LENGTH] = {0}; + int racfReturnCode = 0, racfReasonCode = 0; + zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG, "There was no token or credentials found in the request. Server is attempting to map the client certificate.\n"); + int safReturnCode = getUseridByCertificate(clientCertificate, clientCertificateLength, userid, &racfReturnCode, &racfReasonCode); + if (safReturnCode == 0) { + request->username = userid; + zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_DEBUG, "Found user '%s' from client certificate.\n", request->username); + request->password = NULL; + request->flags = HTTP_REQUEST_NO_PASSWORD; + authDataFound = TRUE; + } else { + zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_INFO, "No user was found for client certificate. (rc = 0x%x racfRC = 0x%x racfRSN = 0x%x\n", safReturnCode, racfReturnCode, racfReasonCode); + } + } else { + zowelog(NULL, LOG_COMP_HTTPSERVER, ZOWE_LOG_INFO, "Client certificate was attached to request, but credentials are also attached. Server won't attempt to map the client certificate.\n"); } } - + + if (clientCertificate) { + safeFree(clientCertificate, TLS_CLIENT_CERTIFICATE_MAX_LENGTH); + clientCertificate = NULL; + } + response->sessionCookie = NULL; AUTH_TRACE("AUTH: tokenCookieText: %s\n",(tokenCookieText ? tokenCookieText : "")); diff --git a/c/le.c b/c/le.c index 575dcdf77..ffcccf6ad 100644 --- a/c/le.c +++ b/c/le.c @@ -111,7 +111,7 @@ char *getCAA(void){ } #ifndef LE_MAX_SUPPORTED_ZOS -#define LE_MAX_SUPPORTED_ZOS 0x01020500u +#define LE_MAX_SUPPORTED_ZOS 0x01030100u #endif void abortIfUnsupportedCAA() { @@ -120,9 +120,15 @@ void abortIfUnsupportedCAA() { unsigned int zosVersion = ecvt->ecvtpseq; #ifndef METTLE if (zosVersion > LE_MAX_SUPPORTED_ZOS) { - printf("error: z/OS version = 0x%08X, max supported version = 0x%08X - " - "CAA fields require verification\n", zosVersion, LE_MAX_SUPPORTED_ZOS); - abort(); + const char *continueWithWarning = getenv("ZWE_zowe_launcher_unsafeDisableZosVersionCheck"); + if (!strcmp(continueWithWarning, "true")) { + printf("warning: z/OS version = 0x%08X, max supported version = 0x%08X - " + "CAA fields require verification\n", zosVersion, LE_MAX_SUPPORTED_ZOS); + } else { + printf("error: z/OS version = 0x%08X, max supported version = 0x%08X - " + "CAA fields require verification\n", zosVersion, LE_MAX_SUPPORTED_ZOS); + abort(); + } } #else /* Metal uses its own copy of CAA, reserved fields will always be available */ diff --git a/c/metalio.c b/c/metalio.c index 320651e0e..9b6c45edf 100644 --- a/c/metalio.c +++ b/c/metalio.c @@ -55,6 +55,7 @@ #include "qsam.h" #include "metalio.h" #include "alloc.h" +#include "zos.h" static int isopen(void * dcbptr) { @@ -378,30 +379,7 @@ SYSOUT *getSYSOUTStruct(char *ddname, SYSOUT *existingSysout, char *buffer){ */ void message(char *message){ - ALLOC_STRUCT31( - STRUCT31_NAME(below2G), - STRUCT31_FIELDS( - WTOCommon31 common; - char text[126]; /* Maximum length of WTO text is 126 - ABEND D23-xxxx0005 if longer than 126 */ - ) - ); - - int len = strlen(message); - if (len>sizeof(below2G->text)) - len=sizeof(below2G->text); - - below2G->common.length = len+sizeof(below2G->common); /* +4 for header */ - memcpy(below2G->text,message,len); - - __asm(ASM_PREFIX - " WTO MF=(E,(%[wtobuf])) \n" - : - :[wtobuf]"NR:r1"(&below2G->common) - :"r0","r1","r15"); - - FREE_STRUCT31( - STRUCT31_NAME(below2G) - ); + wtoMessage(message); } /* this can only be called from authorized callers */ @@ -485,44 +463,13 @@ void sendWTO(int descriptorCode, int routingCode, char *message, int length){ } #define WTO_MAX_SIZE 126 -void wtoPrintf(char *formatString, ...){ - char text[WTO_MAX_SIZE+1]; /* Allow for trailing null character */ +void wtoPrintf(char *formatString, ...) { va_list argPointer; - int cnt; - - for (int pass=0; pass<2; pass++){ - - /* The resulting text string from vsnprintf is unpredictable if - there is an error in the format string or arguments. In that - case we will set the output text area to null, repeat the - vsnprintf, and then find the length of the null terminated - string. This avoids initializing the output text area prior - to every successful request. - */ - - va_start(argPointer,formatString); - cnt = vsnprintf(text,sizeof(text),formatString,argPointer); - va_end(argPointer); - - if (cnt<0){ - if (pass==0) - memset(text,0,sizeof(text)); /* Clear the text buffer before retrying the vsnprint request */ - else { - text[WTO_MAX_SIZE] = 0; /* Ensure strlen stops at the end of the text buffer */ - cnt = strlen(text); /* Find the end of the text string */ - } - } else - break; /* vsnprintf did not return an error - cnt was set */ - } - if (cnt>WTO_MAX_SIZE) /* If more data to format than the text buffer length */ - cnt = WTO_MAX_SIZE; /* Truncate the formatted length to the text buffer length */ - - /* We never want to include a final \n character in the WTO text */ - - if (cnt>0 && text[cnt-1] == '\n') /* If text ends with \n */ - text[cnt-1] = 0; /* Change it into a null character */ - - message(text); + va_start(argPointer, formatString); + + wtoPrintf3(formatString, argPointer); + + va_end(argPointer); } void authWTOPrintf(char *formatString, ...){ diff --git a/c/tls.c b/c/tls.c index 44a98aec3..377b68cc0 100644 --- a/c/tls.c +++ b/c/tls.c @@ -8,6 +8,7 @@ Copyright Contributors to the Zowe Project. */ #include +#include #include #include #include "alloc.h" @@ -15,6 +16,44 @@ #include "fdpoll.h" #include "tls.h" +int getClientCertificate(gsk_handle soc_handle, char *clientCertificate, unsigned int clientCertificateBufferSize, unsigned int *clientCertificateLength) { + + int rc = 0; + + if (clientCertificate == NULL || clientCertificateBufferSize <= 0) { + return -1; + } + + memset(clientCertificate, 0, clientCertificateBufferSize); + *clientCertificateLength = 0; + + gsk_cert_data_elem *gskCertificateArray = NULL; + int gskCertificateArrayElementCount = 0; + + rc = gsk_attribute_get_cert_info(soc_handle, GSK_PARTNER_CERT_INFO, &gskCertificateArray, &gskCertificateArrayElementCount); + + if (rc != 0) { + return rc; + } + + for (int i = 0; i < gskCertificateArrayElementCount; i++) { + gsk_cert_data_elem *tmp = &gskCertificateArray[i]; + if (tmp->cert_data_id == CERT_BODY_DER) { + if (clientCertificateBufferSize >= tmp->cert_data_l) { + memcpy(clientCertificate, tmp->cert_data_p, tmp->cert_data_l); + *clientCertificateLength = tmp->cert_data_l; + } else { + rc = -1; /* tls rc are all positive */ + } + break; + } + } + + gsk_free_cert_data(gskCertificateArray, gskCertificateArrayElementCount); + + return rc; +} + int tlsInit(TlsEnvironment **outEnv, TlsSettings *settings) { int rc = 0; TlsEnvironment *env = (TlsEnvironment *)safeMalloc(sizeof(*env), "Tls Environment"); @@ -29,6 +68,11 @@ int tlsInit(TlsEnvironment **outEnv, TlsSettings *settings) { rc = rc || gsk_attribute_set_enum(env->envHandle, GSK_PROTOCOL_TLSV1_1, GSK_PROTOCOL_TLSV1_1_OFF); rc = rc || gsk_attribute_set_enum(env->envHandle, GSK_PROTOCOL_TLSV1_2, GSK_PROTOCOL_TLSV1_2_ON); rc = rc || gsk_attribute_set_enum(env->envHandle, GSK_SERVER_EPHEMERAL_DH_GROUP_SIZE, GSK_SERVER_EPHEMERAL_DH_GROUP_SIZE_2048); + +#ifdef DEV_DO_NOT_VALIDATE_CLIENT_CERTIFICATES + rc = rc || gsk_attribute_set_enum(env->envHandle, GSK_CLIENT_AUTH_TYPE, GSK_CLIENT_AUTH_PASSTHRU_TYPE); +#endif + rc = rc || gsk_attribute_set_buffer(env->envHandle, GSK_KEYRING_FILE, settings->keyring, 0); if (settings->stash) { rc = rc || gsk_attribute_set_buffer(env->envHandle, GSK_KEYRING_STASH_FILE, settings->stash, 0); @@ -94,9 +138,9 @@ static int secureSocketSend(int fd, void *data, int len, char *userData) { } return rc; } - + int tlsSocketInit(TlsEnvironment *env, TlsSocket **outSocket, int fd, bool isServer) { - int rc = 0; + int rc = 0; gsk_iocallback ioCallbacks = {secureSocketRecv, secureSocketSend, NULL, NULL, NULL, NULL}; TlsSocket *socket = (TlsSocket*)safeMalloc(sizeof(TlsSocket), "Tls Socket"); if (!socket) { @@ -109,8 +153,7 @@ int tlsSocketInit(TlsEnvironment *env, TlsSocket **outSocket, int fd, bool isSer if (label) { rc = rc || gsk_attribute_set_buffer(socket->socketHandle, GSK_KEYRING_LABEL, label, 0); } - rc = rc || gsk_attribute_set_enum(socket->socketHandle, GSK_SESSION_TYPE, - isServer ? GSK_SERVER_SESSION : GSK_CLIENT_SESSION); + rc = rc || gsk_attribute_set_enum(socket->socketHandle, GSK_SESSION_TYPE, isServer ? GSK_SERVER_SESSION_WITH_CL_AUTH : GSK_CLIENT_SESSION); if (ciphers) { rc = rc || gsk_attribute_set_buffer(socket->socketHandle, GSK_V3_CIPHER_SPECS_EXPANDED, ciphers, 0); rc = rc || gsk_attribute_set_enum(socket->socketHandle, GSK_V3_CIPHERS, GSK_V3_CIPHERS_CHAR4); diff --git a/c/zos.c b/c/zos.c index da926e628..2a5879198 100644 --- a/c/zos.c +++ b/c/zos.c @@ -1469,6 +1469,78 @@ int safStat(int options, char *safClass, char *copy, int copyLength, int *racfSt get current TCB AC */ +/* begin WTO SECTION */ + +void wtoMessage(const char *message){ + + ALLOC_STRUCT31( + STRUCT31_NAME(below2G), + STRUCT31_FIELDS( + WTOCommon31 common; + char text[126]; /* Maximum length of WTO text is 126 - ABEND D23-xxxx0005 if longer than 126 */ + ) + ); + + int len = strlen(message); + if (len>sizeof(below2G->text)) + len=sizeof(below2G->text); + + below2G->common.length = len+sizeof(below2G->common); /* +4 for header */ + memcpy(below2G->text,message,len); + + __asm(ASM_PREFIX + " WTO MF=(E,(%[wtobuf])) \n" + : + :[wtobuf]"NR:r1"(&below2G->common) + :"r0","r1","r15"); + + FREE_STRUCT31( + STRUCT31_NAME(below2G) + ); +} + +#define WTO_MAX_SIZE 126 +void wtoPrintf3(const char *formatString, ...) { + char text[WTO_MAX_SIZE+1]; /* Allow for trailing null character */ + va_list argPointer; + int cnt; + + for (int pass=0; pass<2; pass++){ + + /* The resulting text string from vsnprintf is unpredictable if + there is an error in the format string or arguments. In that + case we will set the output text area to null, repeat the + vsnprintf, and then find the length of the null terminated + string. This avoids initializing the output text area prior + to every successful request. + */ + + va_start(argPointer,formatString); + cnt = vsnprintf(text,sizeof(text),formatString,argPointer); + va_end(argPointer); + + if (cnt<0){ + if (pass==0) + memset(text,0,sizeof(text)); /* Clear the text buffer before retrying the vsnprint request */ + else { + text[WTO_MAX_SIZE] = 0; /* Ensure strlen stops at the end of the text buffer */ + cnt = strlen(text); /* Find the end of the text string */ + } + } else + break; /* vsnprintf did not return an error - cnt was set */ + } + if (cnt>WTO_MAX_SIZE) /* If more data to format than the text buffer length */ + cnt = WTO_MAX_SIZE; /* Truncate the formatted length to the text buffer length */ + + /* We never want to include a final \n character in the WTO text */ + + if (cnt>0 && text[cnt-1] == '\n') /* If text ends with \n */ + text[cnt-1] = 0; /* Change it into a null character */ + + wtoMessage(text); +} + +/* end WTO SECTION */ /* LOCATE/CAMLIST */ diff --git a/h/http.h b/h/http.h index b46114ab3..f7462c7c6 100644 --- a/h/http.h +++ b/h/http.h @@ -126,6 +126,7 @@ typedef struct HttpRequest_tag{ Socket *socket; BufferedInputStream *input; int flags; +#define HTTP_REQUEST_NO_PASSWORD 0x01 int characterEncoding; int contentLength; /* -1 if unknown */ char *contentType; diff --git a/h/metalio.h b/h/metalio.h index 7429f550a..cd78cbe53 100644 --- a/h/metalio.h +++ b/h/metalio.h @@ -70,13 +70,6 @@ typedef struct WTORPrefix31_tag{ char *replayECBAddress; } WTORPrefix31; -typedef struct WTOCommon31_tag{ - char replyBufferLength; /* 31-bit WTOR only, else 0 */ - char length; /* message length +4 */ - char mcsFlags1; - char mcsFlags2; -} WTOCommon31; - #define WTO_ROUTE_CODE_OPERATOR_ACTION 1 #define WTO_ROUTE_CODE_OPERATOR_INFORMATION 2 #define WTO_ROUTE_CODE_TAPE_POOL 3 diff --git a/h/tls.h b/h/tls.h index 47ec2935d..f1f9bd59f 100644 --- a/h/tls.h +++ b/h/tls.h @@ -140,6 +140,7 @@ int tlsSocketClose(TlsSocket *socket); int tlsRead(TlsSocket *socket, const char *buf, int size, int *outLength); int tlsWrite(TlsSocket *socket, const char *buf, int size, int *outLength); const char *tlsStrError(int rc); +int getClientCertificate(gsk_handle soc_handle, char *clientCertificate, unsigned int clientCertificateBufferSize, unsigned int *clientCertificateLength); #define TLS_ALLOC_ERROR (-1) diff --git a/h/zos.h b/h/zos.h index b8b03a331..1daba3f21 100644 --- a/h/zos.h +++ b/h/zos.h @@ -1522,6 +1522,13 @@ typedef struct IDTA_tag { char reserved[8]; } IDTA; +typedef struct WTOCommon31_tag{ + char replyBufferLength; /* 31-bit WTOR only, else 0 */ + char length; /* message length +4 */ + char mcsFlags1; + char mcsFlags2; +} WTOCommon31; + ZOWE_PRAGMA_PACK_RESET DSAB *getDSAB(char *ddname); @@ -1529,6 +1536,10 @@ DSAB *getDSAB(char *ddname); int dsabIsOMVS(DSAB *dsab); +void wtoMessage(const char *message); + +void wtoPrintf3(const char *formatString, ...); + int locate(char *dsn, int *volserCount, char *firstVolser); /* diff --git a/manifest.template.yaml b/manifest.template.yaml index 0131e2e36..4bf312223 100644 --- a/manifest.template.yaml +++ b/manifest.template.yaml @@ -1,7 +1,7 @@ --- name: zowe-common-c -version: 2.10.0 +version: 2.11.0 homepage: https://zowe.org keywords: diff --git a/tests/create1.json b/tests/create1.json new file mode 100644 index 000000000..6f8936875 --- /dev/null +++ b/tests/create1.json @@ -0,0 +1,11 @@ +{"ndisp":"CATALOG", + "status":"NEW", + "space":"TRK", + "dsorg":"PO", + "lrecl":80, + "recfm":"FB", + "dir":20, + "prime":3, + "secnd":3, + "dsnt":"PDS", + "close":"true"} diff --git a/tests/datasetCreate.c b/tests/datasetCreate.c new file mode 100644 index 000000000..999f49dee --- /dev/null +++ b/tests/datasetCreate.c @@ -0,0 +1,499 @@ + +/* + 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 not supported +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "zowetypes.h" +#include "alloc.h" +#include "utils.h" +#ifdef __ZOWE_OS_ZOS +#include "zos.h" +#endif +#include "bpxnet.h" +#include "collections.h" +#include "unixfile.h" +#include "socketmgmt.h" +#include "le.h" +#include "logging.h" +#include "scheduling.h" +#include "http.h" +#include "json.h" + +#include "dynalloc.h" + +/** + + Compiling and running this test + + 64 Bit Build + + xlclang -q64 -I ../h -D_OPEN_SYS_FILE_EXT=1 -D_XOPEN_SOURCE=600 -D_OPEN_THREADS=1 -DSUBPOOL=132 -DUSE_ZOWE_TLS=1 "-Wc,float(ieee),longname,langlvl(extc99),gonum,goff,ASM,asmlib('CEE.SCEEMAC','SYS1.MACLIB','SYS1.MODGEN')" -o datasetCreate datasetCreate.c ../c/json.c ../c/charsets.c ../c/dynalloc.c ../c/zosfile.c ../c/xlate.c ../c/timeutls.c ../c/utils.c ../c/alloc.c ../c/logging.c ../c/collections.c ../c/le.c ../c/recovery.c ../c/zos.c ../c/scheduling.c + + 31 Bit Build + + xlc -I ../h -D_OPEN_SYS_FILE_EXT=1 -D_XOPEN_SOURCE=600 -D_OPEN_THREADS=1 -DSUBPOOL=132 -DUSE_ZOWE_TLS=1 "-Wc,float(ieee),longname,langlvl(extc99),gonum,goff,ASM,asmlib('CEE.SCEEMAC','SYS1.MACLIB','SYS1.MODGEN')" -o datasetCreate31 datasetCreate.c ../c/json.c ../c/charsets.c ../c/dynalloc.c ../c/zosfile.c ../c/xlate.c ../c/timeutls.c ../c/utils.c ../c/alloc.c ../c/logging.c ../c/collections.c ../c/le.c ../c/recovery.c ../c/zos.c ../c/scheduling.c + + Running + + ./datasetCreate "//'MY.DS64.NAME'" + + ./datasetCreate31 "//'MY.DS31.NAME'" + */ + +#define DSPATH_PREFIX "//\'" +#define DSPATH_SUFFIX "\'" + +#define ERROR_DECODING_DATASET -2 +#define ERROR_CLOSING_DATASET -3 +#define ERROR_OPENING_DATASET -4 +#define ERROR_ALLOCATING_DATASET -5 +#define ERROR_DEALLOCATING_DATASET -6 +#define ERROR_UNDEFINED_LENGTH_DATASET -7 +#define ERROR_BAD_DATASET_NAME -8 +#define ERROR_INVALID_DATASET_NAME -9 +#define ERROR_INCORRECT_DATASET_TYPE -10 +#define ERROR_DATASET_NOT_EXIST -11 +#define ERROR_MEMBER_ALREADY_EXISTS -12 +#define ERROR_DATASET_ALREADY_EXIST -13 +#define ERROR_DATASET_OR_MEMBER_NOT_EXIST -14 +#define ERROR_VSAM_DATASET_DETECTED -15 +#define ERROR_DELETING_DATASET_OR_MEMBER -16 +#define ERROR_INVALID_JSON_BODY -17 +#define ERROR_COPY_NOT_SUPPORTED -18 +#define ERROR_COPYING_DATASET -19 + +#define INDEXED_DSCB 96 +#define LEN_THREE_BYTES 3 +#define LEN_ONE_BYTE 1 +#define VOLSER_SIZE 6 +#define CLASS_WRITER_SIZE 8 +#define TOTAL_TEXT_UNITS 23 + +#define IS_DAMEMBER_EMPTY($member) \ + (!memcmp(&($member), &(DynallocMemberName){" "}, sizeof($member))) + +static int bytesPerTrack=56664; +static int tracksPerCylinder=15; +static int bytesPerCylinder=849960; + + +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 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]); + } + +} + +/* Returns a quantity of tracks or cylinders for dynalloc in case the user asked for bytes */ +/* Yes, these are approximations but if people really want exact numbers they should use cyl & trk */ +static int getDSSizeValueFromType(int quantity, char *spaceType) { + if (!strcmp(spaceType, "CYL")) { + return quantity; + } else if (!strcmp(spaceType, "TRK")) { + return quantity; + } else if (!strcmp(spaceType, "BYTE")) { + return quantity / bytesPerTrack; + } else if (!strcmp(spaceType, "KB")) { + return (quantity*1024) / bytesPerTrack; + } else if (!strcmp(spaceType, "MB")) { + return (quantity*1048576) / bytesPerCylinder; + } + return quantity; +} + +static int setDatasetAttributesForCreation(JsonObject *object, int *configsCount, TextUnit **inputTextUnit) { + JsonProperty *currentProp = jsonObjectGetFirstProperty(object); + Json *value = NULL; + int parmDefn = DALDSORG_NULL; + int type = TEXT_UNIT_NULL; + int rc = 0; + + parmDefn = DISP_NEW; //ONLY one for create + rc = setTextUnit(TEXT_UNIT_CHAR, 0, NULL, parmDefn, DALSTATS, configsCount, inputTextUnit); + + + // most parameters below explained here https://www.ibm.com/docs/en/zos/2.1.0?topic=dfsms-zos-using-data-sets + // or here https://www.ibm.com/docs/en/zos/2.1.0?topic=function-non-jcl-dynamic-allocation-functions + // or here https://www.ibm.com/docs/en/zos/2.1.0?topic=function-dsname-allocation-text-units + + while(currentProp != NULL){ + value = jsonPropertyGetValue(currentProp); + char *propString = jsonPropertyGetKey(currentProp); + + if(propString != NULL){ + errno = 0; + if (!strcmp(propString, "dsorg")) { + char *valueString = jsonAsString(value); + if (valueString != NULL) { + parmDefn = (!strcmp(valueString, "PS")) ? DALDSORG_PS : DALDSORG_PO; + rc = setTextUnit(TEXT_UNIT_INT16, 0, NULL, parmDefn, DALDSORG, configsCount, inputTextUnit); + } + } else if(!strcmp(propString, "blksz")) { + // https://www.ibm.com/docs/en/zos/2.1.0?topic=statement-blksize-parameter + int64_t valueInt = jsonAsInt64(value); + if(valueInt != 0){ + if (valueInt <= 0x7FF8 && valueInt >= 0) { //<-- If DASD, if tape, it can be 80000000 + type = TEXT_UNIT_INT16; + } else if (valueInt <= 0x80000000){ + type = TEXT_UNIT_LONGINT; + } + if(type != TEXT_UNIT_NULL) { + rc = setTextUnit(type, 0, NULL, valueInt, DALBLKSZ, configsCount, inputTextUnit); + } + } + } else if(!strcmp(propString, "lrecl")) { + int valueInt = jsonAsNumber(value); + if (valueInt == 0x8000 || (valueInt <= 0x7FF8 && valueInt >= 0)) { + rc = setTextUnit(TEXT_UNIT_INT16, 0, NULL, valueInt, DALLRECL, configsCount, inputTextUnit); + } + } else if(!strcmp(propString, "volser")) { + char *valueString = jsonAsString(value); + if (valueString != NULL) { + int valueStrLen = strlen(valueString); + if (valueStrLen <= VOLSER_SIZE){ + rc = setTextUnit(TEXT_UNIT_STRING, VOLSER_SIZE, &(valueString)[0], 0, DALVLSER, configsCount, inputTextUnit); + } + } + } else if(!strcmp(propString, "recfm")) { + char *valueString = jsonAsString(value); + if (valueString != NULL) { + int valueStrLen = strlen(valueString); + int setRECFM = 0; + if (indexOf(valueString, valueStrLen, 'A', 0) != -1){ + setRECFM = setRECFM | DALRECFM_A; + } + if (indexOf(valueString, valueStrLen, 'B', 0) != -1){ + setRECFM = setRECFM | DALRECFM_B; + } + if (indexOf(valueString, valueStrLen, 'V', 0) != -1){ + setRECFM = setRECFM | DALRECFM_V; + } + if (indexOf(valueString, valueStrLen, 'F', 0) != -1){ + setRECFM = setRECFM | DALRECFM_F; + } + if (indexOf(valueString, valueStrLen, 'U', 0) != -1){ + setRECFM = setRECFM | DALRECFM_U; + } + rc = setTextUnit(TEXT_UNIT_CHAR, 0, NULL, setRECFM, DALRECFM, configsCount, inputTextUnit); + } + } else if(!strcmp(propString, "blkln") + && !jsonObjectHasKey(object, "space")) { //mutually exclusive with daltrk, dalcyl + // https://www.ibm.com/docs/en/zos/2.1.0?topic=units-block-length-specification-key-0009 + int valueInt = jsonAsNumber(value); + if (valueInt <= 0xFFFF && valueInt >= 0){ + rc = setTextUnit(TEXT_UNIT_INT24, 0, NULL, valueInt, DALBLKLN, configsCount, inputTextUnit); + } + if (jsonObjectHasKey(object, "prime")) { //in tracks for blkln + int primeSize = jsonObjectGetNumber(object, "prime"); + if (primeSize <= 0xFFFFFF && primeSize >= 0) { + rc = setTextUnit(TEXT_UNIT_INT24, 0, NULL, primeSize, DALPRIME, configsCount, inputTextUnit); + } + } + if (jsonObjectHasKey(object, "secnd")) { //in tracks for blkln + int secondarySize = jsonObjectGetNumber(object, "secnd"); + if (secondarySize <= 0xFFFFFF && secondarySize >= 0) { + rc = setTextUnit(TEXT_UNIT_INT24, 0, NULL, secondarySize, DALSECND, configsCount, inputTextUnit); + } + } + } else if (!strcmp(propString, "ndisp")) { + char *valueString = jsonAsString(value); + if (valueString != NULL) { + if (!strcmp(valueString, "KEEP")){ + parmDefn = DISP_KEEP; + } else { + parmDefn = DISP_CATLG; + } + rc = setTextUnit(TEXT_UNIT_CHAR, 0, NULL, parmDefn, DALNDISP, configsCount, inputTextUnit); + } + } else if(!strcmp(propString, "strcls")) { + char *valueString = jsonAsString(value); + if (valueString != NULL) { + int valueStrLen = strlen(valueString); + if (valueStrLen <= CLASS_WRITER_SIZE){ + rc = setTextUnit(TEXT_UNIT_STRING, CLASS_WRITER_SIZE, &(valueString)[0], 0, DALSTCL, configsCount, inputTextUnit); + } + } + } else if(!strcmp(propString, "mngcls")) { + char *valueString = jsonAsString(value); + if (valueString != NULL) { + int valueStrLen = strlen(valueString); + if (valueStrLen <= CLASS_WRITER_SIZE){ + rc = setTextUnit(TEXT_UNIT_STRING, CLASS_WRITER_SIZE, &(valueString)[0], 0, DALMGCL, configsCount, inputTextUnit); + } + } + } else if(!strcmp(propString, "datacls")) { + char *valueString = jsonAsString(value); + if (valueString != NULL) { + int valueStrLen = strlen(valueString); + if (valueStrLen <= CLASS_WRITER_SIZE){ + rc = setTextUnit(TEXT_UNIT_STRING, CLASS_WRITER_SIZE, &(valueString)[0], 0, DALDACL, configsCount, inputTextUnit); + } + } + } else if(!strcmp(propString, "space")) { + char *spaceType = jsonAsString(value); + if (spaceType != NULL) { + if (!strcmp(spaceType, "CYL")) { + parmDefn = DALCYL; + } else if (!strcmp(spaceType, "TRK")) { + // https://www.ibm.com/docs/en/zos/2.1.0?topic=units-track-space-type-trk-specification-key-0007 + parmDefn = DALTRK; + } else if (!strcmp(spaceType, "BYTE")) { + parmDefn = DALTRK; + } else if (!strcmp(spaceType, "KB")) { + parmDefn = DALTRK; + } else if (!strcmp(spaceType, "MB")) { + parmDefn = DALCYL; + } + if(parmDefn != DALDSORG_NULL) { + rc = setTextUnit(TEXT_UNIT_BOOLEAN, 0, NULL, 0, parmDefn, configsCount, inputTextUnit); + } + if (jsonObjectHasKey(object, "prime")) { //in tracks for blkln + int primeSize = jsonObjectGetNumber(object, "prime"); + if (primeSize <= 0xFFFFFF && primeSize >= 0) { + rc = setTextUnit(TEXT_UNIT_INT24, 0, NULL, getDSSizeValueFromType(primeSize, spaceType), DALPRIME, configsCount, inputTextUnit); + } + } + if (jsonObjectHasKey(object, "secnd")) { //in tracks for blkln + int secondarySize = jsonObjectGetNumber(object, "secnd"); + if (secondarySize <= 0xFFFFFF && secondarySize >= 0) { + rc = setTextUnit(TEXT_UNIT_INT24, 0, NULL, getDSSizeValueFromType(secondarySize, spaceType), DALSECND, configsCount, inputTextUnit); + } + } + } + } else if(!strcmp(propString, "dir")) { + int valueInt = jsonAsNumber(value); + if (valueInt <= 0xFFFFFF && valueInt >= 0) { + rc = setTextUnit(TEXT_UNIT_INT24, 0, NULL, valueInt, DALDIR, configsCount, inputTextUnit); + } + } else if(!strcmp(propString, "avgr")) { + // https://www.ibm.com/docs/en/zos/2.1.0?topic=statement-avgrec-parameter + char *valueString = jsonAsString(value); + if (valueString != NULL) { + if (!strcmp(valueString, "M")) { + parmDefn = DALDSORG_MREC; + } else if (!strcmp(valueString, "K")) { + parmDefn = DALDSORG_KREC; + } else if (!strcmp(valueString, "U")) { + parmDefn = DALDSORG_UREC; + } + if(parmDefn != DALDSORG_NULL) { + rc = setTextUnit(TEXT_UNIT_CHAR, 0, NULL, parmDefn, DALAVGR, configsCount, inputTextUnit); + } + } + } else if(!strcmp(propString, "dsnt")) { + // https://www.ibm.com/docs/en/zos/2.3.0?topic=jcl-allocating-system-managed-data-sets + char *valueString = jsonAsString(value); + if (valueString != NULL) { + if (!strcmp(valueString, "PDSE")) { + parmDefn = DALDSORG_PDSE; + } else if (!strcmp(valueString, "PDS")) { + parmDefn = DALDSORG_PDS; + } else if (!strcmp(valueString, "HFS")) { + parmDefn = DALDSORG_HFS; + } else if (!strcmp(valueString, "EXTREQ")) { + parmDefn = DALDSORG_EXTREQ; + } else if (!strcmp(valueString, "EXTPREF")) { + parmDefn = DALDSORG_EXTPREF; + } else if (!strcmp(valueString, "BASIC")) { + parmDefn = DALDSORG_BASIC; + } else if (!strcmp(valueString, "LARGE")) { + parmDefn = DALDSORG_LARGE; + } + if(parmDefn != DALDSORG_NULL) { + rc = setTextUnit(TEXT_UNIT_CHAR, 0, NULL, parmDefn, DALDSNT, configsCount, inputTextUnit); + } + } + } + + } + if(rc == -1) { + break; + } + currentProp = jsonObjectGetNextProperty(currentProp); + parmDefn = DALDSORG_NULL; + type = TEXT_UNIT_NULL; + } + return rc; +} + +static int createDataset(char* absolutePath, Json *attributesJSON, int* reasonCode) { + #ifdef __ZOWE_OS_ZOS + DatasetName datasetName; + DatasetMemberName memberName; + extractDatasetAndMemberName(absolutePath, &datasetName, &memberName); + + DynallocDatasetName daDatasetName; + DynallocMemberName daMemberName; + memcpy(daDatasetName.name, datasetName.value, sizeof(daDatasetName.name)); + memcpy(daMemberName.name, memberName.value, sizeof(daMemberName.name)); + + DynallocDDName daDDName = {.name = "????????"}; + + int daRC = RC_DYNALLOC_OK, daSysReturnCode = 0, daSysReasonCode = 0; + + bool isMemberEmpty = IS_DAMEMBER_EMPTY(daMemberName); + + if(!isMemberEmpty){ + printf("this test does not make dataset members yet\n"); + return 12; + } + + int configsCount = 0; + char ddNameBuffer[DD_NAME_LEN+1] = "MVD00000"; + TextUnit *inputTextUnit[TOTAL_TEXT_UNITS] = {NULL}; + printf("DDNAME buffer 0x%p '%s'\n",ddNameBuffer,ddNameBuffer); + + ShortLivedHeap *slh = makeShortLivedHeap(0x10000,0x10); + char errorBuffer[2048]; + + int returnCode = 0; + if (jsonIsObject(attributesJSON)){ + JsonObject * jsonObject = jsonAsObject(attributesJSON); + returnCode = setTextUnit(TEXT_UNIT_STRING, DATASET_NAME_LEN, &datasetName.value[0], 0, DALDSNAM, &configsCount, inputTextUnit); + if(returnCode == 0) { + returnCode = setTextUnit(TEXT_UNIT_STRING, DD_NAME_LEN, ddNameBuffer, 0, DALDDNAM, &configsCount, inputTextUnit); + } + if(returnCode == 0) { + returnCode = setDatasetAttributesForCreation(jsonObject, &configsCount, inputTextUnit); + } + } + + if (returnCode == 0) { + returnCode = dynallocNewDataset(inputTextUnit, configsCount, reasonCode); + printf("dND ret=%d reason %d 0x%x\n",returnCode, *reasonCode, *reasonCode); + int ddNumber = 1; + while (*reasonCode==0x4100000 && ddNumber < 100000) { + sprintf(ddNameBuffer, "MVD%05d", ddNumber); + int ddconfig = 1; + setTextUnit(TEXT_UNIT_STRING, DD_NAME_LEN, ddNameBuffer, 0, DALDDNAM, &ddconfig, inputTextUnit); + returnCode = dynallocNewDataset(inputTextUnit, configsCount, reasonCode); + ddNumber++; + } + } + + if (returnCode) { + zowelog(NULL, LOG_COMP_DATASERVICE, ZOWE_LOG_WARNING, + "error: ds alloc dsn=\'%44.44s\' dd=\'%8.8s\', sysRC=%d, sysRSN=0x%08X\n", + daDatasetName.name, ddNameBuffer, returnCode, *reasonCode); + printf("Unable to allocate a DD for Creating New Dataset\n"); + SLHFree(slh); + return ERROR_ALLOCATING_DATASET; + } + + memcpy(daDDName.name, ddNameBuffer, DD_NAME_LEN); + daRC = dynallocUnallocDatasetByDDName(&daDDName, DYNALLOC_UNALLOC_FLAG_NONE, &returnCode, reasonCode); + if (daRC != RC_DYNALLOC_OK) { + zowelog(NULL, LOG_COMP_DATASERVICE, ZOWE_LOG_WARNING, + "error: ds unalloc dsn=\'%44.44s\' dd=\'%8.8s\', rc=%d sysRC=%d, sysRSN=0x%08X\n", + daDatasetName.name, daDDName.name, daRC, returnCode, *reasonCode); + printf("Unable to deallocate DDNAME"); + SLHFree(slh); + return ERROR_DEALLOCATING_DATASET; + } + SLHFree(slh); + return 0; + #endif +} + + +int main(int argc, char **argv){ + LoggingContext *loggingContext = makeLoggingContext(); + +#ifdef __ZOWE_OS_WINDOWS + int stdoutFD = _fileno(stdout); +#else + int stdoutFD = fileno(stdout); +#endif + + if (argc < 3){ + printf("please supply a dataset name and attributes filename\n"); + } + char *datasetName = argv[1]; + char *attributesFilename = argv[2]; + int errorBufferSize = 1024; + char *errorBuffer = safeMalloc(errorBufferSize,"ErrorBuffer"); + memset(errorBuffer,0,errorBufferSize); + + ShortLivedHeap *slh = makeShortLivedHeap(0x10000, 100); + + Json *attributes = jsonParseFile2(slh,attributesFilename,errorBuffer,errorBufferSize); + if (attributes == NULL){ + printf("Json parse fail in %s, with error '%s'\n",attributesFilename,errorBuffer); + return 12; + } else{ + printf("creation attributes parsed as:\n"); + jsonPrinter *p = makeJsonPrinter(stdoutFD); + jsonEnablePrettyPrint(p); + jsonPrint(p,attributes); + int reasonCode = 0; + int status = createDataset(datasetName, attributes, &reasonCode); + printf("create status = %d reason=%d\n",status, reasonCode); + } + + + return 0; +}