Пример #1
0
/**
 * Get the full program path.
 *
 * @return a newly allocated string (through halloc()) that points to the
 * path of the program being run, NULL if we can't compute a suitable path.
 */
char *
file_program_path(const char *argv0)
{
	filestat_t buf;
	char *file = deconstify_char(argv0);
	char filepath[MAX_PATH_LEN + 1];

	if (is_running_on_mingw() && !is_strsuffix(argv0, (size_t) -1, ".exe")) {
		concat_strings(filepath, sizeof filepath, argv0, ".exe", NULL_PTR);
	} else {
		clamp_strcpy(filepath, sizeof filepath, argv0);
	}

	if (-1 == stat(filepath, &buf)) {
		int saved_errno = errno;
		file = file_locate_from_path(argv0);
		if (NULL == file) {
			errno = saved_errno;
			s_warning("%s(): could not stat() \"%s\": %m", G_STRFUNC, filepath);
			return NULL;
		}
	}

	if (file != NULL && file != argv0)
		return file;		/* Allocated by file_locate_from_path() */

	return h_strdup(filepath);
}
Пример #2
0
/**
 * Search executable within the user's PATH.
 *
 * @return full path if found, NULL otherwise.
 * The returned string is allocated with halloc().
 */
char *
file_locate_from_path(const char *argv0)
{
	static bool already_done;
	char *path;
	char *tok;
	char filepath[MAX_PATH_LEN + 1];
	char *result = NULL;
	char *ext = "";

	if (is_running_on_mingw() && !is_strsuffix(argv0, (size_t) -1, ".exe")) {
		ext = ".exe";
	}

	if (filepath_basename(argv0) != argv0) {
		if (!already_done) {
			s_warning("can't locate \"%s\" in PATH: name contains '%c' already",
				argv0,
				strchr(argv0, G_DIR_SEPARATOR) != NULL ? G_DIR_SEPARATOR : '/');
		}
		goto done;
	}

	path = getenv("PATH");
	if (NULL == path) {
		if (!already_done) {
			s_warning("can't locate \"%s\" in PATH: "
				"no such environment variable", argv0);
		}
		goto done;
	}

	path = h_strdup(path);

	tok = strtok(path, G_SEARCHPATH_SEPARATOR_S);
	while (NULL != tok) {
		const char *dir = tok;
		filestat_t buf;

		if ('\0' == *dir)
			dir = ".";
		concat_strings(filepath, sizeof filepath,
			dir, G_DIR_SEPARATOR_S, argv0, ext, NULL);

		if (-1 != stat(filepath, &buf)) {
			if (S_ISREG(buf.st_mode) && -1 != access(filepath, X_OK)) {
				result = h_strdup(filepath);
				break;
			}
		}
		tok = strtok(NULL, G_SEARCHPATH_SEPARATOR_S);
	}

	hfree(path);

done:
	already_done = TRUE;	/* No warning on subsequent invocation */
	return result;
}
Пример #3
0
/**
 * Set iterator to close all known socket descriptors.
 */
static void
fd_socket_close(const void *data, void *udata)
{
	(void) udata;

	if (is_running_on_mingw()) {
		socket_fd_t fd = pointer_to_int(data);
		(void) s_close(fd);
	}
}
Пример #4
0
/**
 * Notifies that a socket descriptor has been closed.
 *
 * This is only required on Windows, since we need to keep track of opened
 * socket descriptors, in order to close them before exec().  Failure to
 * do so would leave listen sockets around, and because we use SO_REUSEADDR
 * to bind our listening sockets, we would have two processes listening on
 * the same socket -- a recipe for blackouts on Windows!
 */
