1 #if defined(linux) || defined(__linux__)
3 /* General purpose Linux console screen save/restore server
4 Copyright (C) 1994 Janne Kukonlehto <jtklehto@stekt.oulu.fi>
5 Original idea from Unix Interactive Tools version 3.2b (tty.c)
6 This code requires root privileges.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 /* cons.saver is run by MC to save and restore screen contents on Linux
23 virtual console. This is done by using file /dev/vcsaN or /dev/vcc/aN,
24 where N is the number of the virtual console.
25 In a properly set up system, /dev/vcsaN should become accessible
26 by the user when the user logs in on the corresponding console
27 /dev/ttyN or /dev/vc/N. In this case, cons.saver doesn't need to be
28 suid root. However, if /dev/vcsaN is not accessible by the user,
29 cons.saver can be made setuid root - this program is designed to be
30 safe even in this case. */
33 #include <sys/types.h>
35 #include <sys/ioctl.h>
44 #include <ctype.h> /* For isdigit() */
47 #define LINUX_CONS_SAVER_C
48 #include "cons.saver.h"
53 static int console_minor
= 0;
54 static char *buffer
= NULL
;
55 static int buffer_size
= 0;
56 static int columns
, rows
;
61 * Get window size for the given terminal.
62 * Return 0 for success, -1 otherwise.
64 static int tty_getsize (int console_fd
)
68 winsz
.ws_col
= winsz
.ws_row
= 0;
69 ioctl (console_fd
, TIOCGWINSZ
, &winsz
);
70 if (winsz
.ws_col
&& winsz
.ws_row
) {
71 columns
= winsz
.ws_col
;
80 * Do various checks to make sure that the supplied filename is
81 * a suitable tty device. If check_console is set, check that we
82 * are dealing with a Linux virtual console.
83 * Return 0 for success, -1 otherwise.
85 static int check_file (char *filename
, int check_console
)
90 /* Avoiding race conditions: use of fstat makes sure that
91 both 'open' and 'stat' operate on the same file */
93 fd
= open (filename
, O_RDWR
);
98 if (fstat (fd
, &stat_buf
) == -1)
101 /* Must be character device */
102 if (!S_ISCHR (stat_buf
.st_mode
)){
107 /* Major number must be 4 */
108 if ((stat_buf
.st_rdev
& 0xff00) != 0x0400){
112 /* Minor number must be between 1 and 63 */
113 console_minor
= (int) (stat_buf
.st_rdev
& 0x00ff);
114 if (console_minor
< 1 || console_minor
> 63){
118 /* Must be owned by the user */
119 if (stat_buf
.st_uid
!= getuid ()){
123 /* Everything seems to be okay */
132 * Check if the supplied filename is a Linux virtual console.
133 * Return 0 if successful, -1 otherwise.
134 * Since the tty name is supplied by the user and cons.saver can be
135 * a setuid program, many checks have to be done to prevent possible
136 * security compromise.
138 static int detect_console (char *tty_name
)
140 char console_name
[16];
141 static char vcs_name
[16];
144 /* Must be console */
145 console_fd
= check_file (tty_name
, 1);
146 if (console_fd
== -1)
149 if (tty_getsize (console_fd
) == -1) {
157 * Only allow /dev/ttyMINOR and /dev/vc/MINOR where MINOR is the minor
158 * device number of the console, set in check_file()
163 snprintf (console_name
, sizeof (console_name
), "/dev/vc/%d",
165 if (strncmp (console_name
, tty_name
, sizeof (console_name
)) != 0)
169 snprintf (console_name
, sizeof (console_name
), "/dev/tty%d",
171 if (strncmp (console_name
, tty_name
, sizeof (console_name
)) != 0)
178 snprintf (vcs_name
, sizeof (vcs_name
), "/dev/vcsa%d", console_minor
);
179 vcs_fd
= check_file (vcs_name
, 0);
183 snprintf (vcs_name
, sizeof (vcs_name
), "/dev/vcc/a%d", console_minor
);
184 vcs_fd
= check_file (vcs_name
, 0);
193 static void save_console (void)
195 lseek (vcs_fd
, 0, SEEK_SET
);
196 read (vcs_fd
, buffer
, buffer_size
);
199 static void restore_console (void)
201 lseek (vcs_fd
, 0, SEEK_SET
);
202 write (vcs_fd
, buffer
, buffer_size
);
205 static void send_contents (void)
207 unsigned char begin_line
=0, end_line
=0;
209 unsigned char message
;
210 unsigned short bytes
;
211 const int bytes_per_char
= 2;
213 /* Inform the invoker that we can handle this command */
214 message
= CONSOLE_CONTENTS
;
215 write (cmd_output
, &message
, 1);
217 /* Read the range of lines wanted */
218 read (cmd_input
, &begin_line
, 1);
219 read (cmd_input
, &end_line
, 1);
220 if (begin_line
> rows
)
225 /* Tell the invoker how many bytes it will be */
226 bytes
= (end_line
- begin_line
) * columns
;
227 write (cmd_output
, &bytes
, 2);
229 /* Send the contents */
230 for (index
= (2 + begin_line
* columns
) * bytes_per_char
;
231 index
< (2 + end_line
* columns
) * bytes_per_char
;
232 index
+= bytes_per_char
)
233 write (cmd_output
, buffer
+ index
, 1);
238 int main (int argc
, char **argv
)
240 unsigned char action
= 0;
243 /* 0 - not a console, 3 - supported Linux console */
244 signed char console_flag
= 0;
247 * Make sure stderr points to a valid place
250 stderr_fd
= open ("/dev/tty", O_RDWR
);
252 /* This may well happen if program is running non-root */
254 stderr_fd
= open ("/dev/null", O_RDWR
);
260 while (dup2 (stderr_fd
, 2) == -1 && errno
== EINTR
)
264 /* Wrong number of arguments */
266 write (cmd_output
, &console_flag
, 1);
270 /* Lose the control terminal */
273 /* Check that the argument is a Linux console */
274 if (detect_console (argv
[1]) == -1) {
275 /* Not a console -> no need for privileges */
279 /* Allocate buffer for screen image */
280 buffer_size
= 4 + 2 * columns
* rows
;
281 buffer
= (char*) malloc (buffer_size
);
284 /* Inform the invoker about the result of the tests */
285 write (cmd_output
, &console_flag
, 1);
287 /* Read commands from the invoker */
288 while (console_flag
&& read (cmd_input
, &action
, 1)){
293 continue; /* Break while loop instead of switch clause */
297 case CONSOLE_RESTORE
:
300 case CONSOLE_CONTENTS
:
303 } /* switch (action) */
305 /* Inform the invoker that command has been handled */
306 write (cmd_output
, &console_flag
, 1);
307 } /* while (read ...) */
316 #error "The Linux console screen saver works only on Linux"
318 #endif /* #ifdef linux */