Ticket #3021: mc segfaults when mc's tempdir doesn't belong to the correct user.
[midnight-commander.git] / src / main.c
blobe8807eec0e1849b36074225286f8bbc1995726ce
1 /*
2 Main program for the Midnight Commander
4 Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
5 2003, 2004, 2005, 2006, 2007, 2009, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Miguel de Icaza, 1994, 1995, 1996, 1997
10 Janne Kukonlehto, 1994, 1995
11 Norbert Warmuth, 1997
13 This file is part of the Midnight Commander.
15 The Midnight Commander is free software: you can redistribute it
16 and/or modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation, either version 3 of the License,
18 or (at your option) any later version.
20 The Midnight Commander is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
29 /** \file main.c
30 * \brief Source: this is a main module
33 #include <config.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <locale.h>
38 #include <pwd.h> /* for username in xterm title */
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/wait.h>
43 #include <signal.h>
45 #include "lib/global.h"
47 #include "lib/event.h"
48 #include "lib/tty/tty.h"
49 #include "lib/tty/key.h" /* For init_key() */
50 #include "lib/tty/mouse.h" /* init_mouse() */
51 #include "lib/skin.h"
52 #include "lib/filehighlight.h"
53 #include "lib/fileloc.h"
54 #include "lib/strutil.h"
55 #include "lib/util.h"
56 #include "lib/vfs/vfs.h" /* vfs_init(), vfs_shut() */
58 #include "filemanager/midnight.h" /* current_panel */
59 #include "filemanager/treestore.h" /* tree_store_save */
60 #include "filemanager/layout.h" /* command_prompt */
61 #include "filemanager/ext.h" /* flush_extension_file() */
62 #include "filemanager/command.h" /* cmdline */
63 #include "filemanager/panel.h" /* panalized_panel */
65 #include "vfs/plugins_init.h"
67 #include "events_init.h"
68 #include "args.h"
69 #ifdef ENABLE_SUBSHELL
70 #include "subshell.h"
71 #endif
72 #include "setup.h" /* load_setup() */
74 #ifdef HAVE_CHARSET
75 #include "lib/charsets.h"
76 #include "selcodepage.h"
77 #endif /* HAVE_CHARSET */
79 #include "consaver/cons.saver.h" /* cons_saver_pid */
81 /*** global variables ****************************************************************************/
83 /*** file scope macro definitions ****************************************************************/
85 /*** file scope type declarations ****************************************************************/
87 /*** file scope variables ************************************************************************/
89 /*** file scope functions ************************************************************************/
90 /* --------------------------------------------------------------------------------------------- */
92 static void
93 check_codeset (void)
95 const char *current_system_codepage = NULL;
97 current_system_codepage = str_detect_termencoding ();
99 #ifdef HAVE_CHARSET
101 const char *_display_codepage;
103 _display_codepage = get_codepage_id (mc_global.display_codepage);
105 if (strcmp (_display_codepage, current_system_codepage) != 0)
107 mc_global.display_codepage = get_codepage_index (current_system_codepage);
108 if (mc_global.display_codepage == -1)
109 mc_global.display_codepage = 0;
111 mc_config_set_string (mc_main_config, CONFIG_MISC_SECTION, "display_codepage",
112 cp_display);
115 #endif
117 mc_global.utf8_display = str_isutf8 (current_system_codepage);
120 /* --------------------------------------------------------------------------------------------- */
122 /** POSIX version. The only version we support. */
123 static void
124 OS_Setup (void)
126 const char *shell_env;
127 const char *datadir_env;
129 shell_env = getenv ("SHELL");
130 if ((shell_env == NULL) || (shell_env[0] == '\0'))
132 struct passwd *pwd;
134 pwd = getpwuid (geteuid ());
135 if (pwd != NULL)
136 mc_global.tty.shell = g_strdup (pwd->pw_shell);
138 else
139 mc_global.tty.shell = g_strdup (shell_env);
141 if ((mc_global.tty.shell == NULL) || (mc_global.tty.shell[0] == '\0'))
143 g_free (mc_global.tty.shell);
144 mc_global.tty.shell = g_strdup ("/bin/sh");
147 /* This is the directory, where MC was installed, on Unix this is DATADIR */
148 /* and can be overriden by the MC_DATADIR environment variable */
149 datadir_env = g_getenv ("MC_DATADIR");
150 if (datadir_env != NULL)
151 mc_global.sysconfig_dir = g_strdup (datadir_env);
152 else
153 mc_global.sysconfig_dir = g_strdup (SYSCONFDIR);
155 mc_global.share_data_dir = g_strdup (DATADIR);
158 /* --------------------------------------------------------------------------------------------- */
160 static void
161 sigchld_handler_no_subshell (int sig)
163 #ifdef __linux__
164 int pid, status;
166 if (!mc_global.tty.console_flag != '\0')
167 return;
169 /* COMMENT: if it were true that after the call to handle_console(..INIT)
170 the value of mc_global.tty.console_flag never changed, we could simply not install
171 this handler at all if (!mc_global.tty.console_flag && !mc_global.tty.use_subshell). */
173 /* That comment is no longer true. We need to wait() on a sigchld
174 handler (that's at least what the tarfs code expects currently). */
176 pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
178 if (pid == cons_saver_pid)
181 if (WIFSTOPPED (status))
183 /* Someone has stopped cons.saver - restart it */
184 kill (pid, SIGCONT);
186 else
188 /* cons.saver has died - disable console saving */
189 handle_console (CONSOLE_DONE);
190 mc_global.tty.console_flag = '\0';
193 /* If we got here, some other child exited; ignore it */
194 #endif /* __linux__ */
196 (void) sig;
199 /* --------------------------------------------------------------------------------------------- */
201 static void
202 init_sigchld (void)
204 struct sigaction sigchld_action;
206 sigchld_action.sa_handler =
207 #ifdef ENABLE_SUBSHELL
208 mc_global.tty.use_subshell ? sigchld_handler :
209 #endif /* ENABLE_SUBSHELL */
210 sigchld_handler_no_subshell;
212 sigemptyset (&sigchld_action.sa_mask);
214 #ifdef SA_RESTART
215 sigchld_action.sa_flags = SA_RESTART;
216 #else
217 sigchld_action.sa_flags = 0;
218 #endif /* !SA_RESTART */
220 if (sigaction (SIGCHLD, &sigchld_action, NULL) == -1)
222 #ifdef ENABLE_SUBSHELL
224 * This may happen on QNX Neutrino 6, where SA_RESTART
225 * is defined but not implemented. Fallback to no subshell.
227 mc_global.tty.use_subshell = FALSE;
228 #endif /* ENABLE_SUBSHELL */
232 /* --------------------------------------------------------------------------------------------- */
233 /*** public functions ****************************************************************************/
234 /* --------------------------------------------------------------------------------------------- */
237 main (int argc, char *argv[])
239 GError *error = NULL;
240 gboolean config_migrated = FALSE;
241 char *config_migrate_msg;
242 int exit_code = EXIT_FAILURE;
244 /* We had LC_CTYPE before, LC_ALL includs LC_TYPE as well */
245 #ifdef HAVE_SETLOCALE
246 (void) setlocale (LC_ALL, "");
247 #endif
248 (void) bindtextdomain (PACKAGE, LOCALEDIR);
249 (void) textdomain (PACKAGE);
251 /* do this before args parsing */
252 str_init_strings (NULL);
254 if (!mc_args_parse (&argc, &argv, "mc", &error))
256 startup_exit_falure:
257 fprintf (stderr, _("Failed to run:\n%s\n"), error->message);
258 g_error_free (error);
259 g_free (mc_global.tty.shell);
260 startup_exit_ok:
261 str_uninit_strings ();
262 return exit_code;
265 /* do this before mc_args_show_info () to view paths in the --datadir-info output */
266 OS_Setup ();
268 if (!g_path_is_absolute (mc_config_get_home_dir ()))
270 error = g_error_new (MC_ERROR, 0, "%s: %s", _("Home directory path is not absolute"),
271 mc_config_get_home_dir ());
272 mc_event_deinit (NULL);
273 goto startup_exit_falure;
276 if (!mc_args_show_info ())
278 exit_code = EXIT_SUCCESS;
279 goto startup_exit_ok;
282 if (!events_init (&error))
283 goto startup_exit_falure;
285 mc_config_init_config_paths (&error);
286 if (error == NULL)
287 config_migrated = mc_config_migrate_from_old_place (&error, &config_migrate_msg);
288 if (error != NULL)
290 mc_event_deinit (NULL);
291 goto startup_exit_falure;
294 vfs_init ();
295 vfs_plugins_init ();
296 vfs_setup_work_dir ();
298 /* Set up temporary directory after VFS initialization */
299 mc_tmpdir ();
301 /* do this after vfs initialization due to mc_setctl() call in mc_setup_by_args() */
302 if (!mc_setup_by_args (argc, argv, &error))
304 vfs_shut ();
305 mc_event_deinit (NULL);
306 goto startup_exit_falure;
309 /* check terminal type
310 * $TEMR must be set and not empty
311 * mc_global.tty.xterm_flag is used in init_key() and tty_init()
312 * Do this after mc_args_handle() where mc_args__force_xterm is set up.
314 mc_global.tty.xterm_flag = tty_check_term (mc_args__force_xterm);
316 /* NOTE: This has to be called before tty_init or whatever routine
317 calls any define_sequence */
318 init_key ();
320 /* Must be done before installing the SIGCHLD handler [[FIXME]] */
321 handle_console (CONSOLE_INIT);
323 #ifdef ENABLE_SUBSHELL
324 /* Don't use subshell when invoked as viewer or editor */
325 if (mc_global.mc_run_mode != MC_RUN_FULL)
326 mc_global.tty.use_subshell = FALSE;
328 if (mc_global.tty.use_subshell)
329 subshell_get_console_attributes ();
330 #endif /* ENABLE_SUBSHELL */
332 /* Install the SIGCHLD handler; must be done before init_subshell() */
333 init_sigchld ();
335 /* We need this, since ncurses endwin () doesn't restore the signals */
336 save_stop_handler ();
338 /* Must be done before init_subshell, to set up the terminal size: */
339 /* FIXME: Should be removed and LINES and COLS computed on subshell */
340 tty_init (!mc_args__nomouse, mc_global.tty.xterm_flag);
342 load_setup ();
344 /* start check mc_global.display_codepage and mc_global.source_codepage */
345 check_codeset ();
347 /* Removing this from the X code let's us type C-c */
348 load_key_defs ();
350 load_keymap_defs (!mc_args__nokeymap);
352 macros_list = g_array_new (TRUE, FALSE, sizeof (macros_t));
354 tty_init_colors (mc_global.tty.disable_colors, mc_args__force_colors);
356 mc_skin_init (&error);
357 if (error != NULL)
359 message (D_ERROR, _("Warning"), "%s", error->message);
360 g_error_free (error);
361 error = NULL;
364 dlg_set_default_colors ();
366 #ifdef ENABLE_SUBSHELL
367 /* Done here to ensure that the subshell doesn't */
368 /* inherit the file descriptors opened below, etc */
369 if (mc_global.tty.use_subshell)
370 init_subshell ();
371 #endif /* ENABLE_SUBSHELL */
373 /* Also done after init_subshell, to save any shell init file messages */
374 if (mc_global.tty.console_flag != '\0')
375 handle_console (CONSOLE_SAVE);
377 if (mc_global.tty.alternate_plus_minus)
378 application_keypad_mode ();
380 /* Done after subshell initialization to allow select and paste text by mouse
381 w/o Shift button in subshell in the native console */
382 init_mouse ();
384 /* subshell_prompt is NULL here */
385 mc_prompt = (geteuid () == 0) ? "# " : "$ ";
387 if (config_migrated)
389 message (D_ERROR, _("Warning"), "%s", config_migrate_msg);
390 g_free (config_migrate_msg);
393 /* Program main loop */
394 if (mc_global.midnight_shutdown)
395 exit_code = EXIT_SUCCESS;
396 else
397 exit_code = do_nc ()? EXIT_SUCCESS : EXIT_FAILURE;
399 /* Save the tree store */
400 (void) tree_store_save ();
402 free_keymap_defs ();
404 /* Virtual File System shutdown */
405 vfs_shut ();
407 flush_extension_file (); /* does only free memory */
409 mc_skin_deinit ();
410 tty_colors_done ();
412 tty_shutdown ();
414 done_setup ();
416 if (mc_global.tty.console_flag != '\0' && (quit & SUBSHELL_EXIT) == 0)
417 handle_console (CONSOLE_RESTORE);
418 if (mc_global.tty.alternate_plus_minus)
419 numeric_keypad_mode ();
421 (void) signal (SIGCHLD, SIG_DFL); /* Disable the SIGCHLD handler */
423 if (mc_global.tty.console_flag != '\0')
424 handle_console (CONSOLE_DONE);
426 if (mc_global.mc_run_mode == MC_RUN_FULL && mc_args__last_wd_file != NULL
427 && last_wd_string != NULL && !print_last_revert)
429 int last_wd_fd;
431 last_wd_fd = open (mc_args__last_wd_file, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
432 S_IRUSR | S_IWUSR);
433 if (last_wd_fd != -1)
435 ssize_t ret1;
436 int ret2;
437 ret1 = write (last_wd_fd, last_wd_string, strlen (last_wd_string));
438 ret2 = close (last_wd_fd);
439 (void) ret1;
440 (void) ret2;
443 g_free (last_wd_string);
445 g_free (mc_global.tty.shell);
447 done_key ();
449 if (macros_list != NULL)
451 guint i;
452 macros_t *macros;
453 for (i = 0; i < macros_list->len; i++)
455 macros = &g_array_index (macros_list, struct macros_t, i);
456 if (macros != NULL && macros->macro != NULL)
457 (void) g_array_free (macros->macro, FALSE);
459 (void) g_array_free (macros_list, TRUE);
462 str_uninit_strings ();
464 if (mc_global.mc_run_mode != MC_RUN_EDITOR)
465 g_free (mc_run_param0);
466 else
468 g_list_foreach ((GList *) mc_run_param0, (GFunc) mcedit_arg_free, NULL);
469 g_list_free ((GList *) mc_run_param0);
471 g_free (mc_run_param1);
473 mc_config_deinit_config_paths ();
475 (void) mc_event_deinit (&error);
476 if (error != NULL)
478 fprintf (stderr, _("\nFailed while close:\n%s\n"), error->message);
479 g_error_free (error);
480 exit_code = EXIT_FAILURE;
483 (void) putchar ('\n'); /* Hack to make shell's prompt start at left of screen */
485 return exit_code;
488 /* --------------------------------------------------------------------------------------------- */