void
fd_notify_socket_closed(socket_fd_t fd)
{
	if (!is_running_on_mingw()) {
		s_carp_once("%s(): not needed on UNIX", G_STRFUNC);
		return;
	} else {
		if G_LIKELY(fd_sockets != NULL)
			hset_remove(fd_sockets, int_to_pointer(fd));
	}
}
Пример #5
0
static bool
filename_is_reserved(const char *filename)
{
	const char *endptr;

	if ('\0' == filename[0])
		return TRUE;

	/**
	 * FIXME: Doesn't this apply to CYGWIN, too?
	 */
	if (!is_running_on_mingw())
		return FALSE;

	/**
	 * The following may be a superset because PRN1 is (probably) not reserved.
	 */

	if (!(
	 	(endptr = is_strcaseprefix(filename, "aux")) ||
		(endptr = is_strcaseprefix(filename, "com")) ||
		(endptr = is_strcaseprefix(filename, "con")) ||
		(endptr = is_strcaseprefix(filename, "lpt")) ||
		(endptr = is_strcaseprefix(filename, "nul")) ||
		(endptr = is_strcaseprefix(filename, "prn"))
	))
		return FALSE;

	switch (*endptr) {
	case '\0':
		return TRUE;
	case '.':
		/* con.txt is reserved con.blah.txt isn't */
		return NULL == strchr(&endptr[1], '.');
	case '1': case '2': case '3': case '4': case '5': case '6': case '7':
	case '8': case '9':
		/* lpt0, com0 are not reserved */
		endptr++;
		switch (*endptr) {
		case '\0':
			return TRUE;
		case '.':
			/* com1.txt is reserved com1.blah.txt isn't */
			return NULL == strchr(&endptr[1], '.');
		}
		break;
	}

	/* com1blah.txt is not reserved */
	return FALSE;
}
Пример #6
0
/**
 * Check whether path is an absolute path.
 */
bool
is_absolute_path(const char *path)
{
	g_assert(path != NULL);

	if (is_dir_separator(path[0]))
		return TRUE;

	/* On Windows also check for something like C:\ and x:/ */
	return is_running_on_mingw() &&
			is_ascii_alpha(path[0]) && 
			':' == path[1] &&
			is_dir_separator(path[2]);
}
Пример #7
0
/**
 * Execute special operation.
 *
 * @param old_name	An absolute pathname, the old file name.
 * @param new_name	An absolute pathname, the new file name.
 *
 * @return TRUE if operation was successful, FALSE otherwise, with errno set.
 */
