1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-06-28 01:35:32 +00:00
elinks/src/main/main.c
Scott Mcdermott b2556aa953 [fg] track master proc for suspend via pids, not term fds, for fork_on_start
When ui.sessions.fork_on_start, we fork a process that has a different
fdout than get_output_handle() (which has hardcoded fdout of 1), so it
will never be considered term->master, yet this is the process we want
to wake up in SIGTSTP handler.

Since we cannot rely on term->master to determine if we are the master
process, we instead move master_pid to be set explicitly at the places
where we have information about whether our process is a master or a
slave: first on start, then once the interlink determination has been
made.

master_pid has to be set in both parent and child, because both will get
suspended and need to know which one needs to resume in background with
SIGCONT (the master).  We can't inherit from the parent because it's
unknown at the time of fork.

Previously, master_pid worked correctly with fork_on_start=0,
-no-connect or -dump, but not with fork_on_start=1.

See #287 for background.
2024-04-10 15:14:23 -07:00

378 lines
8.7 KiB
C

/* The main program - startup */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h> /* OS/2 needs this after sys/types.h */
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h> /* OS/2 needs this after sys/types.h */
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "elinks.h"
#include "bfu/dialog.h"
#include "cache/cache.h"
#include "config/cmdline.h"
#include "config/conf.h"
#include "config/home.h"
#include "config/options.h"
#include "dialogs/menu.h"
#include "document/document.h"
#include "intl/charsets.h"
#include "intl/libintl.h"
#include "main/event.h"
#include "main/interlink.h"
#include "main/main.h"
#include "main/module.h"
#include "main/select.h"
#include "main/version.h"
#include "network/connection.h"
#include "network/dns.h"
#include "network/state.h"
#include "osdep/osdep.h"
#include "osdep/signals.h"
#include "osdep/sysname.h"
#include "protocol/auth/auth.h"
#include "session/download.h"
#include "session/session.h"
#include "terminal/kbd.h"
#include "terminal/screen.h"
#include "terminal/terminal.h"
#include "util/color.h"
#include "util/error.h"
#include "util/file.h"
#include "util/hash.h"
#include "util/memdebug.h"
#include "util/memory.h"
#include "viewer/dump/dump.h"
#include "viewer/text/marks.h"
struct program program;
pid_t master_pid = 0;
static int ac;
static char **av;
static int init_b = 0;
static int init_o = 0;
/* Check if either stdin or stdout are pipes */
static void
check_stdio(LIST_OF(struct string_list_item) *url_list)
{
assert(!remote_session_flags);
/* Should the document be read from stdin? */
if (!isatty(STDIN_FILENO)) {
/* Only start reading from stdin if no URL was given on the
* command line. */
if (url_list && list_empty(*url_list)) {
get_opt_bool("protocol.file.allow_special_files",
NULL) = 1;
add_to_string_list(url_list, "file:///dev/stdin", 17);
}
get_cmd_opt_bool("no-connect") = 1;
}
/* If called for outputting to a pipe without -dump or -source
* specified default to using dump viewer. */
if (!isatty(STDOUT_FILENO)) {
int *dump = &get_cmd_opt_bool("dump");
if (!*dump && !get_cmd_opt_bool("source"))
*dump = 1;
}
}
static void
check_cwd(void)
{
char *cwd = get_cwd();
if (!cwd || !file_is_dir(cwd)) {
char *home = getenv("HOME");
if (home && file_is_dir(home))
(void)(!chdir(home));
}
mem_free_if(cwd);
}
void
parse_options_again(void)
{
if (!init_o) {
load_config();
update_options_visibility();
/* Parse commandline options again, in order to override any
* config file options. */
parse_options(ac - 1, av + 1, NULL);
/* ... and re-check stdio, in order to override any command
* line options! >;) */
if (!remote_session_flags) {
check_stdio(NULL);
}
init_o = 1;
}
}
static void
init(void)
{
INIT_LIST_OF(struct string_list_item, url_list);
int fd = -1;
enum retval ret;
init_osdep();
check_cwd();
#ifdef CONFIG_NLS
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
set_language(0);
#endif
init_event();
init_charsets_lookup();
init_colors_lookup();
init_modules(main_modules);
init_options();
init_static_version();
register_modules_options(main_modules);
register_modules_options(builtin_modules);
set_sigcld();
get_system_name();
/* XXX: OS/2 has some stupid bug and the pipe must be created before
* socket :-/. -- Mikulas */
if (check_terminal_pipes()) {
ERROR(gettext("Cannot create a pipe for internal communication."));
program.retval = RET_FATAL;
program.terminate = 1;
return;
}
/* Parsing command line options */
ret = parse_options(ac - 1, av + 1, &url_list);
if (ret != RET_OK) {
/* Command line handlers return RET_COMMAND to signal that they
* completed successfully and that the program should stop. */
if (ret != RET_COMMAND)
program.retval = ret;
program.terminate = 1;
free_string_list(&url_list);
return;
}
if (!remote_session_flags) {
check_stdio(&url_list);
} else {
program.terminate = 1;
}
if (!get_cmd_opt_bool("no-home")) {
init_home();
}
/* If there's no -no-connect, -dump or -source option, check if there's
* no other ELinks running. If we found any, by-pass initialization of
* non critical subsystems, open socket and act as a slave for it. */
if (get_cmd_opt_bool("no-connect")
|| get_cmd_opt_bool("dump")
|| get_cmd_opt_bool("source")
|| (fd = init_interlink()) == -1) {
#ifdef HAVE_GETPID
master_pid = getpid();
#endif
parse_options_again();
init_b = 1;
init_modules(builtin_modules);
}
if (get_cmd_opt_bool("always-load-config")) {
parse_options_again();
}
if (get_cmd_opt_bool("dump")
|| get_cmd_opt_bool("source")) {
/* Dump the URL list */
#ifdef CONFIG_ECMASCRIPT
/* The ECMAScript code is not good at coping with this. And it
* makes currently no sense to evaluate ECMAScript in this
* context anyway. */
get_opt_bool("ecmascript.enable", NULL) = 0;
#endif
if (!list_empty(url_list)) {
dump_next(&url_list);
} else {
const char *arg = get_cmd_opt_bool("dump")
? "dump" : "source";
usrerror(gettext("URL expected after -%s"), arg);
program.retval = RET_SYNTAX;
program.terminate = 1;
}
} else if (remote_session_flags & SES_REMOTE_PING) {
/* If no instance was running return ping failure */
if (fd == -1) {
usrerror(gettext("No running ELinks found."));
program.retval = RET_PING;
}
} else if (remote_session_flags && fd == -1) {
/* The remote session(s) can not be created */
usrerror(gettext("No remote session to connect to."));
program.retval = RET_REMOTE;
} else {
struct string info;
struct terminal *term = NULL;
if (!encode_session_info(&info, &url_list)) {
ERROR(gettext("Unable to encode session info."));
program.retval = RET_FATAL;
program.terminate = 1;
} else if (fd != -1) {
/* Attach to already running ELinks and act as a slave
* for it. */
close_terminal_pipes();
handle_trm(get_input_handle(), get_output_handle(),
fd, fd, get_ctl_handle(), info.source, info.length,
remote_session_flags);
} else if (get_cmd_opt_bool("no-connect")
|| !get_opt_bool("ui.sessions.fork_on_start", NULL)) {
/* Setup a master terminal */
term = attach_terminal(get_input_handle(), get_output_handle(),
get_ctl_handle(), info.source, info.length);
if (!term) {
ERROR(gettext("Unable to attach_terminal()."));
program.retval = RET_FATAL;
program.terminate = 1;
}
}
/* OK, this is race condition, but it must be so; GPM installs
* it's own buggy TSTP handler. */
if (!program.terminate) handle_basic_signals(term);
done_string(&info);
}
if (program.terminate) close_terminal_pipes();
free_string_list(&url_list);
}
static void
terminate_all_subsystems(void)
{
done_interlink();
check_bottom_halves();
abort_all_downloads();
check_bottom_halves();
destroy_all_terminals();
check_bottom_halves();
free_all_itrms();
/* When aborting all connections also keep-alive connections are
* aborted. A (normal) connection will be started for any keep-alive
* connection that needs to send a command to the server before
* aborting. This means we need to abort_all_connections() twice.
*
* It forces a some what unclean connection tear-down since at most the
* shutdown routine will be able to send one command. But else it would
* take too long time to terminate. */
abort_all_connections();
check_bottom_halves();
abort_all_connections();
if (init_b) {
#ifdef CONFIG_SCRIPTING
trigger_event_name("quit");
#endif
free_history_lists();
done_modules(builtin_modules);
done_saved_session_info();
}
shrink_memory(1);
free_charsets_lookup();
free_colors_lookup();
done_modules(main_modules);
free_conv_table();
check_bottom_halves();
done_home();
done_state_message();
done_bfu_colors();
unregister_modules_options(builtin_modules);
unregister_modules_options(main_modules);
done_options();
done_event();
terminate_select();
terminate_osdep();
#ifdef CONFIG_COMBINE
free_combined();
#endif
clean_temporary_files();
}
void
shrink_memory(int whole)
{
shrink_dns_cache(whole);
shrink_format_cache(whole);
garbage_collection(whole);
}
#ifdef CONFIG_NO_ROOT_EXEC
static void
check_if_root(void)
{
if (!getuid() || !geteuid()) {
fprintf(stderr, "%s\n\n"
"Permission to run this program as root "
"user was disabled at compile time.\n\n",
full_static_version);
exit(-1);
}
}
#else
#define check_if_root()
#endif
int
main(int argc, char *argv[])
{
check_if_root();
program.terminate = 0;
program.retval = RET_OK;
program.path = argv[0];
ac = argc;
av = (char **) argv;
select_loop(init);
terminate_all_subsystems();
#ifdef DEBUG_MEMLEAK
check_memory_leaks();
#endif
return program.retval;
}