#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BUFFER_SIZE 256 #ifdef INTERCEPT_PRELOAD static void *(*__real_malloc)(size_t); static void *(*__real_calloc)(size_t, size_t); static void *(*__real_realloc)(void *, size_t); static void *(*__real_reallocarray)(void *, size_t, size_t); static void (*__real_free)(void *); static int (*__real_getopt)(int, char *const [], const char *); static void (*__real_exit)(int); static int (*__real_close)(int); static int (*__real_sigaction)(int, const struct sigaction *, struct sigaction *); static int (*__real_sem_init)(sem_t *, int, unsigned int); static sem_t *(*__real_sem_open)(const char *, int, ...); static int (*__real_sem_post)(sem_t *); static int (*__real_sem_wait)(sem_t *); static int (*__real_sem_trywait)(sem_t *); static int (*__real_sem_timedwait)(sem_t *restrict, const struct timespec *restrict); static int (*__real_sem_getvalue)(sem_t *restrict, int *restrict); static int (*__real_sem_close)(sem_t *); static int (*__real_sem_unlink)(const char *); static int (*__real_sem_destroy)(sem_t *); static int (*__real_shm_open)(const char *, int, mode_t); static int (*__real_shm_unlink)(const char *); static void *(*__real_mmap)(void *, size_t, int, int, int, off_t); static int (*__real_munmap)(void *, size_t); static int (*__real_ftruncate)(int, off_t); #define load(name) \ if (((__real_ ## name) = dlsym(RTLD_NEXT, #name)) == NULL) { \ fprintf(stderr, "intercept: unable to load symbol '%s': %s", #name, strerror(errno)); \ return; \ } #define sym(name) name #else extern void *__real_malloc(size_t); extern void *__real_calloc(size_t, size_t); extern void *__real_realloc(void *, size_t); extern void *__real_reallocarray(void *, size_t, size_t); extern void __real_free(void *); extern int __real_getopt(int, char *const [], const char *); extern void __real_exit(int); extern int __real_close(int); extern int __real_sigaction(int, const struct sigaction *, struct sigaction *); extern int __real_sem_init(sem_t *, int, unsigned int); extern sem_t *__real_sem_open(const char *, int, ...); extern int __real_sem_post(sem_t *); extern int __real_sem_wait(sem_t *); extern int __real_sem_trywait(sem_t *); extern int __real_sem_timedwait(sem_t *restrict, const struct timespec *restrict); extern int __real_sem_getvalue(sem_t *restrict, int *restrict); extern int __real_sem_close(sem_t *); extern int __real_sem_unlink(const char *); extern int __real_sem_destroy(sem_t *); extern int __real_shm_open(const char *, int, mode_t); extern int __real_shm_unlink(const char *); extern void *__real_mmap(void *, size_t, int, int, int, off_t); extern int __real_munmap(void *, size_t); extern int __real_ftruncate(int, off_t); #define sym(name) __wrap_ ## name #endif #define ret_addr __builtin_return_address(0) #define if_invalid(name) if (strcmp(buf, "ok") != 0) { fprintf(stderr, "intercept: %s: invalid command: '%s'\n", #name, buf); } #define if_modify_int(name, type_1, var_1) if (strncmp(buf, "modify ", 7) == 0) { \ char *end_ptr = NULL; \ long val = strtol(buf + 7, &end_ptr, 0); \ if (end_ptr != NULL && end_ptr[0] == 0 && end_ptr != buf + 7) { \ var_1 = (type_1)val; \ } else { \ fprintf(stderr, "intercept: %s: invalid args in modify command: '%s'\n", #name, buf + 7); \ } } #define if_modify_int_int(name, type_1, var_1, type_2, var_2) if (strncmp(buf, "modify ", 7) == 0) { \ char *end_ptr_1 = NULL, *end_ptr_2 = NULL; \ long val_1 = strtol(buf + 7, &end_ptr_1, 0); \ long val_2 = 0; \ if (end_ptr_1 != NULL) val_2 = strtol(end_ptr_1 + 1, &end_ptr_2, 0); \ if (end_ptr_1 != NULL && end_ptr_1[0] == ',' && end_ptr_1 != buf + 7 && \ end_ptr_2 != NULL && end_ptr_2[0] == 0 && end_ptr_2 != end_ptr_1 + 1) \ { \ var_1 = (type_1)val_1; \ var_2 = (type_2)val_2; \ } else { \ fprintf(stderr, "intercept: %s: invalid args in modify command: '%s'\n", #name, buf + 7); \ } } #define if_modify_int_int_int(name, type_1, var_1, type_2, var_2, type_3, var_3) if (strncmp(buf, "modify ", 7) == 0) { \ char *end_ptr_1 = NULL, *end_ptr_2 = NULL, *end_ptr_3; \ long val_1 = strtol(buf + 7, &end_ptr_1, 0); \ long val_2 = 0, val_3 = 0; \ if (end_ptr_1 != NULL) val_2 = strtol(end_ptr_1 + 1, &end_ptr_2, 0); \ if (end_ptr_2 != NULL) val_3 = strtol(end_ptr_2 + 1, &end_ptr_3, 0); \ if (end_ptr_1 != NULL && end_ptr_1[0] == ',' && end_ptr_1 != buf + 7 && \ end_ptr_2 != NULL && end_ptr_2[0] == ',' && end_ptr_2 != end_ptr_1 + 1 && \ end_ptr_3 != NULL && end_ptr_3[0] == 0 && end_ptr_3 != end_ptr_2 + 1) \ { \ var_1 = (type_1)val_1; \ var_2 = (type_2)val_2; \ var_3 = (type_3)val_3; \ } else { \ fprintf(stderr, "intercept: %s: invalid args in modify command: '%s'\n", #name, buf + 7); \ } } #define if_return if (strcmp(buf, "return") == 0) { \ msg("return"); \ return; } #define if_return_int(name) if (strncmp(buf, "return ", 7) == 0) { \ char *end_ptr = NULL; \ long val = strtol(buf + 7, &end_ptr, 0); \ if (end_ptr != NULL && end_ptr[0] == 0 && end_ptr != buf + 7) { \ msg("return %i", val); \ return val; \ } else { \ fprintf(stderr, "intercept: %s: invalid args in return command: '%s'\n", #name, buf + 7); \ } } #define if_return_int_errno(name) if (strncmp(buf, "return ", 7) == 0) { \ char *end_ptr = NULL; \ long val = strtol(buf + 7, &end_ptr, 0); \ if (end_ptr != NULL && end_ptr[0] == 0 && end_ptr != buf + 7) { \ msg("return %i; errno %s", val, strerrorname_np(errno)); \ return val; \ } else { \ fprintf(stderr, "intercept: %s: invalid args in return command: '%s'\n", #name, buf + 7); \ } } #define if_return_int_errno_int(name, type_1, var_1, name_1) if (strncmp(buf, "return ", 7) == 0) { \ char *end_ptr_1 = NULL, *end_ptr_2 = NULL; \ long val_1 = strtol(buf + 7, &end_ptr_1, 0); \ long val_2 = 0; \ if (end_ptr_1 != NULL) val_2 = strtol(end_ptr_1 + 1, &end_ptr_2, 0); \ if (end_ptr_1 != NULL && end_ptr_1[0] == ',' && end_ptr_1 != buf + 7 && \ end_ptr_2 != NULL && end_ptr_2[0] == 0 && end_ptr_2 != end_ptr_1 + 1) \ { \ var_1 = (type_1)val_2; \ msg("return %i; errno %s; %s=%i", val_1, strerrorname_np(errno), name_1, var_1); \ return val_1; \ } else { \ fprintf(stderr, "intercept: %s: invalid args in return command: '%s'\n", #name, buf + 7); \ } } #define if_return_ptr_errno(name) if (strncmp(buf, "return ", 7) == 0) { \ char *end_ptr = NULL; \ long val = strtol(buf + 7, &end_ptr, 0); \ if (end_ptr != NULL && end_ptr[0] == 0 && end_ptr != buf + 7) { \ msg("return %p; errno %s", val, strerrorname_np(errno)); \ return (void *)val; \ } else { \ fprintf(stderr, "intercept: %s: invalid args in return command: '%s'\n", #name, buf + 7); \ } } #define if_error_1_ptr_errno(name, err1) if (strncmp(buf, "fail ", 5) == 0) { \ if (strcmp(buf + 5, #err1) == 0) { errno = err1; \ } else { \ errno = 0; \ fprintf(stderr, "intercept: %s: invalid error code in fail command: '%s'\n", #name, buf + 5); \ } \ msg("return %p; errno %s", NULL, strerrorname_np(errno)); \ return NULL; } #define if_error_1_int_errno(name, err1) if (strncmp(buf, "fail ", 5) == 0) { \ if (strcmp(buf + 5, #err1) == 0) { errno = err1; \ } else { \ errno = 0; \ fprintf(stderr, "intercept: %s: invalid error code in fail command: '%s'\n", #name, buf + 5); \ } \ msg("return %i; errno %s", -1, strerrorname_np(errno)); \ return -1; } #define if_error_2_int_errno(name, err1, err2) if (strncmp(buf, "fail ", 5) == 0) { \ if (strcmp(buf + 5, #err1) == 0) { errno = err1; \ } else if (strcmp(buf + 5, #err2) == 0) { errno = err2; \ } else { \ errno = 0; \ fprintf(stderr, "intercept: %s: invalid error code in fail command: '%s'\n", #name, buf + 5); \ } \ msg("return %i; errno %s", -1, strerrorname_np(errno)); \ return -1; } #define if_error_3_int_errno(name, err1, err2, err3) if (strncmp(buf, "fail ", 5) == 0) { \ if (strcmp(buf + 5, #err1) == 0) { errno = err1; \ } else if (strcmp(buf + 5, #err2) == 0) { errno = err2; \ } else if (strcmp(buf + 5, #err3) == 0) { errno = err3; \ } else { \ errno = 0; \ fprintf(stderr, "intercept: %s: invalid error code in fail command: '%s'\n", #name, buf + 5); \ } \ msg("return %i; errno %s", -1, strerrorname_np(errno)); \ return -1; } #define if_error_5_int_errno(name, err1, err2, err3, err4, err5) if (strncmp(buf, "fail ", 5) == 0) { \ if (strcmp(buf + 5, #err1) == 0) { errno = err1; \ } else if (strcmp(buf + 5, #err2) == 0) { errno = err2; \ } else if (strcmp(buf + 5, #err3) == 0) { errno = err3; \ } else if (strcmp(buf + 5, #err4) == 0) { errno = err4; \ } else if (strcmp(buf + 5, #err5) == 0) { errno = err5; \ } else { \ errno = 0; \ fprintf(stderr, "intercept: %s: invalid error code in fail command: '%s'\n", #name, buf + 5); \ } \ msg("return %i; errno %s", -1, strerrorname_np(errno)); \ return -1; } #define if_error_7_int_errno(name, err1, err2, err3, err4, err5, err6, err7) if (strncmp(buf, "fail ", 5) == 0) { \ if (strcmp(buf + 5, #err1) == 0) { errno = err1; \ } else if (strcmp(buf + 5, #err2) == 0) { errno = err2; \ } else if (strcmp(buf + 5, #err3) == 0) { errno = err3; \ } else if (strcmp(buf + 5, #err4) == 0) { errno = err4; \ } else if (strcmp(buf + 5, #err5) == 0) { errno = err5; \ } else if (strcmp(buf + 5, #err6) == 0) { errno = err6; \ } else if (strcmp(buf + 5, #err7) == 0) { errno = err7; \ } else { \ errno = 0; \ fprintf(stderr, "intercept: %s: invalid error code in fail command: '%s'\n", #name, buf + 5); \ } \ msg("return %i; errno %s", -1, strerrorname_np(errno)); \ return -1; } #define if_error_8_ptr_errno(name, err1, err2, err3, err4, err5, err6, err7, err8) if (strncmp(buf, "fail ", 5) == 0) { \ if (strcmp(buf + 5, #err1) == 0) { errno = err1; \ } else if (strcmp(buf + 5, #err2) == 0) { errno = err2; \ } else if (strcmp(buf + 5, #err3) == 0) { errno = err3; \ } else if (strcmp(buf + 5, #err4) == 0) { errno = err4; \ } else if (strcmp(buf + 5, #err5) == 0) { errno = err5; \ } else if (strcmp(buf + 5, #err6) == 0) { errno = err6; \ } else if (strcmp(buf + 5, #err6) == 0) { errno = err7; \ } else if (strcmp(buf + 5, #err8) == 0) { errno = err8; \ } else { \ errno = 0; \ fprintf(stderr, "intercept: %s: invalid error code in fail command: '%s'\n", #name, buf + 5); \ } \ msg("return %p; errno %s", NULL, strerrorname_np(errno)); \ return NULL; } #define if_error_10_map_errno(name, err1, err2, err3, err4, err5, err6, err7, err8, err9, err10) if (strncmp(buf, "fail ", 5) == 0) { \ if (strcmp(buf + 5, #err1) == 0) { errno = err1; \ } else if (strcmp(buf + 5, #err2) == 0) { errno = err2; \ } else if (strcmp(buf + 5, #err3) == 0) { errno = err3; \ } else if (strcmp(buf + 5, #err4) == 0) { errno = err4; \ } else if (strcmp(buf + 5, #err5) == 0) { errno = err5; \ } else if (strcmp(buf + 5, #err6) == 0) { errno = err6; \ } else if (strcmp(buf + 5, #err7) == 0) { errno = err7; \ } else if (strcmp(buf + 5, #err8) == 0) { errno = err8; \ } else if (strcmp(buf + 5, #err9) == 0) { errno = err9; \ } else if (strcmp(buf + 5, #err10) == 0) { errno = err10; \ } else { \ errno = 0; \ fprintf(stderr, "intercept: %s: invalid error code in fail command: '%s'\n", #name, buf + 5); \ } \ msg("return %i; errno %s", MAP_FAILED, strerrorname_np(errno)); \ return MAP_FAILED; } static char *strtostr(char *ptr, char **end_ptr, char *buf) { if (ptr[0] != '"') return NULL; int i = 0, esc = 0, hex = 0; for (*end_ptr = ptr + 1; *end_ptr[0] != 0; (*end_ptr)++) { char ch = *end_ptr[0]; if (esc) { if (esc == 2) { if (ch >= '0' && ch <= '9') { hex = (ch - '0') << 4; } else if (ch >= 'A' && ch <= 'F') { hex = (ch - 'A' + 10) << 4; } else if (ch >= 'a' && ch <= 'f') { hex = (ch - 'a' + 10) << 4; } else { return NULL; } esc = 3; continue; } else if (esc == 3) { if (ch >= '0' && ch <= '9') { hex |= (ch - '0'); } else if (ch >= 'A' && ch <= 'F') { hex |= (ch - 'A' + 10); } else if (ch >= 'a' && ch <= 'f') { hex |= (ch - 'a' + 10); } else { return NULL; } buf[i++] = (char)hex; esc = 0; continue; } switch (ch) { case 'a': ch = '\a'; break; case 'b': ch = '\b'; break; case 't': ch = '\t'; break; case 'n': ch = '\n'; break; case 'v': ch = '\v'; break; case 'f': ch = '\f'; break; case 'r': ch = '\r'; break; case '0': ch = 0; break; case 'x': esc = 2; continue; case '"': case '\\': break; default: return NULL; } buf[i++] = ch; esc = 0; } else if (ch == '\\') { esc = 1; } else if (ch == '"') { (*end_ptr)++; return buf; } else { buf[i++] = ch; } } return NULL; } static int mode = 0; static int intercept = 0; static size_t msg_str(char *buf, size_t maxlen, const char *str) { size_t offset = snprintf(buf, maxlen, "%p:\"", str); for (char ch; (ch = *str) != 0 && offset <= maxlen - 2; str++) { if (ch == '\\' || ch == '"') { buf[offset++] = '\\'; buf[offset++] = ch; } else if (ch == '\t') { buf[offset++] = '\\'; buf[offset++] = 't'; } else if (ch == '\n') { buf[offset++] = '\\'; buf[offset++] = 'n'; } else if (ch == '\r') { buf[offset++] = '\\'; buf[offset++] = 'r'; } else if ((ch >= 0 && ch < 0x20) || ch == 0x7F) { offset += snprintf(buf + offset, maxlen - offset, "\\x%02x", ch); } else { buf[offset++] = ch; } } if (offset <= maxlen - 2) { buf[offset++] = '"'; buf[offset] = 0; } return offset; } static size_t msg_array_str(char *buf, size_t maxlen, char *const array[], int n) { size_t offset = snprintf(buf, maxlen, "%p:[", (void *)array); for (int i = 0; i < n && offset <= maxlen - 2; i++) { if (i > 0) { buf[offset++] = ','; buf[offset++] = ' '; } offset += msg_str(buf + offset, maxlen - offset, array[i]); } if (offset <= maxlen - 2) { buf[offset++] = ']'; buf[offset] = 0; } return offset; } static void msg(const char *fmt, ...) { if (!intercept) return; char buf[1024], sub_fmt[16]; int sub_fmt_p = 0; va_list args; va_start(args, fmt); struct timespec spec; clock_gettime(CLOCK_REALTIME, &spec); size_t offset = snprintf(buf, sizeof(buf), "%li.%09li ", spec.tv_sec, spec.tv_nsec); for (char ch, state = 0; (ch = *fmt) != 0 && offset < sizeof(buf); fmt++) { if (state == '%') { if (ch == '%') { buf[offset++] = ch; state = 0; continue; } else if (ch == 'e') { state = 'e'; continue; } else if (ch == 'a') { state = 'a'; continue; } else { sub_fmt_p = 0; state = '_'; sub_fmt[sub_fmt_p++] = '%'; sub_fmt[sub_fmt_p] = 0; } } if (state == '_') { sub_fmt[sub_fmt_p++] = ch; sub_fmt[sub_fmt_p] = 0; if (ch == 's' || ch == 'i' || ch == 'd' || ch == 'o' || ch == 'u' || ch == 'x' || ch == 'X' || ch == 'f' || ch == 'p') { state = 0; offset += snprintf(buf + offset, sizeof(buf) - offset, sub_fmt, va_arg(args, long int)); } } else if (state == 'e') { if (ch == 's') { // escaped string offset += msg_str(buf + offset, sizeof(buf) - offset, va_arg(args, const char *)); } else { // error va_end(args); return; } state = 0; } else if (state == 'a') { if (ch == 's') { // string array char *const *array = va_arg(args, char *const *); const int len = va_arg(args, int); offset += msg_array_str(buf + offset, sizeof(buf) - offset, array, len); } else { // error va_end(args); return; } state = 0; } else if (ch == '%') { state = '%'; } else { buf[offset++] = ch; } } if (offset <= sizeof(buf) - 2) { buf[offset++] = '\n'; buf[offset] = 0; } const size_t size = offset >= sizeof(buf) ? sizeof(buf) - 1 : offset; ssize_t ret; for (size_t written = 0; written < size; written += ret) { if ((ret = write(intercept, buf, size)) == -1) { if (errno == EINTR) { ret = 0; continue; } va_end(args); return; } } va_end(args); } static void rcv(char *buf, const int size) { if (!intercept) return; size_t num = 0; for (ssize_t ret; num == 0 || buf[num - 1] != '\n'; num += ret) { if ((ret = read(intercept, buf, size)) == -1) { if (errno == EINTR) { ret = 0; continue; } buf[0] = 0; return; } } buf[num - 1] = 0; } static void fin(void) { if (intercept && mode > 2) __real_close(intercept); if (mode > 0) fprintf(stderr, "intercept: stopped\n"); mode = 0, intercept = 0; } static void init(void) { if (mode) return; #ifdef INTERCEPT_PRELOAD load(malloc); load(calloc); load(realloc); load(reallocarray); load(free); load(getopt); load(exit); load(close); load(sigaction); load(sem_init); load(sem_open); load(sem_post); load(sem_wait); load(sem_trywait); load(sem_timedwait); load(sem_getvalue); load(sem_close); load(sem_unlink); load(sem_destroy); load(shm_open); load(shm_unlink); load(mmap); load(munmap); load(ftruncate); #endif atexit(fin); const char *val = getenv("INTERCEPT"); if (val && strcmp(val, "stdout") == 0) { mode = 1; fprintf(stderr, "intercept: intercepting function/system calls and logging to stdout\n"); intercept = STDOUT_FILENO; } else if (val && strcmp(val, "stderr") == 0) { mode = 2; fprintf(stderr, "intercept: intercepting function/system calls and logging to stderr\n"); intercept = STDERR_FILENO; } else if (val && strncmp(val, "file:", 5) == 0) { mode = 3; if ((intercept = open(val + 5, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) { fprintf(stderr, "intercept: unable to open log file '%s': %s\nintercept: not logging or manipulating function/system calls\n", val + 5, strerror(errno)); errno = 0; mode = -1; return; } fprintf(stderr, "intercept: intercepting function/system calls and logging to file\n"); } else if (val && strncmp(val, "unix:", 5) == 0) { mode = 4; if ((intercept = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { fprintf(stderr, "intercept: unable to open unix socket '%s': %s\nintercept: not logging or manipulating function/system calls\n", val + 5, strerror(errno)); errno = 0; mode = -1; return; } struct sockaddr_un addr = {.sun_family = AF_UNIX}; strncpy(addr.sun_path, val + 5, sizeof(addr.sun_path)); if (connect(intercept, (struct sockaddr *)&addr, sizeof(addr)) == -1) { fprintf(stderr, "intercept: unable to connect to unix socket '%s': %s\nintercept: not logging or manipulating function/system calls\n", val + 5, strerror(errno)); close(intercept); errno = 0; mode = -1; return; } fprintf(stderr, "intercept: intercepting function/system calls and logging to unix socket\n"); char buf[256] = ""; const ssize_t ret = readlink("/proc/self/exe", buf, sizeof(buf)); msg("PID:%li%s%s", getpid(), ret != -1 ? ";PATH:" : "", buf); } else if (val && strncmp(val, "tcp://", 6) == 0) { mode = 5; // TODO socket/tcp mode } else { mode = -1; fprintf(stderr, "intercept: not logging or manipulating function/system calls\n"); } } void *sym(malloc)(size_t size) { init(); msg("malloc(%li): %p", size, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int(malloc, size_t, size) else if_return_ptr_errno(malloc) else if_error_1_ptr_errno(malloc, ENOMEM) else if_invalid(malloc) } void *ret = __real_malloc(size); msg("return %p; errno %s", ret, strerrorname_np(errno)); return ret; } void *sym(calloc)(size_t nmemb, size_t size) { init(); msg("calloc(%li, %li): %p", nmemb, size, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int_int(calloc, size_t, nmemb, size_t, size) else if_return_ptr_errno(calloc) else if_error_1_ptr_errno(calloc, ENOMEM) else if_invalid(calloc) } void *ret = __real_calloc(nmemb, size); msg("return %p; errno %s", ret, strerrorname_np(errno)); return ret; } void *sym(realloc)(void *ptr, size_t size) { init(); msg("realloc(%p, %li): %p", ptr, size, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int_int(realloc, void *, ptr, size_t, size) else if_return_ptr_errno(realloc) else if_error_1_ptr_errno(realloc, ENOMEM) else if_invalid(realloc) } void *ret = __real_realloc(ptr, size); msg("return %p; errno %s", ret, strerrorname_np(errno)); return ret; } void *sym(reallocarray)(void *ptr, size_t nmemb, size_t size) { init(); msg("reallocarray(%p, %li): %p", ptr, size, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int_int_int(reallocarray, void *, ptr, size_t, nmemb, size_t, size) else if_return_ptr_errno(reallocarray) else if_error_1_ptr_errno(reallocarray, ENOMEM) else if_invalid(reallocarray) } void *ret = __real_reallocarray(ptr, nmemb, size); msg("return %p; errno %s", ret, strerrorname_np(errno)); return ret; } void sym(free)(void *ptr) { init(); msg("free(%p): %p", ptr, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int(free, void *, ptr) else if_return else if_invalid(free) } __real_free(ptr); msg("return"); } int sym(getopt)(const int argc, char *const argv[], const char *shortopts) { init(); msg("getopt(%i, %as, %es): %p", argc, argv, argc, shortopts, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if (strncmp(buf, "modify ", 7) == 0) { // modify ,, // TODO getopt modify fprintf(stderr, "intercept: %s: modify command not implemented\n", "getopt"); } else if_return_int(getopt) else if_invalid(getopt) } const int ret = __real_getopt(argc, argv, shortopts); msg("return %i", ret); return ret; } void sym(exit)(int status) { init(); msg("exit(%i): %p", status, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int("exit", int, status) else if_invalid(getopt) } __real_exit(status); } int sym(close)(int fildes) { init(); msg("close(%i): %p", fildes, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int(close, int, fildes) else if_return_int_errno(close) else if_error_3_int_errno(close, EBADF, EINTR, EIO) else if_invalid(close) } const int ret = __real_close(fildes); msg("return %i; errno %s", ret, strerrorname_np(errno)); return ret; } static const char *getsigstr(int sig) { if (sig == SIGINT) return "SIGINT"; if (sig == SIGILL) return "SIGILL"; if (sig == SIGABRT) return "SIGABRT"; if (sig == SIGFPE) return "SIGFPE"; if (sig == SIGSEGV) return "SIGSEGV"; if (sig == SIGTERM) return "SIGTERM"; if (sig == SIGHUP) return "SIGHUP"; if (sig == SIGQUIT) return "SIGQUIT"; if (sig == SIGTRAP) return "SIGTRAP"; if (sig == SIGKILL) return "SIGKILL"; if (sig == SIGPIPE) return "SIGPIPE"; if (sig == SIGALRM) return "SIGALRM"; if (sig == SIGSTKFLT) return "SIGSTKFLT"; if (sig == SIGPWR) return "SIGPWR"; if (sig == SIGBUS) return "SIGBUS"; if (sig == SIGSYS) return "SIGSYS"; if (sig == SIGURG) return "SIGURG"; if (sig == SIGSTOP) return "SIGSTOP"; if (sig == SIGTSTP) return "SIGTSTP"; if (sig == SIGCONT) return "SIGCONT"; if (sig == SIGCHLD) return "SIGCHLD"; if (sig == SIGTTIN) return "SIGTTIN"; if (sig == SIGTTOU) return "SIGTTOU"; if (sig == SIGPOLL) return "SIGPOLL"; if (sig == SIGXFSZ) return "SIGXFSZ"; if (sig == SIGXCPU) return "SIGXCPU"; if (sig == SIGVTALRM) return "SIGVTALRM"; if (sig == SIGPROF) return "SIGPROF"; if (sig == SIGUSR1) return "SIGUSR1"; if (sig == SIGUSR2) return "SIGUSR2"; if (sig == SIGWINCH) return "SIGWINCH"; return "?"; } int sym(sigaction)(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact) { init(); const char *sigstr = getsigstr(sig); if (act != NULL) { char *name = "sa_handler"; void *ptr = (void *)act->sa_handler; if (act->sa_flags & SA_SIGINFO) { name = "sa_sigaction"; ptr = (void *)act->sa_sigaction; } char flgstr[64] = "|"; if (act->sa_flags & SA_NOCLDSTOP) strcat(flgstr, "SA_NOCLDSTOP|"); if (act->sa_flags & SA_NOCLDWAIT) strcat(flgstr, "SA_NOCLDWAIT|"); if (act->sa_flags & SA_SIGINFO) strcat(flgstr, "SA_SIGINFO|"); if (act->sa_flags & ~(SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO)) strcat(flgstr, "?|"); char maskstr[512] = ""; for (int i = 0; i < 64; i++) { if (sigismember(&act->sa_mask, i) != 1) continue; if (maskstr[0] != 0) strcat(maskstr, ","); strcat(maskstr, getsigstr(i)); } msg("sigaction(%i:%s, %p:{sa_flags: 0x%x:%s, %s: %p, sa_mask: [%s]}, %p): %p", sig, sigstr, act, act->sa_flags, flgstr, name, ptr, maskstr, oact, ret_addr); } else { msg("sigaction(%i:%s, %p:{}, %p): %p", sig, sigstr, act, oact, ret_addr); } if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if (strncmp(buf, "modify ", 7) == 0) { // TODO sigaction modify fprintf(stderr, "intercept: %s: modify command not implemented\n", "sigaction"); } else if (strncmp(buf, "return ", 7) == 0) { // TODO sigaction return fprintf(stderr, "intercept: %s: return command not implemented\n", "sigaction"); } else if_error_1_int_errno(sigaction, EINVAL) else if_invalid(sigaction) } const int ret = __real_sigaction(sig, act, oact); if (oact != NULL) { char *name = "sa_handler"; void *ptr = (void *)oact->sa_handler; if (oact->sa_flags & SA_SIGINFO) { name = "sa_sigaction"; ptr = (void *)oact->sa_sigaction; } char flgstr[64] = "|"; if (oact->sa_flags & SA_NOCLDSTOP) strcat(flgstr, "SA_NOCLDSTOP|"); if (oact->sa_flags & SA_NOCLDWAIT) strcat(flgstr, "SA_NOCLDWAIT|"); if (oact->sa_flags & SA_SIGINFO) strcat(flgstr, "SA_SIGINFO|"); if (oact->sa_flags & ~(SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO)) strcat(flgstr, "?|"); char maskstr[512] = ""; for (int i = 0; i < 64; i++) { if (sigismember(&oact->sa_mask, i) != 1) continue; if (maskstr[0] != 0) strcat(maskstr, ","); strcat(maskstr, getsigstr(i)); } msg("return %i; errno %s; oact={sa_flags: 0x%x:%s, %s: %p, sa-mask: [%s]}", ret, strerrorname_np(errno), oact->sa_flags, flgstr, name, ptr, maskstr); } else { msg("return %i; errno %s", ret, strerrorname_np(errno)); } return ret; } int sym(sem_init)(sem_t *sem, int pshared, unsigned int value) { init(); msg("sem_init(%p, %i, %u): %p", sem, pshared, value, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int_int_int(sem_init, sem_t *, sem, int, pshared, unsigned int, value) else if_return_int_errno(sem_init) else if_error_2_int_errno(sem_init, EINVAL, ENOSYS) else if_invalid(sem_init) } const int ret = __real_sem_init(sem, pshared, value); msg("return %i; errno %s", ret, strerrorname_np(errno)); return ret; } sem_t *sym(sem_open)(const char *name, int oflag, ...) { init(); char ostr[32] = "|"; if (oflag & O_CREAT) strcat(ostr, "O_CREAT|"); if (oflag & O_EXCL) strcat(ostr, "O_EXCL|"); if (oflag & ~(O_CREAT | O_EXCL)) strcat(ostr, "?|"); mode_t mode_arg = 0; unsigned int value = 0; if (oflag & O_CREAT) { va_list args; va_start(args, oflag); mode_arg = va_arg(args, mode_t); value = va_arg(args, unsigned int); va_end(args); msg("sem_open(%es, 0%o:%s, 0%03o, %u): %p", name, oflag, ostr, mode_arg, value, ret_addr); } else { msg("sem_open(%es, 0%o:|%s): %p", name, oflag, ostr, ret_addr); } char overwrite[BUFFER_SIZE]; if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if (strncmp(buf, "modify ", 7) == 0) { // modify , // modify ,,, char *val_1 = NULL, *end_ptr_1 = NULL, *end_ptr_2 = NULL, *end_ptr_3 = NULL, *end_ptr_4 = NULL; long val_2 = 0, val_3 = 0, val_4 = 0; if (buf[7] == '"') { // modify "",... val_1 = strtostr(buf + 7, &end_ptr_1, overwrite); if (val_1 == NULL) end_ptr_1 = NULL; } else { // modify ,... val_1 = (char *)strtol(buf + 7, &end_ptr_1, 0); } if (end_ptr_1 != NULL && end_ptr_1[0] != 0) { if (end_ptr_1[1] == '|') { val_2 = 0; end_ptr_2 = end_ptr_1 + 1; if (strncmp(end_ptr_2, "O_CREAT|", 8) == 0) { val_2 |= O_CREAT; end_ptr_2 += 8; } if (strncmp(end_ptr_2, "O_EXCL|", 7) == 0) { val_2 |= O_EXCL; end_ptr_2 += 7; } } else { val_2 = strtol(end_ptr_1 + 1, &end_ptr_2, 0); } } if (end_ptr_2 != NULL && end_ptr_2[0] != 0) val_3 = strtol(end_ptr_2 + 1, &end_ptr_3, 0); if (end_ptr_3 != NULL && end_ptr_3[0] != 0) val_4 = strtol(end_ptr_3 + 1, &end_ptr_4, 0); if (end_ptr_1 != NULL && end_ptr_1[0] == ',' && end_ptr_1 != buf + 7 && end_ptr_2 != NULL && (end_ptr_2[0] == 0 || end_ptr_2[0] == ',') && end_ptr_2 != end_ptr_1 + 1 && (end_ptr_2[0] == 0 || (end_ptr_3 != NULL && end_ptr_3[0] == ',' && end_ptr_3 != end_ptr_2 + 1 && end_ptr_4 != NULL && end_ptr_4[0] == 0 && end_ptr_4 != end_ptr_3 + 1))) { name = val_1; oflag = (int)val_2; if (end_ptr_2[0] != 0) { mode_arg = val_3; value = val_4; } } else { fprintf(stderr, "intercept: %s: invalid args in modify command: '%s'\n", "sem_open", buf + 7); \ } } else if_return_ptr_errno(sem_open) else if_error_8_ptr_errno(sem_open, EACCES, EEXIST, EINVAL, EMFILE, ENAMETOOLONG, ENFILE, ENOENT, ENOMEM) else if_invalid(sem_open) } sem_t *ret; if (oflag & O_CREAT) { ret = __real_sem_open(name, oflag, mode_arg, value); } else { ret = __real_sem_open(name, oflag); } msg("return %p; errno %s", ret, strerrorname_np(errno)); return ret; } int sym(sem_post)(sem_t *sem) { init(); msg("sem_post(%p): %p", sem, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int(sem_post, sem_t *, sem) else if_return_int_errno(sem_post) else if_error_2_int_errno(sem_post, EINVAL, EOVERFLOW) else if_invalid(sem_post) } const int ret = __real_sem_post(sem); msg("return %i; errno %s", ret, strerrorname_np(errno)); return ret; } int sym(sem_wait)(sem_t *sem) { init(); msg("sem_wait(%p): %p", sem, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int(sem_wait, sem_t *, sem) else if_return_int_errno(sem_wait) else if_error_2_int_errno(sem_wait, EINTR, EINVAL) else if_invalid(sem_wait) } const int ret = __real_sem_wait(sem); msg("return %i; errno %s", ret, strerrorname_np(errno)); return ret; } int sym(sem_trywait)(sem_t *sem) { init(); msg("sem_trywait(%p): %p", sem, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int(sem_trywait, sem_t *, sem) else if_return_int_errno(sem_trywait) else if_error_3_int_errno(sem_trywait, EAGAIN, EINTR, EINVAL) else if_invalid(sem_trywait) } const int ret = __real_sem_trywait(sem); msg("return %i; errno %s", ret, strerrorname_np(errno)); return ret; } int sym(sem_timedwait)(sem_t *restrict sem, const struct timespec *restrict abs_timeout) { init(); msg("sem_timedwait(%p, %p:{tv_sec: %li, tv_nsec: %li}): %p", sem, abs_timeout, abs_timeout->tv_sec, abs_timeout->tv_nsec, ret_addr); struct timespec overwrite; if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if (strncmp(buf, "modify ", 7) == 0) { // modfiy , // modify ,{,} char *end_ptr_1 = NULL, *end_ptr_2 = NULL, *end_ptr_3 = NULL; long val_1 = strtol(buf + 7, &end_ptr_1, 0); struct timespec *val_2 = 0; if (end_ptr_1 != NULL && end_ptr_1[0] != 0) { if (end_ptr_1[1] == '{') { long sec = 0, nsec = 0; sec = strtol(end_ptr_1 + 2, &end_ptr_2, 0); if (end_ptr_2 != NULL && end_ptr_2[0] == ',') nsec = strtol(end_ptr_2 + 1, &end_ptr_3, 0); overwrite.tv_sec = sec; overwrite.tv_nsec = nsec; if (end_ptr_3 != NULL && end_ptr_3[0] == '}' && end_ptr_3[1] == 0) { val_2 = &overwrite; end_ptr_2 = end_ptr_3 + 1; } } else { val_2 = (void *)strtol(end_ptr_1 + 1, &end_ptr_2, 0); } } if (end_ptr_1 != NULL && end_ptr_1[0] == ',' && end_ptr_1 != buf + 7 && end_ptr_2 != NULL && end_ptr_2[0] == 0 && end_ptr_2 != end_ptr_1 + 1) { sem = (sem_t *)val_1; abs_timeout = val_2; } else { fprintf(stderr, "intercept: %s: invalid args in modify command: '%s'\n", "sem_timedwait", buf + 7); \ } } else if_return_int_errno(sem_timedwait) else if_error_3_int_errno(sem_timedwait, EINTR, EINVAL, ETIMEDOUT) else if_invalid(sem_timedwait) } const int ret = __real_sem_timedwait(sem, abs_timeout); msg("return %i; errno %s", ret, strerrorname_np(errno)); return ret; } int sym(sem_getvalue)(sem_t *restrict sem, int *restrict value) { init(); msg("sem_getvalue(%p, %p): %p", sem, value, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int_int(sem_getvalue, sem_t *, sem, int *, value) else if_return_int_errno_int(sem_getvalue, int, *value, "value") else if_error_1_int_errno(sem_getvalue, EINVAL) else if_invalid(sem_getvalue) } const int ret = __real_sem_getvalue(sem, value); if (value != NULL) { msg("return %i; errno %s; value=%i", ret, strerrorname_np(errno), *value); } else { msg("return %i; errno %s", ret, strerrorname_np(errno)); } return ret; } int sym(sem_close)(sem_t *sem) { init(); msg("sem_close(%p): %p", sem, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int(sem_close, sem_t *, sem) else if_return_int_errno(sem_close) else if_error_1_int_errno(sem_close, EINVAL) else if_invalid(sem_close) } const int ret = __real_sem_close(sem); msg("return %i; errno %s", ret, strerrorname_np(errno)); return ret; } int sym(sem_unlink)(const char *name) { init(); msg("sem_unlink(%es): %p", name, ret_addr); char overwrite[BUFFER_SIZE]; if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if (strncmp(buf, "modify ", 7) == 0) { char *end_ptr = NULL, *val = NULL; if (buf[7] == '"') { // modify "" val = strtostr(buf + 7, &end_ptr, overwrite); if (val == NULL) end_ptr = NULL; } else { // modify val = (char *)strtol(buf + 7, &end_ptr, 0); } if (end_ptr != NULL && end_ptr[0] == 0 && end_ptr != buf + 7) { name = val; } else { fprintf(stderr, "intercept: %s: invalid args in modify command: '%s'\n", "sem_unlink", buf + 7); \ } } else if_return_int_errno(sem_unlink) else if_error_3_int_errno(sem_unlink, EACCES, ENAMETOOLONG, ENOENT) else if_invalid(sem_unlink) } const int ret = __real_sem_unlink(name); msg("return %p; errno %s", ret, strerrorname_np(errno)); return ret; } int sym(sem_destroy)(sem_t *sem) { init(); msg("sem_destroy(%p): %p", sem, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int(sem_destroy, sem_t *, sem) else if_return_int_errno(sem_destroy) else if_error_1_int_errno(sem_destroy, EINVAL) else if_invalid(sem_destroy) } const int ret = __real_sem_destroy(sem); msg("return %p; errno %s", ret, strerrorname_np(errno)); return ret; } int sym(shm_open)(const char *name, int oflag, mode_t mode_arg) { init(); char ostr[64] = "|"; if (oflag & O_RDONLY) strcat(ostr, "O_RDONLY|"); if (oflag & O_RDWR) strcat(ostr, "O_RDWR|"); if (oflag & O_CREAT) strcat(ostr, "O_CREAT|"); if (oflag & O_EXCL) strcat(ostr, "O_EXCL|"); if (oflag & O_TRUNC) strcat(ostr, "O_TRUNC|"); if (oflag & ~(O_RDONLY | O_RDWR | O_CREAT | O_EXCL | O_TRUNC)) strcat(ostr, "?|"); msg("shm_open(%es, 0%o:%s, 0%03o): %p", name, oflag, ostr, mode_arg, ret_addr); char overwrite[BUFFER_SIZE]; if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if (strncmp(buf, "modify ", 7) == 0) { // modify ,, char *val_1 = NULL, *end_ptr_1 = NULL, *end_ptr_2 = NULL, *end_ptr_3 = NULL; long val_2 = 0, val_3 = 0; if (buf[7] == '"') { // modify "",... val_1 = strtostr(buf + 7, &end_ptr_1, overwrite); if (val_1 == NULL) end_ptr_1 = NULL; } else { // modify ,... val_1 = (char *)strtol(buf + 7, &end_ptr_1, 0); } if (end_ptr_1 != NULL && end_ptr_1[0] != 0) { if (end_ptr_1[1] == '|') { val_2 = 0; end_ptr_2 = end_ptr_1 + 1; if (strncmp(end_ptr_2, "O_RDONLY|", 8) == 0) { val_2 |= O_RDONLY; end_ptr_2 += 9; } if (strncmp(end_ptr_2, "O_RDWR|", 8) == 0) { val_2 |= O_RDWR; end_ptr_2 += 7; } if (strncmp(end_ptr_2, "O_CREAT|", 8) == 0) { val_2 |= O_CREAT; end_ptr_2 += 8; } if (strncmp(end_ptr_2, "O_EXCL|", 7) == 0) { val_2 |= O_EXCL; end_ptr_2 += 7; } if (strncmp(end_ptr_2, "O_TRUNC|", 7) == 0) { val_2 |= O_TRUNC; end_ptr_2 += 8; } } else { val_2 = strtol(end_ptr_1 + 1, &end_ptr_2, 0); } } if (end_ptr_2 != NULL && end_ptr_2[0] != 0) val_3 = strtol(end_ptr_2 + 1, &end_ptr_3, 0); if (end_ptr_1 != NULL && end_ptr_1[0] == ',' && end_ptr_1 != buf + 7 && end_ptr_2 != NULL && end_ptr_2[0] == ',' && end_ptr_2 != end_ptr_1 + 1 && end_ptr_3 != NULL && end_ptr_3[0] == 0 && end_ptr_3 != end_ptr_2 + 1) { name = val_1; oflag = (int)val_2; mode_arg = val_3; } else { fprintf(stderr, "intercept: %s: invalid args in modify command: '%s'\n", "shm_open", buf + 7); \ } } else if_return_int_errno(shm_open) else if_error_7_int_errno(shm_open, EACCES, EEXIST, EINVAL, EMFILE, ENAMETOOLONG, ENFILE, ENOENT) else if_invalid(shm_open) } const int ret = __real_shm_open(name, oflag, mode); msg("return %i; errno %s", ret, strerrorname_np(errno)); return ret; } int sym(shm_unlink)(const char *name) { init(); msg("shm_unlink(%es): %p", name, ret_addr); char overwrite[BUFFER_SIZE]; if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if (strncmp(buf, "modify ", 7) == 0) { char *end_ptr = NULL, *val = NULL; if (buf[7] == '"') { // modify "" val = strtostr(buf + 7, &end_ptr, overwrite); if (val == NULL) end_ptr = NULL; } else { // modify val = (char *)strtol(buf + 7, &end_ptr, 0); } if (end_ptr != NULL && end_ptr[0] == 0 && end_ptr != buf + 7) { name = val; } else { fprintf(stderr, "intercept: %s: invalid args in modify command: '%s'\n", "shm_unlink", buf + 7); \ } } else if_return_int_errno(shm_unlink) else if_error_3_int_errno(shm_unlink, EACCES, ENAMETOOLONG, ENOENT) else if_invalid(shm_unlink) } const int ret = __real_shm_unlink(name); msg("return %i; errno %s", ret, strerrorname_np(errno)); return ret; } void *sym(mmap)(void *addr, size_t len, int prot, int flags, int fildes, off_t off) { init(); char pstr[64] = "|"; if (prot == PROT_NONE) strcat(pstr, "PROT_NONE|"); if (prot & PROT_READ) strcat(pstr, "PROT_READ|"); if (prot & PROT_WRITE) strcat(pstr, "PROT_WRITE|"); if (prot & PROT_EXEC) strcat(pstr, "PROT_EXEC|"); if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) strcat(pstr, "?|"); char fstr[64] = "|"; if (prot & MAP_SHARED) strcat(fstr, "MAP_SHARED|"); if (prot & MAP_PRIVATE) strcat(fstr, "MAP_PRIVATE|"); if (prot & MAP_FIXED) strcat(fstr, "MAP_FIXED|"); if (prot & MAP_ANONYMOUS) strcat(fstr, "MAP_ANONYMOUS|"); if (prot & ~(MAP_SHARED | MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS)) strcat(fstr, "?|"); msg("mmap(%p, %i, 0x%x:%s, %0x%x:%s, %i, %i): %p", addr, len, prot, pstr, flags, fstr, fildes, off, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if (strncmp(buf, "modify ", 7) == 0) { // TODO mmap modify fprintf(stderr, "intercept: %s: modify command not implemented\n", "mmap"); } else if_return_ptr_errno(mmap) else if_error_10_map_errno(mmap, EACCES, EAGAIN, EBADF, EINVAL, EMFILE, ENODEV, ENOMEM, ENOTSUP, ENXIO, EOVERFLOW) else if_invalid(mmap) } void *ret = __real_mmap(addr, len, prot, flags, fildes, off); msg("return %p; errno %s", ret, strerrorname_np(errno)); return ret; } int sym(munmap)(void *addr, size_t len) { init(); msg("munmap(%p, %i): %p", addr, len, ret_addr); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int_int(munmap, void *, addr, size_t, len) else if_return_int_errno(munmap) else if_error_1_int_errno(munmap, EINVAL) else if_invalid(munmap) } const int ret = __real_munmap(addr, len); msg("return %i; errno %s", ret, strerrorname_np(errno)); return ret; } int sym(ftruncate)(int fildes, off_t length) { init(); msg("ftruncate(%i, %i): %p", fildes, length); if (mode >= 4) { char buf[BUFFER_SIZE]; rcv(buf, sizeof(buf)); if_modify_int_int(ftruncate, int, fildes, off_t, length) else if_return_int_errno(ftruncate) else if_error_5_int_errno(ftruncate, EINTR, EINVAL, EFBIG, EIO, EBADF) else if_invalid(ftruncate) } const int ret = __real_ftruncate(fildes, length); msg("return %i; errno %s", ret, strerrorname_np(errno)); return ret; }