/** * @file compat.c * @author Michal Vasko * @brief compatibility functions * * Copyright (c) 2021 - 2023 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://opensource.org/licenses/BSD-3-Clause */ #define _POSIX_C_SOURCE 200809L /* fdopen, _POSIX_PATH_MAX, strdup */ #define _ISOC99_SOURCE /* vsnprintf */ #include "compat.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime) { int64_t nsec_diff; struct timespec cur, dur; int rc; /* try to acquire the lock and, if we fail, sleep for 5ms. */ while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) { /* get time */ clock_gettime(COMPAT_CLOCK_ID, &cur); /* get time diff */ nsec_diff = 0; nsec_diff += (((int64_t)abstime->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L; nsec_diff += ((int64_t)abstime->tv_nsec) - ((int64_t)cur.tv_nsec); if (nsec_diff <= 0) { /* timeout */ rc = ETIMEDOUT; break; } else if (nsec_diff < 5000000) { /* sleep until timeout */ dur.tv_sec = 0; dur.tv_nsec = nsec_diff; } else { /* sleep 5 ms */ dur.tv_sec = 0; dur.tv_nsec = 5000000; } nanosleep(&dur, NULL); } return rc; } #endif #ifndef HAVE_PTHREAD_MUTEX_CLOCKLOCK int pthread_mutex_clocklock(pthread_mutex_t *mutex, clockid_t clockid, const struct timespec *abstime) { /* only real time supported without this function */ if (clockid != CLOCK_REALTIME) { return EINVAL; } return pthread_mutex_timedlock(mutex, abstime); } #endif #ifndef HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, const struct timespec *abstime) { int64_t nsec_diff; struct timespec cur, dur; int rc; /* try to acquire the lock and, if we fail, sleep for 5ms. */ while ((rc = pthread_rwlock_tryrdlock(rwlock)) == EBUSY) { /* get time */ clock_gettime(COMPAT_CLOCK_ID, &cur); /* get time diff */ nsec_diff = 0; nsec_diff += (((int64_t)abstime->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L; nsec_diff += ((int64_t)abstime->tv_nsec) - ((int64_t)cur.tv_nsec); if (nsec_diff <= 0) { /* timeout */ rc = ETIMEDOUT; break; } else if (nsec_diff < 5000000) { /* sleep until timeout */ dur.tv_sec = 0; dur.tv_nsec = nsec_diff; } else { /* sleep 5 ms */ dur.tv_sec = 0; dur.tv_nsec = 5000000; } nanosleep(&dur, NULL); } return rc; } #endif #ifndef HAVE_PTHREAD_RWLOCK_CLOCKRDLOCK int pthread_rwlock_clockrdlock(pthread_rwlock_t *rwlock, clockid_t clockid, const struct timespec *abstime) { /* only real time supported without this function */ if (clockid != CLOCK_REALTIME) { return EINVAL; } return pthread_rwlock_timedrdlock(rwlock, abstime); } #endif #ifndef HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, const struct timespec *abstime) { int64_t nsec_diff; struct timespec cur, dur; int rc; /* try to acquire the lock and, if we fail, sleep for 5ms. */ while ((rc = pthread_rwlock_trywrlock(rwlock)) == EBUSY) { /* get time */ clock_gettime(COMPAT_CLOCK_ID, &cur); /* get time diff */ nsec_diff = 0; nsec_diff += (((int64_t)abstime->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L; nsec_diff += ((int64_t)abstime->tv_nsec) - ((int64_t)cur.tv_nsec); if (nsec_diff <= 0) { /* timeout */ rc = ETIMEDOUT; break; } else if (nsec_diff < 5000000) { /* sleep until timeout */ dur.tv_sec = 0; dur.tv_nsec = nsec_diff; } else { /* sleep 5 ms */ dur.tv_sec = 0; dur.tv_nsec = 5000000; } nanosleep(&dur, NULL); } return rc; } #endif #ifndef HAVE_PTHREAD_RWLOCK_CLOCKWRLOCK int pthread_rwlock_clockwrlock(pthread_rwlock_t *rwlock, clockid_t clockid, const struct timespec *abstime) { /* only real time supported without this function */ if (clockid != CLOCK_REALTIME) { return EINVAL; } return pthread_rwlock_timedwrlock(rwlock, abstime); } #endif #ifndef HAVE_PTHREAD_COND_CLOCKWAIT int pthread_cond_clockwait(pthread_cond_t *cond, pthread_mutex_t *mutex, clockid_t UNUSED(clockid), const struct timespec *abstime) { /* assume the correct clock is set during cond init */ return pthread_cond_timedwait(cond, mutex, abstime); } #endif #ifndef HAVE_VDPRINTF int vdprintf(int fd, const char *format, va_list ap) { FILE *stream; int count = 0; stream = fdopen(dup(fd), "a+"); if (stream) { count = vfprintf(stream, format, ap); fclose(stream); } return count; } #endif #ifndef HAVE_ASPRINTF int asprintf(char **strp, const char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = vasprintf(strp, fmt, ap); va_end(ap); return ret; } #endif #ifndef HAVE_VASPRINTF int vasprintf(char **strp, const char *fmt, va_list ap) { va_list ap2; va_copy(ap2, ap); int l = vsnprintf(0, 0, fmt, ap2); va_end(ap2); if ((l < 0) || !(*strp = malloc(l + 1U))) { return -1; } return vsnprintf(*strp, l + 1U, fmt, ap); } #endif #ifndef HAVE_GETLINE ssize_t getline(char **lineptr, size_t *n, FILE *stream) { static char line[256]; char *ptr; ssize_t len; if (!lineptr || !n) { errno = EINVAL; return -1; } if (ferror(stream) || feof(stream)) { return -1; } if (!fgets(line, 256, stream)) { return -1; } ptr = strchr(line, '\n'); if (ptr) { *ptr = '\0'; } len = strlen(line); if (len + 1 < 256) { ptr = realloc(*lineptr, 256); if (!ptr) { return -1; } *lineptr = ptr; *n = 256; } strcpy(*lineptr, line); return len; } #endif #ifndef HAVE_STRNDUP char * strndup(const char *s, size_t n) { char *buf; size_t len = 0; /* strnlen */ for ( ; (len < n) && (s[len] != '\0'); ++len) {} if (!(buf = malloc(len + 1U))) { return NULL; } memcpy(buf, s, len); buf[len] = '\0'; return buf; } #endif #ifndef HAVE_STRNSTR char * strnstr(const char *s, const char *find, size_t slen) { char c, sc; size_t len; if ((c = *find++) != '\0') { len = strlen(find); do { do { if ((slen-- < 1) || ((sc = *s++) == '\0')) { return NULL; } } while (sc != c); if (len > slen) { return NULL; } } while (strncmp(s, find, len)); s--; } return (char *)s; } #endif #ifndef HAVE_STRCHRNUL char * strchrnul(const char *s, int c) { char *p = strchr(s, c); return p ? p : (char *)s + strlen(s); } #endif #ifndef HAVE_GET_CURRENT_DIR_NAME char * get_current_dir_name(void) { char tmp[_POSIX_PATH_MAX]; char *retval = NULL; if (getcwd(tmp, sizeof(tmp))) { retval = strdup(tmp); if (!retval) { errno = ENOMEM; } } return retval; } #endif #ifndef HAVE_CRYPT_R char * crypt_r(const char *phrase, const char *setting, struct crypt_data *data) { static pthread_mutex_t crypt_lock = PTHREAD_MUTEX_INITIALIZER; char *hash; (void) data; pthread_mutex_lock(&crypt_lock); hash = crypt(phrase, setting); pthread_mutex_unlock(&crypt_lock); return hash; } #endif #ifndef HAVE_TIMEGM time_t timegm(struct tm *tm) { pthread_mutex_t tz_lock = PTHREAD_MUTEX_INITIALIZER; time_t ret; char *tz; pthread_mutex_lock(&tz_lock); tz = getenv("TZ"); if (tz) { tz = strdup(tz); } setenv("TZ", "", 1); tzset(); ret = mktime(tm); if (tz) { setenv("TZ", tz, 1); free(tz); } else { unsetenv("TZ"); } tzset(); pthread_mutex_unlock(&tz_lock); return ret; } #endif