Always terminate ELinks when using -remote.
[elinks.git] / src / main / main.c
blob5901665f93a57a74fa332e0926e192298c64635a
1 /* The main program - startup */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <errno.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #ifdef HAVE_FCNTL_H
13 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
14 #endif
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
19 #include "elinks.h"
21 #include "bfu/dialog.h"
22 #include "cache/cache.h"
23 #include "config/cmdline.h"
24 #include "config/conf.h"
25 #include "config/home.h"
26 #include "config/options.h"
27 #include "dialogs/menu.h"
28 #include "document/document.h"
29 #include "intl/charsets.h"
30 #include "intl/gettext/libintl.h"
31 #include "main/event.h"
32 #include "main/interlink.h"
33 #include "main/main.h"
34 #include "main/module.h"
35 #include "main/select.h"
36 #include "main/version.h"
37 #include "network/connection.h"
38 #include "network/dns.h"
39 #include "network/state.h"
40 #include "osdep/osdep.h"
41 #include "osdep/signals.h"
42 #include "osdep/sysname.h"
43 #include "protocol/auth/auth.h"
44 #include "session/download.h"
45 #include "session/session.h"
46 #include "terminal/kbd.h"
47 #include "terminal/screen.h"
48 #include "terminal/terminal.h"
49 #include "util/color.h"
50 #include "util/error.h"
51 #include "util/file.h"
52 #include "util/memdebug.h"
53 #include "util/memory.h"
54 #include "viewer/dump/dump.h"
55 #include "viewer/text/marks.h"
57 struct program program;
59 static int ac;
60 static unsigned char **av;
61 static int init_b = 0;
63 /* Check if either stdin or stdout are pipes */
64 static void
65 check_stdio(struct list_head *url_list)
67 assert(!remote_session_flags);
69 /* Should the document be read from stdin? */
70 if (!isatty(STDIN_FILENO)) {
71 /* Only start reading from stdin if no URL was given on the
72 * command line. */
73 if (url_list && list_empty(*url_list)) {
74 get_opt_bool("protocol.file.allow_special_files") = 1;
75 add_to_string_list(url_list, "file:///dev/stdin", 17);
77 get_cmd_opt_bool("no-connect") = 1;
80 /* If called for outputting to a pipe without -dump or -source
81 * specified default to using dump viewer. */
82 if (!isatty(STDOUT_FILENO)) {
83 int *dump = &get_cmd_opt_bool("dump");
85 if (!*dump && !get_cmd_opt_bool("source"))
86 *dump = 1;
90 static void
91 check_cwd(void)
93 unsigned char *cwd = get_cwd();
95 if (!cwd || !file_is_dir(cwd)) {
96 unsigned char *home = getenv("HOME");
98 if (home && file_is_dir(home))
99 chdir(home);
102 mem_free_if(cwd);
105 static void
106 init(void)
108 INIT_LIST_HEAD(url_list);
109 int fd = -1;
110 enum retval ret;
112 init_osdep();
113 check_cwd();
115 #ifdef CONFIG_NLS
116 bindtextdomain(PACKAGE, LOCALEDIR);
117 textdomain(PACKAGE);
118 set_language(0);
119 #endif
120 init_event();
121 init_charsets_lookup();
122 init_colors_lookup();
123 init_modules(main_modules);
125 init_options();
126 init_static_version();
128 register_modules_options(main_modules);
129 register_modules_options(builtin_modules);
130 set_sigcld();
131 get_system_name();
133 /* XXX: OS/2 has some stupid bug and the pipe must be created before
134 * socket :-/. -- Mikulas */
135 if (check_terminal_pipes()) {
136 ERROR(gettext("Cannot create a pipe for internal communication."));
137 program.retval = RET_FATAL;
138 program.terminate = 1;
139 return;
142 /* Parsing command line options */
143 ret = parse_options(ac - 1, av + 1, &url_list);
144 if (ret != RET_OK) {
145 /* Command line handlers return RET_COMMAND to signal that they
146 * completed successfully and that the program should stop. */
147 if (ret != RET_COMMAND)
148 program.retval = ret;
149 program.terminate = 1;
150 free_string_list(&url_list);
151 return;
154 if (!remote_session_flags) {
155 check_stdio(&url_list);
156 } else {
157 program.terminate = 1;
160 if (!get_cmd_opt_bool("no-home")) {
161 init_home();
164 /* If there's no -no-connect, -dump or -source option, check if there's
165 * no other ELinks running. If we found any, by-pass initialization of
166 * non critical subsystems, open socket and act as a slave for it. */
167 if (get_cmd_opt_bool("no-connect")
168 || get_cmd_opt_bool("dump")
169 || get_cmd_opt_bool("source")
170 || (fd = init_interlink()) == -1) {
172 load_config();
173 update_options_visibility();
174 /* Parse commandline options again, in order to override any
175 * config file options. */
176 parse_options(ac - 1, av + 1, NULL);
177 /* ... and re-check stdio, in order to override any command
178 * line options! >;) */
179 if (!remote_session_flags) {
180 check_stdio(NULL);
183 init_b = 1;
184 init_modules(builtin_modules);
187 if (get_cmd_opt_bool("dump")
188 || get_cmd_opt_bool("source")) {
189 /* Dump the URL list */
190 #ifdef CONFIG_ECMASCRIPT
191 /* The ECMAScript code is not good at coping with this. And it
192 * makes currently no sense to evaluate ECMAScript in this
193 * context anyway. */
194 get_opt_bool("ecmascript.enable") = 0;
195 #endif
196 if (!list_empty(url_list)) {
197 dump_next(&url_list);
198 } else {
199 unsigned char *arg = get_cmd_opt_bool("dump")
200 ? "dump" : "source";
202 usrerror(gettext("URL expected after -%s"), arg);
203 program.retval = RET_SYNTAX;
204 program.terminate = 1;
207 } else if (remote_session_flags & SES_REMOTE_PING) {
208 /* If no instance was running return ping failure */
209 if (fd == -1) {
210 usrerror(gettext("No running ELinks found."));
211 program.retval = RET_PING;
214 } else if (remote_session_flags && fd == -1) {
215 /* The remote session(s) can not be created */
216 usrerror(gettext("No remote session to connect to."));
217 program.retval = RET_REMOTE;
219 } else {
220 struct string info;
221 struct terminal *term = NULL;
223 if (!encode_session_info(&info, &url_list)) {
224 ERROR(gettext("Unable to encode session info."));
225 program.retval = RET_FATAL;
226 program.terminate = 1;
228 } else if (fd != -1) {
229 /* Attach to already running ELinks and act as a slave
230 * for it. */
231 close_terminal_pipes();
233 handle_trm(get_input_handle(), get_output_handle(),
234 fd, fd, get_ctl_handle(), info.source, info.length,
235 remote_session_flags);
236 } else {
237 /* Setup a master terminal */
238 term = attach_terminal(get_input_handle(), get_output_handle(),
239 get_ctl_handle(), info.source, info.length);
240 if (!term) {
241 ERROR(gettext("Unable to attach_terminal()."));
242 program.retval = RET_FATAL;
243 program.terminate = 1;
247 /* OK, this is race condition, but it must be so; GPM installs
248 * it's own buggy TSTP handler. */
249 if (!program.terminate) handle_basic_signals(term);
250 done_string(&info);
253 if (program.terminate) close_terminal_pipes();
254 free_string_list(&url_list);
258 static void
259 terminate_all_subsystems(void)
261 done_interlink();
262 check_bottom_halves();
263 abort_all_downloads();
264 check_bottom_halves();
265 destroy_all_terminals();
266 check_bottom_halves();
267 free_all_itrms();
269 /* When aborting all connections also keep-alive connections are
270 * aborted. A (normal) connection will be started for any keep-alive
271 * connection that needs to send a command to the server before
272 * aborting. This means we need to abort_all_connections() twice.
274 * It forces a some what unclean connection tear-down since at most the
275 * shutdown routine will be able to send one command. But else it would
276 * take too long time to terminate. */
277 abort_all_connections();
278 check_bottom_halves();
279 abort_all_connections();
281 if (init_b) {
282 #ifdef CONFIG_SCRIPTING
283 trigger_event_name("quit");
284 #endif
285 free_history_lists();
286 done_modules(builtin_modules);
287 done_saved_session_info();
290 shrink_memory(1);
291 free_charsets_lookup();
292 free_colors_lookup();
293 done_modules(main_modules);
294 free_conv_table();
295 check_bottom_halves();
296 done_home();
297 done_state_message();
298 done_bfu_colors();
299 unregister_modules_options(builtin_modules);
300 unregister_modules_options(main_modules);
301 done_options();
302 done_event();
303 terminate_osdep();
306 void
307 shrink_memory(int whole)
309 #ifdef CONFIG_SCRIPTING
310 /* The SMJS pre-format-html hook constructs an SMJS object for
311 * each cache entry. Give all scripting modules a cue to garbage
312 * collect any such objects so that the entries can be freed. */
313 if (whole)
314 trigger_event_name("flush-caches");
315 #endif
316 shrink_dns_cache(whole);
317 shrink_format_cache(whole);
318 garbage_collection(whole);
321 #ifdef CONFIG_NO_ROOT_EXEC
322 static void
323 check_if_root(void)
325 if (!getuid() || !geteuid()) {
326 fprintf(stderr, "%s\n\n"
327 "Permission to run this program as root "
328 "user was disabled at compile time.\n\n",
329 full_static_version);
330 exit(-1);
333 #else
334 #define check_if_root()
335 #endif
337 #ifdef CONFIG_GC
338 static void
339 gc_warning(char *msg, GC_word arg)
342 #endif
346 main(int argc, char *argv[])
348 #ifdef CONFIG_GC
349 GC_INIT();
350 GC_set_warn_proc(gc_warning);
351 #endif
352 check_if_root();
354 program.terminate = 0;
355 program.retval = RET_OK;
356 program.path = argv[0];
357 ac = argc;
358 av = (unsigned char **) argv;
360 select_loop(init);
361 terminate_all_subsystems();
363 #ifdef DEBUG_MEMLEAK
364 check_memory_leaks();
365 #endif
366 return program.retval;