fixed compiler hints: -Wwrite-strings option helped a lot
[midnight-commander.git] / src / cons.saver.c
blobcda7219a87f3d372d2d48a65262763a87c961751
1 /* This program should be setuid vcsa and /dev/vcsa* should be
2 owned by the same user too.
3 Partly rewritten by Jakub Jelinek <jakub@redhat.com>. */
5 /* General purpose Linux console screen save/restore server
6 Copyright (C) 1994, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
7 2006, 2007 Free Software Foundation, Inc.
8 Original idea from Unix Interactive Tools version 3.2b (tty.c)
9 This code requires root privileges.
10 You may want to make the cons.saver setuid root.
11 The code should be safe even if it is setuid but who knows?
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
27 /* This code does _not_ need to be setuid root. However, it needs
28 read/write access to /dev/vcsa* (which is priviledged
29 operation). You should create user vcsa, make cons.saver setuid
30 user vcsa, and make all vcsa's owned by user vcsa.
32 Seeing other peoples consoles is bad thing, but believe me, full
33 root is even worse. */
35 /** \file cons.saver.c
36 * \brief Source: general purpose Linux console screen save/restore server
38 * This code does _not_ need to be setuid root. However, it needs
39 * read/write access to /dev/vcsa* (which is priviledged
40 * operation). You should create user vcsa, make cons.saver setuid
41 * user vcsa, and make all vcsa's owned by user vcsa.
42 * Seeing other peoples consoles is bad thing, but believe me, full
43 * root is even worse.
46 #include <config.h>
48 #ifndef _GNU_SOURCE
49 #define _GNU_SOURCE
50 #endif
52 #include <stdlib.h>
53 #include <stdio.h>
54 #include <string.h>
56 #include <sys/types.h>
57 #include <sys/stat.h>
58 #include <sys/ioctl.h>
59 #include <fcntl.h>
60 #ifdef HAVE_TERMIOS_H
61 #include <termios.h>
62 #endif
63 #include <unistd.h>
65 #define LINUX_CONS_SAVER_C
66 #include "cons.saver.h"
68 static void
69 send_contents (char *buffer, unsigned int columns, unsigned int rows)
71 unsigned char begin_line = 0, end_line = 0;
72 unsigned int lastline, index, x;
73 unsigned char message, outbuf[1024], *p;
74 unsigned short bytes;
76 index = 2 * rows * columns;
77 for (lastline = rows; lastline > 0; lastline--)
78 for (x = 0; x < columns; x++)
80 index -= 2;
81 if (buffer [index] != ' ')
82 goto out;
84 out:
86 message = CONSOLE_CONTENTS;
87 write (1, &message, 1);
89 read (0, &begin_line, 1);
90 read (0, &end_line, 1);
91 if (begin_line > lastline)
92 begin_line = lastline;
93 if (end_line > lastline)
94 end_line = lastline;
96 index = (end_line - begin_line) * columns;
97 bytes = index;
98 if (index != bytes)
99 bytes = 0;
100 write (1, &bytes, 2);
101 if (! bytes)
102 return;
104 p = outbuf;
105 for (index = 2 * begin_line * columns;
106 index < 2 * end_line * columns;
107 index += 2)
109 *p++ = buffer [index];
110 if (p == outbuf + sizeof (outbuf))
112 write (1, outbuf, sizeof (outbuf));
113 p = outbuf;
117 if (p != outbuf)
118 write (1, outbuf, p - outbuf);
121 static void __attribute__ ((noreturn))
122 die (void)
124 unsigned char zero = 0;
125 write (1, &zero, 1);
126 exit (3);
130 main (int argc, char **argv)
132 unsigned char action = 0, console_flag = 3;
133 int console_fd, vcsa_fd, console_minor, buffer_size;
134 struct stat st;
135 uid_t uid, euid;
136 char *buffer, *tty_name, console_name [16], vcsa_name [16];
137 const char *p, *q;
138 struct winsize winsz;
140 close (2);
142 if (argc != 2)
143 die ();
145 tty_name = argv [1];
146 if (strnlen (tty_name, 15) == 15
147 || strncmp (tty_name, "/dev/", 5))
148 die ();
150 setsid ();
151 uid = getuid ();
152 euid = geteuid ();
154 if (seteuid (uid) < 0)
155 die ();
156 console_fd = open (tty_name, O_RDONLY);
157 if (console_fd < 0)
158 die ();
159 if (fstat (console_fd, &st) < 0 || ! S_ISCHR (st.st_mode))
160 die ();
161 if ((st.st_rdev & 0xff00) != 0x0400)
162 die ();
163 console_minor = (int) (st.st_rdev & 0x00ff);
164 if (console_minor < 1 || console_minor > 63)
165 die ();
166 if (st.st_uid != uid)
167 die ();
169 switch (tty_name [5])
171 /* devfs */
172 case 'v': p = "/dev/vc/%d"; q = "/dev/vcc/a%d"; break;
173 /* /dev/ttyN */
174 case 't': p = "/dev/tty%d"; q = "/dev/vcsa%d"; break;
175 default: die (); break;
178 snprintf (console_name, sizeof (console_name), p, console_minor);
179 if (strncmp (console_name, tty_name, sizeof (console_name)) != 0)
180 die ();
182 if (seteuid (euid) < 0)
183 die ();
185 snprintf (vcsa_name, sizeof (vcsa_name), q, console_minor);
186 vcsa_fd = open (vcsa_name, O_RDWR);
187 if (vcsa_fd < 0)
188 die ();
189 if (fstat (vcsa_fd, &st) < 0 || ! S_ISCHR (st.st_mode))
190 die ();
192 if (seteuid (uid) < 0)
193 die ();
195 winsz.ws_col = winsz.ws_row = 0;
196 if (ioctl (console_fd, TIOCGWINSZ, &winsz) < 0
197 || winsz.ws_col <= 0 || winsz.ws_row <= 0
198 || winsz.ws_col >= 256 || winsz.ws_row >= 256)
199 die ();
201 buffer_size = 4 + 2 * winsz.ws_col * winsz.ws_row;
202 buffer = calloc (buffer_size, 1);
203 if (buffer == NULL)
204 die ();
206 write (1, &console_flag, 1);
208 while (console_flag && read (0, &action, 1) == 1)
210 switch (action)
212 case CONSOLE_DONE:
213 console_flag = 0;
214 continue;
215 case CONSOLE_SAVE:
216 if (seteuid (euid) < 0
217 || lseek (vcsa_fd, 0, 0) != 0
218 || fstat (console_fd, &st) < 0 || st.st_uid != uid
219 || read (vcsa_fd, buffer, buffer_size) != buffer_size
220 || fstat (console_fd, &st) < 0 || st.st_uid != uid)
221 memset (buffer, 0, buffer_size);
222 if (seteuid (uid) < 0)
223 die ();
224 break;
225 case CONSOLE_RESTORE:
226 if (seteuid (euid) >= 0
227 && lseek (vcsa_fd, 0, 0) == 0
228 && fstat (console_fd, &st) >= 0 && st.st_uid == uid)
229 write (vcsa_fd, buffer, buffer_size);
230 if (seteuid (uid) < 0)
231 die ();
232 break;
233 case CONSOLE_CONTENTS:
234 send_contents (buffer + 4, winsz.ws_col, winsz.ws_row);
235 break;
238 write (1, &console_flag, 1);
241 exit (0);