};
+/* Can be extended to handle a FIXME in setup_redirects about not saving script fds */
+struct FILE_list {
+ struct FILE_list *next;
+ FILE *fp;
+};
+
+
/* "Globals" within this file */
/* Sorted roughly by size (smaller offsets == smaller code) */
struct globals {
unsigned count_SIGCHLD;
unsigned handled_SIGCHLD;
smallint we_have_children;
+#endif
+#if ENABLE_FEATURE_SH_STANDALONE
+ struct FILE_list *FILE_list;
#endif
/* Which signals have non-DFL handler (even with no traps set)?
* Set at the start to:
}
+/* Manipulating the list of open FILEs */
+static FILE *remember_FILE(FILE *fp)
+{
+ if (fp) {
+#if ENABLE_FEATURE_SH_STANDALONE
+ struct FILE_list *n = xmalloc(sizeof(*n));
+ n->fp = fp;
+ n->next = G.FILE_list;
+ G.FILE_list = n;
+#endif
+ close_on_exec_on(fileno(fp));
+ }
+ return fp;
+}
+#if ENABLE_FEATURE_SH_STANDALONE
+static void close_all_FILE_list(void)
+{
+ struct FILE_list *fl = G.FILE_list;
+ while (fl) {
+ /* fclose would also free FILE object.
+ * It is disastrous if we share memory with a vforked parent.
+ * I'm not sure we never come here after vfork.
+ * Therefore just close fd, nothing more.
+ */
+ /*fclose(fl->fp); - unsafe */
+ close(fileno(fl->fp));
+ fl = fl->next;
+ }
+}
+static void fclose_and_forget(FILE *fp)
+{
+ struct FILE_list **pp = &G.FILE_list;
+ while (*pp) {
+ struct FILE_list *cur = *pp;
+ if (cur->fp == fp) {
+ *pp = cur->next;
+ free(cur);
+ break;
+ }
+ pp = &cur->next;
+ }
+ fclose(fp);
+}
+#else
+# define fclose_and_forget(fp) fclose(fp);
+#endif
+
+
/* Helpers for setting new $n and restoring them back
*/
typedef struct save_arg_t {
free(to_free);
# endif
close(channel[1]);
- close_on_exec_on(channel[0]);
- return xfdopen_for_read(channel[0]);
+ return remember_FILE(xfdopen_for_read(channel[0]));
}
/* Return code is exit status of the process that is run. */
}
debug_printf("done reading from `cmd` pipe, closing it\n");
- fclose(fp);
+ fclose_and_forget(fp);
/* We need to extract exitcode. Test case
* "true; echo `sleep 1; false` $?"
* should print 1 */
if (a >= 0) {
# if BB_MMU /* see above why on NOMMU it is not allowed */
if (APPLET_IS_NOEXEC(a)) {
+ /* Do not leak open fds from opened script files etc */
+ close_all_FILE_list();
debug_printf_exec("running applet '%s'\n", argv[0]);
run_applet_no_and_exit(a, argv);
}
debug_printf("sourcing /etc/profile\n");
input = fopen_for_read("/etc/profile");
if (input != NULL) {
- close_on_exec_on(fileno(input));
+ remember_FILE(input);
install_special_sighandlers();
parse_and_run_file(input);
- fclose(input);
+ fclose_and_forget(input);
}
/* bash: after sourcing /etc/profile,
* tries to source (in the given order):
G.global_argv++;
debug_printf("running script '%s'\n", G.global_argv[0]);
input = xfopen_for_read(G.global_argv[0]);
- close_on_exec_on(fileno(input));
+ remember_FILE(input);
install_special_sighandlers();
parse_and_run_file(input);
#if ENABLE_FEATURE_CLEAN_UP
- fclose(input);
+ fclose_and_forget(input);
#endif
goto final_return;
}
if (arg_path)
filename = arg_path;
}
- input = fopen_or_warn(filename, "r");
+ input = remember_FILE(fopen_or_warn(filename, "r"));
free(arg_path);
if (!input) {
/* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
*/
return EXIT_FAILURE;
}
- close_on_exec_on(fileno(input));
#if ENABLE_HUSH_FUNCTIONS
sv_flg = G.flag_return_in_progress;
save_and_replace_G_args(&sv, argv);
parse_and_run_file(input);
- fclose(input);
+ fclose_and_forget(input);
if (argv[1])
restore_G_args(&sv, argv);