Manual pages: mc • mcdiff • mcedit • mcview

root/src/main.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. OS_Setup
  2. sigchld_handler_no_subshell
  3. init_sigchld
  4. check_sid
  5. main

   1 /*
   2    Main program for the Midnight Commander
   3 
   4    Copyright (C) 1994-2026
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Miguel de Icaza, 1994, 1995, 1996, 1997
   9    Janne Kukonlehto, 1994, 1995
  10    Norbert Warmuth, 1997
  11 
  12    This file is part of the Midnight Commander.
  13 
  14    The Midnight Commander is free software: you can redistribute it
  15    and/or modify it under the terms of the GNU General Public License as
  16    published by the Free Software Foundation, either version 3 of the License,
  17    or (at your option) any later version.
  18 
  19    The Midnight Commander is distributed in the hope that it will be useful,
  20    but WITHOUT ANY WARRANTY; without even the implied warranty of
  21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22    GNU General Public License for more details.
  23 
  24    You should have received a copy of the GNU General Public License
  25    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  26  */
  27 
  28 /** \file main.c
  29  *  \brief Source: this is a main module
  30  */
  31 
  32 #include <config.h>
  33 
  34 #include <locale.h>
  35 #include <pwd.h>  // for username in xterm title
  36 #include <stdio.h>
  37 #include <stdlib.h>
  38 #include <string.h>
  39 #include <sys/types.h>
  40 #include <sys/wait.h>
  41 #include <signal.h>
  42 #include <unistd.h>  // getsid()
  43 
  44 #include "lib/global.h"
  45 
  46 #include "lib/event.h"
  47 #include "lib/tty/tty.h"
  48 #include "lib/tty/key.h"    // For init_key()
  49 #include "lib/tty/mouse.h"  // init_mouse()
  50 #include "lib/skin.h"
  51 #include "lib/filehighlight.h"
  52 #include "lib/fileloc.h"
  53 #include "lib/strutil.h"
  54 #include "lib/util.h"
  55 #include "lib/vfs/vfs.h"  // vfs_init(), vfs_shut()
  56 
  57 #include "filemanager/filemanager.h"
  58 #include "filemanager/treestore.h"  // tree_store_save
  59 #include "filemanager/layout.h"
  60 #include "filemanager/ext.h"    // flush_extension_file()
  61 #include "filemanager/panel.h"  // panalized_panel
  62 
  63 #ifdef USE_INTERNAL_EDIT
  64 #include "editor/edit.h"  // edit_arg_free()
  65 #endif
  66 
  67 #include "vfs/plugins_init.h"
  68 
  69 #include "events_init.h"
  70 #include "args.h"
  71 #ifdef ENABLE_SUBSHELL
  72 #include "subshell/subshell.h"
  73 #endif
  74 #include "keymap.h"
  75 #include "setup.h"  // load_setup()
  76 
  77 #include "lib/charsets.h"
  78 #include "selcodepage.h"
  79 
  80 #include "consaver/cons.saver.h"  // cons_saver_pid
  81 
  82 /*** global variables ****************************************************************************/
  83 
  84 /*** file scope macro definitions ****************************************************************/
  85 
  86 /*** file scope type declarations ****************************************************************/
  87 
  88 /*** forward declarations (file scope functions) *************************************************/
  89 
  90 /*** file scope variables ************************************************************************/
  91 
  92 /* --------------------------------------------------------------------------------------------- */
  93 /*** file scope functions ************************************************************************/
  94 /* --------------------------------------------------------------------------------------------- */
  95 
  96 /** POSIX version.  The only version we support.  */
  97 static void
  98 OS_Setup (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
  99 {
 100     mc_shell_init ();
 101 
 102     // This is the directory, where MC was installed, on Unix this is DATADIR
 103     // and can be overridden by the MC_DATADIR environment variable
 104     const char *datadir_env = g_getenv ("MC_DATADIR");
 105 
 106     if (datadir_env != NULL)
 107         mc_global.sysconfig_dir = g_strdup (datadir_env);
 108     else
 109         mc_global.sysconfig_dir = g_strdup (SYSCONFDIR);
 110 
 111     mc_global.share_data_dir = g_strdup (DATADIR);
 112 }
 113 
 114 /* --------------------------------------------------------------------------------------------- */
 115 
 116 static void
 117 sigchld_handler_no_subshell (int sig)
     /* [previous][next][first][last][top][bottom][index][help]  */
 118 {
 119 #ifdef __linux__
 120     int pid, status;
 121 
 122     if (mc_global.tty.console_flag == '\0')
 123         return;
 124 
 125     /* COMMENT: if it were true that after the call to handle_console(..INIT)
 126        the value of mc_global.tty.console_flag never changed, we could simply not install
 127        this handler at all if (!mc_global.tty.console_flag && !mc_global.tty.use_subshell). */
 128 
 129     /* That comment is no longer true.  We need to wait() on a sigchld
 130        handler (that's at least what the tarfs code expects currently). */
 131 
 132     pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
 133 
 134     if (pid == cons_saver_pid)
 135     {
 136         if (WIFSTOPPED (status))
 137         {
 138             // Someone has stopped cons.saver - restart it
 139             kill (pid, SIGCONT);
 140         }
 141         else
 142         {
 143             // cons.saver has died - disable console saving
 144             handle_console (CONSOLE_DONE);
 145             mc_global.tty.console_flag = '\0';
 146         }
 147     }
 148     // If we got here, some other child exited; ignore it
 149 #endif
 150 
 151     (void) sig;
 152 }
 153 
 154 /* --------------------------------------------------------------------------------------------- */
 155 
 156 static void
 157 init_sigchld (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 158 {
 159     struct sigaction sigchld_action;
 160 
 161     memset (&sigchld_action, 0, sizeof (sigchld_action));
 162     sigchld_action.sa_handler =
 163 #ifdef ENABLE_SUBSHELL
 164         mc_global.tty.use_subshell ? sigchld_handler :
 165 #endif
 166                                    sigchld_handler_no_subshell;
 167 
 168     sigemptyset (&sigchld_action.sa_mask);
 169 
 170 #ifdef SA_RESTART
 171     sigchld_action.sa_flags = SA_RESTART;
 172 #endif
 173 
 174     if (my_sigaction (SIGCHLD, &sigchld_action, NULL) == -1)
 175     {
 176 #ifdef ENABLE_SUBSHELL
 177         /*
 178          * This may happen on QNX Neutrino 6, where SA_RESTART
 179          * is defined but not implemented.  Fallback to no subshell.
 180          */
 181         mc_global.tty.use_subshell = FALSE;
 182 #endif
 183     }
 184 }
 185 
 186 /* --------------------------------------------------------------------------------------------- */
 187 /**
 188  * Check MC_SID to prevent running one mc from another.
 189  *
 190  * @return TRUE if no parent mc in our session was found, FALSE otherwise.
 191  */
 192 
 193 static gboolean
 194 check_sid (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 195 {
 196     pid_t my_sid, old_sid;
 197     const char *sid_str;
 198 
 199     sid_str = getenv ("MC_SID");
 200     if (sid_str == NULL)
 201         return TRUE;
 202 
 203     old_sid = (pid_t) strtol (sid_str, NULL, 0);
 204     if (old_sid == 0)
 205         return TRUE;
 206 
 207     my_sid = getsid (0);
 208     if (my_sid == -1)
 209         return TRUE;
 210 
 211     // The parent mc is in a different session, it's OK
 212     return (old_sid != my_sid);
 213 }
 214 
 215 /* --------------------------------------------------------------------------------------------- */
 216 /*** public functions ****************************************************************************/
 217 /* --------------------------------------------------------------------------------------------- */
 218 
 219 int
 220 main (int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help]  */
 221 {
 222     GError *mcerror = NULL;
 223     int exit_code = EXIT_FAILURE;
 224     vfs_path_t *tmp_vpath = NULL;
 225 
 226     mc_global.run_from_parent_mc = !check_sid ();
 227 
 228     // We had LC_CTYPE before, LC_ALL includs LC_TYPE as well
 229 #ifdef HAVE_SETLOCALE
 230     (void) setlocale (LC_ALL, "");
 231 #endif
 232     (void) bindtextdomain (PACKAGE, LOCALEDIR);
 233     (void) textdomain (PACKAGE);
 234 
 235     // do this before args parsing
 236     str_init_strings (NULL);
 237 
 238     mc_setup_run_mode (argv);  // are we mc? editor? viewer? etc...
 239 
 240     if (!mc_args_parse (&argc, &argv, "mc", &mcerror))
 241     {
 242     startup_exit_falure:
 243         fprintf (stderr, _ ("Failed to run:\n%s\n"), mcerror->message);
 244         g_error_free (mcerror);
 245     startup_exit_ok:
 246         mc_shell_deinit ();
 247         str_uninit_strings ();
 248         return exit_code;
 249     }
 250 
 251     /* check terminal type
 252      * $TERM must be set and not empty
 253      * mc_global.tty.xterm_flag is used in init_key() and tty_init()
 254      * Do this after mc_args_parse() where mc_args__force_xterm is set up.
 255      */
 256     mc_global.tty.xterm_flag = tty_check_xterm_compat (mc_args__force_xterm);
 257 
 258     // do this before mc_args_show_info () to view paths in the --datadir-info output
 259     OS_Setup ();
 260 
 261     if (!g_path_is_absolute (mc_config_get_home_dir ()))
 262     {
 263         mc_propagate_error (&mcerror, 0, "%s: %s", _ ("Home directory path is not absolute"),
 264                             mc_config_get_home_dir ());
 265         mc_event_deinit (NULL);
 266         goto startup_exit_falure;
 267     }
 268 
 269     if (!mc_args_show_info ())
 270     {
 271         exit_code = EXIT_SUCCESS;
 272         goto startup_exit_ok;
 273     }
 274 
 275     if (!events_init (&mcerror))
 276         goto startup_exit_falure;
 277 
 278     mc_config_init_config_paths (&mcerror);
 279     if (mcerror != NULL)
 280     {
 281         mc_event_deinit (NULL);
 282         goto startup_exit_falure;
 283     }
 284 
 285     vfs_init ();
 286     vfs_plugins_init ();
 287 
 288     load_setup ();
 289 
 290     // Must be done after load_setup because depends on mc_global.vfs.cd_symlinks
 291     vfs_setup_work_dir ();
 292 
 293     // Set up temporary directory after VFS initialization
 294     const char *tmpdir = mc_tmpdir ();
 295 
 296     tmp_vpath = vfs_path_from_str (tmpdir);
 297 
 298     /* do this after vfs initialization and vfs working directory setup
 299        due to mc_setctl() and mcedit_arg_vpath_new() calls in mc_setup_by_args() */
 300     if (!mc_setup_by_args (argc, argv, &mcerror))
 301     {
 302         /* At exit, do this before vfs_shut():
 303            normally, temporary directory should be empty */
 304         vfs_expire (TRUE);
 305         (void) mc_rmdir (tmp_vpath);
 306         vfs_path_free (tmp_vpath, TRUE);
 307 
 308         vfs_shut ();
 309         done_setup ();
 310         g_free (saved_other_dir);
 311         mc_event_deinit (NULL);
 312         goto startup_exit_falure;
 313     }
 314 
 315     /* Resolve the other_dir panel option.
 316      * 1. Must be done after vfs_setup_work_dir().
 317      * 2. Must be done after mc_setup_by_args() because of mc_run_mode.
 318      */
 319     if (mc_global.mc_run_mode == MC_RUN_FULL)
 320     {
 321         char *buffer;
 322         vfs_path_t *vpath;
 323 
 324         buffer = mc_config_get_string (mc_global.panels_config, "Dirs", "other_dir", ".");
 325         vpath = vfs_path_from_str (buffer);
 326         if (vfs_file_is_local (vpath))
 327             saved_other_dir = buffer;
 328         else
 329             g_free (buffer);
 330         vfs_path_free (vpath, TRUE);
 331     }
 332 
 333     /* NOTE: This has to be called before tty_init or whatever routine
 334        calls any define_sequence */
 335     init_key ();
 336 
 337     // Must be done before installing the SIGCHLD handler [[FIXME]]
 338     handle_console (CONSOLE_INIT);
 339 
 340 #ifdef ENABLE_SUBSHELL
 341     // Disallow subshell when invoked as standalone viewer or editor from running mc
 342     if (mc_global.mc_run_mode != MC_RUN_FULL && mc_global.run_from_parent_mc)
 343         mc_global.tty.use_subshell = FALSE;
 344 
 345     if (mc_global.tty.use_subshell)
 346         subshell_get_console_attributes ();
 347 #endif
 348 
 349     // Install the SIGCHLD handler; must be done before init_subshell()
 350     init_sigchld ();
 351 
 352     // We need this, since ncurses endwin () doesn't restore the signals
 353     save_stop_handler ();
 354 
 355     // Must be done before init_subshell, to set up the terminal size:
 356     // FIXME: Should be removed and LINES and COLS computed on subshell
 357     tty_init (!mc_args__nomouse, mc_global.tty.xterm_flag);
 358 
 359     // Removing this from the X code let's us type C-c
 360     load_key_defs ();
 361 
 362     keymap_load (!mc_args__nokeymap);
 363 
 364 #ifdef USE_INTERNAL_EDIT
 365     macros_list = g_array_new (TRUE, FALSE, sizeof (macros_t));
 366 #endif
 367 
 368     tty_init_colors (mc_global.tty.disable_colors, mc_args__force_colors, COLOR_MAP_SIZE);
 369 
 370     mc_skin_init (NULL, &mcerror);
 371 
 372     mc_error_message (&mcerror, NULL);
 373 
 374 #ifdef ENABLE_SUBSHELL
 375     // Done here to ensure that the subshell doesn't
 376     // inherit the file descriptors opened below, etc
 377     if (mc_global.tty.use_subshell && mc_global.run_from_parent_mc)
 378     {
 379         char *text;
 380 
 381         text = g_strdup_printf (_ ("%s\nis already running on this terminal.\n"
 382                                    "Subshell support will be disabled."),
 383                                 PACKAGE_NAME);
 384 
 385         const int quit_mc = query_dialog (_ ("Warning"), text, D_ERROR, 2, _ ("&OK"), _ ("&Quit"));
 386 
 387         g_free (text);
 388 
 389         if (quit_mc != 0)
 390         {
 391             // parent mc was found and the user wants to quit mc
 392             mc_global.midnight_shutdown = TRUE;
 393         }
 394 
 395         // quit or not, disable nested subshell
 396         mc_global.tty.use_subshell = FALSE;
 397     }
 398 
 399     if (mc_global.tty.use_subshell)
 400         init_subshell ();
 401 #endif
 402 
 403     if (!mc_global.midnight_shutdown)
 404     {
 405         // Also done after init_subshell, to save any shell init file messages
 406         if (mc_global.tty.console_flag != '\0')
 407             handle_console (CONSOLE_SAVE);
 408 
 409         if (mc_global.tty.alternate_plus_minus)
 410             application_keypad_mode ();
 411 
 412         /* Done after subshell initialization to allow select and paste text by mouse
 413            w/o Shift button in subshell in the native console */
 414         init_mouse ();
 415 
 416         /* Done after tty_enter_ca_mode (tty_init) because in VTE bracketed mode is
 417            separate for the normal and alternate screens */
 418         enable_bracketed_paste ();
 419 
 420         // subshell_prompt is NULL here
 421         mc_prompt = g_strdup ((geteuid () == 0) ? "# " : "$ ");
 422     }
 423 
 424     // Program main loop
 425     if (mc_global.midnight_shutdown)
 426         exit_code = EXIT_SUCCESS;
 427     else
 428         exit_code = do_nc () ? EXIT_SUCCESS : EXIT_FAILURE;
 429 
 430     g_free (mc_prompt);
 431 
 432     disable_bracketed_paste ();
 433 
 434     disable_mouse ();
 435 
 436     // Save the tree store
 437     (void) tree_store_save ();
 438 
 439     keymap_free ();
 440 
 441     /* At exit, do this before vfs_shut():
 442        normally, temporary directory should be empty */
 443     vfs_expire (TRUE);
 444     (void) mc_rmdir (tmp_vpath);
 445     vfs_path_free (tmp_vpath, TRUE);
 446 
 447     // Virtual File System shutdown
 448     vfs_shut ();
 449 
 450     flush_extension_file ();  // does only free memory
 451 
 452     mc_skin_deinit ();
 453     tty_colors_done ();
 454 
 455     tty_shutdown ();
 456 
 457     done_setup ();
 458 
 459     if (mc_global.tty.console_flag != '\0' && (quit & SUBSHELL_EXIT) == 0)
 460         handle_console (CONSOLE_RESTORE);
 461     if (mc_global.tty.alternate_plus_minus)
 462         numeric_keypad_mode ();
 463 
 464     (void) my_signal (SIGCHLD, SIG_DFL);  // Disable the SIGCHLD handler
 465 
 466     if (mc_global.tty.console_flag != '\0')
 467         handle_console (CONSOLE_DONE);
 468 
 469     if (mc_global.mc_run_mode == MC_RUN_FULL && mc_args__last_wd_file != NULL && last_wd_str != NULL
 470         && !print_last_revert)
 471     {
 472         const int last_wd_fd =
 473             open (mc_args__last_wd_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
 474 
 475         if (last_wd_fd != -1)
 476         {
 477             MC_UNUSED const ssize_t ret1 = write (last_wd_fd, last_wd_str, strlen (last_wd_str));
 478             MC_UNUSED const int ret2 = close (last_wd_fd);
 479         }
 480     }
 481 
 482     g_free (last_wd_str);
 483 
 484     mc_shell_deinit ();
 485 
 486     done_key ();
 487 
 488 #ifdef USE_INTERNAL_EDIT
 489     if (macros_list != NULL)
 490     {
 491         guint i;
 492 
 493         for (i = 0; i < macros_list->len; i++)
 494         {
 495             macros_t *macros;
 496 
 497             macros = &g_array_index (macros_list, struct macros_t, i);
 498             if (macros != NULL && macros->macro != NULL)
 499                 (void) g_array_free (macros->macro, TRUE);
 500         }
 501         (void) g_array_free (macros_list, TRUE);
 502     }
 503 #endif
 504 
 505     str_uninit_strings ();
 506 
 507     if (mc_global.mc_run_mode != MC_RUN_EDITOR)
 508         g_free (mc_run_param0);
 509 #ifdef USE_INTERNAL_EDIT
 510     else
 511         g_list_free_full ((GList *) mc_run_param0, (GDestroyNotify) edit_arg_free);
 512 #endif
 513 
 514     g_free (mc_run_param1);
 515     g_free (saved_other_dir);
 516 
 517     mc_config_deinit_config_paths ();
 518 
 519     (void) mc_event_deinit (&mcerror);
 520     if (mcerror != NULL)
 521     {
 522         fprintf (stderr, _ ("\nFailed while close:\n%s\n"), mcerror->message);
 523         g_error_free (mcerror);
 524         exit_code = EXIT_FAILURE;
 525     }
 526 
 527     (void) putchar ('\n');  // Hack to make shell's prompt start at left of screen
 528 
 529     return exit_code;
 530 }
 531 
 532 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */