Manual pages: mcmcdiffmceditmcview

root/src/cons.handler.c

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

DEFINITIONS

This source file includes following definitions.
  1. show_console_contents_linux
  2. handle_console_linux
  3. console_init
  4. set_attr
  5. console_restore
  6. console_shutdown
  7. console_save
  8. show_console_contents_freebsd
  9. handle_console_freebsd
  10. show_console_contents
  11. handle_console

   1 /*
   2    Client interface for General purpose Linux console save/restore server
   3 
   4    Copyright (C) 1994-2025
   5    Free Software Foundation, Inc.
   6 
   7    This file is part of the Midnight Commander.
   8 
   9    The Midnight Commander is free software: you can redistribute it
  10    and/or modify it under the terms of the GNU General Public License as
  11    published by the Free Software Foundation, either version 3 of the License,
  12    or (at your option) any later version.
  13 
  14    The Midnight Commander is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18 
  19    You should have received a copy of the GNU General Public License
  20    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  21  */
  22 
  23 /** \file cons.handler.c
  24  *  \brief Source: client %interface for General purpose Linux console save/restore server
  25  */
  26 
  27 #include <config.h>
  28 
  29 #include <stdlib.h>
  30 #include <sys/wait.h>
  31 #include <signal.h>
  32 #include <stdio.h>
  33 #include <sys/types.h>
  34 #ifdef __FreeBSD__
  35 #include <sys/consio.h>
  36 #ifdef HAVE_SYS_IOCTL_H
  37 #include <sys/ioctl.h>
  38 #endif
  39 #endif
  40 
  41 #include "lib/global.h"
  42 
  43 #include "lib/unixcompat.h"
  44 #include "lib/tty/tty.h"
  45 #include "lib/tty/color.h"  // tty_set_normal_attrs
  46 #include "lib/tty/win.h"
  47 #include "lib/util.h"  // mc_build_filename()
  48 
  49 #include "consaver/cons.saver.h"
  50 
  51 /*** global variables ****************************************************************************/
  52 
  53 #ifdef __linux__
  54 int cons_saver_pid = 1;
  55 #endif
  56 
  57 /*** file scope macro definitions ****************************************************************/
  58 
  59 #if defined(__FreeBSD__)
  60 #define FD_OUT 1
  61 #define cursor_to(x, y)                                                                            \
  62     do                                                                                             \
  63     {                                                                                              \
  64         printf ("\x1B[%d;%df", (y) + 1, (x) + 1);                                                  \
  65         fflush (stdout);                                                                           \
  66     }                                                                                              \
  67     while (0)
  68 #endif
  69 
  70 /*** file scope type declarations ****************************************************************/
  71 
  72 /*** forward declarations (file scope functions) *************************************************/
  73 
  74 /*** file scope variables ************************************************************************/
  75 
  76 #ifdef __linux__
  77 /* The cons saver can't have a pid of 1, used to prevent bunches of
  78  * #ifdef linux */
  79 static int pipefd1[2] = { -1, -1 };
  80 static int pipefd2[2] = { -1, -1 };
  81 #elif defined(__FreeBSD__)
  82 static struct scrshot screen_shot;
  83 static struct vid_info screen_info;
  84 #endif
  85 
  86 /* --------------------------------------------------------------------------------------------- */
  87 /*** file scope functions ************************************************************************/
  88 /* --------------------------------------------------------------------------------------------- */
  89 
  90 #ifdef __linux__
  91 static void
  92 show_console_contents_linux (int starty, unsigned char begin_line, unsigned char end_line)
     /* [previous][next][first][last][top][bottom][index][help]  */
  93 {
  94     unsigned char message = 0;
  95     unsigned short bytes = 0;
  96     int i;
  97     ssize_t ret;
  98 
  99     // Is tty console?
 100     if (mc_global.tty.console_flag == '\0')
 101         return;
 102     // Paranoid: Is the cons.saver still running?
 103     if (cons_saver_pid < 1 || kill (cons_saver_pid, SIGCONT))
 104     {
 105         cons_saver_pid = 0;
 106         mc_global.tty.console_flag = '\0';
 107         return;
 108     }
 109 
 110     // Send command to the console handler
 111     message = CONSOLE_CONTENTS;
 112     ret = write (pipefd1[1], &message, 1);
 113     // Check for outdated cons.saver
 114     ret = read (pipefd2[0], &message, 1);
 115     if (message != CONSOLE_CONTENTS)
 116         return;
 117 
 118     // Send the range of lines that we want
 119     ret = write (pipefd1[1], &begin_line, 1);
 120     ret = write (pipefd1[1], &end_line, 1);
 121     // Read the corresponding number of bytes
 122     ret = read (pipefd2[0], &bytes, 2);
 123 
 124     // Read the bytes and output them
 125     for (i = 0; i < bytes; i++)
 126     {
 127         if ((i % COLS) == 0)
 128             tty_gotoyx (starty + (i / COLS), 0);
 129         ret = read (pipefd2[0], &message, 1);
 130         tty_print_char (message);
 131     }
 132 
 133     // Read the value of the mc_global.tty.console_flag
 134     ret = read (pipefd2[0], &message, 1);
 135     (void) ret;
 136 }
 137 
 138 /* --------------------------------------------------------------------------------------------- */
 139 
 140 static void
 141 handle_console_linux (console_action_t action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 142 {
 143     int status;
 144 
 145     switch (action)
 146     {
 147     case CONSOLE_INIT:
 148         // Close old pipe ends in case it is the 2nd time we run cons.saver
 149         status = close (pipefd1[1]);
 150         status = close (pipefd2[0]);
 151         // Create two pipes for communication
 152         if (!((pipe (pipefd1) == 0) && ((pipe (pipefd2)) == 0)))
 153         {
 154             mc_global.tty.console_flag = '\0';
 155             break;
 156         }
 157         // Get the console saver running
 158         cons_saver_pid = my_fork ();
 159         if (cons_saver_pid < 0)
 160         {
 161             // Cannot fork
 162             // Delete pipes
 163             status = close (pipefd1[1]);
 164             status = close (pipefd1[0]);
 165             status = close (pipefd2[1]);
 166             status = close (pipefd2[0]);
 167             mc_global.tty.console_flag = '\0';
 168         }
 169         else if (cons_saver_pid > 0)
 170         {
 171             // Parent
 172             // Close the extra pipe ends
 173             status = close (pipefd1[0]);
 174             status = close (pipefd2[1]);
 175             // Was the child successful?
 176             status = read (pipefd2[0], &mc_global.tty.console_flag, 1);
 177             if (mc_global.tty.console_flag == '\0')
 178             {
 179                 pid_t ret;
 180                 status = close (pipefd1[1]);
 181                 status = close (pipefd2[0]);
 182                 ret = waitpid (cons_saver_pid, &status, 0);
 183                 (void) ret;
 184             }
 185         }
 186         else
 187         {
 188             // Child
 189             char *tty_name;
 190 
 191             // Close the extra pipe ends
 192             status = close (pipefd1[1]);
 193             status = close (pipefd2[0]);
 194             tty_name = ttyname (0);
 195             // Bind the pipe 0 to the standard input
 196             do
 197             {
 198                 gboolean ok;
 199 
 200                 if (dup2 (pipefd1[0], STDIN_FILENO) == -1)
 201                     break;
 202                 status = close (pipefd1[0]);
 203                 // Bind the pipe 1 to the standard output
 204                 if (dup2 (pipefd2[1], STDOUT_FILENO) == -1)
 205                     break;
 206 
 207                 status = close (pipefd2[1]);
 208                 // Bind standard error to /dev/null
 209                 status = open ("/dev/null", O_WRONLY);
 210                 if (status == -1)
 211                     break;
 212                 ok = dup2 (status, STDERR_FILENO) != -1;
 213                 status = close (status);
 214                 if (!ok)
 215                     break;
 216 
 217                 if (tty_name != NULL)
 218                 {
 219                     char *mc_conssaver;
 220 
 221                     // Exec the console save/restore handler
 222                     mc_conssaver = mc_build_filename (SAVERDIR, "cons.saver", (char *) NULL);
 223                     execl (mc_conssaver, "cons.saver", tty_name, (char *) NULL);
 224                 }
 225                 // Console is not a tty or execl() failed
 226             }
 227             while (0);
 228             mc_global.tty.console_flag = '\0';
 229             status = write (1, &mc_global.tty.console_flag, 1);
 230             my_exit (3);
 231         }  // if (cons_saver_pid ...)
 232         break;
 233 
 234     case CONSOLE_DONE:
 235     case CONSOLE_SAVE:
 236     case CONSOLE_RESTORE:
 237         // Is tty console?
 238         if (mc_global.tty.console_flag == '\0')
 239             return;
 240         // Paranoid: Is the cons.saver still running?
 241         if (cons_saver_pid < 1 || kill (cons_saver_pid, SIGCONT))
 242         {
 243             cons_saver_pid = 0;
 244             mc_global.tty.console_flag = '\0';
 245             return;
 246         }
 247         // Send command to the console handler
 248         {
 249             /* Convert enum (i.e. int) to char to write the correct value
 250              * (the least byte) regardless of machine endianness. */
 251             char act = (char) action;
 252 
 253             status = write (pipefd1[1], &act, 1);
 254         }
 255         if (action != CONSOLE_DONE)
 256         {
 257             // Wait the console handler to do its job
 258             status = read (pipefd2[0], &mc_global.tty.console_flag, 1);
 259         }
 260         if (action == CONSOLE_DONE || mc_global.tty.console_flag == '\0')
 261         {
 262             // We are done -> Let's clean up
 263             pid_t ret;
 264             close (pipefd1[1]);
 265             close (pipefd2[0]);
 266             ret = waitpid (cons_saver_pid, &status, 0);
 267             (void) ret;
 268             mc_global.tty.console_flag = '\0';
 269         }
 270         break;
 271     default:
 272         break;
 273     }
 274 }
 275 
 276 #elif defined(__FreeBSD__)
 277 
 278 /* --------------------------------------------------------------------------------------------- */
 279 /**
 280  * FreeBSD support copyright (C) 2003 Alexander Serkov <serkov@ukrpost.net>.
 281  * Support for screenmaps by Max Khon <fjoe@FreeBSD.org>
 282  */
 283 
 284 static void
 285 console_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 286 {
 287     if (mc_global.tty.console_flag != '\0')
 288         return;
 289 
 290     screen_info.size = sizeof (screen_info);
 291     if (ioctl (FD_OUT, CONS_GETINFO, &screen_info) == -1)
 292         return;
 293 
 294     memset (&screen_shot, 0, sizeof (screen_shot));
 295     screen_shot.xsize = screen_info.mv_csz;
 296     screen_shot.ysize = screen_info.mv_rsz;
 297     screen_shot.buf = g_try_malloc (screen_info.mv_csz * screen_info.mv_rsz * 2);
 298     if (screen_shot.buf != NULL)
 299         mc_global.tty.console_flag = '\001';
 300 }
 301 
 302 /* --------------------------------------------------------------------------------------------- */
 303 
 304 static void
 305 set_attr (unsigned attr)
     /* [previous][next][first][last][top][bottom][index][help]  */
 306 {
 307     /*
 308      * Convert color indices returned by SCRSHOT (red=4, green=2, blue=1)
 309      * to indices for ANSI sequences (red=1, green=2, blue=4).
 310      */
 311     static const int color_map[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
 312     int bc, tc;
 313 
 314     tc = attr & 0xF;
 315     bc = (attr >> 4) & 0xF;
 316 
 317     printf ("\x1B[%d;%d;3%d;4%dm", (bc & 8) ? 5 : 25, (tc & 8) ? 1 : 22, color_map[tc & 7],
 318             color_map[bc & 7]);
 319 }
 320 
 321 /* --------------------------------------------------------------------------------------------- */
 322 
 323 static void
 324 console_restore (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 325 {
 326     int i, last;
 327 
 328     if (mc_global.tty.console_flag == '\0')
 329         return;
 330 
 331     cursor_to (0, 0);
 332 
 333     // restoring all content up to cursor position
 334     last = screen_info.mv_row * screen_info.mv_csz + screen_info.mv_col;
 335     for (i = 0; i < last; ++i)
 336     {
 337         set_attr ((screen_shot.buf[i] >> 8) & 0xFF);
 338         putc (screen_shot.buf[i] & 0xFF, stdout);
 339     }
 340 
 341     // restoring cursor color
 342     set_attr ((screen_shot.buf[last] >> 8) & 0xFF);
 343 
 344     fflush (stdout);
 345 }
 346 
 347 /* --------------------------------------------------------------------------------------------- */
 348 
 349 static void
 350 console_shutdown (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 351 {
 352     if (mc_global.tty.console_flag == '\0')
 353         return;
 354 
 355     g_free (screen_shot.buf);
 356 
 357     mc_global.tty.console_flag = '\0';
 358 }
 359 
 360 /* --------------------------------------------------------------------------------------------- */
 361 
 362 static void
 363 console_save (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 364 {
 365     int i;
 366     scrmap_t map;
 367     scrmap_t revmap;
 368 
 369     if (mc_global.tty.console_flag == '\0')
 370         return;
 371 
 372     // screen_info.size is already set in console_init()
 373     if (ioctl (FD_OUT, CONS_GETINFO, &screen_info) == -1)
 374     {
 375         console_shutdown ();
 376         return;
 377     }
 378 
 379     // handle console resize
 380     if (screen_info.mv_csz != screen_shot.xsize || screen_info.mv_rsz != screen_shot.ysize)
 381     {
 382         console_shutdown ();
 383         console_init ();
 384     }
 385 
 386     if (ioctl (FD_OUT, CONS_SCRSHOT, &screen_shot) == -1)
 387     {
 388         console_shutdown ();
 389         return;
 390     }
 391 
 392     if (ioctl (FD_OUT, GIO_SCRNMAP, &map) == -1)
 393     {
 394         console_shutdown ();
 395         return;
 396     }
 397 
 398     for (i = 0; i < 256; i++)
 399     {
 400         char *p = memchr (map.scrmap, i, 256);
 401         revmap.scrmap[i] = p ? p - map.scrmap : i;
 402     }
 403 
 404     for (i = 0; i < screen_shot.xsize * screen_shot.ysize; i++)
 405     {
 406         screen_shot.buf[i] = (screen_shot.buf[i] & 0xff00)
 407             | (unsigned char) revmap.scrmap[screen_shot.buf[i] & 0xff];
 408     }
 409 }
 410 
 411 /* --------------------------------------------------------------------------------------------- */
 412 
 413 static void
 414 show_console_contents_freebsd (int starty, unsigned char begin_line, unsigned char end_line)
     /* [previous][next][first][last][top][bottom][index][help]  */
 415 {
 416     int col, line;
 417     char c;
 418 
 419     if (mc_global.tty.console_flag == '\0')
 420         return;
 421 
 422     for (line = begin_line; line <= end_line; line++)
 423     {
 424         tty_gotoyx (starty + line - begin_line, 0);
 425         for (col = 0; col < MIN (COLS, screen_info.mv_csz); col++)
 426         {
 427             c = screen_shot.buf[line * screen_info.mv_csz + col] & 0xFF;
 428             tty_print_char (c);
 429         }
 430     }
 431 }
 432 
 433 /* --------------------------------------------------------------------------------------------- */
 434 
 435 static void
 436 handle_console_freebsd (console_action_t action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 437 {
 438     switch (action)
 439     {
 440     case CONSOLE_INIT:
 441         console_init ();
 442         break;
 443 
 444     case CONSOLE_DONE:
 445         console_shutdown ();
 446         break;
 447 
 448     case CONSOLE_SAVE:
 449         console_save ();
 450         break;
 451 
 452     case CONSOLE_RESTORE:
 453         console_restore ();
 454         break;
 455     default:
 456         break;
 457     }
 458 }
 459 #endif
 460 
 461 /* --------------------------------------------------------------------------------------------- */
 462 /*** public functions ****************************************************************************/
 463 /* --------------------------------------------------------------------------------------------- */
 464 
 465 void
 466 show_console_contents (int starty, unsigned char begin_line, unsigned char end_line)
     /* [previous][next][first][last][top][bottom][index][help]  */
 467 {
 468     tty_set_normal_attrs ();
 469 
 470     if (look_for_rxvt_extensions ())
 471     {
 472         show_rxvt_contents (starty, begin_line, end_line);
 473         return;
 474     }
 475 #ifdef __linux__
 476     show_console_contents_linux (starty, begin_line, end_line);
 477 #elif defined(__FreeBSD__)
 478     show_console_contents_freebsd (starty, begin_line, end_line);
 479 #else
 480     mc_global.tty.console_flag = '\0';
 481 #endif
 482 }
 483 
 484 /* --------------------------------------------------------------------------------------------- */
 485 
 486 void
 487 handle_console (console_action_t action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 488 {
 489     (void) action;
 490 
 491     if (look_for_rxvt_extensions ())
 492         return;
 493 
 494 #ifdef __linux__
 495     handle_console_linux (action);
 496 #elif defined(__FreeBSD__)
 497     handle_console_freebsd (action);
 498 #endif
 499 }
 500 
 501 /* --------------------------------------------------------------------------------------------- */

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