X-Git-Url: https://ruderich.org/simon/gitweb/?p=coloredstderr%2Fcoloredstderr.git;a=blobdiff_plain;f=src%2Ftrackfds.h;h=f84e20daee373c405c9b95642e4306232123245a;hp=474b072d2026d9eee0faeb4505529f2d06a2fa9f;hb=9af3c2c72729536f5822c78572510b251895eb70;hpb=b9b86029be18bd02cb7c4396c364e372488d2faa diff --git a/src/trackfds.h b/src/trackfds.h index 474b072..f84e20d 100644 --- a/src/trackfds.h +++ b/src/trackfds.h @@ -20,13 +20,52 @@ #ifndef TRACKFDS_H #define TRACKFDS_H 1 -/* List of tracked file descriptors. */ -static int *tracked_fds; +/* Array of tracked file descriptors. Used for fast lookups for the normally + * used file descriptors (0 <= fd < TRACKFDS_STATIC_COUNT). */ +static int tracked_fds[TRACKFDS_STATIC_COUNT]; + +/* List of tracked file descriptors >= TRACKFDS_STATIC_COUNT. */ +static int *tracked_fds_list; /* Current number of items in the list. */ -static size_t tracked_fds_count; +static size_t tracked_fds_list_count; /* Allocated items, used to reduce realloc()s. */ -static size_t tracked_fds_space; +static size_t tracked_fds_list_space; + + +#ifdef DEBUG +static void tracked_fds_debug(void) { + size_t i; + + for (i = 0; i < TRACKFDS_STATIC_COUNT; i++) { + if (tracked_fds[i]) { + debug(" tracked_fds[%d]: %d\n", i, tracked_fds[i]); + } + } + debug(" tracked_fds_list: %d/%d\t[%d]\n", tracked_fds_list_count, + tracked_fds_list_space, + getpid()); + for (i = 0; i < tracked_fds_list_count; i++) { + debug(" tracked_fds_list[%d]: %d\n", i, tracked_fds_list[i]); + } +} +#endif + +static int init_tracked_fds_list(size_t count) { + /* Reduce reallocs. */ + count += TRACKFDS_REALLOC_STEP; + + tracked_fds_list = malloc(count * sizeof(*tracked_fds_list)); + if (!tracked_fds_list) { +#ifdef WARNING + warning("malloc(tracked_fds_list, %d) failed [%d]\n", + count * sizeof(*tracked_fds_list), getpid()); +#endif + return 0; + } + tracked_fds_list_space = count; + return 1; +} /* Load tracked file descriptors from the environment. The environment is used * to pass the information to child processes. @@ -34,17 +73,34 @@ static size_t tracked_fds_space; * ENV_NAME_FDS has the following format: Each descriptor as string followed * by a comma; there's a trailing comma. Example: "2,4,". */ static void init_from_environment(void) { - tracked_fds_count = 0; +#ifdef DEBUG + debug("init_from_environment()\t\t[%d]\n", getpid()); +#endif + char const *env; + + int saved_errno = errno; + + initialized = 1; + tracked_fds_list_count = 0; + + /* If ENV_NAME_FORCE_WRITE is set and not empty, allow writes to a non-tty + * device. Use with care! Mainly used for the test suite. */ + env = getenv(ENV_NAME_FORCE_WRITE); + if (env && env[0] != '\0') { + force_write_to_non_tty = 1; + } - const char *env = getenv(ENV_NAME_FDS); + env = getenv(ENV_NAME_FDS); if (!env) { + errno = saved_errno; return; } +#ifdef DEBUG + debug(" getenv(\"%s\"): \"%s\"\n", ENV_NAME_FDS, env); +#endif /* Environment is read-only. */ - char *env_copy = strdup(env); - if (!env_copy) { - return; - } + char env_copy[strlen(env) + 1]; + strcpy(env_copy, env); char *x; @@ -54,18 +110,11 @@ static void init_from_environment(void) { count++; } } - tracked_fds_space = count + TRACKFDS_REALLOC_STEP; - - tracked_fds = malloc(tracked_fds_space * sizeof(*tracked_fds)); - if (!tracked_fds) { - free(env_copy); - return; - } size_t i = 0; /* Parse file descriptor numbers from environment string and store them as - * integers in tracked_fds. */ + * integers in tracked_fds and tracked_fds_list. */ char *last; for (x = env_copy, last = env_copy; *x; x++) { if (*x != ',') { @@ -82,81 +131,167 @@ static void init_from_environment(void) { } *x = 0; - tracked_fds[i++] = atoi(last); + int fd = atoi(last); + if (fd < TRACKFDS_STATIC_COUNT) { + tracked_fds[fd] = 1; + } else { + if (!tracked_fds_list) { + /* Pessimistic count estimate, but allocating a few more + * elements doesn't hurt. */ + if (!init_tracked_fds_list(count)) { + /* Couldn't allocate memory, skip this entry. */ + goto next; + } + } + tracked_fds_list[i++] = fd; +#ifdef DEBUG + debug(" large fd: %d\n", fd); +#endif + } + +next: last = x + 1; } - tracked_fds_count = count; + tracked_fds_list_count = i; + +#ifdef DEBUG + tracked_fds_debug(); +#endif - free(env_copy); + errno = saved_errno; } -static void update_environment(void) { - /* An integer (32-bit) has at most 10 digits, + 1 for the comma after each - * number. Bigger file descriptors (which shouldn't occur in reality) are - * truncated. */ - char env[tracked_fds_count * (10 + 1) * sizeof(char)]; - char *x = env; +static char *update_environment_buffer_entry(char *x, int fd) { + int length = snprintf(x, 10 + 1, "%d", fd); + if (length >= 10 + 1) { + /* Integer too big to fit the buffer, skip it. */ +#ifdef WARNING + warning("update_environment_buffer_entry(): truncated fd: %d [%d]\n", + fd, getpid()); +#endif + return x; + } + /* Write comma after number. */ + x += length; + *x++ = ','; + /* Make sure the string is always zero terminated. */ + *x = 0; + + return x; +} +static void update_environment_buffer(char *x) { size_t i; - for (i = 0; i < tracked_fds_count; i++) { - int length = snprintf(x, 10 + 1, "%d", tracked_fds[i]); - if (length >= 10 + 1) - return; + for (i = 0; i < TRACKFDS_STATIC_COUNT; i++) { + if (tracked_fds[i]) { + x = update_environment_buffer_entry(x, (int)i); + } + } + for (i = 0; i < tracked_fds_list_count; i++) { + x = update_environment_buffer_entry(x, tracked_fds_list[i]); + } +} +inline static size_t update_environment_buffer_size(void) { + /* Use the maximum count (TRACKFDS_STATIC_COUNT) of used descriptors + * because it's simple and small enough not to be a problem. + * + * An integer (32-bit) has at most 10 digits, + 1 for the comma after each + * number. Bigger file descriptors (which shouldn't occur in reality) are + * skipped. */ + return (TRACKFDS_STATIC_COUNT + tracked_fds_list_count) + * (10 + 1) + 1 /* to fit '\0' */; +} +static void update_environment(void) { +#ifdef DEBUG + debug("update_environment()\t\t[%d]\n", getpid()); +#endif - /* Write comma after number. */ - x += length; - *x++ = ','; - /* Make sure the string is always zero terminated. */ - *x = 0; + /* If we haven't parsed the environment we also haven't modified it - so + * nothing to do. */ + if (!initialized) { + return; } + char env[update_environment_buffer_size()]; + env[0] = 0; + + update_environment_buffer(env); + +#if 0 + debug(" setenv(\"%s\", \"%s\", 1)\n", ENV_NAME_FDS, env); +#endif + setenv(ENV_NAME_FDS, env, 1 /* overwrite */); } -#ifdef DEBUG -static void tracked_fds_debug(void) { - debug("tracked_fds: %d/%d\t[%d]\n", tracked_fds_count, tracked_fds_space, - getpid()); - size_t i; - for (i = 0; i < tracked_fds_count; i++) { - debug("tracked_fds[%d]: %d\n", i, tracked_fds[i]); - } -} -#endif static void tracked_fds_add(int fd) { - if (tracked_fds_count >= tracked_fds_space) { - size_t new_space = tracked_fds_space + TRACKFDS_REALLOC_STEP; - if (!realloc(tracked_fds, sizeof(*tracked_fds) * new_space)) { + if (fd < TRACKFDS_STATIC_COUNT) { + tracked_fds[fd] = 1; +#if 0 + debug("tracked_fds_add(): %-3d\t\t[%d]\n", fd, getpid()); + tracked_fds_debug(); +#endif + return; + } + + if (tracked_fds_list_count >= tracked_fds_list_space) { + int saved_errno = errno; + + size_t new_space = tracked_fds_list_space + TRACKFDS_REALLOC_STEP; + int *tmp = realloc(tracked_fds_list, + sizeof(*tracked_fds_list) * new_space); + if (!tmp) { /* We can do nothing, just ignore the error. We made sure not to * destroy our state, so the new descriptor is ignored without any * other consequences. */ +#ifdef WARNING + warning("realloc(tracked_fds_list, %zu) failed! [%d]\n", + sizeof(*tracked_fds_list) * new_space, getpid()); +#endif + errno = saved_errno; return; } - tracked_fds_space = new_space; + errno = saved_errno; + + tracked_fds_list = tmp; + tracked_fds_list_space = new_space; } - tracked_fds[tracked_fds_count++] = fd; + tracked_fds_list[tracked_fds_list_count++] = fd; #ifdef DEBUG + debug("tracked_fds_add(): %-3d\t\t[%d]\n", fd, getpid()); tracked_fds_debug(); #endif } static int tracked_fds_remove(int fd) { + if (fd < TRACKFDS_STATIC_COUNT) { + int old_value = tracked_fds[fd]; + tracked_fds[fd] = 0; + +#if 0 + debug("tracked_fds_remove(): %-3d\t[%d]\n", fd, getpid()); + tracked_fds_debug(); +#endif + return old_value; /* Found vs. not found. */ + } + size_t i; - for (i = 0; i < tracked_fds_count; i++) { - if (fd != tracked_fds[i]) { + for (i = 0; i < tracked_fds_list_count; i++) { + if (fd != tracked_fds_list[i]) { continue; } - memmove(tracked_fds + i, tracked_fds + i + 1, - sizeof(*tracked_fds) * (tracked_fds_count - i - 1)); - tracked_fds_count--; + memmove(tracked_fds_list + i, tracked_fds_list + i + 1, + sizeof(*tracked_fds_list) * (tracked_fds_list_count - i - 1)); + tracked_fds_list_count--; #ifdef DEBUG + debug("tracked_fds_remove(): %-3d\t[%d]\n", fd, getpid()); tracked_fds_debug(); #endif @@ -167,10 +302,32 @@ static int tracked_fds_remove(int fd) { /* Not found. */ return 0; } + +static int tracked_fds_find_slow(int fd) noinline; +/* + * tracked_fds_find() is called for each hook call and should be as fast as + * possible. As most file descriptors are < TRACKFDS_STATIC_COUNT, force the + * compiler to inline that part which is almost exclusively used. + * + * Inlining tracked_fds_add()/tracked_fds_remove() isn't worth the effort as + * they are not called often enough. + */ +inline static int tracked_fds_find(int fd) always_inline; static int tracked_fds_find(int fd) { + if (fd < TRACKFDS_STATIC_COUNT) { + return tracked_fds[fd]; + } + + return tracked_fds_find_slow(fd); +} +static int tracked_fds_find_slow(int fd) { + if (tracked_fds_list_count == 0) { + return 0; + } + size_t i; - for (i = 0; i < tracked_fds_count; i++) { - if (fd == tracked_fds[i]) { + for (i = 0; i < tracked_fds_list_count; i++) { + if (fd == tracked_fds_list[i]) { return 1; } }