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.
7 You may want to make the cons.saver setuid root.
8 The code should be safe even if it is setuid but who knows?
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
25 #include <sys/types.h>
27 #include <sys/ioctl.h>
35 #include <ctype.h> /* For isdigit() */
36 typedef struct WINDOW WINDOW
;
37 #include "cons.saver.h"
42 /* Meaning of console_flag:
45 1 == is a console, Linux < 1.1.67 (black & white)
46 2 == is a console, Linux >= 1.1.67 (color)
47 3 == is a console, Linux >= 1.1.92 (color, use /dev/vcsa$num
49 static signed char console_flag
= -1;
51 Meaning of console_fd:
55 static int console_fd
= -1;
56 static char *tty_name
;
58 static char *buffer
= NULL
;
59 static int buffer_size
= 0;
60 static int columns
, rows
;
61 static char vcs_name
[40];
64 static void dwrite (int fd
, char *buffer
)
66 write (fd
, buffer
, strlen (buffer
));
69 static void tty_getsize ()
73 winsz
.ws_col
= winsz
.ws_row
= 0;
74 ioctl (console_fd
, TIOCGWINSZ
, &winsz
);
75 if (winsz
.ws_col
&& winsz
.ws_row
){
76 columns
= winsz
.ws_col
;
79 /* Never happens (I think) */
80 dwrite (2, "TIOCGWINSZ failed\n");
87 inline void tty_cursormove(int y
, int x
)
91 /* Standard ANSI escape sequence for cursor positioning */
92 sprintf (buffer
,"\33[%d;%dH", y
+ 1, x
+ 1);
93 dwrite (console_fd
, buffer
);
96 int check_file (char *filename
, int check_console
, char **msg
)
101 /* Avoiding race conditions: use of fstat makes sure that
102 both 'open' and 'stat' operate on the same file */
106 fd
= open (filename
, O_RDWR
);
110 if (fstat (fd
, &stat_buf
) == -1)
113 /* Must be character device */
114 if (!S_ISCHR (stat_buf
.st_mode
)){
115 *msg
= "Not a character device";
120 fprintf (stderr
, "Device: %x\n", stat_buf
.st_rdev
);
123 /* Second time: must be console */
124 if ((stat_buf
.st_rdev
& 0xff00) != 0x0400){
125 *msg
= "Not a console";
129 if ((stat_buf
.st_rdev
& 0x00ff) > 63){
130 *msg
= "Minor device number too big";
134 /* Must be owned by the user */
135 if (stat_buf
.st_uid
!= getuid ()){
136 *msg
= "Not a owner";
141 /* Everything seems to be okay */
146 /* Because the name of the tty is supplied by the user and this
147 can be a setuid program a lot of checks has to done to avoid
148 creating a security hole */
149 char *detect_console (void)
154 /* Must be console */
155 /* Handle the case for /dev/tty?? */
156 if (tty_name
[len
-5] == 't')
161 /* General: /dev/ttyn */
162 if (tty_name
[xlen
- 5] != '/' ||
163 tty_name
[xlen
- 4] != 't' ||
164 tty_name
[xlen
- 3] != 't' ||
165 tty_name
[xlen
- 2] != 'y' ||
166 !isdigit(tty_name
[xlen
- 1]) ||
167 !isdigit(tty_name
[len
- 1]))
168 return "Doesn't look like console";
170 sprintf (vcs_name
, "/dev/vcsa%s", tty_name
+ xlen
- 1);
171 vcs_fd
= check_file (vcs_name
, 0, &msg
);
172 console_fd
= check_file (tty_name
, 1, &msg
);
175 fprintf (stderr
, "vcs_fd = %d console_fd = %d\n", vcs_fd
, console_fd
);
182 if (console_fd
== -1)
188 void save_console (void)
194 buffer
[1] = tty_name
[len
-1] - '0';
195 if (console_flag
>= 2){
196 /* Linux >= 1.1.67 */
197 /* Get screen contents and cursor position */
199 if (console_flag
== 2){
200 if ((i
= ioctl (console_fd
, TIOCLINUX
, buffer
)) == -1){
201 /* Oops, this is not Linux 1.1.67 */
205 lseek (vcs_fd
, 0, 0);
206 read (vcs_fd
, buffer
, buffer_size
);
209 if (console_flag
== 1){
213 /* Get screen contents */
215 if (ioctl(console_fd
, TIOCLINUX
, buffer
) == -1){
216 buffer
[0] = buffer
[1] = 0;
218 /* Linux bug: bad ioctl on console 8 */
219 if (ioctl(console_fd
, TIOCLINUX
, buffer
) == -1){
220 /* Oops, this is not a console after all */
225 /* Select the beginning of the bottommost empty line
226 to be the cursor position */
227 index
= 2 + rows
* columns
;
228 for (y
= rows
- 1; y
>= 0; y
--)
229 for (x
= columns
- 1; x
>= 0; x
--)
230 if (buffer
[--index
] != ' ')
231 goto non_space_found
;
235 /*tty_cursormove(y + 1, 0);*/
239 void restore_console (void)
243 if (console_flag
== 2){
244 /* Linux >= 1.1.67 */
245 /* Restore screen contents and cursor position */
247 buffer
[1] = tty_name
[len
-1] - '0';
248 ioctl (console_fd
, TIOCLINUX
, buffer
);
250 if (console_flag
== 3){
251 lseek (vcs_fd
, 0, 0);
252 write (vcs_fd
, buffer
, buffer_size
);
254 if (console_flag
== 1){
256 write(console_fd
, "\033[H\033[2J", 7);
257 /* Output saved screen contents */
258 write(console_fd
, buffer
+ 2, rows
* columns
);
259 /* Move the cursor to the previously selected position */
260 tty_cursormove(buffer
[0], buffer
[1]);
264 void send_contents ()
266 unsigned char begin_line
=0, end_line
=0;
269 unsigned char message
;
270 unsigned short bytes
;
273 bytes_per_char
= console_flag
== 1 ? 1 : 2;
275 /* Calculate the number of used lines */
276 if (console_flag
== 2 || console_flag
== 1 || console_flag
== 3){
277 index
= (2 + rows
* columns
) * bytes_per_char
;
278 for (y
= rows
- 1; y
>= 0; y
--)
279 for (x
= columns
- 1; x
>= 0; x
--){
280 index
-= bytes_per_char
;
281 if (buffer
[index
] != ' ')
282 goto non_space_found
;
289 /* Inform the invoker that we can handle this command */
290 message
= CONSOLE_CONTENTS
;
291 write (cmd_output
, &message
, 1);
293 /* Read the range of lines wanted */
294 read (cmd_input
, &begin_line
, 1);
295 read (cmd_input
, &end_line
, 1);
296 if (begin_line
> lastline
)
297 begin_line
= lastline
;
298 if (end_line
> lastline
)
301 /* Tell the invoker how many bytes it will be */
302 bytes
= (end_line
- begin_line
) * columns
;
303 write (cmd_output
, &bytes
, 2);
305 /* Send the contents */
306 for (index
= (2 + begin_line
* columns
) * bytes_per_char
;
307 index
< (2 + end_line
* columns
) * bytes_per_char
;
308 index
+= bytes_per_char
)
309 write (cmd_output
, buffer
+ index
, 1);
314 int main (int argc
, char **argv
)
317 unsigned char action
= 0;
320 /* Wrong number of arguments */
322 dwrite (2, "Usage: cons.saver <ttyname>\n");
324 write (cmd_output
, &console_flag
, 1);
328 /* Lose the control terminal */
331 /* Check that the argument is a legal console */
333 len
= strlen(tty_name
);
334 error
= detect_console ();
337 /* Not a console -> no need for privileges */
344 /* Console was detected */
345 if (console_flag
!= 3)
346 console_flag
= 2; /* Default to Linux >= 1.1.67 */
347 /* Allocate buffer for screen image */
349 buffer_size
= 4 + 2 * columns
* rows
;
350 buffer
= (char*) malloc (buffer_size
);
353 /* If using /dev/vcs*, we don't need anymore the console fd */
354 if (console_flag
== 3)
357 /* Inform the invoker about the result of the tests */
358 write (cmd_output
, &console_flag
, 1);
360 /* Read commands from the invoker */
361 while (console_flag
&& read (cmd_input
, &action
, 1)){
366 continue; /* Break while loop instead of switch clause */
370 case CONSOLE_RESTORE
:
373 case CONSOLE_CONTENTS
:
376 } /* switch (action) */
378 /* Inform the invoker that command is handled */
379 write (cmd_output
, &console_flag
, 1);
380 } /* while (read ...) */
389 #error The Linux console screen saver works only on Linux.
391 #endif /* #ifdef linux */