/* Load tracked file descriptors from the environment. The environment is used
* to pass the information to child processes.
*
- * ENV_NAME_FDS has the following format: Each descriptor as string followed
- * by a comma; there's a trailing comma. Example: "2,4,". */
+ * ENV_NAME_FDS and ENV_NAME_PRIVATE_FDS have 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) {
#ifdef DEBUG
debug("init_from_environment()\t\t[%d]\n", getpid());
int saved_errno = errno;
+ assert(!initialized);
+
initialized = 1;
tracked_fds_list_count = 0;
force_write_to_non_tty = 1;
}
+ /* Prefer user defined list of file descriptors, fall back to file
+ * descriptors passed through the environment from the parent process. */
env = getenv(ENV_NAME_FDS);
+ if (env) {
+ used_fds_set_by_user = 1;
+ } else {
+ env = getenv(ENV_NAME_PRIVATE_FDS);
+ }
if (!env) {
errno = saved_errno;
return;
}
#ifdef DEBUG
debug(" getenv(\"%s\"): \"%s\"\n", ENV_NAME_FDS, env);
+ debug(" getenv(\"%s\"): \"%s\"\n", ENV_NAME_PRIVATE_FDS, env);
#endif
- /* Environment is read-only. */
+
+ /* Environment must be treated read-only. */
char env_copy[strlen(env) + 1];
strcpy(env_copy, env);
}
/* ',' at the beginning or double ',' - ignore. */
if (x == last) {
- last = x + 1;
- continue;
+ goto next;
}
if (i == count) {
break;
}
+ /* Replace ',' to null-terminate number for atoi(). */
*x = 0;
int fd = atoi(last);
- if (fd < TRACKFDS_STATIC_COUNT) {
+ if (fd < 0) {
+ goto next;
+
+ } else if (fd < TRACKFDS_STATIC_COUNT) {
tracked_fds[fd] = 1;
} else {
if (!tracked_fds_list) {
assert(fd >= 0);
int length = snprintf(x, 10 + 1, "%d", fd);
- if (length >= 10 + 1) {
+ if (length >= 10 + 1 || length <= 0 /* shouldn't happen */) {
/* Integer too big to fit the buffer, skip it. */
#ifdef WARNING
warning("update_environment_buffer_entry(): truncated fd: %d [%d]\n",
/* Write comma after number. */
x += length;
*x++ = ',';
- /* Make sure the string is always zero terminated. */
+ /* Make sure the string is always null-terminated. */
*x = 0;
return x;
return;
}
+ int saved_errno = errno;
+
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);
+ debug(" setenv(\"%s\", \"%s\", 1)\n", ENV_NAME_PRIVATE_FDS, env);
#endif
+ setenv(ENV_NAME_PRIVATE_FDS, env, 1 /* overwrite */);
- setenv(ENV_NAME_FDS, env, 1 /* overwrite */);
+ /* Child processes must use ENV_NAME_PRIVATE_FDS to get the updated list
+ * of tracked file descriptors, not the static list provided by the user
+ * in ENV_NAME_FDS.
+ *
+ * But only remove it if the static list in ENV_NAME_FDS was loaded by
+ * init_from_environment() and merged into ENV_NAME_PRIVATE_FDS. */
+ if (used_fds_set_by_user) {
+ unsetenv(ENV_NAME_FDS);
+ }
+
+ errno = saved_errno;
}
*/
inline static int tracked_fds_find(int fd) always_inline;
inline static int tracked_fds_find(int fd) {
- assert(fd >= 0);
+ /* Invalid file descriptor. No assert() as we're called from the hooked
+ * macro. */
+ if (unlikely(fd < 0)) {
+ return 0;
+ }
if (fd < TRACKFDS_STATIC_COUNT) {
return tracked_fds[fd];
}
static int tracked_fds_find_slow(int fd) {
assert(initialized);
+ assert(fd >= 0);
if (tracked_fds_list_count == 0) {
return 0;