static bool
file_object_special_op(enum file_object_op op,
                       const char * const old_name, const char * const new_name)
{
    const int accmodes[] = { O_RDONLY, O_WRONLY, O_RDWR };
    unsigned i;
    GSList *objects = NULL;
    bool ok = TRUE;
    int saved_errno = 0;

    errno = EINVAL;		/* In case one of the soft assertions fails */

    g_return_val_if_fail(old_name, FALSE);
    g_return_val_if_fail(is_absolute_path(old_name), FALSE);
    if (op != FO_OP_UNLINK) {
        g_return_val_if_fail(new_name, FALSE);
        g_return_val_if_fail(is_absolute_path(new_name), FALSE);
    }

    /*
     * Identify all the file objects currently active for the old name.
     */

    for (i = 0; i < G_N_ELEMENTS(accmodes); i++) {
        struct file_object *fo;

        fo = file_object_find(old_name, accmodes[i]);
        if (fo != NULL) {
            if (NULL == g_slist_find(objects, fo)) {
                objects = g_slist_prepend(objects, fo);
            }
        }
    }

    /*
     * On Windows, close all the files prior renaming / unlinking.
     *
     * On UNIX, only close all the files on unlink and moving.  There is
     * no need to do anything for a rename() operation.
     */

    if (op != FO_OP_RENAME || is_running_on_mingw()) {
        GSList *sl;

        GM_SLIST_FOREACH(objects, sl) {
            struct file_object *fo = sl->data;

            fd_forget_and_close(&fo->fd);
        }
    }
Пример #8
0
static inline ssize_t
unix_write(int fd, const void *buf, size_t size)
{
	/*
	 * On Windows, we have to call s_write() for sockets, not write().
	 * API fragmentation (winsocks versus other handles) at its best.
	 *		--RAM, 2011-01-05
	 */

	if (is_running_on_mingw()) {
		ssize_t ret = s_write(fd, buf, size);
		if (ret >= 0 || ENOTSOCK != errno)
			return ret;
		/* FALL THROUGH -- fd is a plain file, not a socket */
	}

	return write(fd, buf, size);
}
Пример #9
0
static inline ssize_t
unix_read(int fd, void *buf, size_t size)
{
	/*
	 * On Windows, we have to call s_read() for sockets, not read(),
	 * or it does not work since winsock descriptors are distinct
	 * from other file objects and the Windows kernel is too stupid
	 * to do the dirty work for us.
	 *		--RAM, 2011-01-05
	 */

	if (is_running_on_mingw()) {
		ssize_t ret = s_read(fd, buf, size);
		if (ret >= 0 || ENOTSOCK != errno)
			return ret;
		/* FALL THROUGH -- fd is a plain file, not a socket */
	}

	return read(fd, buf, size);
}
Пример #10
0
static inline int
is_okay_for_select(int fd)
{
	return is_valid_fd(fd) &&
		(is_running_on_mingw() || UNSIGNED(fd) < FD_SETSIZE);
}
Пример #11
0
/**
 * Collect information from file system.
 */
static void
entropy_collect_filesystem(SHA1Context *ctx)
{
	const char *path[RANDOM_SHUFFLE_MAX];
	size_t i;

	i = 0;
	path[i++] = gethomedir();
	path[i++] = ".";
	path[i++] = "..";
	path[i++] = "/";

	g_assert(i <= G_N_ELEMENTS(path));

	entropy_array_stat_collect(ctx, path, i);

	i = 0;

	if (is_running_on_mingw()) {
		path[i++] = "C:/";
		path[i++] = mingw_get_admin_tools_path();
		path[i++] = mingw_get_common_appdata_path();
		path[i++] = mingw_get_common_docs_path();
		path[i++] = mingw_get_cookies_path();
		path[i++] = mingw_get_fonts_path();
		path[i++] = mingw_get_history_path();

		g_assert(i <= G_N_ELEMENTS(path));

		entropy_array_stat_collect(ctx, path, i);

		i = 0;
		path[i++] = mingw_get_home_path();
		path[i++] = mingw_get_internet_cache_path();
		path[i++] = mingw_get_mypictures_path();
		path[i++] = mingw_get_personal_path();
		path[i++] = mingw_get_program_files_path();
		path[i++] = mingw_get_startup_path();
		path[i++] = mingw_get_system_path();
		path[i++] = mingw_get_windows_path();

		g_assert(i <= G_N_ELEMENTS(path));

		entropy_array_stat_collect(ctx, path, i);
	} else {
		path[i++] = "/bin";
		path[i++] = "/boot";
		path[i++] = "/dev";
		path[i++] = "/etc";
		path[i++] = "/home";
		path[i++] = "/lib";
		path[i++] = "/mnt";
		path[i++] = "/opt";

		g_assert(i <= G_N_ELEMENTS(path));

		entropy_array_stat_collect(ctx, path, i);

		i = 0;
		path[i++] = "/proc";
		path[i++] = "/root";
		path[i++] = "/sbin";
		path[i++] = "/sys";
		path[i++] = "/tmp";
		path[i++] = "/usr";
		path[i++] = "/var";

		g_assert(i <= G_N_ELEMENTS(path));

		entropy_array_stat_collect(ctx, path, i);
	}
}
Пример #12
0
/**
 * Initialize the bfd context.
 *
 * @return TRUE if OK.
 */
static bool
bfd_util_open(bfd_ctx_t *bc, const char *path)
{
	static mutex_t bfd_library_mtx = MUTEX_INIT;
	bfd *b;
	void *symbols = NULL;
	unsigned size = 0;
	long count;
	int fd = -1;
	const char *libpath = path;

	/*
	 * On Debian systems, there is a debugging version of libraries held
	 * under /usr/lib/debug.  We'll get better symbol resolution by
	 * opening these instead of the usually stripped runtime versions
	 * that will only contain externally visible symbols.
	 */

	if (!is_running_on_mingw() && is_absolute_path(path)) {
		static char debugpath[MAX_PATH_LEN];
		const char *base = filepath_basename(path);

		concat_strings(debugpath, sizeof debugpath,
			"/usr/lib/debug/", base, NULL_PTR);

		fd = open(debugpath, O_RDONLY);
		if (-1 == fd) {
			concat_strings(debugpath, sizeof debugpath,
				"/usr/lib/debug", path, NULL_PTR);
			fd = open(debugpath, O_RDONLY);
		}
		if (-1 != fd)
			libpath = debugpath;
	}

	if (-1 == fd)
		fd = open(libpath, O_RDONLY);

	if (-1 == fd) {
		s_miniwarn("%s: can't open %s: %m", G_STRFUNC, libpath);
		return FALSE;
	}

	/*
	 * Protect calls to BFD opening: they don't appear to be fully
	 * thread-safe and we could enter here concurrently.
	 */

	mutex_lock_fast(&bfd_library_mtx);

	b = bfd_fdopenr(libpath, NULL, fd);
	if (NULL == b) {
		mutex_unlock_fast(&bfd_library_mtx);
		close(fd);
		return FALSE;
	}

	if (!bfd_util_check_format(b, bfd_object, libpath)) {
		s_miniwarn("%s: %s is not an object", G_STRFUNC, libpath);
		goto failed;
	}

	if (0 == (bfd_get_file_flags(b) & HAS_SYMS)) {
		s_miniwarn("%s: %s has no symbols", G_STRFUNC, libpath);
		goto failed;
	}

	count = bfd_read_minisymbols(b, FALSE, &symbols, &size);
	if (count <= 0) {
		bc->dynamic = TRUE;
		count = bfd_read_minisymbols(b, TRUE, &symbols, &size);
	}

	if (count >= 0)
		goto done;

	s_miniwarn("%s: unable to load symbols from %s ", G_STRFUNC, libpath);
	symbols = NULL;
	/* FALL THROUGH */

	/*
	 * We keep the context on errors to avoid logging them over and over
	 * each time we attempt to access the same file.  The BFD and system
	 * resources are released though.
	 */

failed:
	bfd_close(b);
	b = NULL;
	count = 0;
	/* FALL THROUGH */

done:
	mutex_unlock_fast(&bfd_library_mtx);

	bc->magic = BFD_CTX_MAGIC;
	bc->handle = b;
	bc->symbols = symbols;		/* Allocated by the bfd library */
	bc->count = count;
	bc->symsize = size;
	mutex_init(&bc->lock);

	return TRUE;
}
Пример #13
0
/**
 * Open configuration file, renaming it as ".orig" when `renaming' is TRUE.
 * If configuration file cannot be found, try opening the ".orig" variant
 * if already present and `renaming' is TRUE.
 * If not found, try with successive alternatives, if supplied.
 *
 * @attention
 * NB: the supplied `fv' argument is a vector of `fvcnt' elements.  Items
 * with a NULL `dir' field are ignored, but fv[0].dir cannot be NULL.
 *
 * @param what is what is being opened, for logging purposes.
 * @param fv is a vector of files to try to open, in sequence
 * @param fvcnt is the size of the vector
 * @param renaming indicates whether the opened file should be renamed .orig.
 * @param chosen is filled with the index of the chosen path in the vector,
 * unless NULL is given.
 *
 * @return opened FILE, or NULL if we were unable to open any.  `chosen' is
 * only filled if the file is opened.
 */
static FILE *
open_read(
	const char *what, const file_path_t *fv, int fvcnt, bool renaming,
	int *chosen)
{
	FILE *in;
	char *path;
	char *path_orig;
	const char *instead = empty_str;
	int idx = 0;

	g_assert(fv != NULL);
	g_assert(fvcnt >= 1);
	g_assert(fv->dir != NULL);

	path = make_pathname(fv->dir, fv->name);
	if (!is_absolute_path(path)) {
		HFREE_NULL(path);
		return NULL;
	}

	path_orig = h_strdup_printf("%s.%s", path, orig_ext);
	in = fopen(path, "r");
	if (in) {
		if (is_running_on_mingw()) {
			/* Windows can't rename an open file */
			fclose(in);
			in = NULL;
		}
		if (renaming && -1 == rename(path, path_orig)) {
			s_warning("[%s] could not rename \"%s\" as \"%s\": %m",
				what, path, path_orig);
		}
		if (NULL == in) {
			in = fopen(path_orig, "r");
		}
		goto out;
    } else {
		if (ENOENT == errno) {
			if (common_dbg > 0) {
				g_debug("[%s] cannot load non-existent \"%s\"", what, path);
			}
		} else {
			instead = instead_str;			/* Regular file was present */
			s_warning("[%s] failed to retrieve from \"%s\": %m", what, path);
		}
        if (fvcnt > 1 && common_dbg > 0)
            g_debug("[%s] trying to load from alternate locations...", what);
    }

	/*
	 * Maybe we crashed after having retrieved the file in a previous run
	 * but before being able to write it again correctly?  Try to open the
	 * ".orig" file instead.
	 */

	g_assert(in == NULL);

	if (renaming)
		in = fopen(path_orig, "r");		/* The ".orig", in case of a crash */

	if (in != NULL) {
		instead = instead_str;

		HFREE_NULL(path);
		path = path_orig;
		path_orig = NULL;
	}

	/*
	 * Try with alternatives, if supplied.
	 */

	if (in == NULL && fvcnt > 1) {
		const file_path_t *xfv;
		int xfvcnt;

		instead = instead_str;

		for (xfv = fv + 1, xfvcnt = fvcnt - 1; xfvcnt; xfv++, xfvcnt--) {
			HFREE_NULL(path);
			if (NULL == xfv->dir)	/* In alternatives, dir may be NULL */
				continue;
			path = make_pathname(xfv->dir, xfv->name);
			idx++;
			if (NULL != path && NULL != (in = fopen(path, "r")))
				break;
			if (path != NULL && common_dbg > 0) {
				g_debug("[%s] cannot load non-existent \"%s\" either",
					what, path);
			}
		}
	}

	if (common_dbg > 0) {
		if (in) {
			g_debug("[%s] retrieving from \"%s\"%s", what, path, instead);
		} else if (instead == instead_str) {
			g_debug("[%s] unable to retrieve: tried %d alternate location%s",
				what, fvcnt, fvcnt == 1 ? "" : "s");
		} else {
			g_debug("[%s] unable to retrieve: no alternate locations known",
				what);
		}
	}

out:

	HFREE_NULL(path);
	HFREE_NULL(path_orig);
	if (in != NULL && chosen != NULL)
		*chosen = idx;

	return in;
}
Пример #14
0
/**
 * Creates a valid and sanitized filename from the supplied string. For most
 * Unix-like platforms anything goes but for security reasons, shell meta
 * characters are replaced by harmless characters.
 *
 * @param filename the suggested filename.
 * @param no_spaces if TRUE, spaces are replaced with underscores.
 * @param no_evil if TRUE, "evil" characters are replaced with underscores.
 *
 * @returns a newly allocated string using halloc() or ``filename''
 *			if it was a valid filename already.
 */
char *
filename_sanitize(const char *filename, bool no_spaces, bool no_evil)
{
	const char *p;
	const char *s;
	char *q;

	g_assert(filename);

	/* Almost all evil characters are forbidden on Windows, anyway */
	no_evil |= is_running_on_mingw();

	/* Leading spaces are just confusing */
	p = skip_ascii_spaces(filename);

	/* Make sure the filename isn't too long */
	if (strlen(p) >= FILENAME_MAXBYTES) {
		q = halloc(FILENAME_MAXBYTES);
		filename_shrink(p, q, FILENAME_MAXBYTES);
		s = q;
	} else {
		s = p;
		q = NULL;
	}

	/*
	 * Replace shell meta characters and likely problematic characters.
	 *
	 * Although parentheses are not evil per se, they make it a pain to
	 * copy-n-paste filenames without going through the shell's auto-
	 * completion (which normally does the necessary escaping).
	 *
	 * To keep things "readable", we replace parentheses with brackets.
	 * Although brackets are meaningful for the shells, they are only
	 * interpreted in the presence of "*" or "?", two characters that we
	 * strip already.
	 *		--RAM, 2013-11-03
	 */
	{
		size_t i;
		uchar c;

		for (i = 0; '\0' != (c = s[i]); i++) {
			if (
				c < 32
				|| is_ascii_cntrl(c)
				|| G_DIR_SEPARATOR == c
				|| '/' == c
				|| (0 == i && ('.' == c || '-' == c))
				|| (no_spaces && is_ascii_space(c))
				|| (no_evil && filename_is_evil_char(c))
			) {
				if (!q)
					q = h_strdup(s);
				q[i] = '_';	/* replace undesired char with underscore */
			} else if ('(' == c) {
				if (!q)
					q = h_strdup(s);
				q[i] = '[';
			} else if (')' == c) {
				if (!q)
					q = h_strdup(s);
				q[i] = ']';
			}
		}

		/**
		 * Windows does not like filenames ending with a space or period.
		 */
		while (i-- > 0 && (is_ascii_space(s[i]) || '.' == s[i])) {
			if (!q)
				q = h_strdup(s);
			q[i] = '\0';	/* truncate string */
		}
	}

	if (filename_is_reserved(q ? q : s)) {
		HFREE_NULL(q);
		q = h_strdup("noname");
	}

	if (NULL == q && s != filename)
		q = h_strdup(s);		/* Trimmed leading white space, must copy */

	return q ? q : deconstify_gchar(s);
}
Пример #15
0
/**
 * Search executable within the user's PATH.
 *
 * @return full path if found, NULL otherwise.
 * The returned string is allocated with halloc().
 */
char *
file_locate_from_path(const char *argv0)
{
	static bool already_done;
	char *path;
	char *tok;
	char filepath[MAX_PATH_LEN + 1];
	char *result = NULL;
	char *ext = "";

	if (is_running_on_mingw() && !is_strsuffix(argv0, (size_t) -1, ".exe")) {
		ext = ".exe";
	}

	if (filepath_basename(argv0) != argv0) {
		if (!already_done) {
			s_warning("can't locate \"%s\" in PATH: name contains '%c' already",
				argv0,
				strchr(argv0, G_DIR_SEPARATOR) != NULL ? G_DIR_SEPARATOR : '/');
		}
		result = h_strdup(argv0);
		goto done;
	}

	path = getenv("PATH");
	if (NULL == path) {
		if (!already_done) {
			s_warning("can't locate \"%s\" in PATH: "
				"no such environment variable", argv0);
		}
		goto done;
	}

	/*
	 * On Windows, we need to implicitly add "." to the path if not already
	 * present -- this is done by appending a separator and a dot, not by
	 * checking whether "." is already part of the path.
	 *
	 * The reason is that "." is implied, and also because one may omit the
	 * ".exe" extension when launching a program.  This means checks done
	 * in crash_init() for instance to see whether the file listed in
	 * argv[0] exists and which do not account for a missing ".exe" will
	 * attempt to locate the program in the PATH to get a full name and will
	 * fail if we do not add ".".
	 *
	 * On UNIX this cannot happen because there is no hidden extension and
	 * the "." is never made part of the PATH implictly.
	 *
	 *		--RAM, 2015-12-06
	 */

	if (is_running_on_mingw())
		path = h_strdup_printf("%s%c.", path, *G_SEARCHPATH_SEPARATOR_S);
	else
		path = h_strdup(path);

	path = h_strdup(path);

	/* FIXME: strtok() is not thread-safe --RAM, 2015-12-06 */

	tok = strtok(path, G_SEARCHPATH_SEPARATOR_S);
	while (NULL != tok) {
		const char *dir = tok;
		filestat_t buf;

		if ('\0' == *dir)
			dir = ".";
		concat_strings(filepath, sizeof filepath,
			dir, G_DIR_SEPARATOR_S, argv0, ext, NULL_PTR);

		if (-1 != stat(filepath, &buf)) {
			if (S_ISREG(buf.st_mode) && -1 != access(filepath, X_OK)) {
				result = h_strdup(filepath);
				break;
			}
		}
		tok = strtok(NULL, G_SEARCHPATH_SEPARATOR_S);
	}

	hfree(path);

done:
	already_done = TRUE;	/* No warning on subsequent invocation */
	return result;
}
Пример #16
0
/**
 * Closes all file descriptors greater or equal to ``first_fd'', skipping
 * preserved ones if ``preserve'' is TRUE.
 */
static void
fd_close_from_internal(const int first_fd, bool preserve)
{
	int fd;

	g_return_if_fail(first_fd >= 0);

	if (!preserve && try_close_from(first_fd))
		return;

	fd = getdtablesize() - 1;
	while (fd >= first_fd) {
		if (preserve && hset_contains(fd_preserved, int_to_pointer(fd)))
			goto next;

#ifdef HAVE_GTKOSXAPPLICATION
		/* OS X doesn't allow fds being closed not opened by us. During
		 * GUI initialisation a new kqueue fd is created for UI events. This
		 * is visible to us as a fifo which we are not allowed to close.
		 * Set close on exec on all fifo's so we won't leak any of our other
		 * fifo's
		 *	-- JA 2011-11-28 */
		if (is_a_fifo(fd))
			fd_set_close_on_exec(fd);
		else
#endif
		/* OS X frowns upon random fds being closed --RAM 2011-11-13  */
		if (fd_is_opened(fd)) {
			if (close(fd)) {
#if defined(F_MAXFD)
				fd = fcntl(0, F_MAXFD);
				continue;
#endif	/* F_MAXFD */
			}
		}
	next:
		fd--;
	}

	/*
	 * When called with a first_fd of 3, and we are on Windows, also make
	 * sure we close all the known sockets we have.  This lets the process
	 * safely auto-restart, avoiding multiple listening sockets on the same
	 * port.
	 *		--RAM, 2015-04-05
	 */

	if (
		is_running_on_mingw() && !preserve &&
		3 == first_fd && NULL != fd_sockets
	) {
		hset_t *fds = fd_sockets;

		/*
		 * We're about to exec() another process, and we may be crashing,
		 * hence do not bother using hset_foreach_remove() to ensure minimal
		 * processing.  We also reset the fd_sockets pointer to NULL to
		 * make sure s_close() will do nothing when fd_notify_socket_closed()
		 * is called.
		 */

		fd_sockets = NULL;		/* We don't expect race conditions here */
		hset_foreach(fds, fd_socket_close, NULL);

		/* Don't bother freeing / clearing set, we're about to exec() */
	}
}