/*
Client interface for General purpose Linux console save/restore server
Copyright (C) 1994-2022
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 ****************************************************************/
/*** 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
}
/* --------------------------------------------------------------------------------------------- */