/* Client interface for General purpose Linux console save/restore server Copyright (C) 1994-2024 Free Software Foundation, Inc. This file is part of the Midnight Commander. The Midnight Commander is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The Midnight Commander is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** \file cons.handler.c * \brief Source: client %interface for General purpose Linux console save/restore server */ #include #include #include #include #include #include #ifdef __FreeBSD__ #include #ifdef HAVE_SYS_IOCTL_H #include #endif #endif #include "lib/global.h" #include "lib/unixcompat.h" #include "lib/tty/tty.h" #include "lib/tty/color.h" /* tty_set_normal_attrs */ #include "lib/tty/win.h" #include "lib/util.h" /* mc_build_filename() */ #include "consaver/cons.saver.h" /*** global variables ****************************************************************************/ #ifdef __linux__ int cons_saver_pid = 1; #endif /* __linux__ */ /*** file scope macro definitions ****************************************************************/ #if defined(__FreeBSD__) #define FD_OUT 1 #define cursor_to(x, y) \ do \ { \ printf("\x1B[%d;%df", (y) + 1, (x) + 1); \ fflush(stdout); \ } while (0) #endif /* __linux__ */ /*** file scope type declarations ****************************************************************/ /*** forward declarations (file scope functions) *************************************************/ /*** file scope variables ************************************************************************/ #ifdef __linux__ /* The cons saver can't have a pid of 1, used to prevent bunches of * #ifdef linux */ static int pipefd1[2] = { -1, -1 }; static int pipefd2[2] = { -1, -1 }; #elif defined(__FreeBSD__) static struct scrshot screen_shot; static struct vid_info screen_info; #endif /* __linux__ */ /* --------------------------------------------------------------------------------------------- */ /*** file scope functions ************************************************************************/ /* --------------------------------------------------------------------------------------------- */ #ifdef __linux__ static void show_console_contents_linux (int starty, unsigned char begin_line, unsigned char end_line) { unsigned char message = 0; unsigned short bytes = 0; int i; ssize_t ret; /* Is tty console? */ if (mc_global.tty.console_flag == '\0') return; /* Paranoid: Is the cons.saver still running? */ if (cons_saver_pid < 1 || kill (cons_saver_pid, SIGCONT)) { cons_saver_pid = 0; mc_global.tty.console_flag = '\0'; return; } /* Send command to the console handler */ message = CONSOLE_CONTENTS; ret = write (pipefd1[1], &message, 1); /* Check for outdated cons.saver */ ret = read (pipefd2[0], &message, 1); if (message != CONSOLE_CONTENTS) return; /* Send the range of lines that we want */ ret = write (pipefd1[1], &begin_line, 1); ret = write (pipefd1[1], &end_line, 1); /* Read the corresponding number of bytes */ ret = read (pipefd2[0], &bytes, 2); /* Read the bytes and output them */ for (i = 0; i < bytes; i++) { if ((i % COLS) == 0) tty_gotoyx (starty + (i / COLS), 0); ret = read (pipefd2[0], &message, 1); tty_print_char (message); } /* Read the value of the mc_global.tty.console_flag */ ret = read (pipefd2[0], &message, 1); (void) ret; } /* --------------------------------------------------------------------------------------------- */ static void handle_console_linux (console_action_t action) { int status; switch (action) { case CONSOLE_INIT: /* Close old pipe ends in case it is the 2nd time we run cons.saver */ status = close (pipefd1[1]); status = close (pipefd2[0]); /* Create two pipes for communication */ if (!((pipe (pipefd1) == 0) && ((pipe (pipefd2)) == 0))) { mc_global.tty.console_flag = '\0'; break; } /* Get the console saver running */ cons_saver_pid = fork (); if (cons_saver_pid < 0) { /* Cannot fork */ /* Delete pipes */ status = close (pipefd1[1]); status = close (pipefd1[0]); status = close (pipefd2[1]); status = close (pipefd2[0]); mc_global.tty.console_flag = '\0'; } else if (cons_saver_pid > 0) { /* Parent */ /* Close the extra pipe ends */ status = close (pipefd1[0]); status = close (pipefd2[1]); /* Was the child successful? */ status = read (pipefd2[0], &mc_global.tty.console_flag, 1); if (mc_global.tty.console_flag == '\0') { pid_t ret; status = close (pipefd1[1]); status = close (pipefd2[0]); ret = waitpid (cons_saver_pid, &status, 0); (void) ret; } } else { /* Child */ char *tty_name; /* Close the extra pipe ends */ status = close (pipefd1[1]); status = close (pipefd2[0]); tty_name = ttyname (0); /* Bind the pipe 0 to the standard input */ do { gboolean ok; if (dup2 (pipefd1[0], STDIN_FILENO) == -1) break; status = close (pipefd1[0]); /* Bind the pipe 1 to the standard output */ if (dup2 (pipefd2[1], STDOUT_FILENO) == -1) break; status = close (pipefd2[1]); /* Bind standard error to /dev/null */ status = open ("/dev/null", O_WRONLY); if (status == -1) break; ok = dup2 (status, STDERR_FILENO) != -1; status = close (status); if (!ok) break; if (tty_name != NULL) { char *mc_conssaver; /* Exec the console save/restore handler */ mc_conssaver = mc_build_filename (SAVERDIR, "cons.saver", (char *) NULL); execl (mc_conssaver, "cons.saver", tty_name, (char *) NULL); } /* Console is not a tty or execl() failed */ } while (0); mc_global.tty.console_flag = '\0'; status = write (1, &mc_global.tty.console_flag, 1); my_exit (3); } /* if (cons_saver_pid ...) */ break; case CONSOLE_DONE: case CONSOLE_SAVE: case CONSOLE_RESTORE: /* Is tty console? */ if (mc_global.tty.console_flag == '\0') return; /* Paranoid: Is the cons.saver still running? */ if (cons_saver_pid < 1 || kill (cons_saver_pid, SIGCONT)) { cons_saver_pid = 0; mc_global.tty.console_flag = '\0'; return; } /* Send command to the console handler */ { /* Convert enum (i.e. int) to char to write the correct value * (the least byte) regardless of machine endianness. */ char act = (char) action; status = write (pipefd1[1], &act, 1); } if (action != CONSOLE_DONE) { /* Wait the console handler to do its job */ status = read (pipefd2[0], &mc_global.tty.console_flag, 1); } if (action == CONSOLE_DONE || mc_global.tty.console_flag == '\0') { /* We are done -> Let's clean up */ pid_t ret; close (pipefd1[1]); close (pipefd2[0]); ret = waitpid (cons_saver_pid, &status, 0); (void) ret; mc_global.tty.console_flag = '\0'; } break; default: break; } } #elif defined(__FreeBSD__) /* --------------------------------------------------------------------------------------------- */ /** * FreeBSD support copyright (C) 2003 Alexander Serkov . * Support for screenmaps by Max Khon */ static void console_init (void) { if (mc_global.tty.console_flag != '\0') return; screen_info.size = sizeof (screen_info); if (ioctl (FD_OUT, CONS_GETINFO, &screen_info) == -1) return; memset (&screen_shot, 0, sizeof (screen_shot)); screen_shot.xsize = screen_info.mv_csz; screen_shot.ysize = screen_info.mv_rsz; screen_shot.buf = g_try_malloc (screen_info.mv_csz * screen_info.mv_rsz * 2); if (screen_shot.buf != NULL) mc_global.tty.console_flag = '\001'; } /* --------------------------------------------------------------------------------------------- */ static void set_attr (unsigned attr) { /* * Convert color indices returned by SCRSHOT (red=4, green=2, blue=1) * to indices for ANSI sequences (red=1, green=2, blue=4). */ static const int color_map[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; int bc, tc; tc = attr & 0xF; bc = (attr >> 4) & 0xF; printf ("\x1B[%d;%d;3%d;4%dm", (bc & 8) ? 5 : 25, (tc & 8) ? 1 : 22, color_map[tc & 7], color_map[bc & 7]); } /* --------------------------------------------------------------------------------------------- */ static void console_restore (void) { int i, last; if (mc_global.tty.console_flag == '\0') return; cursor_to (0, 0); /* restoring all content up to cursor position */ last = screen_info.mv_row * screen_info.mv_csz + screen_info.mv_col; for (i = 0; i < last; ++i) { set_attr ((screen_shot.buf[i] >> 8) & 0xFF); putc (screen_shot.buf[i] & 0xFF, stdout); } /* restoring cursor color */ set_attr ((screen_shot.buf[last] >> 8) & 0xFF); fflush (stdout); } /* --------------------------------------------------------------------------------------------- */ static void console_shutdown (void) { if (mc_global.tty.console_flag == '\0') return; g_free (screen_shot.buf); mc_global.tty.console_flag = '\0'; } /* --------------------------------------------------------------------------------------------- */ static void console_save (void) { int i; scrmap_t map; scrmap_t revmap; if (mc_global.tty.console_flag == '\0') return; /* screen_info.size is already set in console_init() */ if (ioctl (FD_OUT, CONS_GETINFO, &screen_info) == -1) { console_shutdown (); return; } /* handle console resize */ if (screen_info.mv_csz != screen_shot.xsize || screen_info.mv_rsz != screen_shot.ysize) { console_shutdown (); console_init (); } if (ioctl (FD_OUT, CONS_SCRSHOT, &screen_shot) == -1) { console_shutdown (); return; } if (ioctl (FD_OUT, GIO_SCRNMAP, &map) == -1) { console_shutdown (); return; } for (i = 0; i < 256; i++) { char *p = memchr (map.scrmap, i, 256); revmap.scrmap[i] = p ? p - map.scrmap : i; } for (i = 0; i < screen_shot.xsize * screen_shot.ysize; i++) { /* *INDENT-OFF* */ screen_shot.buf[i] = (screen_shot.buf[i] & 0xff00) | (unsigned char) revmap.scrmap[screen_shot.buf[i] & 0xff]; /* *INDENT-ON* */ } } /* --------------------------------------------------------------------------------------------- */ static void show_console_contents_freebsd (int starty, unsigned char begin_line, unsigned char end_line) { int col, line; char c; if (mc_global.tty.console_flag == '\0') return; for (line = begin_line; line <= end_line; line++) { tty_gotoyx (starty + line - begin_line, 0); for (col = 0; col < MIN (COLS, screen_info.mv_csz); col++) { c = screen_shot.buf[line * screen_info.mv_csz + col] & 0xFF; tty_print_char (c); } } } /* --------------------------------------------------------------------------------------------- */ static void handle_console_freebsd (console_action_t action) { switch (action) { case CONSOLE_INIT: console_init (); break; case CONSOLE_DONE: console_shutdown (); break; case CONSOLE_SAVE: console_save (); break; case CONSOLE_RESTORE: console_restore (); break; default: break; } } #endif /* __FreeBSD__ */ /* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ void show_console_contents (int starty, unsigned char begin_line, unsigned char end_line) { tty_set_normal_attrs (); if (look_for_rxvt_extensions ()) { show_rxvt_contents (starty, begin_line, end_line); return; } #ifdef __linux__ show_console_contents_linux (starty, begin_line, end_line); #elif defined (__FreeBSD__) show_console_contents_freebsd (starty, begin_line, end_line); #else mc_global.tty.console_flag = '\0'; #endif } /* --------------------------------------------------------------------------------------------- */ void handle_console (console_action_t action) { (void) action; if (look_for_rxvt_extensions ()) return; #ifdef __linux__ handle_console_linux (action); #elif defined (__FreeBSD__) handle_console_freebsd (action); #endif } /* --------------------------------------------------------------------------------------------- */