extern "C" intptr_t SystemNative_MksTemps(char* pathTemplate, int32_t suffixLength) { intptr_t result; #if HAVE_MKSTEMPS while (CheckInterrupted(result = mkstemps(pathTemplate, suffixLength))); #elif HAVE_MKSTEMP // mkstemps is not available bionic/Android, but mkstemp is // mkstemp doesn't allow the suffix that msktemps does allow, so we'll need to // remove that before passing pathTemplate to mkstemp int32_t pathTemplateLength = static_cast<int32_t>(strlen(pathTemplate)); // pathTemplate must include at least XXXXXX (6 characters) which are not part of // the suffix if (suffixLength < 0 || suffixLength > pathTemplateLength - 6) { errno = EINVAL; return -1; } // Make mkstemp ignore the suffix by setting the first char of the suffix to \0, // if there is a suffix int32_t firstSuffixIndex = 0; char firstSuffixChar = 0; if (suffixLength > 0) { firstSuffixIndex = pathTemplateLength - suffixLength; firstSuffixChar = pathTemplate[firstSuffixIndex]; pathTemplate[firstSuffixIndex] = 0; } while (CheckInterrupted(result = mkstemp(pathTemplate))); // Reset the first char of the suffix back to its original value, if there is a suffix if (suffixLength > 0) { pathTemplate[firstSuffixIndex] = firstSuffixChar; } #else #error "Cannot find mkstemps nor mkstemp on this platform" #endif return result; }
extern "C" int32_t SystemNative_Pipe(int32_t pipeFds[2], int32_t flags) { switch (flags) { case 0: break; case PAL_O_CLOEXEC: flags = O_CLOEXEC; break; default: assert(false && "Unknown flag."); errno = EINVAL; return -1; } int32_t result; #if HAVE_PIPE2 // If pipe2 is available, use it. This will handle O_CLOEXEC if it was set. while (CheckInterrupted(result = pipe2(pipeFds, flags))); #else // Otherwise, use pipe. while (CheckInterrupted(result = pipe(pipeFds))); // Then, if O_CLOEXEC was specified, use fcntl to configure the file descriptors appropriately. if ((flags & O_CLOEXEC) != 0 && result == 0) { while (CheckInterrupted(result = fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC))); if (result == 0) { while (CheckInterrupted(result = fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC))); } if (result != 0) { int tmpErrno = errno; close(pipeFds[0]); close(pipeFds[1]); errno = tmpErrno; } } #endif return result; }
extern "C" void SystemNative_ReadEvents(int32_t sock, NetworkChangeEvent onNetworkChange) { char buffer[4096]; iovec iov = {buffer, sizeof(buffer)}; sockaddr_nl sanl; msghdr msg = { .msg_name = reinterpret_cast<void*>(&sanl), .msg_namelen = sizeof(sockaddr_nl), .msg_iov = &iov, .msg_iovlen = 1 }; ssize_t len; while (CheckInterrupted(len = recvmsg(sock, &msg, 0))); if (len == -1) { // Probably means the socket has been closed. return; } for (nlmsghdr* hdr = reinterpret_cast<nlmsghdr*>(buffer); NLMSG_OK(hdr, UnsignedCast(len)); NLMSG_NEXT(hdr, len)) { switch (hdr->nlmsg_type) { case NLMSG_DONE: return; // End of a multi-part message; stop reading. case NLMSG_ERROR: return; case RTM_NEWADDR: onNetworkChange(sock, NetworkChangeKind::AddressAdded); break; case RTM_DELADDR: onNetworkChange(sock, NetworkChangeKind::AddressRemoved); break; case RTM_NEWLINK: onNetworkChange(sock, ReadNewLinkMessage(hdr)); break; case RTM_DELLINK: onNetworkChange(sock, NetworkChangeKind::LinkRemoved); break; default: break; } } } NetworkChangeKind ReadNewLinkMessage(nlmsghdr* hdr) { assert(hdr != nullptr); ifinfomsg* ifimsg; ifimsg = reinterpret_cast<ifinfomsg*>(NLMSG_DATA(hdr)); if (ifimsg->ifi_family == AF_INET) { if ((ifimsg->ifi_flags & IFF_UP) != 0) { return NetworkChangeKind::LinkAdded; } } return NetworkChangeKind::None; }
static void WriteKeypadXmit() { // If a terminfo "application mode" keypad_xmit string has been supplied, // write it out to the terminal to enter the mode. if (g_keypadXmit != NULL) { ssize_t ret; while (CheckInterrupted(ret = write(STDOUT_FILENO, g_keypadXmit, (size_t)(sizeof(char) * strlen(g_keypadXmit))))); assert(ret >= 0); // failure to change the mode should not prevent app from continuing } }
extern "C" int32_t SystemNative_UTime(const char* path, UTimBuf* times) { assert(times != nullptr); utimbuf temp; ConvertUTimBuf(*times, temp); int32_t result; while (CheckInterrupted(result = utime(path, &temp))); return result; }
extern "C" NetworkChangeKind SystemNative_ReadSingleEvent(int32_t sock) { char buffer[4096]; iovec iov = {buffer, sizeof(buffer)}; sockaddr_nl sanl; msghdr msg = { .msg_name = reinterpret_cast<void*>(&sanl), .msg_namelen = sizeof(sockaddr_nl), .msg_iov = &iov, .msg_iovlen = 1 }; ssize_t len; while (CheckInterrupted(len = recvmsg(sock, &msg, 0))); if (len == -1) { // Probably means the socket has been closed. // If so, the managed side will ignore the return value. return NetworkChangeKind::None; } nlmsghdr* hdr = reinterpret_cast<nlmsghdr*>(buffer); // This channel should only send a single message at a time. // This means there should be no multi-part messages (NLM_F_MULTI). assert((hdr->nlmsg_flags & NLM_F_MULTI) == 0); switch (hdr->nlmsg_type) { case NLMSG_DONE: return NetworkChangeKind::None; case NLMSG_ERROR: return NetworkChangeKind::None; case RTM_NEWADDR: return NetworkChangeKind::AddressAdded; case RTM_DELADDR: return NetworkChangeKind::AddressRemoved; case RTM_NEWLINK: return ReadNewLinkMessage(hdr); case RTM_DELLINK: return NetworkChangeKind::LinkRemoved; default: return NetworkChangeKind::None; } } NetworkChangeKind ReadNewLinkMessage(nlmsghdr* hdr) { assert(hdr != nullptr); ifinfomsg* ifimsg; ifimsg = reinterpret_cast<ifinfomsg*>(NLMSG_DATA(hdr)); if (ifimsg->ifi_family == AF_INET) { if ((ifimsg->ifi_flags & IFF_UP) != 0) { return NetworkChangeKind::LinkAdded; } } return NetworkChangeKind::None; }
extern "C" int32_t SystemNative_FcntlSetPipeSz(intptr_t fd, int32_t size) { #ifdef F_SETPIPE_SZ int32_t result; while (CheckInterrupted(result = fcntl(ToFileDescriptor(fd), F_SETPIPE_SZ, size))); return result; #else (void)fd, (void)size; errno = ENOTSUP; return -1; #endif }
extern "C" int32_t SystemNative_FStat(intptr_t fd, FileStatus* output) { struct stat_ result; int ret; while (CheckInterrupted(ret = fstat_(ToFileDescriptor(fd), &result))); if (ret == 0) { ConvertFileStatus(result, output); } return ret; }
extern "C" int32_t SystemNative_ShmUnlink(const char* name) { #if HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP int32_t result; while (CheckInterrupted(result = shm_unlink(name))); return result; #else // Not supported on e.g. Android. Also, prevent a compiler error because name is unused (void)name; errno = ENOTSUP; return -1; #endif }
extern "C" int32_t SystemNative_Stat(const char* path, FileStatus* output) { struct stat_ result; int ret; while (CheckInterrupted(ret = stat_(path, &result))); if (ret == 0) { ConvertFileStatus(result, output); } return ret; }
extern "C" intptr_t SystemNative_Open(const char* path, int32_t flags, int32_t mode) { flags = ConvertOpenFlags(flags); if (flags == -1) { errno = EINVAL; return -1; } int result; while (CheckInterrupted(result = open(path, flags, static_cast<mode_t>(mode)))); return result; }
int32_t SystemNative_UTimensat(const char* path, TimeSpec* times) { int32_t result; #if HAVE_UTIMENSAT struct timespec updatedTimes[2]; updatedTimes[0].tv_sec = (time_t)times[0].tv_sec; updatedTimes[0].tv_nsec = (long)times[0].tv_nsec; updatedTimes[1].tv_sec = (time_t)times[1].tv_sec; updatedTimes[1].tv_nsec = (long)times[1].tv_nsec; while (CheckInterrupted(result = utimensat(AT_FDCWD, path, updatedTimes, 0))); #else struct timeval updatedTimes[2]; updatedTimes[0].tv_sec = (long)times[0].tv_sec; updatedTimes[0].tv_usec = (int)times[0].tv_nsec / 1000; updatedTimes[1].tv_sec = (long)times[1].tv_sec; updatedTimes[1].tv_usec = (int)times[1].tv_nsec / 1000; while (CheckInterrupted(result = utimes(path, updatedTimes))); #endif return result; }
extern "C" int32_t SystemNative_FTruncate(intptr_t fd, int64_t length) { int32_t result; while (CheckInterrupted( result = #if HAVE_FTRUNCATE64 ftruncate64( #else ftruncate( #endif ToFileDescriptor(fd), length))); return result; }
extern "C" int32_t SystemNative_Pipe(int32_t pipeFds[2], int32_t flags) { switch (flags) { case 0: break; case PAL_O_CLOEXEC: flags = O_CLOEXEC; break; default: assert(false && "Unknown flag."); errno = EINVAL; return -1; } int32_t result; #if HAVE_PIPE2 while (CheckInterrupted(result = pipe2(pipeFds, flags))); #else while (CheckInterrupted(result = pipe(pipeFds))); // CLOEXEC intentionally ignored on platforms without pipe2. #endif return result; }
extern "C" int64_t SystemNative_LSeek(intptr_t fd, int64_t offset, SeekWhence whence) { int64_t result; while (CheckInterrupted( result = #if HAVE_LSEEK64 lseek64( #else lseek( #endif ToFileDescriptor(fd), offset, whence))); return result; }
int32_t SystemNative_ReadStdin(void* buffer, int32_t bufferSize) { assert(buffer != NULL || bufferSize == 0); assert(bufferSize >= 0); if (bufferSize < 0) { errno = EINVAL; return -1; } ssize_t count; while (CheckInterrupted(count = read(STDIN_FILENO, buffer, Int32ToSizeT(bufferSize)))); return (int32_t)count; }
extern "C" int32_t SystemNative_ReadStdinUnbuffered(void* buffer, int32_t bufferSize) { assert(buffer != nullptr || bufferSize == 0); assert(bufferSize >= 0); if (bufferSize < 0) { errno = EINVAL; return -1; } ssize_t count; while (CheckInterrupted(count = read(STDIN_FILENO, buffer, UnsignedCast(bufferSize)))); return static_cast<int32_t>(count); }
extern "C" int32_t SystemNative_Write(intptr_t fd, const void* buffer, int32_t bufferSize) { assert(buffer != nullptr || bufferSize == 0); assert(bufferSize >= 0); if (bufferSize < 0) { errno = ERANGE; return -1; } ssize_t count; while (CheckInterrupted(count = write(ToFileDescriptor(fd), buffer, UnsignedCast(bufferSize)))); assert(count >= -1 && count <= bufferSize); return static_cast<int32_t>(count); }
extern "C" int32_t SystemNative_LockFileRegion(intptr_t fd, int64_t offset, int64_t length, int16_t lockType) { if (offset < 0 || length < 0) { errno = EINVAL; return -1; } #if HAVE_FLOCK64 struct flock64 lockArgs; #else struct flock lockArgs; #endif lockArgs.l_type = lockType; lockArgs.l_whence = SEEK_SET; lockArgs.l_start = offset; lockArgs.l_len = length; int32_t ret; while (CheckInterrupted(ret = fcntl (ToFileDescriptor(fd), F_SETLK, &lockArgs))); return ret; }
extern "C" int32_t SystemNative_RmDir(const char* path) { int32_t result; while (CheckInterrupted(result = rmdir(path))); return result; }
extern "C" int32_t SystemNative_Rename(const char* oldPath, const char* newPath) { int32_t result; while (CheckInterrupted(result = rename(oldPath, newPath))); return result; }
extern "C" Error SystemNative_Poll(PollEvent* pollEvents, uint32_t eventCount, int32_t milliseconds, uint32_t* triggered) { if (pollEvents == nullptr || triggered == nullptr) { return PAL_EFAULT; } if (milliseconds < -1) { return PAL_EINVAL; } size_t bufferSize = sizeof(pollfd) * static_cast<size_t>(eventCount); bool useStackBuffer = bufferSize <= 2048; pollfd* pollfds = reinterpret_cast<pollfd*>(useStackBuffer ? alloca(bufferSize) : malloc(bufferSize)); for (uint32_t i = 0; i < eventCount; i++) { const PollEvent& event = pollEvents[i]; pollfds[i] = { .fd = event.FileDescriptor, .events = event.Events, .revents = 0 }; } int rv; while (CheckInterrupted(rv = poll(pollfds, static_cast<nfds_t>(eventCount), milliseconds))); if (rv < 0) { if (!useStackBuffer) { free(pollfds); } *triggered = 0; return SystemNative_ConvertErrorPlatformToPal(errno); } for (uint32_t i = 0; i < eventCount; i++) { const pollfd& pfd = pollfds[i]; assert(pfd.fd == pollEvents[i].FileDescriptor); assert(pfd.events == pollEvents[i].Events); pollEvents[i].TriggeredEvents = static_cast<PollEvents>(pfd.revents); } *triggered = static_cast<uint32_t>(rv); if (!useStackBuffer) { free(pollfds); } return PAL_SUCCESS; } extern "C" int32_t SystemNative_PosixFAdvise(intptr_t fd, int64_t offset, int64_t length, FileAdvice advice) { #if HAVE_POSIX_ADVISE int32_t result; while (CheckInterrupted(result = posix_fadvise(ToFileDescriptor(fd), offset, length, advice))); return result; #else // Not supported on this platform. Caller can ignore this failure since it's just a hint. (void)fd, (void)offset, (void)length, (void)advice; return ENOTSUP; #endif }
extern "C" int32_t SystemNative_FTruncate(intptr_t fd, int64_t length) { int32_t result; while (CheckInterrupted(result = ftruncate(ToFileDescriptor(fd), length))); return result; }
extern "C" intptr_t SystemNative_MksTemps(char* pathTemplate, int32_t suffixLength) { intptr_t result; while (CheckInterrupted(result = mkstemps(pathTemplate, suffixLength))); return result; }
extern "C" int32_t SystemNative_Link(const char* source, const char* linkTarget) { int32_t result; while (CheckInterrupted(result = link(source, linkTarget))); return result; }
extern "C" int64_t SystemNative_LSeek(intptr_t fd, int64_t offset, SeekWhence whence) { int64_t result; while (CheckInterrupted(result = lseek(ToFileDescriptor(fd), offset, whence))); return result; }
extern "C" int32_t SystemNative_FLock(intptr_t fd, LockOperations operation) { int32_t result; while (CheckInterrupted(result = flock(ToFileDescriptor(fd), operation))); return result; }
extern "C" int32_t SystemNative_FSync(intptr_t fd) { int32_t result; while (CheckInterrupted(result = fsync(ToFileDescriptor(fd)))); return result; }
extern "C" int32_t SystemNative_FChMod(intptr_t fd, int32_t mode) { int32_t result; while (CheckInterrupted(result = fchmod(ToFileDescriptor(fd), static_cast<mode_t>(mode)))); return result; }
extern "C" int32_t SystemNative_ChMod(const char* path, int32_t mode) { int32_t result; while (CheckInterrupted(result = chmod(path, static_cast<mode_t>(mode)))); return result; }