diff --git a/include/c++/v1/__threading_support b/include/c++/v1/__threading_support index 2c5cbf3..c53266e 100644 --- a/include/c++/v1/__threading_support +++ b/include/c++/v1/__threading_support @@ -11,6 +11,7 @@ //TODO(itodorov) - zos: workaround for nanosleep WoZ conflict, revisit // once WoZ allow overriding or LE provides a nanosleep definition +#include #define nanosleep cpp_nanosleep #include_next <__threading_support> #undef nanosleep diff --git a/include/stdlib.h b/include/stdlib.h index c7a3eb7..51bd88a 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -68,11 +68,23 @@ extern "C" { * Replace getenv with the ascii implementation of __getenv (@@A00423) which copies pointer to a buffer and is retained even after the environment changes */ -__Z_EXPORT char* getenv(const char*) asm("@@A00423"); - +__Z_EXPORT char* getenv(const char*) __asm("@@A00423"); #if defined(__cplusplus) } #endif #endif +#if defined(__cplusplus) +extern "C" { +#endif +/** + * C Lib functions that do not conflict with z/OS LE + */ +__Z_EXPORT char *mkdtemp(char *templ); +__Z_EXPORT int getloadavg(double loadavg[], int nelem); +__Z_EXPORT const char * getprogname(void); +#if defined(__cplusplus) +} +#endif + #endif diff --git a/include/sys/sysmacros.h b/include/sys/sysmacros.h new file mode 100644 index 0000000..a505888 --- /dev/null +++ b/include/sys/sysmacros.h @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////////////////////// +// Licensed Materials - Property of IBM +// ZOSLIB +// (C) Copyright IBM Corp. 2021. All Rights Reserved. +// US Government Users Restricted Rights - Use, duplication +// or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef ZOS_SYSMACROS_H_ +#define ZOS_SYSMACROS_H_ + +#define major(x) (((unsigned)(x) >> 8) & 0x7f) +#define minor(x) ((x) & 0xff) +#define makedev(x, y) (unsigned short)(((x) << 8) | ((y) & 0xff)) + +#endif diff --git a/include/zos-base.h b/include/zos-base.h index e6d6cc0..cd60500 100644 --- a/include/zos-base.h +++ b/include/zos-base.h @@ -593,6 +593,11 @@ __Z_EXPORT bool __doLogMemoryWarning(); */ __Z_EXPORT void __mainTerminating(); +/** + * Returns the program's directory as an absolute path + */ +__Z_EXPORT char* __getprogramdir(); + #ifdef __cplusplus } #endif diff --git a/include/zos-sys-info.h b/include/zos-sys-info.h index db1256d..a54fcd0 100644 --- a/include/zos-sys-info.h +++ b/include/zos-sys-info.h @@ -32,8 +32,16 @@ typedef enum { // CPU Management Control Table (CCT). typedef struct ZOSCCT { - uint8_t filler[110]; - uint16_t cpuCount; // Number of online CPUs. + // Field offsets described in SYS1.MODGEN(IRACCT) + // Field descriptions provided in https://www.ibm.com/docs/en/zos/3.1.0?topic=ttar-contents-report + uint8_t filler1[72]; // 0:72 Ignore fields not relevant to current implementation + uint32_t ccvrbswt; // Recent base system wait time + uint8_t filler2[4]; // Ignore fields not relevant to current implementation + uint32_t ccvrbstd; // Recent base time of day + uint8_t filler3[18]; // 84:18 Ignore fields not relevant to current implementation + uint16_t ccvutilp; // System CPU utilization + uint8_t filler4[6]; // 104:6 Ignore fields not relevant to current implementation + uint16_t cpuCount; // Number of online CPUs. } ZOSCCT_t; // System Resources Manager Control Table (RMCT). @@ -146,6 +154,8 @@ __Z_EXPORT bool __is_vef1_available(); */ __Z_EXPORT char *__get_cpu_model(char *buffer, size_t size); +__Z_EXPORT int getloadavg(double loadavg[], int nelem); + #ifdef __cplusplus } #endif diff --git a/include/zos-v2r5-symbolfixes.h b/include/zos-v2r5-symbolfixes.h new file mode 100644 index 0000000..3da9936 --- /dev/null +++ b/include/zos-v2r5-symbolfixes.h @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////// +// ZOSLIB +// (C) Copyright IBM Corp. 2023. All Rights Reserved. +// US Government Users Restricted Rights - Use, duplication +// or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef ZOS_V2R5_SYMBOLFIXES_H +#define ZOS_V2R5_SYMBOLFIXES_H + +// This enables builds on >=V2R5 systems when the target is =V2R5 symbols with _undefined suffix to trigger a linker +// error so they are not detected by configure scripts +#if (__TARGET_LIB__ < 0x42050000) +#pragma redefine_extname readlinkat readlinkat_undefined +#pragma redefine_extname openat openat_undefined +#pragma redefine_extname linkat linkat_undefined +#pragma redefine_extname faccessat faccessat_undefined +#pragma redefine_extname fstatat fstatat_undefined +#pragma redefine_extname unlinkat unlinkat_undefined +#pragma redefine_extname symlinkat symlinkat_undefined +#pragma redefine_extname renameat rename_undefined +#pragma redefine_extname getrandom getrandom_undefined +#pragma redefine_extname pipe2 pipe2_undefined +#pragma redefine_extname fsstatfs fsstatfs_undefined +#pragma redefine_extname getline getline_undefined +#pragma redefine_extname dprintf dprintf_undefined +#pragma redefine_extname dirfd dirfd_undefined +#pragma redefine_extname fchmodat fchmodat_undefined +#pragma redefine_extname mkdirat mkdirat_undefined +#pragma redefine_extname mkfifoat mkfifoat_undefined +#pragma redefine_extname mknodat mknodat_undefined +#pragma redefine_extname renameat2 renameat2_undefined +#pragma redefine_extname futimesat futimesat_undefined +#pragma redefine_extname fchownat fchownat_undefined +#pragma redefine_extname strchrnul strchrnul_undefined +#pragma redefine_extname sethostname sethostname_undefined +#pragma redefine_extname syncfs syncfs_undefined +#pragma redefine_extname sysinfo sysinfo_undefined +#pragma redefine_extname fdatasync fdatasync_undefined +#pragma redefine_extname inotify_init inotify_init_undefined +#pragma redefine_extname prctl prctl_undefined +#pragma redefine_extname fstatfs fstatfs_undefined +#pragma redefine_extname setresuid setresuid_undefined +#pragma redefine_extname setresgid setresgid_undefined +#pragma redefine_extname dup3 dup3_undefined +#pragma redefine_extname flock flock_undefined +#pragma redefine_extname shm_open shm_open_undefined +#endif + +#endif // ZOS_V2R5_SYMBOLFIXES_H diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt index 131bc28..57970a6 100644 --- a/man/CMakeLists.txt +++ b/man/CMakeLists.txt @@ -30,4 +30,4 @@ add_custom_command( # Define the install rule for the man page -#install(FILES "${MAN_OUTPUT_DIR}/${INPUT_FILE}" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man1") +install(FILES "${CMAKE_SOURCE_DIR}/man/${INPUT_FILE}" DESTINATION "share/man/man1") diff --git a/man/zoslib.1 b/man/zoslib.1 index d5af44a..b7b1b4f 100644 --- a/man/zoslib.1 +++ b/man/zoslib.1 @@ -1,4 +1,4 @@ -.TH ZOSLIB 1 "February 2023" "ZOSLIB" +.TH ZOSLIB 1 "September 2023" "ZOSLIB" .SH NAME zoslib \- a z/OS C/C++ library @@ -12,16 +12,16 @@ ZOSLIB is a z/OS C/C++ library. It is an extended implementation of the z/OS LE Specify the CCSID for the stdio file descriptors. If these environment variables are not set and if the stdio file descriptor represents an untagged tty, it will be set to 1047 by default. .TP -.B __MEMORY_USAGE_LOG_LEVEL -set to 1 to display only warnings when memory is allocated or freed, and 2 to display all messages; the process started/terminated messages that include memory stats summary, as well as any error messages +.B _ENCODE_FILE_NEW=ISO8859-1 +(Default) New files are created with encoding ISO8859-1 and tagged ISO8859-1. .TP -.B __MEMORY_USAGE_LOG_FILE -name of the log file associated with __MEMORY_USAGE_LOG_LEVEL, including 'stdout' and 'stderr', to which diagnostic messages for memory allocation and release are to be written +.B _ENCODE_FILE_NEW=IBM-1047 +New files are created with encoding IBM-1047 and tagged IBM-1047. .TP -.B __RUNDEBUG -set to toggle debug ZOSLIB mode +.B _ENCODE_FILE_NEW=BINARY +New files are created without translation and are tagged as BINARY. .TP .B __UNTAGGED_READ_MODE=AUTO @@ -44,8 +44,16 @@ for no explicit conversion of data for same behavior as "AUTO" but issue a warning if conversion occurs .TP -.B __IPC_CLEANUP -set to toggle IPC cleanup at startup +.B __MEMORY_USAGE_LOG_LEVEL +set to 1 to display only warnings when memory is allocated or freed, and 2 to display all messages; the process started/terminated messages that include memory stats summary, as well as any error messages + +.TP +.B __MEMORY_USAGE_LOG_FILE +name of the log file associated with __MEMORY_USAGE_LOG_LEVEL, including 'stdout' and 'stderr', to which diagnostic messages for memory allocation and release are to be written + +.TP +.B __RUNDEBUG +set to toggle debug ZOSLIB mode .SH EXAMPLES To set the __UNTAGGED_READ_MODE environment variable to STRICT and disable explicit conversion of data: diff --git a/src/zos-char-util.cc b/src/zos-char-util.cc index aaa4cee..de0ab3f 100644 --- a/src/zos-char-util.cc +++ b/src/zos-char-util.cc @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -446,12 +447,12 @@ struct IntHash { typedef unsigned long fd_attribute; -typedef std::unordered_map::const_iterator cursor_t; - // [[clang::no_destroy]] attribute can be set for this, but the attribute is // not available for xlclang. static bool bfdcache_destroyed = false; +typedef std::unordered_map::const_iterator cursor_t; + class fdAttributeCache { std::unordered_map cache; pthread_mutex_t access_lock; diff --git a/src/zos-io.cc b/src/zos-io.cc index d2453b5..2de3605 100644 --- a/src/zos-io.cc +++ b/src/zos-io.cc @@ -702,6 +702,23 @@ int __disableautocvt(int fd) { return fcntl(fd, F_CONTROL_CVT, &req); } +int __tag_new_file(int fd) { + char* encode_file_new = getenv("_ENCODE_FILE_NEW"); + + int ccsid = 819; + + if (encode_file_new) { + if (strcmp(encode_file_new, "IBM-1047") == 0) { + ccsid = 1047; + } else if (strcmp(encode_file_new, "BINARY") == 0) { + // Set the file descriptor to binary mode + return __setfdbinary(fd); + } + } + + return __chgfdccsid(fd, ccsid); +} + int __chgfdcodeset(int fd, char* codeset) { unsigned short ccsid = __toCcsid(codeset); if (!ccsid) @@ -808,9 +825,9 @@ int __open_ascii(const char *filename, int opts, ...) { if (fd >= 0) { // Tag new files as ASCII (819) if (is_new_file) { - __chgfdccsid(fd, 819); - /* Calling __chgfdccsid() should not clobber errno. */ - errno = old_errno; + __tag_new_file(fd); + /* Calling __tag_new_file() should not clobber errno. */ + errno = old_errno; } // Enable auto-conversion of untagged files else if (S_ISREG(sb.st_mode)) { @@ -852,22 +869,20 @@ FILE *__fopen_ascii(const char *filename, const char *mode) { if (fp) { int fd = fileno(fp); if (is_new_file) { - __chgfdccsid(fd, 819); - errno = old_errno; + __tag_new_file(fd); + errno = old_errno; } // Enable auto-conversion of untagged files else if (S_ISREG(sb.st_mode)) { struct file_tag *t = &sb.st_tag; if (t->ft_txtflag == 0 && (t->ft_ccsid == 0 || t->ft_ccsid == 1047) && strcmp(mode, "r") == 0) { + __disableautocvt(fd); // disable z/OS autocvt on untagged file and use our heuristic if (__file_needs_conversion_init(filename, fd)) { struct f_cnvrt cvtreq = {SETCVTON, 0, 1047}; fcntl(fd, F_CONTROL_CVT, &cvtreq); /* Calling fcntl() should not clobber errno. */ errno = old_errno; - } else { - // fopen tags untagged files, which enables auto-conversion - __disableautocvt(fd); } } } else if (isatty(fd)) { @@ -910,13 +925,42 @@ int __mkstemp_ascii(char * tmpl) { if (ret < 0) return ret; - // Default ccsid for new fds should be ASCII (819) - if (__chgfdccsid(ret, 819) != 0) - return -1; + __tag_new_file(ret); return ret; } +char *mkdtemp(char *templ) { + size_t len = strlen(templ); + int retries = 10; // Number of retries to generate a unique directory name + + // Check that the templ ends with "XXXXXX" as required + if (len < 6 || strcmp(templ + len - 6, "XXXXXX") != 0) { + errno = EINVAL; + return NULL; + } + + while (retries--) { + // Generate a random string to replace "XXXXXX" in the templ + for (size_t i = len - 6; i < len; i++) { + templ[i] = 'a' + rand() % 26; + } + + // Attempt to create the directory + if (mkdir(templ, 0700) == 0) { + return templ; + } + + // If mkdir failed, check if it was due to a collision with an existing directory + if (errno != EEXIST) { + return NULL; + } + } + + // If all retries fail, return NULL + return NULL; +} + int __close(int fd) { int ret = __close_orig(fd); if (ret < 0) diff --git a/src/zos-sys-info.cc b/src/zos-sys-info.cc index b5f4013..2d9d896 100644 --- a/src/zos-sys-info.cc +++ b/src/zos-sys-info.cc @@ -40,6 +40,22 @@ int __get_num_frames(void) { return static_cast(rce->pool); } +// Adapted from OMR codebase: https://github.com/eclipse/omr/blob/master/port/unix/omrsysinfo.c#L4629 +int getloadavg(double loadavg[], int nelem) { + if (nelem > 3 || nelem <= 0) + return -1; + + ZOSCVT* __ptr32 cvt = ((ZOSPSA*)0)->cvt; + ZOSRMCT* __ptr32 rcmt = cvt->rmct; + ZOSCCT* __ptr32 cct = rcmt->cct; + + //Hack: z/OS does not get cpu load in samples, just use the same value + for (int i = 0; i < nelem; i++) + loadavg[i] = (double)cct->ccvutilp / 100.0; + + return nelem; +} + oslvl_t __get_os_level(void) { static oslvl_t oslvl = ZOSLVL_UNKNOWN; if (oslvl != ZOSLVL_UNKNOWN) diff --git a/src/zos.cc b/src/zos.cc index 0056765..efe48b6 100644 --- a/src/zos.cc +++ b/src/zos.cc @@ -3060,6 +3060,60 @@ extern "C" bool __doLogMemoryWarning() { extern "C" void __mainTerminating() { __gMainTerminating = true; } +//TODO: Implement chdir_long properly, for now call chdir +extern "C" int chdir_long(char *dir) { + return chdir(dir); +} + +extern "C" const char* getprogname() { + char argv[PATH_MAX]; + W_PSPROC buf; + int token = 0; + pid_t mypid = getpid(); + + memset(&buf, 0, sizeof(buf)); + buf.ps_pathlen = PATH_MAX; + buf.ps_pathptr = &argv[0]; + + while ((token = w_getpsent(token, &buf, sizeof(buf))) > 0) { + if (buf.ps_pid == mypid) { + // return the basename of the found executable + return strdup(basename(buf.ps_pathptr)); + } + } + + return NULL; +} + +extern "C" char* __getprogramdir() { + char argv[PATH_MAX]; + W_PSPROC buf; + int token = 0; + pid_t mypid = getpid(); + + memset(&buf, 0, sizeof(buf)); + buf.ps_pathlen = PATH_MAX; + buf.ps_pathptr = &argv[0]; + + while ((token = w_getpsent(token, &buf, sizeof(buf))) > 0) { + if (buf.ps_pid == mypid) { + // Resolve path to find the true location of the executable. + char* parent = __realpath_extended(argv, NULL); // realpath allocates parent + + if (parent == NULL) { + // handle error or return an appropriate value + return NULL; + } + + // Get the parent directory. + dirname(parent); + return parent; + } + } + + return NULL; +} + #if defined(ZOSLIB_INITIALIZE) __init_zoslib __zoslib; #endif diff --git a/test/test-clib-override.cc b/test/test-clib-override.cc index a7c7fe8..dddd2d1 100644 --- a/test/test-clib-override.cc +++ b/test/test-clib-override.cc @@ -67,6 +67,60 @@ TEST_F(CLIBOverrides, open) { EXPECT_EQ(strcmp(buff, buff2), 0); close(fd); #endif + + // Delete and re-open temp_path _ENCODE_FILE_NEW=IBM-1047 + char buff[] = "This is a test"; + + setenv("_ENCODE_FILE_NEW", "IBM-1047", 1); + remove(temp_path); + fd = open(temp_path, O_CREAT | O_WRONLY, 0777); + EXPECT_EQ(__getfdccsid(fd), 0x10000 + 1047); + write(fd, buff, sizeof(buff)); + close(fd); + + fd = open(temp_path, O_RDONLY); + EXPECT_EQ(__getfdccsid(fd), 0x10000 + 1047); + char* buff2 = (char*)malloc(sizeof(buff)); + memset(buff2, sizeof(buff), 1); + read(fd, buff2, sizeof(buff)); + EXPECT_EQ(strcmp(buff, buff2), 0); + free(buff2); + close(fd); + + // Delete and re-open temp_path _ENCODE_FILE_NEW=BINARY + setenv("_ENCODE_FILE_NEW", "BINARY", 1); + remove(temp_path); + fd = open(temp_path, O_CREAT | O_WRONLY, 0777); + EXPECT_EQ(__getfdccsid(fd), 65535); + write(fd, buff, sizeof(buff)); + close(fd); + + fd = open(temp_path, O_RDONLY); + EXPECT_EQ(__getfdccsid(fd), 65535); + buff2 = (char*)malloc(sizeof(buff)); + memset(buff2, sizeof(buff), 1); + read(fd, buff2, sizeof(buff)); + EXPECT_EQ(strcmp(buff, buff2), 0); + free(buff2); + close(fd); + + // Delete and re-open temp_path _ENCODE_FILE_NEW=ISO8859-1 + setenv("_ENCODE_FILE_NEW", "ISO8859-1", 1); + remove(temp_path); + fd = open(temp_path, O_CREAT | O_WRONLY, 0777); + EXPECT_EQ(__getfdccsid(fd), 0x10000 + 819); + write(fd, buff, sizeof(buff)); + close(fd); + + fd = open(temp_path, O_RDONLY); + EXPECT_EQ(__getfdccsid(fd), 0x10000 + 819); + buff2 = (char*)malloc(sizeof(buff)); + memset(buff2, sizeof(buff), 1); + read(fd, buff2, sizeof(buff)); + EXPECT_EQ(strcmp(buff, buff2), 0); + free(buff2); + close(fd); + unsetenv("_ENCODE_FILE_NEW"); } TEST_F(CLIBOverrides, pipe) { diff --git a/test/test-programinfo.cc b/test/test-programinfo.cc new file mode 100644 index 0000000..836d621 --- /dev/null +++ b/test/test-programinfo.cc @@ -0,0 +1,28 @@ +#include "zos.h" +#include "gtest/gtest.h" +#include + +namespace { + +TEST(ProgramInfoTest, GetProgDir) { + const char* path = __getprogramdir(); + EXPECT_STRNE(path, NULL); + + // Get program path in a different way + char** argv = __getargv(); + + // Check if program path is equal to the current working directory + EXPECT_STREQ(path, dirname(__realpath_extended(argv[0], NULL))); +} + +TEST(ProgramInfoTest, GetProgName) { + const char* path = getprogname(); + const char* substring = "cctest"; + + // Get program name in a different way + char** argv = __getargv(); + + EXPECT_STREQ(path, basename(argv[0])); +} + +} // namespace diff --git a/test/test-sys-info.cc b/test/test-sys-info.cc index fbc0e24..9b9d004 100644 --- a/test/test-sys-info.cc +++ b/test/test-sys-info.cc @@ -11,6 +11,16 @@ TEST(SysInfoTest, NumFrames) { EXPECT_GE(__get_num_frames(), 0); } +TEST(SysInfoTest, LoadAvg) { + double load[3]; + EXPECT_EQ(getloadavg(load, 1), 1); + EXPECT_EQ(getloadavg(load, 3), 3); + EXPECT_EQ(getloadavg(load, 4), -1); + EXPECT_GE(load[0], 0.0); + EXPECT_GE(load[1], 0.0); + EXPECT_GE(load[2], 0.0); +} + TEST(SysInfoTest, CPUModel) { size_t size = ZOSCPU_MODEL_LENGTH + 1; char model[size];