/* * Copyright (c) 2008-2022 Cisco Systems, Inc. All rights reserved. * Copyright (c) 2009 Sandia National Laboratories. All rights reserved. * Copyright (c) 2014-2020 Intel, Inc. All rights reserved. * Copyright (c) 2015 Research Organization for Information Science * and Technology (RIST). All rights reserved. * Copyright (c) 2021-2022 Nanook Consulting. All rights reserved. * Copyright (c) 2022 The University of Tennessee and The University * of Tennessee Research Foundation. All rights * reserved. * $COPYRIGHT$ * * Additional copyrights may follow * * $HEADER$ */ #include "src/include/pmix_config.h" #include "pmix_common.h" #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_SOCKET_H # include #endif #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_NETINET_IN_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #ifdef HAVE_PWD_H # include #endif #ifdef HAVE_DIRENT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include "src/util/pmix_error.h" #include "src/util/pmix_fd.h" #include "src/util/pmix_string_copy.h" #include "src/runtime/pmix_rte.h" /* * Simple loop over reading from a fd */ pmix_status_t pmix_fd_read(int fd, int len, void *buffer) { int rc; char *b = buffer; while (len > 0) { rc = read(fd, b, len); if (rc < 0 && (EAGAIN == errno || EINTR == errno)) { continue; } else if (rc > 0) { len -= rc; b += rc; } else if (0 == rc) { return PMIX_ERR_TIMEOUT; } else { return PMIX_ERR_IN_ERRNO; } } return PMIX_SUCCESS; } /* * Simple loop over writing to an fd */ pmix_status_t pmix_fd_write(int fd, int len, const void *buffer) { int rc; const char *b = buffer; while (len > 0) { rc = write(fd, b, len); if (rc < 0 && (EAGAIN == errno || EINTR == errno)) { continue; } else if (rc > 0) { len -= rc; b += rc; } else { return PMIX_ERR_IN_ERRNO; } } return PMIX_SUCCESS; } pmix_status_t pmix_fd_set_cloexec(int fd) { #ifdef FD_CLOEXEC int flags; /* Stevens says that we should get the fd's flags before we set them. So say we all. */ flags = fcntl(fd, F_GETFD, 0); if (-1 == flags) { return PMIX_ERR_IN_ERRNO; } if (fcntl(fd, F_SETFD, FD_CLOEXEC | flags) == -1) { return PMIX_ERR_IN_ERRNO; } #endif return PMIX_SUCCESS; } bool pmix_fd_is_regular(int fd) { struct stat buf; /* coverity[TOCTOU] */ if (fstat(fd, &buf)) { return false; } return S_ISREG(buf.st_mode); } bool pmix_fd_is_chardev(int fd) { struct stat buf; /* coverity[TOCTOU] */ if (fstat(fd, &buf)) { return false; } return S_ISCHR(buf.st_mode); } bool pmix_fd_is_blkdev(int fd) { struct stat buf; /* coverity[TOCTOU] */ if (fstat(fd, &buf)) { return false; } return S_ISBLK(buf.st_mode); } #if PMIX_ENABLE_IPV6 static char str[INET6_ADDRSTRLEN]; #else static char str[INET_ADDRSTRLEN]; #endif const char *pmix_fd_get_peer_name(int fd) { const char *ret = NULL; struct sockaddr sa; socklen_t slt = (socklen_t) sizeof(sa); int rc; memset(str, 0, sizeof(str)); rc = getpeername(fd, &sa, &slt); if (0 != rc) { pmix_string_copy(str, "Unknown", sizeof(str) - 1); ret = str; return ret; } if (sa.sa_family == AF_INET) { struct sockaddr_in *si; si = (struct sockaddr_in *) &sa; ret = inet_ntop(AF_INET, &(si->sin_addr), str, INET_ADDRSTRLEN); } #if PMIX_ENABLE_IPV6 else if (sa.sa_family == AF_INET6) { struct sockaddr_in6 *si6; si6 = (struct sockaddr_in6 *) &sa; ret = inet_ntop(AF_INET6, &(si6->sin6_addr), str, INET6_ADDRSTRLEN); } #endif else { // This string is guaranteed to be <= INET_ADDRSTRLEN memset(str, 0, sizeof(str)); pmix_string_copy(str, "Unknown", sizeof(str) - 1); ret = str; } return ret; } static int fdmax = -1; /* close all open file descriptors w/ exception of stdin/stdout/stderr and the pipe up to the parent. */ void pmix_close_open_file_descriptors(int protected_fd) { #if defined(__OSX__) DIR *dir = opendir("/dev/fd"); #else /* Linux */ DIR *dir = opendir("/proc/self/fd"); #endif /* defined(__OSX__) */ struct dirent *files; int dir_scan_fd = -1; if (NULL == dir) { goto slow; } /* grab the fd of the opendir above so we don't close in the * middle of the scan. */ dir_scan_fd = dirfd(dir); if (dir_scan_fd < 0) { goto slow; } while (NULL != (files = readdir(dir))) { if (!isdigit(files->d_name[0])) { continue; } int fd = strtol(files->d_name, NULL, 10); if (errno == EINVAL || errno == ERANGE) { closedir(dir); goto slow; } if (fd >= 3 && (-1 == protected_fd || fd != protected_fd) && fd != dir_scan_fd) { close(fd); } } closedir(dir); return; slow: // close *all* file descriptors -- slow if (0 > fdmax) { fdmax = sysconf(_SC_OPEN_MAX); } // On some OS's (e.g., macOS), the value returned by // sysconf(_SC_OPEN_MAX) can be set by the user via "ulimit -n X", // where X can be -1 (unlimited) or a positive integer. On macOS // in particular, if the user does not set this value, it's // unclear how the default value is chosen. Some users have // reported seeing arbitrarily large default values (in the // billions), resulting in a very large loop over close() that can // take minutes/hours to complete, leading the user to think that // the app has hung. To avoid this, ensure that we cap the max FD // that we'll try to close. This is not a perfect scheme, and // there's uncertainty on how the macOS default value works, so we // provide the pmix_maxfd MCA var to allow the user to set the max // FD value if needed. if (-1 == fdmax || pmix_maxfd < fdmax) { fdmax = pmix_maxfd; } for (int fd = 3; fd < fdmax; fd++) { if (fd != protected_fd) { close(fd); } } }